Root/
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> |
20 | #include <linux/list.h> |
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 | |
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) |
38 | { |
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; |
131 | |
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; |
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 | |
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 | |
188 | /* If we're not using interrupts then poll the |
189 | * interrupt status register */ |
190 | timeout = 5; |
191 | while (timeout) { |
192 | msleep(1); |
193 | |
194 | ret = wm831x_reg_read(wm831x, |
195 | WM831X_INTERRUPT_STATUS_1); |
196 | if (ret < 0) { |
197 | dev_err(wm831x->dev, |
198 | "ISR 1 read failed: %d\n", ret); |
199 | goto disable; |
200 | } |
201 | |
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"); |
211 | ret = -EBUSY; |
212 | goto disable; |
213 | } |
214 | } |
215 | |
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) |
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 { |
234 | ret &= WM831X_AUX_DATA_MASK; |
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 | } |
243 | |
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) |
251 | { |
252 | return wm831x->auxadc_read(wm831x, input); |
253 | } |
254 | EXPORT_SYMBOL_GPL(wm831x_auxadc_read); |
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); |
281 | INIT_LIST_HEAD(&wm831x->auxadc_pending); |
282 | |
283 | if (wm831x->irq) { |
284 | wm831x->auxadc_read = wm831x_auxadc_read_irq; |
285 | |
286 | ret = request_threaded_irq(wm831x_irq(wm831x, |
287 | WM831X_IRQ_AUXADC_DATA), |
288 | NULL, wm831x_auxadc_irq, 0, |
289 | "auxadc", wm831x); |
290 | if (ret < 0) { |
291 | dev_err(wm831x->dev, "AUXADC IRQ request failed: %d\n", |
292 | ret); |
293 | wm831x->auxadc_read = NULL; |
294 | } |
295 | } |
296 | |
297 | if (!wm831x->auxadc_read) |
298 | wm831x->auxadc_read = wm831x_auxadc_read_polled; |
299 | } |
300 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9