diff options
author | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2011-06-02 14:18:53 -0400 |
---|---|---|
committer | Samuel Ortiz <sameo@linux.intel.com> | 2011-07-31 17:28:20 -0400 |
commit | e69b6de181167a132eee7c38c7e4b47dea3d8e49 (patch) | |
tree | 2c0fceff037fa76791155bbbf6bec8d75a5bbc1e /drivers/mfd/wm831x-auxadc.c | |
parent | c1a82780b41e78f31636c49279ce940afe60a453 (diff) |
mfd: Refactor wm831x AUXADC handling into a separate file
In preparation for some additional work on the wm831x AUXADC code move the
support into a separate file. This is a simple code motion patch, there
should be no functional changes.
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
Diffstat (limited to 'drivers/mfd/wm831x-auxadc.c')
-rw-r--r-- | drivers/mfd/wm831x-auxadc.c | 199 |
1 files changed, 199 insertions, 0 deletions
diff --git a/drivers/mfd/wm831x-auxadc.c b/drivers/mfd/wm831x-auxadc.c new file mode 100644 index 000000000000..2fc9531b243d --- /dev/null +++ b/drivers/mfd/wm831x-auxadc.c | |||
@@ -0,0 +1,199 @@ | |||
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 | |||
21 | #include <linux/mfd/wm831x/core.h> | ||
22 | #include <linux/mfd/wm831x/pdata.h> | ||
23 | #include <linux/mfd/wm831x/irq.h> | ||
24 | #include <linux/mfd/wm831x/auxadc.h> | ||
25 | #include <linux/mfd/wm831x/otp.h> | ||
26 | #include <linux/mfd/wm831x/regulator.h> | ||
27 | |||
28 | /** | ||
29 | * wm831x_auxadc_read: Read a value from the WM831x AUXADC | ||
30 | * | ||
31 | * @wm831x: Device to read from. | ||
32 | * @input: AUXADC input to read. | ||
33 | */ | ||
34 | int wm831x_auxadc_read(struct wm831x *wm831x, enum wm831x_auxadc input) | ||
35 | { | ||
36 | int ret, src, irq_masked, timeout; | ||
37 | |||
38 | /* Are we using the interrupt? */ | ||
39 | irq_masked = wm831x_reg_read(wm831x, WM831X_INTERRUPT_STATUS_1_MASK); | ||
40 | irq_masked &= WM831X_AUXADC_DATA_EINT; | ||
41 | |||
42 | mutex_lock(&wm831x->auxadc_lock); | ||
43 | |||
44 | ret = wm831x_set_bits(wm831x, WM831X_AUXADC_CONTROL, | ||
45 | WM831X_AUX_ENA, WM831X_AUX_ENA); | ||
46 | if (ret < 0) { | ||
47 | dev_err(wm831x->dev, "Failed to enable AUXADC: %d\n", ret); | ||
48 | goto out; | ||
49 | } | ||
50 | |||
51 | /* We force a single source at present */ | ||
52 | src = input; | ||
53 | ret = wm831x_reg_write(wm831x, WM831X_AUXADC_SOURCE, | ||
54 | 1 << src); | ||
55 | if (ret < 0) { | ||
56 | dev_err(wm831x->dev, "Failed to set AUXADC source: %d\n", ret); | ||
57 | goto out; | ||
58 | } | ||
59 | |||
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, | ||
64 | WM831X_AUX_CVT_ENA, WM831X_AUX_CVT_ENA); | ||
65 | if (ret < 0) { | ||
66 | dev_err(wm831x->dev, "Failed to start AUXADC: %d\n", ret); | ||
67 | goto disable; | ||
68 | } | ||
69 | |||
70 | if (irq_masked) { | ||
71 | /* If we're not using interrupts then poll the | ||
72 | * interrupt status register */ | ||
73 | timeout = 5; | ||
74 | while (timeout) { | ||
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 | |||
99 | ret = wm831x_reg_read(wm831x, WM831X_AUXADC_DATA); | ||
100 | if (ret < 0) { | ||
101 | dev_err(wm831x->dev, | ||
102 | "Failed to read AUXADC data: %d\n", ret); | ||
103 | goto disable; | ||
104 | } | ||
105 | |||
106 | wm831x->auxadc_data = ret; | ||
107 | |||
108 | } else { | ||
109 | /* If we are using interrupts then wait for the | ||
110 | * interrupt to complete. Use an extremely long | ||
111 | * timeout to handle situations with heavy load where | ||
112 | * the notification of the interrupt may be delayed by | ||
113 | * threaded IRQ handling. */ | ||
114 | if (!wait_for_completion_timeout(&wm831x->auxadc_done, | ||
115 | msecs_to_jiffies(500))) { | ||
116 | dev_err(wm831x->dev, "Timed out waiting for AUXADC\n"); | ||
117 | ret = -EBUSY; | ||
118 | goto disable; | ||
119 | } | ||
120 | } | ||
121 | |||
122 | src = ((wm831x->auxadc_data & WM831X_AUX_DATA_SRC_MASK) | ||
123 | >> WM831X_AUX_DATA_SRC_SHIFT) - 1; | ||
124 | |||
125 | if (src == 14) | ||
126 | src = WM831X_AUX_CAL; | ||
127 | |||
128 | if (src != input) { | ||
129 | dev_err(wm831x->dev, "Data from source %d not %d\n", | ||
130 | src, input); | ||
131 | ret = -EINVAL; | ||
132 | } else { | ||
133 | ret = wm831x->auxadc_data & WM831X_AUX_DATA_MASK; | ||
134 | } | ||
135 | |||
136 | disable: | ||
137 | wm831x_set_bits(wm831x, WM831X_AUXADC_CONTROL, WM831X_AUX_ENA, 0); | ||
138 | out: | ||
139 | mutex_unlock(&wm831x->auxadc_lock); | ||
140 | return ret; | ||
141 | } | ||
142 | EXPORT_SYMBOL_GPL(wm831x_auxadc_read); | ||
143 | |||
144 | static irqreturn_t wm831x_auxadc_irq(int irq, void *irq_data) | ||
145 | { | ||
146 | struct wm831x *wm831x = irq_data; | ||
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 | } | ||
162 | |||
163 | /** | ||
164 | * wm831x_auxadc_read_uv: Read a voltage from the WM831x AUXADC | ||
165 | * | ||
166 | * @wm831x: Device to read from. | ||
167 | * @input: AUXADC input to read. | ||
168 | */ | ||
169 | int wm831x_auxadc_read_uv(struct wm831x *wm831x, enum wm831x_auxadc input) | ||
170 | { | ||
171 | int ret; | ||
172 | |||
173 | ret = wm831x_auxadc_read(wm831x, input); | ||
174 | if (ret < 0) | ||
175 | return ret; | ||
176 | |||
177 | ret *= 1465; | ||
178 | |||
179 | return ret; | ||
180 | } | ||
181 | EXPORT_SYMBOL_GPL(wm831x_auxadc_read_uv); | ||
182 | |||
183 | void wm831x_auxadc_init(struct wm831x *wm831x) | ||
184 | { | ||
185 | int ret; | ||
186 | |||
187 | mutex_init(&wm831x->auxadc_lock); | ||
188 | init_completion(&wm831x->auxadc_done); | ||
189 | |||
190 | if (wm831x->irq_base) { | ||
191 | ret = request_threaded_irq(wm831x->irq_base + | ||
192 | WM831X_IRQ_AUXADC_DATA, | ||
193 | NULL, wm831x_auxadc_irq, 0, | ||
194 | "auxadc", wm831x); | ||
195 | if (ret < 0) | ||
196 | dev_err(wm831x->dev, "AUXADC IRQ request failed: %d\n", | ||
197 | ret); | ||
198 | } | ||
199 | } | ||