1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Device access for Dialog DA9055 PMICs. |
4 | * |
5 | * Copyright(c) 2012 Dialog Semiconductor Ltd. |
6 | * |
7 | * Author: David Dajun Chen <dchen@diasemi.com> |
8 | */ |
9 | |
10 | #include <linux/module.h> |
11 | #include <linux/device.h> |
12 | #include <linux/input.h> |
13 | #include <linux/irq.h> |
14 | #include <linux/mutex.h> |
15 | |
16 | #include <linux/mfd/core.h> |
17 | #include <linux/mfd/da9055/core.h> |
18 | #include <linux/mfd/da9055/pdata.h> |
19 | #include <linux/mfd/da9055/reg.h> |
20 | |
21 | #define DA9055_IRQ_NONKEY_MASK 0x01 |
22 | #define DA9055_IRQ_ALM_MASK 0x02 |
23 | #define DA9055_IRQ_TICK_MASK 0x04 |
24 | #define DA9055_IRQ_ADC_MASK 0x08 |
25 | #define DA9055_IRQ_BUCK_ILIM_MASK 0x08 |
26 | |
27 | static bool da9055_register_readable(struct device *dev, unsigned int reg) |
28 | { |
29 | switch (reg) { |
30 | case DA9055_REG_STATUS_A: |
31 | case DA9055_REG_STATUS_B: |
32 | case DA9055_REG_EVENT_A: |
33 | case DA9055_REG_EVENT_B: |
34 | case DA9055_REG_EVENT_C: |
35 | case DA9055_REG_IRQ_MASK_A: |
36 | case DA9055_REG_IRQ_MASK_B: |
37 | case DA9055_REG_IRQ_MASK_C: |
38 | |
39 | case DA9055_REG_CONTROL_A: |
40 | case DA9055_REG_CONTROL_B: |
41 | case DA9055_REG_CONTROL_C: |
42 | case DA9055_REG_CONTROL_D: |
43 | case DA9055_REG_CONTROL_E: |
44 | |
45 | case DA9055_REG_ADC_MAN: |
46 | case DA9055_REG_ADC_CONT: |
47 | case DA9055_REG_VSYS_MON: |
48 | case DA9055_REG_ADC_RES_L: |
49 | case DA9055_REG_ADC_RES_H: |
50 | case DA9055_REG_VSYS_RES: |
51 | case DA9055_REG_ADCIN1_RES: |
52 | case DA9055_REG_ADCIN2_RES: |
53 | case DA9055_REG_ADCIN3_RES: |
54 | |
55 | case DA9055_REG_COUNT_S: |
56 | case DA9055_REG_COUNT_MI: |
57 | case DA9055_REG_COUNT_H: |
58 | case DA9055_REG_COUNT_D: |
59 | case DA9055_REG_COUNT_MO: |
60 | case DA9055_REG_COUNT_Y: |
61 | case DA9055_REG_ALARM_H: |
62 | case DA9055_REG_ALARM_D: |
63 | case DA9055_REG_ALARM_MI: |
64 | case DA9055_REG_ALARM_MO: |
65 | case DA9055_REG_ALARM_Y: |
66 | |
67 | case DA9055_REG_GPIO0_1: |
68 | case DA9055_REG_GPIO2: |
69 | case DA9055_REG_GPIO_MODE0_2: |
70 | |
71 | case DA9055_REG_BCORE_CONT: |
72 | case DA9055_REG_BMEM_CONT: |
73 | case DA9055_REG_LDO1_CONT: |
74 | case DA9055_REG_LDO2_CONT: |
75 | case DA9055_REG_LDO3_CONT: |
76 | case DA9055_REG_LDO4_CONT: |
77 | case DA9055_REG_LDO5_CONT: |
78 | case DA9055_REG_LDO6_CONT: |
79 | case DA9055_REG_BUCK_LIM: |
80 | case DA9055_REG_BCORE_MODE: |
81 | case DA9055_REG_VBCORE_A: |
82 | case DA9055_REG_VBMEM_A: |
83 | case DA9055_REG_VLDO1_A: |
84 | case DA9055_REG_VLDO2_A: |
85 | case DA9055_REG_VLDO3_A: |
86 | case DA9055_REG_VLDO4_A: |
87 | case DA9055_REG_VLDO5_A: |
88 | case DA9055_REG_VLDO6_A: |
89 | case DA9055_REG_VBCORE_B: |
90 | case DA9055_REG_VBMEM_B: |
91 | case DA9055_REG_VLDO1_B: |
92 | case DA9055_REG_VLDO2_B: |
93 | case DA9055_REG_VLDO3_B: |
94 | case DA9055_REG_VLDO4_B: |
95 | case DA9055_REG_VLDO5_B: |
96 | case DA9055_REG_VLDO6_B: |
97 | return true; |
98 | default: |
99 | return false; |
100 | } |
101 | } |
102 | |
103 | static bool da9055_register_writeable(struct device *dev, unsigned int reg) |
104 | { |
105 | switch (reg) { |
106 | case DA9055_REG_STATUS_A: |
107 | case DA9055_REG_STATUS_B: |
108 | case DA9055_REG_EVENT_A: |
109 | case DA9055_REG_EVENT_B: |
110 | case DA9055_REG_EVENT_C: |
111 | case DA9055_REG_IRQ_MASK_A: |
112 | case DA9055_REG_IRQ_MASK_B: |
113 | case DA9055_REG_IRQ_MASK_C: |
114 | |
115 | case DA9055_REG_CONTROL_A: |
116 | case DA9055_REG_CONTROL_B: |
117 | case DA9055_REG_CONTROL_C: |
118 | case DA9055_REG_CONTROL_D: |
119 | case DA9055_REG_CONTROL_E: |
120 | |
121 | case DA9055_REG_ADC_MAN: |
122 | case DA9055_REG_ADC_CONT: |
123 | case DA9055_REG_VSYS_MON: |
124 | case DA9055_REG_ADC_RES_L: |
125 | case DA9055_REG_ADC_RES_H: |
126 | case DA9055_REG_VSYS_RES: |
127 | case DA9055_REG_ADCIN1_RES: |
128 | case DA9055_REG_ADCIN2_RES: |
129 | case DA9055_REG_ADCIN3_RES: |
130 | |
131 | case DA9055_REG_COUNT_S: |
132 | case DA9055_REG_COUNT_MI: |
133 | case DA9055_REG_COUNT_H: |
134 | case DA9055_REG_COUNT_D: |
135 | case DA9055_REG_COUNT_MO: |
136 | case DA9055_REG_COUNT_Y: |
137 | case DA9055_REG_ALARM_H: |
138 | case DA9055_REG_ALARM_D: |
139 | case DA9055_REG_ALARM_MI: |
140 | case DA9055_REG_ALARM_MO: |
141 | case DA9055_REG_ALARM_Y: |
142 | |
143 | case DA9055_REG_GPIO0_1: |
144 | case DA9055_REG_GPIO2: |
145 | case DA9055_REG_GPIO_MODE0_2: |
146 | |
147 | case DA9055_REG_BCORE_CONT: |
148 | case DA9055_REG_BMEM_CONT: |
149 | case DA9055_REG_LDO1_CONT: |
150 | case DA9055_REG_LDO2_CONT: |
151 | case DA9055_REG_LDO3_CONT: |
152 | case DA9055_REG_LDO4_CONT: |
153 | case DA9055_REG_LDO5_CONT: |
154 | case DA9055_REG_LDO6_CONT: |
155 | case DA9055_REG_BUCK_LIM: |
156 | case DA9055_REG_BCORE_MODE: |
157 | case DA9055_REG_VBCORE_A: |
158 | case DA9055_REG_VBMEM_A: |
159 | case DA9055_REG_VLDO1_A: |
160 | case DA9055_REG_VLDO2_A: |
161 | case DA9055_REG_VLDO3_A: |
162 | case DA9055_REG_VLDO4_A: |
163 | case DA9055_REG_VLDO5_A: |
164 | case DA9055_REG_VLDO6_A: |
165 | case DA9055_REG_VBCORE_B: |
166 | case DA9055_REG_VBMEM_B: |
167 | case DA9055_REG_VLDO1_B: |
168 | case DA9055_REG_VLDO2_B: |
169 | case DA9055_REG_VLDO3_B: |
170 | case DA9055_REG_VLDO4_B: |
171 | case DA9055_REG_VLDO5_B: |
172 | case DA9055_REG_VLDO6_B: |
173 | return true; |
174 | default: |
175 | return false; |
176 | } |
177 | } |
178 | |
179 | static bool da9055_register_volatile(struct device *dev, unsigned int reg) |
180 | { |
181 | switch (reg) { |
182 | case DA9055_REG_STATUS_A: |
183 | case DA9055_REG_STATUS_B: |
184 | case DA9055_REG_EVENT_A: |
185 | case DA9055_REG_EVENT_B: |
186 | case DA9055_REG_EVENT_C: |
187 | |
188 | case DA9055_REG_CONTROL_A: |
189 | case DA9055_REG_CONTROL_E: |
190 | |
191 | case DA9055_REG_ADC_MAN: |
192 | case DA9055_REG_ADC_RES_L: |
193 | case DA9055_REG_ADC_RES_H: |
194 | case DA9055_REG_VSYS_RES: |
195 | case DA9055_REG_ADCIN1_RES: |
196 | case DA9055_REG_ADCIN2_RES: |
197 | case DA9055_REG_ADCIN3_RES: |
198 | |
199 | case DA9055_REG_COUNT_S: |
200 | case DA9055_REG_COUNT_MI: |
201 | case DA9055_REG_COUNT_H: |
202 | case DA9055_REG_COUNT_D: |
203 | case DA9055_REG_COUNT_MO: |
204 | case DA9055_REG_COUNT_Y: |
205 | case DA9055_REG_ALARM_MI: |
206 | |
207 | case DA9055_REG_BCORE_CONT: |
208 | case DA9055_REG_BMEM_CONT: |
209 | case DA9055_REG_LDO1_CONT: |
210 | case DA9055_REG_LDO2_CONT: |
211 | case DA9055_REG_LDO3_CONT: |
212 | case DA9055_REG_LDO4_CONT: |
213 | case DA9055_REG_LDO5_CONT: |
214 | case DA9055_REG_LDO6_CONT: |
215 | return true; |
216 | default: |
217 | return false; |
218 | } |
219 | } |
220 | |
221 | static const struct regmap_irq da9055_irqs[] = { |
222 | [DA9055_IRQ_NONKEY] = { |
223 | .reg_offset = 0, |
224 | .mask = DA9055_IRQ_NONKEY_MASK, |
225 | }, |
226 | [DA9055_IRQ_ALARM] = { |
227 | .reg_offset = 0, |
228 | .mask = DA9055_IRQ_ALM_MASK, |
229 | }, |
230 | [DA9055_IRQ_TICK] = { |
231 | .reg_offset = 0, |
232 | .mask = DA9055_IRQ_TICK_MASK, |
233 | }, |
234 | [DA9055_IRQ_HWMON] = { |
235 | .reg_offset = 0, |
236 | .mask = DA9055_IRQ_ADC_MASK, |
237 | }, |
238 | [DA9055_IRQ_REGULATOR] = { |
239 | .reg_offset = 1, |
240 | .mask = DA9055_IRQ_BUCK_ILIM_MASK, |
241 | }, |
242 | }; |
243 | |
244 | const struct regmap_config da9055_regmap_config = { |
245 | .reg_bits = 8, |
246 | .val_bits = 8, |
247 | |
248 | .cache_type = REGCACHE_RBTREE, |
249 | |
250 | .max_register = DA9055_MAX_REGISTER_CNT, |
251 | .readable_reg = da9055_register_readable, |
252 | .writeable_reg = da9055_register_writeable, |
253 | .volatile_reg = da9055_register_volatile, |
254 | }; |
255 | EXPORT_SYMBOL_GPL(da9055_regmap_config); |
256 | |
257 | static const struct resource da9055_onkey_resource = |
258 | DEFINE_RES_IRQ_NAMED(DA9055_IRQ_NONKEY, "ONKEY" ); |
259 | |
260 | static const struct resource da9055_rtc_resource[] = { |
261 | DEFINE_RES_IRQ_NAMED(DA9055_IRQ_ALARM, "ALM" ), |
262 | DEFINE_RES_IRQ_NAMED(DA9055_IRQ_TICK, "TICK" ), |
263 | }; |
264 | |
265 | static const struct resource da9055_hwmon_resource = |
266 | DEFINE_RES_IRQ_NAMED(DA9055_IRQ_HWMON, "HWMON" ); |
267 | |
268 | static const struct resource da9055_ld05_6_resource = |
269 | DEFINE_RES_IRQ_NAMED(DA9055_IRQ_REGULATOR, "REGULATOR" ); |
270 | |
271 | static const struct mfd_cell da9055_devs[] = { |
272 | { |
273 | .of_compatible = "dlg,da9055-gpio" , |
274 | .name = "da9055-gpio" , |
275 | }, |
276 | { |
277 | .of_compatible = "dlg,da9055-regulator" , |
278 | .name = "da9055-regulator" , |
279 | .id = 1, |
280 | }, |
281 | { |
282 | .of_compatible = "dlg,da9055-regulator" , |
283 | .name = "da9055-regulator" , |
284 | .id = 2, |
285 | }, |
286 | { |
287 | .of_compatible = "dlg,da9055-regulator" , |
288 | .name = "da9055-regulator" , |
289 | .id = 3, |
290 | }, |
291 | { |
292 | .of_compatible = "dlg,da9055-regulator" , |
293 | .name = "da9055-regulator" , |
294 | .id = 4, |
295 | }, |
296 | { |
297 | .of_compatible = "dlg,da9055-regulator" , |
298 | .name = "da9055-regulator" , |
299 | .id = 5, |
300 | }, |
301 | { |
302 | .of_compatible = "dlg,da9055-regulator" , |
303 | .name = "da9055-regulator" , |
304 | .id = 6, |
305 | }, |
306 | { |
307 | .of_compatible = "dlg,da9055-regulator" , |
308 | .name = "da9055-regulator" , |
309 | .id = 7, |
310 | .resources = &da9055_ld05_6_resource, |
311 | .num_resources = 1, |
312 | }, |
313 | { |
314 | .of_compatible = "dlg,da9055-regulator" , |
315 | .name = "da9055-regulator" , |
316 | .resources = &da9055_ld05_6_resource, |
317 | .num_resources = 1, |
318 | .id = 8, |
319 | }, |
320 | { |
321 | .of_compatible = "dlg,da9055-onkey" , |
322 | .name = "da9055-onkey" , |
323 | .resources = &da9055_onkey_resource, |
324 | .num_resources = 1, |
325 | }, |
326 | { |
327 | .of_compatible = "dlg,da9055-rtc" , |
328 | .name = "da9055-rtc" , |
329 | .resources = da9055_rtc_resource, |
330 | .num_resources = ARRAY_SIZE(da9055_rtc_resource), |
331 | }, |
332 | { |
333 | .of_compatible = "dlg,da9055-hwmon" , |
334 | .name = "da9055-hwmon" , |
335 | .resources = &da9055_hwmon_resource, |
336 | .num_resources = 1, |
337 | }, |
338 | { |
339 | .of_compatible = "dlg,da9055-watchdog" , |
340 | .name = "da9055-watchdog" , |
341 | }, |
342 | }; |
343 | |
344 | static const struct regmap_irq_chip da9055_regmap_irq_chip = { |
345 | .name = "da9055_irq" , |
346 | .status_base = DA9055_REG_EVENT_A, |
347 | .mask_base = DA9055_REG_IRQ_MASK_A, |
348 | .ack_base = DA9055_REG_EVENT_A, |
349 | .num_regs = 3, |
350 | .irqs = da9055_irqs, |
351 | .num_irqs = ARRAY_SIZE(da9055_irqs), |
352 | }; |
353 | |
354 | int da9055_device_init(struct da9055 *da9055) |
355 | { |
356 | struct da9055_pdata *pdata = dev_get_platdata(dev: da9055->dev); |
357 | int ret; |
358 | uint8_t clear_events[3] = {0xFF, 0xFF, 0xFF}; |
359 | |
360 | if (pdata && pdata->init != NULL) |
361 | pdata->init(da9055); |
362 | |
363 | if (!pdata || !pdata->irq_base) |
364 | da9055->irq_base = -1; |
365 | else |
366 | da9055->irq_base = pdata->irq_base; |
367 | |
368 | ret = da9055_group_write(da9055, DA9055_REG_EVENT_A, reg_cnt: 3, val: clear_events); |
369 | if (ret < 0) |
370 | return ret; |
371 | |
372 | ret = regmap_add_irq_chip(map: da9055->regmap, irq: da9055->chip_irq, |
373 | IRQF_TRIGGER_LOW | IRQF_ONESHOT, |
374 | irq_base: da9055->irq_base, chip: &da9055_regmap_irq_chip, |
375 | data: &da9055->irq_data); |
376 | if (ret < 0) |
377 | return ret; |
378 | |
379 | da9055->irq_base = regmap_irq_chip_get_base(data: da9055->irq_data); |
380 | |
381 | ret = mfd_add_devices(parent: da9055->dev, id: -1, |
382 | cells: da9055_devs, ARRAY_SIZE(da9055_devs), |
383 | NULL, irq_base: da9055->irq_base, NULL); |
384 | if (ret) |
385 | goto err; |
386 | |
387 | return 0; |
388 | |
389 | err: |
390 | mfd_remove_devices(parent: da9055->dev); |
391 | return ret; |
392 | } |
393 | |
394 | void da9055_device_exit(struct da9055 *da9055) |
395 | { |
396 | regmap_del_irq_chip(irq: da9055->chip_irq, data: da9055->irq_data); |
397 | mfd_remove_devices(parent: da9055->dev); |
398 | } |
399 | |
400 | MODULE_DESCRIPTION("Core support for the DA9055 PMIC" ); |
401 | MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>" ); |
402 | |