1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Core driver for ams AS3722 PMICs |
4 | * |
5 | * Copyright (C) 2013 AMS AG |
6 | * Copyright (c) 2013, NVIDIA Corporation. All rights reserved. |
7 | * |
8 | * Author: Florian Lobmaier <florian.lobmaier@ams.com> |
9 | * Author: Laxman Dewangan <ldewangan@nvidia.com> |
10 | */ |
11 | |
12 | #include <linux/err.h> |
13 | #include <linux/i2c.h> |
14 | #include <linux/interrupt.h> |
15 | #include <linux/irq.h> |
16 | #include <linux/kernel.h> |
17 | #include <linux/module.h> |
18 | #include <linux/mfd/core.h> |
19 | #include <linux/mfd/as3722.h> |
20 | #include <linux/of.h> |
21 | #include <linux/regmap.h> |
22 | #include <linux/slab.h> |
23 | |
24 | #define AS3722_DEVICE_ID 0x0C |
25 | |
26 | static const struct resource as3722_rtc_resource[] = { |
27 | DEFINE_RES_IRQ_NAMED(AS3722_IRQ_RTC_ALARM, "as3722-rtc-alarm" ), |
28 | }; |
29 | |
30 | static const struct resource as3722_adc_resource[] = { |
31 | DEFINE_RES_IRQ_NAMED(AS3722_IRQ_ADC, "as3722-adc" ), |
32 | }; |
33 | |
34 | static const struct mfd_cell as3722_devs[] = { |
35 | { |
36 | .name = "as3722-pinctrl" , |
37 | }, |
38 | { |
39 | .name = "as3722-regulator" , |
40 | }, |
41 | { |
42 | .name = "as3722-rtc" , |
43 | .num_resources = ARRAY_SIZE(as3722_rtc_resource), |
44 | .resources = as3722_rtc_resource, |
45 | }, |
46 | { |
47 | .name = "as3722-adc" , |
48 | .num_resources = ARRAY_SIZE(as3722_adc_resource), |
49 | .resources = as3722_adc_resource, |
50 | }, |
51 | { |
52 | .name = "as3722-power-off" , |
53 | }, |
54 | { |
55 | .name = "as3722-wdt" , |
56 | }, |
57 | }; |
58 | |
59 | static const struct regmap_irq as3722_irqs[] = { |
60 | /* INT1 IRQs */ |
61 | [AS3722_IRQ_LID] = { |
62 | .mask = AS3722_INTERRUPT_MASK1_LID, |
63 | }, |
64 | [AS3722_IRQ_ACOK] = { |
65 | .mask = AS3722_INTERRUPT_MASK1_ACOK, |
66 | }, |
67 | [AS3722_IRQ_ENABLE1] = { |
68 | .mask = AS3722_INTERRUPT_MASK1_ENABLE1, |
69 | }, |
70 | [AS3722_IRQ_OCCUR_ALARM_SD0] = { |
71 | .mask = AS3722_INTERRUPT_MASK1_OCURR_ALARM_SD0, |
72 | }, |
73 | [AS3722_IRQ_ONKEY_LONG_PRESS] = { |
74 | .mask = AS3722_INTERRUPT_MASK1_ONKEY_LONG, |
75 | }, |
76 | [AS3722_IRQ_ONKEY] = { |
77 | .mask = AS3722_INTERRUPT_MASK1_ONKEY, |
78 | }, |
79 | [AS3722_IRQ_OVTMP] = { |
80 | .mask = AS3722_INTERRUPT_MASK1_OVTMP, |
81 | }, |
82 | [AS3722_IRQ_LOWBAT] = { |
83 | .mask = AS3722_INTERRUPT_MASK1_LOWBAT, |
84 | }, |
85 | |
86 | /* INT2 IRQs */ |
87 | [AS3722_IRQ_SD0_LV] = { |
88 | .mask = AS3722_INTERRUPT_MASK2_SD0_LV, |
89 | .reg_offset = 1, |
90 | }, |
91 | [AS3722_IRQ_SD1_LV] = { |
92 | .mask = AS3722_INTERRUPT_MASK2_SD1_LV, |
93 | .reg_offset = 1, |
94 | }, |
95 | [AS3722_IRQ_SD2_LV] = { |
96 | .mask = AS3722_INTERRUPT_MASK2_SD2345_LV, |
97 | .reg_offset = 1, |
98 | }, |
99 | [AS3722_IRQ_PWM1_OV_PROT] = { |
100 | .mask = AS3722_INTERRUPT_MASK2_PWM1_OV_PROT, |
101 | .reg_offset = 1, |
102 | }, |
103 | [AS3722_IRQ_PWM2_OV_PROT] = { |
104 | .mask = AS3722_INTERRUPT_MASK2_PWM2_OV_PROT, |
105 | .reg_offset = 1, |
106 | }, |
107 | [AS3722_IRQ_ENABLE2] = { |
108 | .mask = AS3722_INTERRUPT_MASK2_ENABLE2, |
109 | .reg_offset = 1, |
110 | }, |
111 | [AS3722_IRQ_SD6_LV] = { |
112 | .mask = AS3722_INTERRUPT_MASK2_SD6_LV, |
113 | .reg_offset = 1, |
114 | }, |
115 | [AS3722_IRQ_RTC_REP] = { |
116 | .mask = AS3722_INTERRUPT_MASK2_RTC_REP, |
117 | .reg_offset = 1, |
118 | }, |
119 | |
120 | /* INT3 IRQs */ |
121 | [AS3722_IRQ_RTC_ALARM] = { |
122 | .mask = AS3722_INTERRUPT_MASK3_RTC_ALARM, |
123 | .reg_offset = 2, |
124 | }, |
125 | [AS3722_IRQ_GPIO1] = { |
126 | .mask = AS3722_INTERRUPT_MASK3_GPIO1, |
127 | .reg_offset = 2, |
128 | }, |
129 | [AS3722_IRQ_GPIO2] = { |
130 | .mask = AS3722_INTERRUPT_MASK3_GPIO2, |
131 | .reg_offset = 2, |
132 | }, |
133 | [AS3722_IRQ_GPIO3] = { |
134 | .mask = AS3722_INTERRUPT_MASK3_GPIO3, |
135 | .reg_offset = 2, |
136 | }, |
137 | [AS3722_IRQ_GPIO4] = { |
138 | .mask = AS3722_INTERRUPT_MASK3_GPIO4, |
139 | .reg_offset = 2, |
140 | }, |
141 | [AS3722_IRQ_GPIO5] = { |
142 | .mask = AS3722_INTERRUPT_MASK3_GPIO5, |
143 | .reg_offset = 2, |
144 | }, |
145 | [AS3722_IRQ_WATCHDOG] = { |
146 | .mask = AS3722_INTERRUPT_MASK3_WATCHDOG, |
147 | .reg_offset = 2, |
148 | }, |
149 | [AS3722_IRQ_ENABLE3] = { |
150 | .mask = AS3722_INTERRUPT_MASK3_ENABLE3, |
151 | .reg_offset = 2, |
152 | }, |
153 | |
154 | /* INT4 IRQs */ |
155 | [AS3722_IRQ_TEMP_SD0_SHUTDOWN] = { |
156 | .mask = AS3722_INTERRUPT_MASK4_TEMP_SD0_SHUTDOWN, |
157 | .reg_offset = 3, |
158 | }, |
159 | [AS3722_IRQ_TEMP_SD1_SHUTDOWN] = { |
160 | .mask = AS3722_INTERRUPT_MASK4_TEMP_SD1_SHUTDOWN, |
161 | .reg_offset = 3, |
162 | }, |
163 | [AS3722_IRQ_TEMP_SD2_SHUTDOWN] = { |
164 | .mask = AS3722_INTERRUPT_MASK4_TEMP_SD6_SHUTDOWN, |
165 | .reg_offset = 3, |
166 | }, |
167 | [AS3722_IRQ_TEMP_SD0_ALARM] = { |
168 | .mask = AS3722_INTERRUPT_MASK4_TEMP_SD0_ALARM, |
169 | .reg_offset = 3, |
170 | }, |
171 | [AS3722_IRQ_TEMP_SD1_ALARM] = { |
172 | .mask = AS3722_INTERRUPT_MASK4_TEMP_SD1_ALARM, |
173 | .reg_offset = 3, |
174 | }, |
175 | [AS3722_IRQ_TEMP_SD6_ALARM] = { |
176 | .mask = AS3722_INTERRUPT_MASK4_TEMP_SD6_ALARM, |
177 | .reg_offset = 3, |
178 | }, |
179 | [AS3722_IRQ_OCCUR_ALARM_SD6] = { |
180 | .mask = AS3722_INTERRUPT_MASK4_OCCUR_ALARM_SD6, |
181 | .reg_offset = 3, |
182 | }, |
183 | [AS3722_IRQ_ADC] = { |
184 | .mask = AS3722_INTERRUPT_MASK4_ADC, |
185 | .reg_offset = 3, |
186 | }, |
187 | }; |
188 | |
189 | static const struct regmap_irq_chip as3722_irq_chip = { |
190 | .name = "as3722" , |
191 | .irqs = as3722_irqs, |
192 | .num_irqs = ARRAY_SIZE(as3722_irqs), |
193 | .num_regs = 4, |
194 | .status_base = AS3722_INTERRUPT_STATUS1_REG, |
195 | .mask_base = AS3722_INTERRUPT_MASK1_REG, |
196 | }; |
197 | |
198 | static int as3722_check_device_id(struct as3722 *as3722) |
199 | { |
200 | u32 val; |
201 | int ret; |
202 | |
203 | /* Check that this is actually a AS3722 */ |
204 | ret = as3722_read(as3722, AS3722_ASIC_ID1_REG, dest: &val); |
205 | if (ret < 0) { |
206 | dev_err(as3722->dev, "ASIC_ID1 read failed: %d\n" , ret); |
207 | return ret; |
208 | } |
209 | |
210 | if (val != AS3722_DEVICE_ID) { |
211 | dev_err(as3722->dev, "Device is not AS3722, ID is 0x%x\n" , val); |
212 | return -ENODEV; |
213 | } |
214 | |
215 | ret = as3722_read(as3722, AS3722_ASIC_ID2_REG, dest: &val); |
216 | if (ret < 0) { |
217 | dev_err(as3722->dev, "ASIC_ID2 read failed: %d\n" , ret); |
218 | return ret; |
219 | } |
220 | |
221 | dev_info(as3722->dev, "AS3722 with revision 0x%x found\n" , val); |
222 | return 0; |
223 | } |
224 | |
225 | static int as3722_configure_pullups(struct as3722 *as3722) |
226 | { |
227 | int ret; |
228 | u32 val = 0; |
229 | |
230 | if (as3722->en_intern_int_pullup) |
231 | val |= AS3722_INT_PULL_UP; |
232 | if (as3722->en_intern_i2c_pullup) |
233 | val |= AS3722_I2C_PULL_UP; |
234 | |
235 | ret = as3722_update_bits(as3722, AS3722_IOVOLTAGE_REG, |
236 | AS3722_INT_PULL_UP | AS3722_I2C_PULL_UP, val); |
237 | if (ret < 0) |
238 | dev_err(as3722->dev, "IOVOLTAGE_REG update failed: %d\n" , ret); |
239 | return ret; |
240 | } |
241 | |
242 | static const struct regmap_range as3722_readable_ranges[] = { |
243 | regmap_reg_range(AS3722_SD0_VOLTAGE_REG, AS3722_SD6_VOLTAGE_REG), |
244 | regmap_reg_range(AS3722_GPIO0_CONTROL_REG, AS3722_LDO7_VOLTAGE_REG), |
245 | regmap_reg_range(AS3722_LDO9_VOLTAGE_REG, AS3722_REG_SEQU_MOD3_REG), |
246 | regmap_reg_range(AS3722_SD_PHSW_CTRL_REG, AS3722_PWM_CONTROL_H_REG), |
247 | regmap_reg_range(AS3722_WATCHDOG_TIMER_REG, AS3722_WATCHDOG_TIMER_REG), |
248 | regmap_reg_range(AS3722_WATCHDOG_SOFTWARE_SIGNAL_REG, |
249 | AS3722_BATTERY_VOLTAGE_MONITOR2_REG), |
250 | regmap_reg_range(AS3722_SD_CONTROL_REG, AS3722_PWM_VCONTROL4_REG), |
251 | regmap_reg_range(AS3722_BB_CHARGER_REG, AS3722_SRAM_REG), |
252 | regmap_reg_range(AS3722_RTC_ACCESS_REG, AS3722_RTC_ACCESS_REG), |
253 | regmap_reg_range(AS3722_RTC_STATUS_REG, AS3722_TEMP_STATUS_REG), |
254 | regmap_reg_range(AS3722_ADC0_CONTROL_REG, AS3722_ADC_CONFIGURATION_REG), |
255 | regmap_reg_range(AS3722_ASIC_ID1_REG, AS3722_ASIC_ID2_REG), |
256 | regmap_reg_range(AS3722_LOCK_REG, AS3722_LOCK_REG), |
257 | regmap_reg_range(AS3722_FUSE7_REG, AS3722_FUSE7_REG), |
258 | }; |
259 | |
260 | static const struct regmap_access_table as3722_readable_table = { |
261 | .yes_ranges = as3722_readable_ranges, |
262 | .n_yes_ranges = ARRAY_SIZE(as3722_readable_ranges), |
263 | }; |
264 | |
265 | static const struct regmap_range as3722_writable_ranges[] = { |
266 | regmap_reg_range(AS3722_SD0_VOLTAGE_REG, AS3722_SD6_VOLTAGE_REG), |
267 | regmap_reg_range(AS3722_GPIO0_CONTROL_REG, AS3722_LDO7_VOLTAGE_REG), |
268 | regmap_reg_range(AS3722_LDO9_VOLTAGE_REG, AS3722_GPIO_SIGNAL_OUT_REG), |
269 | regmap_reg_range(AS3722_REG_SEQU_MOD1_REG, AS3722_REG_SEQU_MOD3_REG), |
270 | regmap_reg_range(AS3722_SD_PHSW_CTRL_REG, AS3722_PWM_CONTROL_H_REG), |
271 | regmap_reg_range(AS3722_WATCHDOG_TIMER_REG, AS3722_WATCHDOG_TIMER_REG), |
272 | regmap_reg_range(AS3722_WATCHDOG_SOFTWARE_SIGNAL_REG, |
273 | AS3722_BATTERY_VOLTAGE_MONITOR2_REG), |
274 | regmap_reg_range(AS3722_SD_CONTROL_REG, AS3722_PWM_VCONTROL4_REG), |
275 | regmap_reg_range(AS3722_BB_CHARGER_REG, AS3722_SRAM_REG), |
276 | regmap_reg_range(AS3722_INTERRUPT_MASK1_REG, AS3722_TEMP_STATUS_REG), |
277 | regmap_reg_range(AS3722_ADC0_CONTROL_REG, AS3722_ADC1_CONTROL_REG), |
278 | regmap_reg_range(AS3722_ADC1_THRESHOLD_HI_MSB_REG, |
279 | AS3722_ADC_CONFIGURATION_REG), |
280 | regmap_reg_range(AS3722_LOCK_REG, AS3722_LOCK_REG), |
281 | }; |
282 | |
283 | static const struct regmap_access_table as3722_writable_table = { |
284 | .yes_ranges = as3722_writable_ranges, |
285 | .n_yes_ranges = ARRAY_SIZE(as3722_writable_ranges), |
286 | }; |
287 | |
288 | static const struct regmap_range as3722_cacheable_ranges[] = { |
289 | regmap_reg_range(AS3722_SD0_VOLTAGE_REG, AS3722_LDO11_VOLTAGE_REG), |
290 | regmap_reg_range(AS3722_SD_CONTROL_REG, AS3722_LDOCONTROL1_REG), |
291 | }; |
292 | |
293 | static const struct regmap_access_table as3722_volatile_table = { |
294 | .no_ranges = as3722_cacheable_ranges, |
295 | .n_no_ranges = ARRAY_SIZE(as3722_cacheable_ranges), |
296 | }; |
297 | |
298 | static const struct regmap_config as3722_regmap_config = { |
299 | .reg_bits = 8, |
300 | .val_bits = 8, |
301 | .max_register = AS3722_MAX_REGISTER, |
302 | .cache_type = REGCACHE_RBTREE, |
303 | .rd_table = &as3722_readable_table, |
304 | .wr_table = &as3722_writable_table, |
305 | .volatile_table = &as3722_volatile_table, |
306 | }; |
307 | |
308 | static int as3722_i2c_of_probe(struct i2c_client *i2c, |
309 | struct as3722 *as3722) |
310 | { |
311 | struct device_node *np = i2c->dev.of_node; |
312 | struct irq_data *irq_data; |
313 | |
314 | if (!np) { |
315 | dev_err(&i2c->dev, "Device Tree not found\n" ); |
316 | return -EINVAL; |
317 | } |
318 | |
319 | irq_data = irq_get_irq_data(irq: i2c->irq); |
320 | if (!irq_data) { |
321 | dev_err(&i2c->dev, "Invalid IRQ: %d\n" , i2c->irq); |
322 | return -EINVAL; |
323 | } |
324 | |
325 | as3722->en_intern_int_pullup = of_property_read_bool(np, |
326 | propname: "ams,enable-internal-int-pullup" ); |
327 | as3722->en_intern_i2c_pullup = of_property_read_bool(np, |
328 | propname: "ams,enable-internal-i2c-pullup" ); |
329 | as3722->en_ac_ok_pwr_on = of_property_read_bool(np, |
330 | propname: "ams,enable-ac-ok-power-on" ); |
331 | as3722->irq_flags = irqd_get_trigger_type(d: irq_data); |
332 | dev_dbg(&i2c->dev, "IRQ flags are 0x%08lx\n" , as3722->irq_flags); |
333 | return 0; |
334 | } |
335 | |
336 | static int as3722_i2c_probe(struct i2c_client *i2c) |
337 | { |
338 | struct as3722 *as3722; |
339 | unsigned long irq_flags; |
340 | int ret; |
341 | u8 val = 0; |
342 | |
343 | as3722 = devm_kzalloc(dev: &i2c->dev, size: sizeof(struct as3722), GFP_KERNEL); |
344 | if (!as3722) |
345 | return -ENOMEM; |
346 | |
347 | as3722->dev = &i2c->dev; |
348 | as3722->chip_irq = i2c->irq; |
349 | i2c_set_clientdata(client: i2c, data: as3722); |
350 | |
351 | ret = as3722_i2c_of_probe(i2c, as3722); |
352 | if (ret < 0) |
353 | return ret; |
354 | |
355 | as3722->regmap = devm_regmap_init_i2c(i2c, &as3722_regmap_config); |
356 | if (IS_ERR(ptr: as3722->regmap)) { |
357 | ret = PTR_ERR(ptr: as3722->regmap); |
358 | dev_err(&i2c->dev, "regmap init failed: %d\n" , ret); |
359 | return ret; |
360 | } |
361 | |
362 | ret = as3722_check_device_id(as3722); |
363 | if (ret < 0) |
364 | return ret; |
365 | |
366 | irq_flags = as3722->irq_flags | IRQF_ONESHOT; |
367 | ret = devm_regmap_add_irq_chip(dev: as3722->dev, map: as3722->regmap, |
368 | irq: as3722->chip_irq, |
369 | irq_flags, irq_base: -1, chip: &as3722_irq_chip, |
370 | data: &as3722->irq_data); |
371 | if (ret < 0) { |
372 | dev_err(as3722->dev, "Failed to add regmap irq: %d\n" , ret); |
373 | return ret; |
374 | } |
375 | |
376 | ret = as3722_configure_pullups(as3722); |
377 | if (ret < 0) |
378 | return ret; |
379 | |
380 | if (as3722->en_ac_ok_pwr_on) |
381 | val = AS3722_CTRL_SEQU1_AC_OK_PWR_ON; |
382 | ret = as3722_update_bits(as3722, AS3722_CTRL_SEQU1_REG, |
383 | AS3722_CTRL_SEQU1_AC_OK_PWR_ON, val); |
384 | if (ret < 0) { |
385 | dev_err(as3722->dev, "CTRLsequ1 update failed: %d\n" , ret); |
386 | return ret; |
387 | } |
388 | |
389 | ret = devm_mfd_add_devices(dev: &i2c->dev, id: -1, cells: as3722_devs, |
390 | ARRAY_SIZE(as3722_devs), NULL, irq_base: 0, |
391 | irq_domain: regmap_irq_get_domain(data: as3722->irq_data)); |
392 | if (ret) { |
393 | dev_err(as3722->dev, "Failed to add MFD devices: %d\n" , ret); |
394 | return ret; |
395 | } |
396 | |
397 | device_init_wakeup(dev: as3722->dev, enable: true); |
398 | |
399 | dev_dbg(as3722->dev, "AS3722 core driver initialized successfully\n" ); |
400 | return 0; |
401 | } |
402 | |
403 | static int __maybe_unused as3722_i2c_suspend(struct device *dev) |
404 | { |
405 | struct as3722 *as3722 = dev_get_drvdata(dev); |
406 | |
407 | if (device_may_wakeup(dev)) |
408 | enable_irq_wake(irq: as3722->chip_irq); |
409 | disable_irq(irq: as3722->chip_irq); |
410 | |
411 | return 0; |
412 | } |
413 | |
414 | static int __maybe_unused as3722_i2c_resume(struct device *dev) |
415 | { |
416 | struct as3722 *as3722 = dev_get_drvdata(dev); |
417 | |
418 | enable_irq(irq: as3722->chip_irq); |
419 | |
420 | if (device_may_wakeup(dev)) |
421 | disable_irq_wake(irq: as3722->chip_irq); |
422 | |
423 | return 0; |
424 | } |
425 | |
426 | static const struct of_device_id as3722_of_match[] = { |
427 | { .compatible = "ams,as3722" , }, |
428 | {}, |
429 | }; |
430 | MODULE_DEVICE_TABLE(of, as3722_of_match); |
431 | |
432 | static const struct i2c_device_id as3722_i2c_id[] = { |
433 | { "as3722" , 0 }, |
434 | {}, |
435 | }; |
436 | MODULE_DEVICE_TABLE(i2c, as3722_i2c_id); |
437 | |
438 | static const struct dev_pm_ops as3722_pm_ops = { |
439 | SET_SYSTEM_SLEEP_PM_OPS(as3722_i2c_suspend, as3722_i2c_resume) |
440 | }; |
441 | |
442 | static struct i2c_driver as3722_i2c_driver = { |
443 | .driver = { |
444 | .name = "as3722" , |
445 | .of_match_table = as3722_of_match, |
446 | .pm = &as3722_pm_ops, |
447 | }, |
448 | .probe = as3722_i2c_probe, |
449 | .id_table = as3722_i2c_id, |
450 | }; |
451 | |
452 | module_i2c_driver(as3722_i2c_driver); |
453 | |
454 | MODULE_DESCRIPTION("I2C support for AS3722 PMICs" ); |
455 | MODULE_AUTHOR("Florian Lobmaier <florian.lobmaier@ams.com>" ); |
456 | MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>" ); |
457 | MODULE_LICENSE("GPL" ); |
458 | |