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> | |
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 | } |