1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | // |
3 | // max77693.c - mfd core driver for the MAX 77693 |
4 | // |
5 | // Copyright (C) 2012 Samsung Electronics |
6 | // SangYoung Son <hello.son@samsung.com> |
7 | // |
8 | // This program is not provided / owned by Maxim Integrated Products. |
9 | // |
10 | // This driver is based on max8997.c |
11 | |
12 | #include <linux/module.h> |
13 | #include <linux/slab.h> |
14 | #include <linux/i2c.h> |
15 | #include <linux/err.h> |
16 | #include <linux/interrupt.h> |
17 | #include <linux/of.h> |
18 | #include <linux/pm_runtime.h> |
19 | #include <linux/mutex.h> |
20 | #include <linux/mfd/core.h> |
21 | #include <linux/mfd/max77693.h> |
22 | #include <linux/mfd/max77693-common.h> |
23 | #include <linux/mfd/max77693-private.h> |
24 | #include <linux/regulator/machine.h> |
25 | #include <linux/regmap.h> |
26 | |
27 | #define I2C_ADDR_PMIC (0xCC >> 1) /* Charger, Flash LED */ |
28 | #define I2C_ADDR_MUIC (0x4A >> 1) |
29 | #define I2C_ADDR_HAPTIC (0x90 >> 1) |
30 | |
31 | static const struct mfd_cell max77693_devs[] = { |
32 | { .name = "max77693-pmic" , }, |
33 | { |
34 | .name = "max77693-charger" , |
35 | .of_compatible = "maxim,max77693-charger" , |
36 | }, |
37 | { |
38 | .name = "max77693-muic" , |
39 | .of_compatible = "maxim,max77693-muic" , |
40 | }, |
41 | { |
42 | .name = "max77693-haptic" , |
43 | .of_compatible = "maxim,max77693-haptic" , |
44 | }, |
45 | { |
46 | .name = "max77693-led" , |
47 | .of_compatible = "maxim,max77693-led" , |
48 | }, |
49 | }; |
50 | |
51 | static const struct regmap_config max77693_regmap_config = { |
52 | .reg_bits = 8, |
53 | .val_bits = 8, |
54 | .max_register = MAX77693_PMIC_REG_END, |
55 | }; |
56 | |
57 | static const struct regmap_irq max77693_led_irqs[] = { |
58 | { .mask = LED_IRQ_FLED2_OPEN, }, |
59 | { .mask = LED_IRQ_FLED2_SHORT, }, |
60 | { .mask = LED_IRQ_FLED1_OPEN, }, |
61 | { .mask = LED_IRQ_FLED1_SHORT, }, |
62 | { .mask = LED_IRQ_MAX_FLASH, }, |
63 | }; |
64 | |
65 | static const struct regmap_irq_chip max77693_led_irq_chip = { |
66 | .name = "max77693-led" , |
67 | .status_base = MAX77693_LED_REG_FLASH_INT, |
68 | .mask_base = MAX77693_LED_REG_FLASH_INT_MASK, |
69 | .num_regs = 1, |
70 | .irqs = max77693_led_irqs, |
71 | .num_irqs = ARRAY_SIZE(max77693_led_irqs), |
72 | }; |
73 | |
74 | static const struct regmap_irq max77693_topsys_irqs[] = { |
75 | { .mask = TOPSYS_IRQ_T120C_INT, }, |
76 | { .mask = TOPSYS_IRQ_T140C_INT, }, |
77 | { .mask = TOPSYS_IRQ_LOWSYS_INT, }, |
78 | }; |
79 | |
80 | static const struct regmap_irq_chip max77693_topsys_irq_chip = { |
81 | .name = "max77693-topsys" , |
82 | .status_base = MAX77693_PMIC_REG_TOPSYS_INT, |
83 | .mask_base = MAX77693_PMIC_REG_TOPSYS_INT_MASK, |
84 | .num_regs = 1, |
85 | .irqs = max77693_topsys_irqs, |
86 | .num_irqs = ARRAY_SIZE(max77693_topsys_irqs), |
87 | }; |
88 | |
89 | static const struct regmap_irq max77693_charger_irqs[] = { |
90 | { .mask = CHG_IRQ_BYP_I, }, |
91 | { .mask = CHG_IRQ_THM_I, }, |
92 | { .mask = CHG_IRQ_BAT_I, }, |
93 | { .mask = CHG_IRQ_CHG_I, }, |
94 | { .mask = CHG_IRQ_CHGIN_I, }, |
95 | }; |
96 | |
97 | static const struct regmap_irq_chip max77693_charger_irq_chip = { |
98 | .name = "max77693-charger" , |
99 | .status_base = MAX77693_CHG_REG_CHG_INT, |
100 | .mask_base = MAX77693_CHG_REG_CHG_INT_MASK, |
101 | .num_regs = 1, |
102 | .irqs = max77693_charger_irqs, |
103 | .num_irqs = ARRAY_SIZE(max77693_charger_irqs), |
104 | }; |
105 | |
106 | static const struct regmap_config max77693_regmap_muic_config = { |
107 | .reg_bits = 8, |
108 | .val_bits = 8, |
109 | .max_register = MAX77693_MUIC_REG_END, |
110 | }; |
111 | |
112 | static const struct regmap_irq max77693_muic_irqs[] = { |
113 | { .reg_offset = 0, .mask = MUIC_IRQ_INT1_ADC, }, |
114 | { .reg_offset = 0, .mask = MUIC_IRQ_INT1_ADC_LOW, }, |
115 | { .reg_offset = 0, .mask = MUIC_IRQ_INT1_ADC_ERR, }, |
116 | { .reg_offset = 0, .mask = MUIC_IRQ_INT1_ADC1K, }, |
117 | |
118 | { .reg_offset = 1, .mask = MUIC_IRQ_INT2_CHGTYP, }, |
119 | { .reg_offset = 1, .mask = MUIC_IRQ_INT2_CHGDETREUN, }, |
120 | { .reg_offset = 1, .mask = MUIC_IRQ_INT2_DCDTMR, }, |
121 | { .reg_offset = 1, .mask = MUIC_IRQ_INT2_DXOVP, }, |
122 | { .reg_offset = 1, .mask = MUIC_IRQ_INT2_VBVOLT, }, |
123 | { .reg_offset = 1, .mask = MUIC_IRQ_INT2_VIDRM, }, |
124 | |
125 | { .reg_offset = 2, .mask = MUIC_IRQ_INT3_EOC, }, |
126 | { .reg_offset = 2, .mask = MUIC_IRQ_INT3_CGMBC, }, |
127 | { .reg_offset = 2, .mask = MUIC_IRQ_INT3_OVP, }, |
128 | { .reg_offset = 2, .mask = MUIC_IRQ_INT3_MBCCHG_ERR, }, |
129 | { .reg_offset = 2, .mask = MUIC_IRQ_INT3_CHG_ENABLED, }, |
130 | { .reg_offset = 2, .mask = MUIC_IRQ_INT3_BAT_DET, }, |
131 | }; |
132 | |
133 | static const struct regmap_irq_chip max77693_muic_irq_chip = { |
134 | .name = "max77693-muic" , |
135 | .status_base = MAX77693_MUIC_REG_INT1, |
136 | .unmask_base = MAX77693_MUIC_REG_INTMASK1, |
137 | .num_regs = 3, |
138 | .irqs = max77693_muic_irqs, |
139 | .num_irqs = ARRAY_SIZE(max77693_muic_irqs), |
140 | }; |
141 | |
142 | static const struct regmap_config max77693_regmap_haptic_config = { |
143 | .reg_bits = 8, |
144 | .val_bits = 8, |
145 | .max_register = MAX77693_HAPTIC_REG_END, |
146 | }; |
147 | |
148 | static int max77693_i2c_probe(struct i2c_client *i2c) |
149 | { |
150 | const struct i2c_device_id *id = i2c_client_get_device_id(client: i2c); |
151 | struct max77693_dev *max77693; |
152 | unsigned int reg_data; |
153 | int ret = 0; |
154 | |
155 | max77693 = devm_kzalloc(dev: &i2c->dev, |
156 | size: sizeof(struct max77693_dev), GFP_KERNEL); |
157 | if (max77693 == NULL) |
158 | return -ENOMEM; |
159 | |
160 | i2c_set_clientdata(client: i2c, data: max77693); |
161 | max77693->dev = &i2c->dev; |
162 | max77693->i2c = i2c; |
163 | max77693->irq = i2c->irq; |
164 | max77693->type = id->driver_data; |
165 | |
166 | max77693->regmap = devm_regmap_init_i2c(i2c, &max77693_regmap_config); |
167 | if (IS_ERR(ptr: max77693->regmap)) { |
168 | ret = PTR_ERR(ptr: max77693->regmap); |
169 | dev_err(max77693->dev, "failed to allocate register map: %d\n" , |
170 | ret); |
171 | return ret; |
172 | } |
173 | |
174 | ret = regmap_read(map: max77693->regmap, reg: MAX77693_PMIC_REG_PMIC_ID2, |
175 | val: ®_data); |
176 | if (ret < 0) { |
177 | dev_err(max77693->dev, "device not found on this channel\n" ); |
178 | return ret; |
179 | } else |
180 | dev_info(max77693->dev, "device ID: 0x%x\n" , reg_data); |
181 | |
182 | max77693->i2c_muic = i2c_new_dummy_device(adapter: i2c->adapter, I2C_ADDR_MUIC); |
183 | if (IS_ERR(ptr: max77693->i2c_muic)) { |
184 | dev_err(max77693->dev, "Failed to allocate I2C device for MUIC\n" ); |
185 | return PTR_ERR(ptr: max77693->i2c_muic); |
186 | } |
187 | i2c_set_clientdata(client: max77693->i2c_muic, data: max77693); |
188 | |
189 | max77693->i2c_haptic = i2c_new_dummy_device(adapter: i2c->adapter, I2C_ADDR_HAPTIC); |
190 | if (IS_ERR(ptr: max77693->i2c_haptic)) { |
191 | dev_err(max77693->dev, "Failed to allocate I2C device for Haptic\n" ); |
192 | ret = PTR_ERR(ptr: max77693->i2c_haptic); |
193 | goto err_i2c_haptic; |
194 | } |
195 | i2c_set_clientdata(client: max77693->i2c_haptic, data: max77693); |
196 | |
197 | max77693->regmap_haptic = devm_regmap_init_i2c(max77693->i2c_haptic, |
198 | &max77693_regmap_haptic_config); |
199 | if (IS_ERR(ptr: max77693->regmap_haptic)) { |
200 | ret = PTR_ERR(ptr: max77693->regmap_haptic); |
201 | dev_err(max77693->dev, |
202 | "failed to initialize haptic register map: %d\n" , ret); |
203 | goto err_regmap; |
204 | } |
205 | |
206 | /* |
207 | * Initialize register map for MUIC device because use regmap-muic |
208 | * instance of MUIC device when irq of max77693 is initialized |
209 | * before call max77693-muic probe() function. |
210 | */ |
211 | max77693->regmap_muic = devm_regmap_init_i2c(max77693->i2c_muic, |
212 | &max77693_regmap_muic_config); |
213 | if (IS_ERR(ptr: max77693->regmap_muic)) { |
214 | ret = PTR_ERR(ptr: max77693->regmap_muic); |
215 | dev_err(max77693->dev, |
216 | "failed to allocate register map: %d\n" , ret); |
217 | goto err_regmap; |
218 | } |
219 | |
220 | ret = regmap_add_irq_chip(map: max77693->regmap, irq: max77693->irq, |
221 | IRQF_ONESHOT | IRQF_SHARED, irq_base: 0, |
222 | chip: &max77693_led_irq_chip, |
223 | data: &max77693->irq_data_led); |
224 | if (ret) { |
225 | dev_err(max77693->dev, "failed to add irq chip: %d\n" , ret); |
226 | goto err_regmap; |
227 | } |
228 | |
229 | ret = regmap_add_irq_chip(map: max77693->regmap, irq: max77693->irq, |
230 | IRQF_ONESHOT | IRQF_SHARED, irq_base: 0, |
231 | chip: &max77693_topsys_irq_chip, |
232 | data: &max77693->irq_data_topsys); |
233 | if (ret) { |
234 | dev_err(max77693->dev, "failed to add irq chip: %d\n" , ret); |
235 | goto err_irq_topsys; |
236 | } |
237 | |
238 | ret = regmap_add_irq_chip(map: max77693->regmap, irq: max77693->irq, |
239 | IRQF_ONESHOT | IRQF_SHARED, irq_base: 0, |
240 | chip: &max77693_charger_irq_chip, |
241 | data: &max77693->irq_data_chg); |
242 | if (ret) { |
243 | dev_err(max77693->dev, "failed to add irq chip: %d\n" , ret); |
244 | goto err_irq_charger; |
245 | } |
246 | |
247 | ret = regmap_add_irq_chip(map: max77693->regmap_muic, irq: max77693->irq, |
248 | IRQF_ONESHOT | IRQF_SHARED, irq_base: 0, |
249 | chip: &max77693_muic_irq_chip, |
250 | data: &max77693->irq_data_muic); |
251 | if (ret) { |
252 | dev_err(max77693->dev, "failed to add irq chip: %d\n" , ret); |
253 | goto err_irq_muic; |
254 | } |
255 | |
256 | /* Unmask interrupts from all blocks in interrupt source register */ |
257 | ret = regmap_update_bits(map: max77693->regmap, |
258 | reg: MAX77693_PMIC_REG_INTSRC_MASK, |
259 | SRC_IRQ_ALL, val: (unsigned int)~SRC_IRQ_ALL); |
260 | if (ret < 0) { |
261 | dev_err(max77693->dev, |
262 | "Could not unmask interrupts in INTSRC: %d\n" , |
263 | ret); |
264 | goto err_intsrc; |
265 | } |
266 | |
267 | pm_runtime_set_active(dev: max77693->dev); |
268 | |
269 | ret = mfd_add_devices(parent: max77693->dev, id: -1, cells: max77693_devs, |
270 | ARRAY_SIZE(max77693_devs), NULL, irq_base: 0, NULL); |
271 | if (ret < 0) |
272 | goto err_mfd; |
273 | |
274 | return ret; |
275 | |
276 | err_mfd: |
277 | mfd_remove_devices(parent: max77693->dev); |
278 | err_intsrc: |
279 | regmap_del_irq_chip(irq: max77693->irq, data: max77693->irq_data_muic); |
280 | err_irq_muic: |
281 | regmap_del_irq_chip(irq: max77693->irq, data: max77693->irq_data_chg); |
282 | err_irq_charger: |
283 | regmap_del_irq_chip(irq: max77693->irq, data: max77693->irq_data_topsys); |
284 | err_irq_topsys: |
285 | regmap_del_irq_chip(irq: max77693->irq, data: max77693->irq_data_led); |
286 | err_regmap: |
287 | i2c_unregister_device(client: max77693->i2c_haptic); |
288 | err_i2c_haptic: |
289 | i2c_unregister_device(client: max77693->i2c_muic); |
290 | return ret; |
291 | } |
292 | |
293 | static void max77693_i2c_remove(struct i2c_client *i2c) |
294 | { |
295 | struct max77693_dev *max77693 = i2c_get_clientdata(client: i2c); |
296 | |
297 | mfd_remove_devices(parent: max77693->dev); |
298 | |
299 | regmap_del_irq_chip(irq: max77693->irq, data: max77693->irq_data_muic); |
300 | regmap_del_irq_chip(irq: max77693->irq, data: max77693->irq_data_chg); |
301 | regmap_del_irq_chip(irq: max77693->irq, data: max77693->irq_data_topsys); |
302 | regmap_del_irq_chip(irq: max77693->irq, data: max77693->irq_data_led); |
303 | |
304 | i2c_unregister_device(client: max77693->i2c_muic); |
305 | i2c_unregister_device(client: max77693->i2c_haptic); |
306 | } |
307 | |
308 | static const struct i2c_device_id max77693_i2c_id[] = { |
309 | { "max77693" , TYPE_MAX77693 }, |
310 | { } |
311 | }; |
312 | MODULE_DEVICE_TABLE(i2c, max77693_i2c_id); |
313 | |
314 | static int max77693_suspend(struct device *dev) |
315 | { |
316 | struct i2c_client *i2c = to_i2c_client(dev); |
317 | struct max77693_dev *max77693 = i2c_get_clientdata(client: i2c); |
318 | |
319 | if (device_may_wakeup(dev)) { |
320 | enable_irq_wake(irq: max77693->irq); |
321 | disable_irq(irq: max77693->irq); |
322 | } |
323 | |
324 | return 0; |
325 | } |
326 | |
327 | static int max77693_resume(struct device *dev) |
328 | { |
329 | struct i2c_client *i2c = to_i2c_client(dev); |
330 | struct max77693_dev *max77693 = i2c_get_clientdata(client: i2c); |
331 | |
332 | if (device_may_wakeup(dev)) { |
333 | disable_irq_wake(irq: max77693->irq); |
334 | enable_irq(irq: max77693->irq); |
335 | } |
336 | |
337 | return 0; |
338 | } |
339 | |
340 | static const struct dev_pm_ops max77693_pm = { |
341 | .suspend = max77693_suspend, |
342 | .resume = max77693_resume, |
343 | }; |
344 | |
345 | #ifdef CONFIG_OF |
346 | static const struct of_device_id max77693_dt_match[] = { |
347 | { .compatible = "maxim,max77693" }, |
348 | {}, |
349 | }; |
350 | MODULE_DEVICE_TABLE(of, max77693_dt_match); |
351 | #endif |
352 | |
353 | static struct i2c_driver max77693_i2c_driver = { |
354 | .driver = { |
355 | .name = "max77693" , |
356 | .pm = &max77693_pm, |
357 | .of_match_table = of_match_ptr(max77693_dt_match), |
358 | }, |
359 | .probe = max77693_i2c_probe, |
360 | .remove = max77693_i2c_remove, |
361 | .id_table = max77693_i2c_id, |
362 | }; |
363 | |
364 | module_i2c_driver(max77693_i2c_driver); |
365 | |
366 | MODULE_DESCRIPTION("MAXIM 77693 multi-function core driver" ); |
367 | MODULE_AUTHOR("SangYoung, Son <hello.son@samsung.com>" ); |
368 | MODULE_LICENSE("GPL" ); |
369 | |