| // SPDX-License-Identifier: GPL-2.0+ | |
| /* | |
| * Maxim MAX77705 PMIC core driver | |
| * | |
| * Copyright (C) 2025 Dzmitry Sankouski <dsankouski@gmail.com> | |
| **/ | |
| #include <linux/i2c.h> | |
| #include <linux/interrupt.h> | |
| #include <linux/mfd/core.h> | |
| #include <linux/mfd/max77705-private.h> | |
| #include <linux/mfd/max77693-common.h> | |
| #include <linux/pm.h> | |
| #include <linux/power/max17042_battery.h> | |
| #include <linux/module.h> | |
| #include <linux/regmap.h> | |
| #include <linux/of.h> | |
| static struct mfd_cell max77705_devs[] = { | |
| MFD_CELL_OF("max77705-rgb", NULL, NULL, 0, 0, "maxim,max77705-rgb"), | |
| MFD_CELL_OF("max77705-charger", NULL, NULL, 0, 0, "maxim,max77705-charger"), | |
| MFD_CELL_OF("max77705-haptic", NULL, NULL, 0, 0, "maxim,max77705-haptic"), | |
| }; | |
| static const struct regmap_range max77705_readable_ranges[] = { | |
| regmap_reg_range(MAX77705_PMIC_REG_PMICID1, MAX77705_PMIC_REG_BSTOUT_MASK), | |
| regmap_reg_range(MAX77705_PMIC_REG_INTSRC, MAX77705_PMIC_REG_RESERVED_29), | |
| regmap_reg_range(MAX77705_PMIC_REG_BOOSTCONTROL1, MAX77705_PMIC_REG_BOOSTCONTROL1), | |
| regmap_reg_range(MAX77705_PMIC_REG_MCONFIG, MAX77705_PMIC_REG_MCONFIG2), | |
| regmap_reg_range(MAX77705_PMIC_REG_FORCE_EN_MASK, MAX77705_PMIC_REG_FORCE_EN_MASK), | |
| regmap_reg_range(MAX77705_PMIC_REG_BOOSTCONTROL1, MAX77705_PMIC_REG_BOOSTCONTROL1), | |
| regmap_reg_range(MAX77705_PMIC_REG_BOOSTCONTROL2, MAX77705_PMIC_REG_BOOSTCONTROL2), | |
| regmap_reg_range(MAX77705_PMIC_REG_SW_RESET, MAX77705_PMIC_REG_USBC_RESET), | |
| }; | |
| static const struct regmap_range max77705_writable_ranges[] = { | |
| regmap_reg_range(MAX77705_PMIC_REG_MAINCTRL1, MAX77705_PMIC_REG_BSTOUT_MASK), | |
| regmap_reg_range(MAX77705_PMIC_REG_INTSRC, MAX77705_PMIC_REG_RESERVED_29), | |
| regmap_reg_range(MAX77705_PMIC_REG_BOOSTCONTROL1, MAX77705_PMIC_REG_BOOSTCONTROL1), | |
| regmap_reg_range(MAX77705_PMIC_REG_MCONFIG, MAX77705_PMIC_REG_MCONFIG2), | |
| regmap_reg_range(MAX77705_PMIC_REG_FORCE_EN_MASK, MAX77705_PMIC_REG_FORCE_EN_MASK), | |
| regmap_reg_range(MAX77705_PMIC_REG_BOOSTCONTROL1, MAX77705_PMIC_REG_BOOSTCONTROL1), | |
| regmap_reg_range(MAX77705_PMIC_REG_BOOSTCONTROL2, MAX77705_PMIC_REG_BOOSTCONTROL2), | |
| regmap_reg_range(MAX77705_PMIC_REG_SW_RESET, MAX77705_PMIC_REG_USBC_RESET), | |
| }; | |
| static const struct regmap_access_table max77705_readable_table = { | |
| .yes_ranges = max77705_readable_ranges, | |
| .n_yes_ranges = ARRAY_SIZE(max77705_readable_ranges), | |
| }; | |
| static const struct regmap_access_table max77705_writable_table = { | |
| .yes_ranges = max77705_writable_ranges, | |
| .n_yes_ranges = ARRAY_SIZE(max77705_writable_ranges), | |
| }; | |
| static const struct regmap_config max77705_regmap_config = { | |
| .reg_bits = 8, | |
| .val_bits = 8, | |
| .rd_table = &max77705_readable_table, | |
| .wr_table = &max77705_writable_table, | |
| .max_register = MAX77705_PMIC_REG_USBC_RESET, | |
| }; | |
| static const struct regmap_irq max77705_irqs[] = { | |
| { .mask = MAX77705_SRC_IRQ_CHG, }, | |
| { .mask = MAX77705_SRC_IRQ_TOP, }, | |
| { .mask = MAX77705_SRC_IRQ_FG, }, | |
| { .mask = MAX77705_SRC_IRQ_USBC, }, | |
| }; | |
| static const struct regmap_irq_chip max77705_irq_chip = { | |
| .name = "max77705", | |
| .status_base = MAX77705_PMIC_REG_INTSRC, | |
| .ack_base = MAX77705_PMIC_REG_INTSRC, | |
| .mask_base = MAX77705_PMIC_REG_INTSRC_MASK, | |
| .num_regs = 1, | |
| .irqs = max77705_irqs, | |
| .num_irqs = ARRAY_SIZE(max77705_irqs), | |
| }; | |
| static int max77705_i2c_probe(struct i2c_client *i2c) | |
| { | |
| struct device *dev = &i2c->dev; | |
| struct max77693_dev *max77705; | |
| struct regmap_irq_chip_data *irq_data; | |
| struct irq_domain *domain; | |
| enum max77705_hw_rev pmic_rev; | |
| unsigned int pmic_rev_value; | |
| int ret; | |
| max77705 = devm_kzalloc(dev, sizeof(*max77705), GFP_KERNEL); | |
| if (!max77705) | |
| return -ENOMEM; | |
| max77705->i2c = i2c; | |
| max77705->type = TYPE_MAX77705; | |
| i2c_set_clientdata(i2c, max77705); | |
| max77705->regmap = devm_regmap_init_i2c(i2c, &max77705_regmap_config); | |
| if (IS_ERR(max77705->regmap)) | |
| return PTR_ERR(max77705->regmap); | |
| ret = regmap_read(max77705->regmap, MAX77705_PMIC_REG_PMICREV, &pmic_rev_value); | |
| if (ret < 0) | |
| return -ENODEV; | |
| pmic_rev = pmic_rev_value & MAX77705_REVISION_MASK; | |
| if (pmic_rev != MAX77705_PASS3) | |
| return dev_err_probe(dev, -ENODEV, "Rev.0x%x is not tested\n", pmic_rev); | |
| /* Active Discharge Enable */ | |
| regmap_update_bits(max77705->regmap, MAX77705_PMIC_REG_MAINCTRL1, 1, 1); | |
| ret = devm_regmap_add_irq_chip(dev, max77705->regmap, | |
| i2c->irq, | |
| IRQF_ONESHOT, 0, | |
| &max77705_irq_chip, | |
| &irq_data); | |
| if (ret) | |
| return dev_err_probe(dev, ret, "Failed to add IRQ chip\n"); | |
| domain = regmap_irq_get_domain(irq_data); | |
| ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, | |
| max77705_devs, ARRAY_SIZE(max77705_devs), | |
| NULL, 0, domain); | |
| if (ret) | |
| return dev_err_probe(dev, ret, "Failed to register child devices\n"); | |
| ret = devm_device_init_wakeup(dev); | |
| if (ret) | |
| return dev_err_probe(dev, ret, "Failed to init wakeup\n"); | |
| return 0; | |
| } | |
| static int max77705_suspend(struct device *dev) | |
| { | |
| struct i2c_client *i2c = to_i2c_client(dev); | |
| disable_irq(i2c->irq); | |
| if (device_may_wakeup(dev)) | |
| enable_irq_wake(i2c->irq); | |
| return 0; | |
| } | |
| static int max77705_resume(struct device *dev) | |
| { | |
| struct i2c_client *i2c = to_i2c_client(dev); | |
| if (device_may_wakeup(dev)) | |
| disable_irq_wake(i2c->irq); | |
| enable_irq(i2c->irq); | |
| return 0; | |
| } | |
| static DEFINE_SIMPLE_DEV_PM_OPS(max77705_pm_ops, max77705_suspend, max77705_resume); | |
| static const struct of_device_id max77705_i2c_of_match[] = { | |
| { .compatible = "maxim,max77705" }, | |
| { } | |
| }; | |
| MODULE_DEVICE_TABLE(of, max77705_i2c_of_match); | |
| static struct i2c_driver max77705_i2c_driver = { | |
| .driver = { | |
| .name = "max77705", | |
| .of_match_table = max77705_i2c_of_match, | |
| .pm = pm_sleep_ptr(&max77705_pm_ops), | |
| }, | |
| .probe = max77705_i2c_probe | |
| }; | |
| module_i2c_driver(max77705_i2c_driver); | |
| MODULE_DESCRIPTION("Maxim MAX77705 PMIC core driver"); | |
| MODULE_AUTHOR("Dzmitry Sankouski <dsankouski@gmail.com>"); | |
| MODULE_LICENSE("GPL"); |