Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Maxim MAX77705 PMIC core driver
4 *
5 * Copyright (C) 2025 Dzmitry Sankouski <dsankouski@gmail.com>
6 **/
7#include <linux/i2c.h>
8#include <linux/interrupt.h>
9#include <linux/mfd/core.h>
10#include <linux/mfd/max77705-private.h>
11#include <linux/mfd/max77693-common.h>
12#include <linux/pm.h>
13#include <linux/power/max17042_battery.h>
14#include <linux/module.h>
15#include <linux/regmap.h>
16#include <linux/of.h>
17
18static struct mfd_cell max77705_devs[] = {
19 MFD_CELL_OF("max77705-rgb", NULL, NULL, 0, 0, "maxim,max77705-rgb"),
20 MFD_CELL_OF("max77705-charger", NULL, NULL, 0, 0, "maxim,max77705-charger"),
21 MFD_CELL_OF("max77705-haptic", NULL, NULL, 0, 0, "maxim,max77705-haptic"),
22};
23
24static const struct regmap_range max77705_readable_ranges[] = {
25 regmap_reg_range(MAX77705_PMIC_REG_PMICID1, MAX77705_PMIC_REG_BSTOUT_MASK),
26 regmap_reg_range(MAX77705_PMIC_REG_INTSRC, MAX77705_PMIC_REG_RESERVED_29),
27 regmap_reg_range(MAX77705_PMIC_REG_BOOSTCONTROL1, MAX77705_PMIC_REG_BOOSTCONTROL1),
28 regmap_reg_range(MAX77705_PMIC_REG_MCONFIG, MAX77705_PMIC_REG_MCONFIG2),
29 regmap_reg_range(MAX77705_PMIC_REG_FORCE_EN_MASK, MAX77705_PMIC_REG_FORCE_EN_MASK),
30 regmap_reg_range(MAX77705_PMIC_REG_BOOSTCONTROL1, MAX77705_PMIC_REG_BOOSTCONTROL1),
31 regmap_reg_range(MAX77705_PMIC_REG_BOOSTCONTROL2, MAX77705_PMIC_REG_BOOSTCONTROL2),
32 regmap_reg_range(MAX77705_PMIC_REG_SW_RESET, MAX77705_PMIC_REG_USBC_RESET),
33};
34
35static const struct regmap_range max77705_writable_ranges[] = {
36 regmap_reg_range(MAX77705_PMIC_REG_MAINCTRL1, MAX77705_PMIC_REG_BSTOUT_MASK),
37 regmap_reg_range(MAX77705_PMIC_REG_INTSRC, MAX77705_PMIC_REG_RESERVED_29),
38 regmap_reg_range(MAX77705_PMIC_REG_BOOSTCONTROL1, MAX77705_PMIC_REG_BOOSTCONTROL1),
39 regmap_reg_range(MAX77705_PMIC_REG_MCONFIG, MAX77705_PMIC_REG_MCONFIG2),
40 regmap_reg_range(MAX77705_PMIC_REG_FORCE_EN_MASK, MAX77705_PMIC_REG_FORCE_EN_MASK),
41 regmap_reg_range(MAX77705_PMIC_REG_BOOSTCONTROL1, MAX77705_PMIC_REG_BOOSTCONTROL1),
42 regmap_reg_range(MAX77705_PMIC_REG_BOOSTCONTROL2, MAX77705_PMIC_REG_BOOSTCONTROL2),
43 regmap_reg_range(MAX77705_PMIC_REG_SW_RESET, MAX77705_PMIC_REG_USBC_RESET),
44};
45
46static const struct regmap_access_table max77705_readable_table = {
47 .yes_ranges = max77705_readable_ranges,
48 .n_yes_ranges = ARRAY_SIZE(max77705_readable_ranges),
49};
50
51static const struct regmap_access_table max77705_writable_table = {
52 .yes_ranges = max77705_writable_ranges,
53 .n_yes_ranges = ARRAY_SIZE(max77705_writable_ranges),
54};
55
56static const struct regmap_config max77705_regmap_config = {
57 .reg_bits = 8,
58 .val_bits = 8,
59 .rd_table = &max77705_readable_table,
60 .wr_table = &max77705_writable_table,
61 .max_register = MAX77705_PMIC_REG_USBC_RESET,
62};
63
64static const struct regmap_irq max77705_irqs[] = {
65 { .mask = MAX77705_SRC_IRQ_CHG, },
66 { .mask = MAX77705_SRC_IRQ_TOP, },
67 { .mask = MAX77705_SRC_IRQ_FG, },
68 { .mask = MAX77705_SRC_IRQ_USBC, },
69};
70
71static const struct regmap_irq_chip max77705_irq_chip = {
72 .name = "max77705",
73 .status_base = MAX77705_PMIC_REG_INTSRC,
74 .ack_base = MAX77705_PMIC_REG_INTSRC,
75 .mask_base = MAX77705_PMIC_REG_INTSRC_MASK,
76 .num_regs = 1,
77 .irqs = max77705_irqs,
78 .num_irqs = ARRAY_SIZE(max77705_irqs),
79};
80
81static int max77705_i2c_probe(struct i2c_client *i2c)
82{
83 struct device *dev = &i2c->dev;
84 struct max77693_dev *max77705;
85 struct regmap_irq_chip_data *irq_data;
86 struct irq_domain *domain;
87 enum max77705_hw_rev pmic_rev;
88 unsigned int pmic_rev_value;
89 int ret;
90
91 max77705 = devm_kzalloc(dev, sizeof(*max77705), GFP_KERNEL);
92 if (!max77705)
93 return -ENOMEM;
94
95 max77705->i2c = i2c;
96 max77705->type = TYPE_MAX77705;
97 i2c_set_clientdata(i2c, max77705);
98
99 max77705->regmap = devm_regmap_init_i2c(i2c, &max77705_regmap_config);
100 if (IS_ERR(max77705->regmap))
101 return PTR_ERR(max77705->regmap);
102
103 ret = regmap_read(max77705->regmap, MAX77705_PMIC_REG_PMICREV, &pmic_rev_value);
104 if (ret < 0)
105 return -ENODEV;
106
107 pmic_rev = pmic_rev_value & MAX77705_REVISION_MASK;
108 if (pmic_rev != MAX77705_PASS3)
109 return dev_err_probe(dev, -ENODEV, "Rev.0x%x is not tested\n", pmic_rev);
110
111 /* Active Discharge Enable */
112 regmap_update_bits(max77705->regmap, MAX77705_PMIC_REG_MAINCTRL1, 1, 1);
113
114 ret = devm_regmap_add_irq_chip(dev, max77705->regmap,
115 i2c->irq,
116 IRQF_ONESHOT, 0,
117 &max77705_irq_chip,
118 &irq_data);
119 if (ret)
120 return dev_err_probe(dev, ret, "Failed to add IRQ chip\n");
121
122 domain = regmap_irq_get_domain(irq_data);
123
124 ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE,
125 max77705_devs, ARRAY_SIZE(max77705_devs),
126 NULL, 0, domain);
127 if (ret)
128 return dev_err_probe(dev, ret, "Failed to register child devices\n");
129
130 ret = devm_device_init_wakeup(dev);
131 if (ret)
132 return dev_err_probe(dev, ret, "Failed to init wakeup\n");
133
134 return 0;
135}
136
137static int max77705_suspend(struct device *dev)
138{
139 struct i2c_client *i2c = to_i2c_client(dev);
140
141 disable_irq(i2c->irq);
142
143 if (device_may_wakeup(dev))
144 enable_irq_wake(i2c->irq);
145
146 return 0;
147}
148
149static int max77705_resume(struct device *dev)
150{
151 struct i2c_client *i2c = to_i2c_client(dev);
152
153 if (device_may_wakeup(dev))
154 disable_irq_wake(i2c->irq);
155
156 enable_irq(i2c->irq);
157
158 return 0;
159}
160DEFINE_SIMPLE_DEV_PM_OPS(max77705_pm_ops, max77705_suspend, max77705_resume);
161
162static const struct of_device_id max77705_i2c_of_match[] = {
163 { .compatible = "maxim,max77705" },
164 { }
165};
166MODULE_DEVICE_TABLE(of, max77705_i2c_of_match);
167
168static struct i2c_driver max77705_i2c_driver = {
169 .driver = {
170 .name = "max77705",
171 .of_match_table = max77705_i2c_of_match,
172 .pm = pm_sleep_ptr(&max77705_pm_ops),
173 },
174 .probe = max77705_i2c_probe
175};
176module_i2c_driver(max77705_i2c_driver);
177
178MODULE_DESCRIPTION("Maxim MAX77705 PMIC core driver");
179MODULE_AUTHOR("Dzmitry Sankouski <dsankouski@gmail.com>");
180MODULE_LICENSE("GPL");