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