1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * Device access for Dialog DA9063 modules |
4 | * |
5 | * Copyright 2012 Dialog Semiconductors Ltd. |
6 | * Copyright 2013 Philipp Zabel, Pengutronix |
7 | * |
8 | * Author: Krystian Garbaciak, Dialog Semiconductor |
9 | * Author: Michal Hajduk, Dialog Semiconductor |
10 | * |
11 | */ |
12 | |
13 | #include <linux/kernel.h> |
14 | #include <linux/module.h> |
15 | #include <linux/init.h> |
16 | #include <linux/slab.h> |
17 | #include <linux/device.h> |
18 | #include <linux/delay.h> |
19 | #include <linux/interrupt.h> |
20 | #include <linux/mutex.h> |
21 | #include <linux/mfd/core.h> |
22 | #include <linux/regmap.h> |
23 | |
24 | #include <linux/mfd/da9063/core.h> |
25 | #include <linux/mfd/da9063/registers.h> |
26 | |
27 | #include <linux/proc_fs.h> |
28 | #include <linux/kthread.h> |
29 | #include <linux/uaccess.h> |
30 | |
31 | |
32 | static const struct resource da9063_regulators_resources[] = { |
33 | { |
34 | .name = "LDO_LIM" , |
35 | .start = DA9063_IRQ_LDO_LIM, |
36 | .end = DA9063_IRQ_LDO_LIM, |
37 | .flags = IORESOURCE_IRQ, |
38 | }, |
39 | }; |
40 | |
41 | static const struct resource da9063_rtc_resources[] = { |
42 | { |
43 | .name = "ALARM" , |
44 | .start = DA9063_IRQ_ALARM, |
45 | .end = DA9063_IRQ_ALARM, |
46 | .flags = IORESOURCE_IRQ, |
47 | }, |
48 | { |
49 | .name = "TICK" , |
50 | .start = DA9063_IRQ_TICK, |
51 | .end = DA9063_IRQ_TICK, |
52 | .flags = IORESOURCE_IRQ, |
53 | } |
54 | }; |
55 | |
56 | static const struct resource da9063_onkey_resources[] = { |
57 | { |
58 | .name = "ONKEY" , |
59 | .start = DA9063_IRQ_ONKEY, |
60 | .end = DA9063_IRQ_ONKEY, |
61 | .flags = IORESOURCE_IRQ, |
62 | }, |
63 | }; |
64 | |
65 | static const struct resource da9063_hwmon_resources[] = { |
66 | { |
67 | .start = DA9063_IRQ_ADC_RDY, |
68 | .end = DA9063_IRQ_ADC_RDY, |
69 | .flags = IORESOURCE_IRQ, |
70 | }, |
71 | }; |
72 | |
73 | |
74 | static const struct mfd_cell da9063_common_devs[] = { |
75 | { |
76 | .name = DA9063_DRVNAME_REGULATORS, |
77 | .num_resources = ARRAY_SIZE(da9063_regulators_resources), |
78 | .resources = da9063_regulators_resources, |
79 | }, |
80 | { |
81 | .name = DA9063_DRVNAME_LEDS, |
82 | }, |
83 | { |
84 | .name = DA9063_DRVNAME_WATCHDOG, |
85 | .of_compatible = "dlg,da9063-watchdog" , |
86 | }, |
87 | { |
88 | .name = DA9063_DRVNAME_HWMON, |
89 | .num_resources = ARRAY_SIZE(da9063_hwmon_resources), |
90 | .resources = da9063_hwmon_resources, |
91 | }, |
92 | { |
93 | .name = DA9063_DRVNAME_ONKEY, |
94 | .num_resources = ARRAY_SIZE(da9063_onkey_resources), |
95 | .resources = da9063_onkey_resources, |
96 | .of_compatible = "dlg,da9063-onkey" , |
97 | }, |
98 | { |
99 | .name = DA9063_DRVNAME_VIBRATION, |
100 | }, |
101 | }; |
102 | |
103 | /* Only present on DA9063 , not on DA9063L */ |
104 | static const struct mfd_cell da9063_devs[] = { |
105 | { |
106 | .name = DA9063_DRVNAME_RTC, |
107 | .num_resources = ARRAY_SIZE(da9063_rtc_resources), |
108 | .resources = da9063_rtc_resources, |
109 | .of_compatible = "dlg,da9063-rtc" , |
110 | }, |
111 | }; |
112 | |
113 | static int da9063_clear_fault_log(struct da9063 *da9063) |
114 | { |
115 | int ret = 0; |
116 | int fault_log = 0; |
117 | |
118 | ret = regmap_read(map: da9063->regmap, DA9063_REG_FAULT_LOG, val: &fault_log); |
119 | if (ret < 0) { |
120 | dev_err(da9063->dev, "Cannot read FAULT_LOG.\n" ); |
121 | return -EIO; |
122 | } |
123 | |
124 | if (fault_log) { |
125 | if (fault_log & DA9063_TWD_ERROR) |
126 | dev_dbg(da9063->dev, |
127 | "Fault log entry detected: DA9063_TWD_ERROR\n" ); |
128 | if (fault_log & DA9063_POR) |
129 | dev_dbg(da9063->dev, |
130 | "Fault log entry detected: DA9063_POR\n" ); |
131 | if (fault_log & DA9063_VDD_FAULT) |
132 | dev_dbg(da9063->dev, |
133 | "Fault log entry detected: DA9063_VDD_FAULT\n" ); |
134 | if (fault_log & DA9063_VDD_START) |
135 | dev_dbg(da9063->dev, |
136 | "Fault log entry detected: DA9063_VDD_START\n" ); |
137 | if (fault_log & DA9063_TEMP_CRIT) |
138 | dev_dbg(da9063->dev, |
139 | "Fault log entry detected: DA9063_TEMP_CRIT\n" ); |
140 | if (fault_log & DA9063_KEY_RESET) |
141 | dev_dbg(da9063->dev, |
142 | "Fault log entry detected: DA9063_KEY_RESET\n" ); |
143 | if (fault_log & DA9063_NSHUTDOWN) |
144 | dev_dbg(da9063->dev, |
145 | "Fault log entry detected: DA9063_NSHUTDOWN\n" ); |
146 | if (fault_log & DA9063_WAIT_SHUT) |
147 | dev_dbg(da9063->dev, |
148 | "Fault log entry detected: DA9063_WAIT_SHUT\n" ); |
149 | } |
150 | |
151 | ret = regmap_write(map: da9063->regmap, |
152 | DA9063_REG_FAULT_LOG, |
153 | val: fault_log); |
154 | if (ret < 0) |
155 | dev_err(da9063->dev, |
156 | "Cannot reset FAULT_LOG values %d\n" , ret); |
157 | |
158 | return ret; |
159 | } |
160 | |
161 | int da9063_device_init(struct da9063 *da9063, unsigned int irq) |
162 | { |
163 | int ret; |
164 | |
165 | ret = da9063_clear_fault_log(da9063); |
166 | if (ret < 0) |
167 | dev_err(da9063->dev, "Cannot clear fault log\n" ); |
168 | |
169 | da9063->flags = 0; |
170 | da9063->irq_base = -1; |
171 | da9063->chip_irq = irq; |
172 | |
173 | ret = da9063_irq_init(da9063); |
174 | if (ret) { |
175 | dev_err(da9063->dev, "Cannot initialize interrupts.\n" ); |
176 | return ret; |
177 | } |
178 | |
179 | da9063->irq_base = regmap_irq_chip_get_base(data: da9063->regmap_irq); |
180 | |
181 | ret = devm_mfd_add_devices(dev: da9063->dev, PLATFORM_DEVID_NONE, |
182 | cells: da9063_common_devs, |
183 | ARRAY_SIZE(da9063_common_devs), |
184 | NULL, irq_base: da9063->irq_base, NULL); |
185 | if (ret) { |
186 | dev_err(da9063->dev, "Failed to add child devices\n" ); |
187 | return ret; |
188 | } |
189 | |
190 | if (da9063->type == PMIC_TYPE_DA9063) { |
191 | ret = devm_mfd_add_devices(dev: da9063->dev, PLATFORM_DEVID_NONE, |
192 | cells: da9063_devs, ARRAY_SIZE(da9063_devs), |
193 | NULL, irq_base: da9063->irq_base, NULL); |
194 | if (ret) { |
195 | dev_err(da9063->dev, "Failed to add child devices\n" ); |
196 | return ret; |
197 | } |
198 | } |
199 | |
200 | return ret; |
201 | } |
202 | |
203 | MODULE_DESCRIPTION("PMIC driver for Dialog DA9063" ); |
204 | MODULE_AUTHOR("Krystian Garbaciak" ); |
205 | MODULE_AUTHOR("Michal Hajduk" ); |
206 | MODULE_LICENSE("GPL" ); |
207 | |