1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * wm831x-auxadc.c -- AUXADC for Wolfson WM831x PMICs |
4 | * |
5 | * Copyright 2009-2011 Wolfson Microelectronics PLC. |
6 | * |
7 | * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> |
8 | */ |
9 | |
10 | #include <linux/kernel.h> |
11 | #include <linux/module.h> |
12 | #include <linux/delay.h> |
13 | #include <linux/mfd/core.h> |
14 | #include <linux/slab.h> |
15 | #include <linux/list.h> |
16 | |
17 | #include <linux/mfd/wm831x/core.h> |
18 | #include <linux/mfd/wm831x/pdata.h> |
19 | #include <linux/mfd/wm831x/irq.h> |
20 | #include <linux/mfd/wm831x/auxadc.h> |
21 | #include <linux/mfd/wm831x/otp.h> |
22 | #include <linux/mfd/wm831x/regulator.h> |
23 | |
24 | struct wm831x_auxadc_req { |
25 | struct list_head list; |
26 | enum wm831x_auxadc input; |
27 | int val; |
28 | struct completion done; |
29 | }; |
30 | |
31 | static int wm831x_auxadc_read_irq(struct wm831x *wm831x, |
32 | enum wm831x_auxadc input) |
33 | { |
34 | struct wm831x_auxadc_req *req; |
35 | int ret; |
36 | bool ena = false; |
37 | |
38 | req = kzalloc(size: sizeof(*req), GFP_KERNEL); |
39 | if (!req) |
40 | return -ENOMEM; |
41 | |
42 | init_completion(x: &req->done); |
43 | req->input = input; |
44 | req->val = -ETIMEDOUT; |
45 | |
46 | mutex_lock(&wm831x->auxadc_lock); |
47 | |
48 | /* Enqueue the request */ |
49 | list_add(new: &req->list, head: &wm831x->auxadc_pending); |
50 | |
51 | ena = !wm831x->auxadc_active; |
52 | |
53 | if (ena) { |
54 | ret = wm831x_set_bits(wm831x, WM831X_AUXADC_CONTROL, |
55 | WM831X_AUX_ENA, WM831X_AUX_ENA); |
56 | if (ret != 0) { |
57 | dev_err(wm831x->dev, "Failed to enable AUXADC: %d\n" , |
58 | ret); |
59 | goto out; |
60 | } |
61 | } |
62 | |
63 | /* Enable the conversion if not already running */ |
64 | if (!(wm831x->auxadc_active & (1 << input))) { |
65 | ret = wm831x_set_bits(wm831x, WM831X_AUXADC_SOURCE, |
66 | mask: 1 << input, val: 1 << input); |
67 | if (ret != 0) { |
68 | dev_err(wm831x->dev, |
69 | "Failed to set AUXADC source: %d\n" , ret); |
70 | goto out; |
71 | } |
72 | |
73 | wm831x->auxadc_active |= 1 << input; |
74 | } |
75 | |
76 | /* We convert at the fastest rate possible */ |
77 | if (ena) { |
78 | ret = wm831x_set_bits(wm831x, WM831X_AUXADC_CONTROL, |
79 | WM831X_AUX_CVT_ENA | |
80 | WM831X_AUX_RATE_MASK, |
81 | WM831X_AUX_CVT_ENA | |
82 | WM831X_AUX_RATE_MASK); |
83 | if (ret != 0) { |
84 | dev_err(wm831x->dev, "Failed to start AUXADC: %d\n" , |
85 | ret); |
86 | goto out; |
87 | } |
88 | } |
89 | |
90 | mutex_unlock(lock: &wm831x->auxadc_lock); |
91 | |
92 | /* Wait for an interrupt */ |
93 | wait_for_completion_timeout(x: &req->done, timeout: msecs_to_jiffies(m: 500)); |
94 | |
95 | mutex_lock(&wm831x->auxadc_lock); |
96 | ret = req->val; |
97 | |
98 | out: |
99 | list_del(entry: &req->list); |
100 | mutex_unlock(lock: &wm831x->auxadc_lock); |
101 | |
102 | kfree(objp: req); |
103 | |
104 | return ret; |
105 | } |
106 | |
107 | static irqreturn_t wm831x_auxadc_irq(int irq, void *irq_data) |
108 | { |
109 | struct wm831x *wm831x = irq_data; |
110 | struct wm831x_auxadc_req *req; |
111 | int ret, input, val; |
112 | |
113 | ret = wm831x_reg_read(wm831x, WM831X_AUXADC_DATA); |
114 | if (ret < 0) { |
115 | dev_err(wm831x->dev, |
116 | "Failed to read AUXADC data: %d\n" , ret); |
117 | return IRQ_NONE; |
118 | } |
119 | |
120 | input = ((ret & WM831X_AUX_DATA_SRC_MASK) |
121 | >> WM831X_AUX_DATA_SRC_SHIFT) - 1; |
122 | |
123 | if (input == 14) |
124 | input = WM831X_AUX_CAL; |
125 | |
126 | val = ret & WM831X_AUX_DATA_MASK; |
127 | |
128 | mutex_lock(&wm831x->auxadc_lock); |
129 | |
130 | /* Disable this conversion, we're about to complete all users */ |
131 | wm831x_set_bits(wm831x, WM831X_AUXADC_SOURCE, |
132 | mask: 1 << input, val: 0); |
133 | wm831x->auxadc_active &= ~(1 << input); |
134 | |
135 | /* Turn off the entire convertor if idle */ |
136 | if (!wm831x->auxadc_active) |
137 | wm831x_reg_write(wm831x, WM831X_AUXADC_CONTROL, val: 0); |
138 | |
139 | /* Wake up any threads waiting for this request */ |
140 | list_for_each_entry(req, &wm831x->auxadc_pending, list) { |
141 | if (req->input == input) { |
142 | req->val = val; |
143 | complete(&req->done); |
144 | } |
145 | } |
146 | |
147 | mutex_unlock(lock: &wm831x->auxadc_lock); |
148 | |
149 | return IRQ_HANDLED; |
150 | } |
151 | |
152 | static int wm831x_auxadc_read_polled(struct wm831x *wm831x, |
153 | enum wm831x_auxadc input) |
154 | { |
155 | int ret, src, timeout; |
156 | |
157 | mutex_lock(&wm831x->auxadc_lock); |
158 | |
159 | ret = wm831x_set_bits(wm831x, WM831X_AUXADC_CONTROL, |
160 | WM831X_AUX_ENA, WM831X_AUX_ENA); |
161 | if (ret < 0) { |
162 | dev_err(wm831x->dev, "Failed to enable AUXADC: %d\n" , ret); |
163 | goto out; |
164 | } |
165 | |
166 | /* We force a single source at present */ |
167 | src = input; |
168 | ret = wm831x_reg_write(wm831x, WM831X_AUXADC_SOURCE, |
169 | val: 1 << src); |
170 | if (ret < 0) { |
171 | dev_err(wm831x->dev, "Failed to set AUXADC source: %d\n" , ret); |
172 | goto out; |
173 | } |
174 | |
175 | ret = wm831x_set_bits(wm831x, WM831X_AUXADC_CONTROL, |
176 | WM831X_AUX_CVT_ENA, WM831X_AUX_CVT_ENA); |
177 | if (ret < 0) { |
178 | dev_err(wm831x->dev, "Failed to start AUXADC: %d\n" , ret); |
179 | goto disable; |
180 | } |
181 | |
182 | /* If we're not using interrupts then poll the |
183 | * interrupt status register */ |
184 | timeout = 5; |
185 | while (timeout) { |
186 | msleep(msecs: 1); |
187 | |
188 | ret = wm831x_reg_read(wm831x, |
189 | WM831X_INTERRUPT_STATUS_1); |
190 | if (ret < 0) { |
191 | dev_err(wm831x->dev, |
192 | "ISR 1 read failed: %d\n" , ret); |
193 | goto disable; |
194 | } |
195 | |
196 | /* Did it complete? */ |
197 | if (ret & WM831X_AUXADC_DATA_EINT) { |
198 | wm831x_reg_write(wm831x, |
199 | WM831X_INTERRUPT_STATUS_1, |
200 | WM831X_AUXADC_DATA_EINT); |
201 | break; |
202 | } else { |
203 | dev_err(wm831x->dev, |
204 | "AUXADC conversion timeout\n" ); |
205 | ret = -EBUSY; |
206 | goto disable; |
207 | } |
208 | } |
209 | |
210 | ret = wm831x_reg_read(wm831x, WM831X_AUXADC_DATA); |
211 | if (ret < 0) { |
212 | dev_err(wm831x->dev, |
213 | "Failed to read AUXADC data: %d\n" , ret); |
214 | goto disable; |
215 | } |
216 | |
217 | src = ((ret & WM831X_AUX_DATA_SRC_MASK) |
218 | >> WM831X_AUX_DATA_SRC_SHIFT) - 1; |
219 | |
220 | if (src == 14) |
221 | src = WM831X_AUX_CAL; |
222 | |
223 | if (src != input) { |
224 | dev_err(wm831x->dev, "Data from source %d not %d\n" , |
225 | src, input); |
226 | ret = -EINVAL; |
227 | } else { |
228 | ret &= WM831X_AUX_DATA_MASK; |
229 | } |
230 | |
231 | disable: |
232 | wm831x_set_bits(wm831x, WM831X_AUXADC_CONTROL, WM831X_AUX_ENA, val: 0); |
233 | out: |
234 | mutex_unlock(lock: &wm831x->auxadc_lock); |
235 | return ret; |
236 | } |
237 | |
238 | /** |
239 | * wm831x_auxadc_read: Read a value from the WM831x AUXADC |
240 | * |
241 | * @wm831x: Device to read from. |
242 | * @input: AUXADC input to read. |
243 | */ |
244 | int wm831x_auxadc_read(struct wm831x *wm831x, enum wm831x_auxadc input) |
245 | { |
246 | return wm831x->auxadc_read(wm831x, input); |
247 | } |
248 | EXPORT_SYMBOL_GPL(wm831x_auxadc_read); |
249 | |
250 | /** |
251 | * wm831x_auxadc_read_uv: Read a voltage from the WM831x AUXADC |
252 | * |
253 | * @wm831x: Device to read from. |
254 | * @input: AUXADC input to read. |
255 | */ |
256 | int wm831x_auxadc_read_uv(struct wm831x *wm831x, enum wm831x_auxadc input) |
257 | { |
258 | int ret; |
259 | |
260 | ret = wm831x_auxadc_read(wm831x, input); |
261 | if (ret < 0) |
262 | return ret; |
263 | |
264 | ret *= 1465; |
265 | |
266 | return ret; |
267 | } |
268 | EXPORT_SYMBOL_GPL(wm831x_auxadc_read_uv); |
269 | |
270 | void wm831x_auxadc_init(struct wm831x *wm831x) |
271 | { |
272 | int ret; |
273 | |
274 | mutex_init(&wm831x->auxadc_lock); |
275 | INIT_LIST_HEAD(list: &wm831x->auxadc_pending); |
276 | |
277 | if (wm831x->irq) { |
278 | wm831x->auxadc_read = wm831x_auxadc_read_irq; |
279 | |
280 | ret = request_threaded_irq(irq: wm831x_irq(wm831x, |
281 | WM831X_IRQ_AUXADC_DATA), |
282 | NULL, thread_fn: wm831x_auxadc_irq, |
283 | IRQF_ONESHOT, |
284 | name: "auxadc" , dev: wm831x); |
285 | if (ret < 0) { |
286 | dev_err(wm831x->dev, "AUXADC IRQ request failed: %d\n" , |
287 | ret); |
288 | wm831x->auxadc_read = NULL; |
289 | } |
290 | } |
291 | |
292 | if (!wm831x->auxadc_read) |
293 | wm831x->auxadc_read = wm831x_auxadc_read_polled; |
294 | } |
295 | |