Commit | Line | Data |
---|---|---|
2874c5fd | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
e69b6de1 MB |
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> | |
e69b6de1 MB |
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> | |
78bb3688 | 15 | #include <linux/list.h> |
e69b6de1 MB |
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 | ||
78bb3688 MB |
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) | |
e69b6de1 | 33 | { |
78bb3688 MB |
34 | struct wm831x_auxadc_req *req; |
35 | int ret; | |
36 | bool ena = false; | |
37 | ||
38 | req = kzalloc(sizeof(*req), GFP_KERNEL); | |
39 | if (!req) | |
40 | return -ENOMEM; | |
41 | ||
42 | init_completion(&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(&req->list, &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 | 1 << input, 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(&wm831x->auxadc_lock); | |
91 | ||
92 | /* Wait for an interrupt */ | |
93 | wait_for_completion_timeout(&req->done, msecs_to_jiffies(500)); | |
94 | ||
95 | mutex_lock(&wm831x->auxadc_lock); | |
78bb3688 MB |
96 | ret = req->val; |
97 | ||
98 | out: | |
26783d74 | 99 | list_del(&req->list); |
78bb3688 MB |
100 | mutex_unlock(&wm831x->auxadc_lock); |
101 | ||
102 | kfree(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; | |
e69b6de1 | 125 | |
78bb3688 MB |
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 | 1 << input, 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, 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(&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; | |
e69b6de1 MB |
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 | 1 << src); | |
170 | if (ret < 0) { | |
171 | dev_err(wm831x->dev, "Failed to set AUXADC source: %d\n", ret); | |
172 | goto out; | |
173 | } | |
174 | ||
e69b6de1 MB |
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 | ||
78bb3688 MB |
182 | /* If we're not using interrupts then poll the |
183 | * interrupt status register */ | |
184 | timeout = 5; | |
185 | while (timeout) { | |
186 | msleep(1); | |
e69b6de1 | 187 | |
78bb3688 MB |
188 | ret = wm831x_reg_read(wm831x, |
189 | WM831X_INTERRUPT_STATUS_1); | |
e69b6de1 MB |
190 | if (ret < 0) { |
191 | dev_err(wm831x->dev, | |
78bb3688 | 192 | "ISR 1 read failed: %d\n", ret); |
e69b6de1 MB |
193 | goto disable; |
194 | } | |
195 | ||
78bb3688 MB |
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"); | |
e69b6de1 MB |
205 | ret = -EBUSY; |
206 | goto disable; | |
207 | } | |
208 | } | |
209 | ||
78bb3688 MB |
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) | |
e69b6de1 MB |
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 { | |
78bb3688 | 228 | ret &= WM831X_AUX_DATA_MASK; |
e69b6de1 MB |
229 | } |
230 | ||
231 | disable: | |
232 | wm831x_set_bits(wm831x, WM831X_AUXADC_CONTROL, WM831X_AUX_ENA, 0); | |
233 | out: | |
234 | mutex_unlock(&wm831x->auxadc_lock); | |
235 | return ret; | |
236 | } | |
e69b6de1 | 237 | |
78bb3688 MB |
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) | |
e69b6de1 | 245 | { |
78bb3688 | 246 | return wm831x->auxadc_read(wm831x, input); |
e69b6de1 | 247 | } |
78bb3688 | 248 | EXPORT_SYMBOL_GPL(wm831x_auxadc_read); |
e69b6de1 MB |
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); | |
78bb3688 MB |
275 | INIT_LIST_HEAD(&wm831x->auxadc_pending); |
276 | ||
cd99758b | 277 | if (wm831x->irq) { |
78bb3688 | 278 | wm831x->auxadc_read = wm831x_auxadc_read_irq; |
e69b6de1 | 279 | |
cd99758b MB |
280 | ret = request_threaded_irq(wm831x_irq(wm831x, |
281 | WM831X_IRQ_AUXADC_DATA), | |
1a52598c FE |
282 | NULL, wm831x_auxadc_irq, |
283 | IRQF_ONESHOT, | |
e69b6de1 | 284 | "auxadc", wm831x); |
78bb3688 | 285 | if (ret < 0) { |
e69b6de1 MB |
286 | dev_err(wm831x->dev, "AUXADC IRQ request failed: %d\n", |
287 | ret); | |
78bb3688 MB |
288 | wm831x->auxadc_read = NULL; |
289 | } | |
e69b6de1 | 290 | } |
78bb3688 MB |
291 | |
292 | if (!wm831x->auxadc_read) | |
293 | wm831x->auxadc_read = wm831x_auxadc_read_polled; | |
e69b6de1 | 294 | } |