Commit | Line | Data |
---|---|---|
2874c5fd | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2896434c AJ |
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> | |
2896434c AJ |
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 | ||
7ce7b26f | 221 | static const struct regmap_irq da9055_irqs[] = { |
2896434c AJ |
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 | ||
7ce7b26f | 244 | const struct regmap_config da9055_regmap_config = { |
2896434c AJ |
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 | ||
5ac4b850 ZL |
257 | static const struct resource da9055_onkey_resource = |
258 | DEFINE_RES_IRQ_NAMED(DA9055_IRQ_NONKEY, "ONKEY"); | |
2896434c | 259 | |
a0fa0abe | 260 | static const struct resource da9055_rtc_resource[] = { |
5ac4b850 ZL |
261 | DEFINE_RES_IRQ_NAMED(DA9055_IRQ_ALARM, "ALM"), |
262 | DEFINE_RES_IRQ_NAMED(DA9055_IRQ_TICK, "TICK"), | |
2896434c AJ |
263 | }; |
264 | ||
5ac4b850 ZL |
265 | static const struct resource da9055_hwmon_resource = |
266 | DEFINE_RES_IRQ_NAMED(DA9055_IRQ_HWMON, "HWMON"); | |
2896434c | 267 | |
5ac4b850 ZL |
268 | static const struct resource da9055_ld05_6_resource = |
269 | DEFINE_RES_IRQ_NAMED(DA9055_IRQ_REGULATOR, "REGULATOR"); | |
2896434c | 270 | |
c8f675ff | 271 | static const struct mfd_cell da9055_devs[] = { |
2896434c | 272 | { |
bd597f47 | 273 | .of_compatible = "dlg,da9055-gpio", |
2896434c AJ |
274 | .name = "da9055-gpio", |
275 | }, | |
276 | { | |
bd597f47 | 277 | .of_compatible = "dlg,da9055-regulator", |
2896434c AJ |
278 | .name = "da9055-regulator", |
279 | .id = 1, | |
280 | }, | |
281 | { | |
bd597f47 | 282 | .of_compatible = "dlg,da9055-regulator", |
2896434c AJ |
283 | .name = "da9055-regulator", |
284 | .id = 2, | |
285 | }, | |
286 | { | |
bd597f47 | 287 | .of_compatible = "dlg,da9055-regulator", |
2896434c AJ |
288 | .name = "da9055-regulator", |
289 | .id = 3, | |
290 | }, | |
291 | { | |
bd597f47 | 292 | .of_compatible = "dlg,da9055-regulator", |
2896434c AJ |
293 | .name = "da9055-regulator", |
294 | .id = 4, | |
295 | }, | |
296 | { | |
bd597f47 | 297 | .of_compatible = "dlg,da9055-regulator", |
2896434c AJ |
298 | .name = "da9055-regulator", |
299 | .id = 5, | |
300 | }, | |
301 | { | |
bd597f47 | 302 | .of_compatible = "dlg,da9055-regulator", |
2896434c AJ |
303 | .name = "da9055-regulator", |
304 | .id = 6, | |
305 | }, | |
306 | { | |
bd597f47 | 307 | .of_compatible = "dlg,da9055-regulator", |
2896434c AJ |
308 | .name = "da9055-regulator", |
309 | .id = 7, | |
310 | .resources = &da9055_ld05_6_resource, | |
311 | .num_resources = 1, | |
312 | }, | |
313 | { | |
bd597f47 | 314 | .of_compatible = "dlg,da9055-regulator", |
2896434c AJ |
315 | .name = "da9055-regulator", |
316 | .resources = &da9055_ld05_6_resource, | |
317 | .num_resources = 1, | |
318 | .id = 8, | |
319 | }, | |
320 | { | |
bd597f47 | 321 | .of_compatible = "dlg,da9055-onkey", |
2896434c AJ |
322 | .name = "da9055-onkey", |
323 | .resources = &da9055_onkey_resource, | |
324 | .num_resources = 1, | |
325 | }, | |
326 | { | |
bd597f47 | 327 | .of_compatible = "dlg,da9055-rtc", |
2896434c AJ |
328 | .name = "da9055-rtc", |
329 | .resources = da9055_rtc_resource, | |
330 | .num_resources = ARRAY_SIZE(da9055_rtc_resource), | |
331 | }, | |
332 | { | |
bd597f47 | 333 | .of_compatible = "dlg,da9055-hwmon", |
2896434c AJ |
334 | .name = "da9055-hwmon", |
335 | .resources = &da9055_hwmon_resource, | |
336 | .num_resources = 1, | |
337 | }, | |
338 | { | |
bd597f47 | 339 | .of_compatible = "dlg,da9055-watchdog", |
2896434c AJ |
340 | .name = "da9055-watchdog", |
341 | }, | |
342 | }; | |
343 | ||
7ce7b26f | 344 | static const struct regmap_irq_chip da9055_regmap_irq_chip = { |
2896434c AJ |
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 | ||
f791be49 | 354 | int da9055_device_init(struct da9055 *da9055) |
2896434c | 355 | { |
334a41ce | 356 | struct da9055_pdata *pdata = dev_get_platdata(da9055->dev); |
2896434c | 357 | int ret; |
4b3b4a50 | 358 | uint8_t clear_events[3] = {0xFF, 0xFF, 0xFF}; |
2896434c AJ |
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 | ||
4b3b4a50 AR |
368 | ret = da9055_group_write(da9055, DA9055_REG_EVENT_A, 3, clear_events); |
369 | if (ret < 0) | |
370 | return ret; | |
371 | ||
2896434c | 372 | ret = regmap_add_irq_chip(da9055->regmap, da9055->chip_irq, |
3cec5f4e | 373 | IRQF_TRIGGER_LOW | IRQF_ONESHOT, |
2896434c AJ |
374 | da9055->irq_base, &da9055_regmap_irq_chip, |
375 | &da9055->irq_data); | |
376 | if (ret < 0) | |
377 | return ret; | |
378 | ||
379 | da9055->irq_base = regmap_irq_chip_get_base(da9055->irq_data); | |
380 | ||
381 | ret = mfd_add_devices(da9055->dev, -1, | |
382 | da9055_devs, ARRAY_SIZE(da9055_devs), | |
383 | NULL, da9055->irq_base, NULL); | |
384 | if (ret) | |
385 | goto err; | |
386 | ||
387 | return 0; | |
388 | ||
389 | err: | |
390 | mfd_remove_devices(da9055->dev); | |
391 | return ret; | |
392 | } | |
393 | ||
4740f73f | 394 | void da9055_device_exit(struct da9055 *da9055) |
2896434c AJ |
395 | { |
396 | regmap_del_irq_chip(da9055->chip_irq, da9055->irq_data); | |
397 | mfd_remove_devices(da9055->dev); | |
398 | } | |
399 | ||
400 | MODULE_DESCRIPTION("Core support for the DA9055 PMIC"); | |
401 | MODULE_LICENSE("GPL"); | |
402 | MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>"); |