aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMark Brown <broonie@opensource.wolfsonmicro.com>2011-06-07 06:47:28 -0400
committerSamuel Ortiz <sameo@linux.intel.com>2011-07-31 17:28:20 -0400
commit78bb3688ea1830672b8095fb6388593f582cd591 (patch)
tree06e10d5acd11ae3a2235d05fdf82880b0b40371a
parenta2495bc727e2dd8421fb85c80e38f9a4a4c1e58e (diff)
mfd: Support multiple active WM831x AUXADC conversions
The WM831x AUXADC hardware can schedule multiple conversions at once, allowing higher performance when more than one source is in use as we can have the hardware start new conversions without having to wait for a register write. Take advantage of this in the interrupt driven case, maintaining a list of callers that are waiting for AUXADC conversions and completing them all simultaneously. The external interface of the AUXADC is not changed so there will be limited use of the feature immediately. Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com> Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
-rw-r--r--drivers/mfd/wm831x-auxadc.c254
-rw-r--r--include/linux/mfd/wm831x/core.h13
2 files changed, 188 insertions, 79 deletions
diff --git a/drivers/mfd/wm831x-auxadc.c b/drivers/mfd/wm831x-auxadc.c
index 2fc9531b243..87210954a06 100644
--- a/drivers/mfd/wm831x-auxadc.c
+++ b/drivers/mfd/wm831x-auxadc.c
@@ -17,6 +17,7 @@
17#include <linux/delay.h> 17#include <linux/delay.h>
18#include <linux/mfd/core.h> 18#include <linux/mfd/core.h>
19#include <linux/slab.h> 19#include <linux/slab.h>
20#include <linux/list.h>
20 21
21#include <linux/mfd/wm831x/core.h> 22#include <linux/mfd/wm831x/core.h>
22#include <linux/mfd/wm831x/pdata.h> 23#include <linux/mfd/wm831x/pdata.h>
@@ -25,19 +26,139 @@
25#include <linux/mfd/wm831x/otp.h> 26#include <linux/mfd/wm831x/otp.h>
26#include <linux/mfd/wm831x/regulator.h> 27#include <linux/mfd/wm831x/regulator.h>
27 28
28/** 29struct wm831x_auxadc_req {
29 * wm831x_auxadc_read: Read a value from the WM831x AUXADC 30 struct list_head list;
30 * 31 enum wm831x_auxadc input;
31 * @wm831x: Device to read from. 32 int val;
32 * @input: AUXADC input to read. 33 struct completion done;
33 */ 34};
34int wm831x_auxadc_read(struct wm831x *wm831x, enum wm831x_auxadc input) 35
36static int wm831x_auxadc_read_irq(struct wm831x *wm831x,
37 enum wm831x_auxadc input)
35{ 38{
36 int ret, src, irq_masked, timeout; 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
105out:
106 mutex_unlock(&wm831x->auxadc_lock);
107
108 kfree(req);
109
110 return ret;
111}
112
113static 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;
37 131
38 /* Are we using the interrupt? */ 132 val = ret & WM831X_AUX_DATA_MASK;
39 irq_masked = wm831x_reg_read(wm831x, WM831X_INTERRUPT_STATUS_1_MASK); 133
40 irq_masked &= WM831X_AUXADC_DATA_EINT; 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
158static int wm831x_auxadc_read_polled(struct wm831x *wm831x,
159 enum wm831x_auxadc input)
160{
161 int ret, src, timeout;
41 162
42 mutex_lock(&wm831x->auxadc_lock); 163 mutex_lock(&wm831x->auxadc_lock);
43 164
@@ -57,9 +178,6 @@ int wm831x_auxadc_read(struct wm831x *wm831x, enum wm831x_auxadc input)
57 goto out; 178 goto out;
58 } 179 }
59 180
60 /* Clear any notification from a very late arriving interrupt */
61 try_wait_for_completion(&wm831x->auxadc_done);
62
63 ret = wm831x_set_bits(wm831x, WM831X_AUXADC_CONTROL, 181 ret = wm831x_set_bits(wm831x, WM831X_AUXADC_CONTROL,
64 WM831X_AUX_CVT_ENA, WM831X_AUX_CVT_ENA); 182 WM831X_AUX_CVT_ENA, WM831X_AUX_CVT_ENA);
65 if (ret < 0) { 183 if (ret < 0) {
@@ -67,59 +185,42 @@ int wm831x_auxadc_read(struct wm831x *wm831x, enum wm831x_auxadc input)
67 goto disable; 185 goto disable;
68 } 186 }
69 187
70 if (irq_masked) { 188 /* If we're not using interrupts then poll the
71 /* If we're not using interrupts then poll the 189 * interrupt status register */
72 * interrupt status register */ 190 timeout = 5;
73 timeout = 5; 191 while (timeout) {
74 while (timeout) { 192 msleep(1);
75 msleep(1);
76
77 ret = wm831x_reg_read(wm831x,
78 WM831X_INTERRUPT_STATUS_1);
79 if (ret < 0) {
80 dev_err(wm831x->dev,
81 "ISR 1 read failed: %d\n", ret);
82 goto disable;
83 }
84
85 /* Did it complete? */
86 if (ret & WM831X_AUXADC_DATA_EINT) {
87 wm831x_reg_write(wm831x,
88 WM831X_INTERRUPT_STATUS_1,
89 WM831X_AUXADC_DATA_EINT);
90 break;
91 } else {
92 dev_err(wm831x->dev,
93 "AUXADC conversion timeout\n");
94 ret = -EBUSY;
95 goto disable;
96 }
97 }
98 193
99 ret = wm831x_reg_read(wm831x, WM831X_AUXADC_DATA); 194 ret = wm831x_reg_read(wm831x,
195 WM831X_INTERRUPT_STATUS_1);
100 if (ret < 0) { 196 if (ret < 0) {
101 dev_err(wm831x->dev, 197 dev_err(wm831x->dev,
102 "Failed to read AUXADC data: %d\n", ret); 198 "ISR 1 read failed: %d\n", ret);
103 goto disable; 199 goto disable;
104 } 200 }
105 201
106 wm831x->auxadc_data = ret; 202 /* Did it complete? */
107 203 if (ret & WM831X_AUXADC_DATA_EINT) {
108 } else { 204 wm831x_reg_write(wm831x,
109 /* If we are using interrupts then wait for the 205 WM831X_INTERRUPT_STATUS_1,
110 * interrupt to complete. Use an extremely long 206 WM831X_AUXADC_DATA_EINT);
111 * timeout to handle situations with heavy load where 207 break;
112 * the notification of the interrupt may be delayed by 208 } else {
113 * threaded IRQ handling. */ 209 dev_err(wm831x->dev,
114 if (!wait_for_completion_timeout(&wm831x->auxadc_done, 210 "AUXADC conversion timeout\n");
115 msecs_to_jiffies(500))) {
116 dev_err(wm831x->dev, "Timed out waiting for AUXADC\n");
117 ret = -EBUSY; 211 ret = -EBUSY;
118 goto disable; 212 goto disable;
119 } 213 }
120 } 214 }
121 215
122 src = ((wm831x->auxadc_data & WM831X_AUX_DATA_SRC_MASK) 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)
123 >> WM831X_AUX_DATA_SRC_SHIFT) - 1; 224 >> WM831X_AUX_DATA_SRC_SHIFT) - 1;
124 225
125 if (src == 14) 226 if (src == 14)
@@ -130,7 +231,7 @@ int wm831x_auxadc_read(struct wm831x *wm831x, enum wm831x_auxadc input)
130 src, input); 231 src, input);
131 ret = -EINVAL; 232 ret = -EINVAL;
132 } else { 233 } else {
133 ret = wm831x->auxadc_data & WM831X_AUX_DATA_MASK; 234 ret &= WM831X_AUX_DATA_MASK;
134 } 235 }
135 236
136disable: 237disable:
@@ -139,26 +240,18 @@ out:
139 mutex_unlock(&wm831x->auxadc_lock); 240 mutex_unlock(&wm831x->auxadc_lock);
140 return ret; 241 return ret;
141} 242}
142EXPORT_SYMBOL_GPL(wm831x_auxadc_read);
143 243
144static irqreturn_t wm831x_auxadc_irq(int irq, void *irq_data) 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 */
250int wm831x_auxadc_read(struct wm831x *wm831x, enum wm831x_auxadc input)
145{ 251{
146 struct wm831x *wm831x = irq_data; 252 return wm831x->auxadc_read(wm831x, input);
147 int ret;
148
149 ret = wm831x_reg_read(wm831x, WM831X_AUXADC_DATA);
150 if (ret < 0) {
151 dev_err(wm831x->dev,
152 "Failed to read AUXADC data: %d\n", ret);
153 wm831x->auxadc_data = 0xffff;
154 } else {
155 wm831x->auxadc_data = ret;
156 }
157
158 complete(&wm831x->auxadc_done);
159
160 return IRQ_HANDLED;
161} 253}
254EXPORT_SYMBOL_GPL(wm831x_auxadc_read);
162 255
163/** 256/**
164 * wm831x_auxadc_read_uv: Read a voltage from the WM831x AUXADC 257 * wm831x_auxadc_read_uv: Read a voltage from the WM831x AUXADC
@@ -185,15 +278,22 @@ void wm831x_auxadc_init(struct wm831x *wm831x)
185 int ret; 278 int ret;
186 279
187 mutex_init(&wm831x->auxadc_lock); 280 mutex_init(&wm831x->auxadc_lock);
188 init_completion(&wm831x->auxadc_done); 281 INIT_LIST_HEAD(&wm831x->auxadc_pending);
282
283 if (wm831x->irq && wm831x->irq_base) {
284 wm831x->auxadc_read = wm831x_auxadc_read_irq;
189 285
190 if (wm831x->irq_base) {
191 ret = request_threaded_irq(wm831x->irq_base + 286 ret = request_threaded_irq(wm831x->irq_base +
192 WM831X_IRQ_AUXADC_DATA, 287 WM831X_IRQ_AUXADC_DATA,
193 NULL, wm831x_auxadc_irq, 0, 288 NULL, wm831x_auxadc_irq, 0,
194 "auxadc", wm831x); 289 "auxadc", wm831x);
195 if (ret < 0) 290 if (ret < 0) {
196 dev_err(wm831x->dev, "AUXADC IRQ request failed: %d\n", 291 dev_err(wm831x->dev, "AUXADC IRQ request failed: %d\n",
197 ret); 292 ret);
293 wm831x->auxadc_read = NULL;
294 }
198 } 295 }
296
297 if (!wm831x->auxadc_read)
298 wm831x->auxadc_read = wm831x_auxadc_read_polled;
199} 299}
diff --git a/include/linux/mfd/wm831x/core.h b/include/linux/mfd/wm831x/core.h
index 592e4fd6ab3..9fa14cdc159 100644
--- a/include/linux/mfd/wm831x/core.h
+++ b/include/linux/mfd/wm831x/core.h
@@ -17,6 +17,7 @@
17 17
18#include <linux/completion.h> 18#include <linux/completion.h>
19#include <linux/interrupt.h> 19#include <linux/interrupt.h>
20#include <linux/list.h>
20 21
21/* 22/*
22 * Register values. 23 * Register values.
@@ -249,6 +250,12 @@ enum wm831x_parent {
249 WM8326 = 0x8326, 250 WM8326 = 0x8326,
250}; 251};
251 252
253struct wm831x;
254enum wm831x_auxadc;
255
256typedef int (*wm831x_auxadc_read_fn)(struct wm831x *wm831x,
257 enum wm831x_auxadc input);
258
252struct wm831x { 259struct wm831x {
253 struct mutex io_lock; 260 struct mutex io_lock;
254 261
@@ -277,8 +284,9 @@ struct wm831x {
277 int gpio_update[WM831X_NUM_GPIO_REGS]; 284 int gpio_update[WM831X_NUM_GPIO_REGS];
278 285
279 struct mutex auxadc_lock; 286 struct mutex auxadc_lock;
280 struct completion auxadc_done; 287 struct list_head auxadc_pending;
281 u16 auxadc_data; 288 u16 auxadc_active;
289 wm831x_auxadc_read_fn auxadc_read;
282 290
283 /* The WM831x has a security key blocking access to certain 291 /* The WM831x has a security key blocking access to certain
284 * registers. The mutex is taken by the accessors for locking 292 * registers. The mutex is taken by the accessors for locking
@@ -305,5 +313,6 @@ void wm831x_device_exit(struct wm831x *wm831x);
305int wm831x_device_suspend(struct wm831x *wm831x); 313int wm831x_device_suspend(struct wm831x *wm831x);
306int wm831x_irq_init(struct wm831x *wm831x, int irq); 314int wm831x_irq_init(struct wm831x *wm831x, int irq);
307void wm831x_irq_exit(struct wm831x *wm831x); 315void wm831x_irq_exit(struct wm831x *wm831x);
316void wm831x_auxadc_init(struct wm831x *wm831x);
308 317
309#endif 318#endif