diff options
author | Sebastian Reichel <sre@debian.org> | 2014-03-15 21:43:31 -0400 |
---|---|---|
committer | Lee Jones <lee.jones@linaro.org> | 2014-03-18 04:16:20 -0400 |
commit | b2931b98cebfb30d940f248d2054b5268eae87a4 (patch) | |
tree | fcd1422ed3fc3aff3c04d60b84c89511eeae1f02 /drivers/iio | |
parent | 1e1bce3956ab888d1627080eb85c5019f2adf955 (diff) |
mfd: twl4030-madc: Move driver to drivers/iio/adc
This is a driver for an A/D converter, which belongs into
drivers/iio/adc.
Signed-off-by: Sebastian Reichel <sre@debian.org>
Acked-by: Jonathan Cameron <jic23@kernel.org>
Signed-off-by: Lee Jones <lee.jones@linaro.org>
Diffstat (limited to 'drivers/iio')
-rw-r--r-- | drivers/iio/adc/Kconfig | 10 | ||||
-rw-r--r-- | drivers/iio/adc/Makefile | 1 | ||||
-rw-r--r-- | drivers/iio/adc/twl4030-madc.c | 896 |
3 files changed, 907 insertions, 0 deletions
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 2209f28441e9..427f75c4f69e 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig | |||
@@ -183,6 +183,16 @@ config TI_AM335X_ADC | |||
183 | Say yes here to build support for Texas Instruments ADC | 183 | Say yes here to build support for Texas Instruments ADC |
184 | driver which is also a MFD client. | 184 | driver which is also a MFD client. |
185 | 185 | ||
186 | config TWL4030_MADC | ||
187 | tristate "TWL4030 MADC (Monitoring A/D Converter)" | ||
188 | depends on TWL4030_CORE | ||
189 | help | ||
190 | This driver provides support for Triton TWL4030-MADC. The | ||
191 | driver supports both RT and SW conversion methods. | ||
192 | |||
193 | This driver can also be built as a module. If so, the module will be | ||
194 | called twl4030-madc. | ||
195 | |||
186 | config TWL6030_GPADC | 196 | config TWL6030_GPADC |
187 | tristate "TWL6030 GPADC (General Purpose A/D Converter) Support" | 197 | tristate "TWL6030 GPADC (General Purpose A/D Converter) Support" |
188 | depends on TWL4030_CORE | 198 | depends on TWL4030_CORE |
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index ba9a10a24cd0..9acf2df2c1a8 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile | |||
@@ -20,5 +20,6 @@ obj-$(CONFIG_MCP3422) += mcp3422.o | |||
20 | obj-$(CONFIG_NAU7802) += nau7802.o | 20 | obj-$(CONFIG_NAU7802) += nau7802.o |
21 | obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o | 21 | obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o |
22 | obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o | 22 | obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o |
23 | obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o | ||
23 | obj-$(CONFIG_TWL6030_GPADC) += twl6030-gpadc.o | 24 | obj-$(CONFIG_TWL6030_GPADC) += twl6030-gpadc.o |
24 | obj-$(CONFIG_VIPERBOARD_ADC) += viperboard_adc.o | 25 | obj-$(CONFIG_VIPERBOARD_ADC) += viperboard_adc.o |
diff --git a/drivers/iio/adc/twl4030-madc.c b/drivers/iio/adc/twl4030-madc.c new file mode 100644 index 000000000000..8a014fbb98bc --- /dev/null +++ b/drivers/iio/adc/twl4030-madc.c | |||
@@ -0,0 +1,896 @@ | |||
1 | /* | ||
2 | * | ||
3 | * TWL4030 MADC module driver-This driver monitors the real time | ||
4 | * conversion of analog signals like battery temperature, | ||
5 | * battery type, battery level etc. | ||
6 | * | ||
7 | * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ | ||
8 | * J Keerthy <j-keerthy@ti.com> | ||
9 | * | ||
10 | * Based on twl4030-madc.c | ||
11 | * Copyright (C) 2008 Nokia Corporation | ||
12 | * Mikko Ylinen <mikko.k.ylinen@nokia.com> | ||
13 | * | ||
14 | * Amit Kucheria <amit.kucheria@canonical.com> | ||
15 | * | ||
16 | * This program is free software; you can redistribute it and/or | ||
17 | * modify it under the terms of the GNU General Public License | ||
18 | * version 2 as published by the Free Software Foundation. | ||
19 | * | ||
20 | * This program is distributed in the hope that it will be useful, but | ||
21 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
23 | * General Public License for more details. | ||
24 | * | ||
25 | * You should have received a copy of the GNU General Public License | ||
26 | * along with this program; if not, write to the Free Software | ||
27 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
28 | * 02110-1301 USA | ||
29 | * | ||
30 | */ | ||
31 | |||
32 | #include <linux/init.h> | ||
33 | #include <linux/device.h> | ||
34 | #include <linux/interrupt.h> | ||
35 | #include <linux/kernel.h> | ||
36 | #include <linux/delay.h> | ||
37 | #include <linux/platform_device.h> | ||
38 | #include <linux/slab.h> | ||
39 | #include <linux/i2c/twl.h> | ||
40 | #include <linux/i2c/twl4030-madc.h> | ||
41 | #include <linux/module.h> | ||
42 | #include <linux/stddef.h> | ||
43 | #include <linux/mutex.h> | ||
44 | #include <linux/bitops.h> | ||
45 | #include <linux/jiffies.h> | ||
46 | #include <linux/types.h> | ||
47 | #include <linux/gfp.h> | ||
48 | #include <linux/err.h> | ||
49 | |||
50 | #include <linux/iio/iio.h> | ||
51 | |||
52 | /** | ||
53 | * struct twl4030_madc_data - a container for madc info | ||
54 | * @dev: Pointer to device structure for madc | ||
55 | * @lock: Mutex protecting this data structure | ||
56 | * @requests: Array of request struct corresponding to SW1, SW2 and RT | ||
57 | * @use_second_irq: IRQ selection (main or co-processor) | ||
58 | * @imr: Interrupt mask register of MADC | ||
59 | * @isr: Interrupt status register of MADC | ||
60 | */ | ||
61 | struct twl4030_madc_data { | ||
62 | struct device *dev; | ||
63 | struct mutex lock; /* mutex protecting this data structure */ | ||
64 | struct twl4030_madc_request requests[TWL4030_MADC_NUM_METHODS]; | ||
65 | bool use_second_irq; | ||
66 | u8 imr; | ||
67 | u8 isr; | ||
68 | }; | ||
69 | |||
70 | static int twl4030_madc_read(struct iio_dev *iio_dev, | ||
71 | const struct iio_chan_spec *chan, | ||
72 | int *val, int *val2, long mask) | ||
73 | { | ||
74 | struct twl4030_madc_data *madc = iio_priv(iio_dev); | ||
75 | struct twl4030_madc_request req; | ||
76 | int ret; | ||
77 | |||
78 | req.method = madc->use_second_irq ? TWL4030_MADC_SW2 : TWL4030_MADC_SW1; | ||
79 | |||
80 | req.channels = BIT(chan->channel); | ||
81 | req.active = false; | ||
82 | req.func_cb = NULL; | ||
83 | req.type = TWL4030_MADC_WAIT; | ||
84 | req.raw = !(mask == IIO_CHAN_INFO_PROCESSED); | ||
85 | req.do_avg = (mask == IIO_CHAN_INFO_AVERAGE_RAW); | ||
86 | |||
87 | ret = twl4030_madc_conversion(&req); | ||
88 | if (ret < 0) | ||
89 | return ret; | ||
90 | |||
91 | *val = req.rbuf[chan->channel]; | ||
92 | |||
93 | return IIO_VAL_INT; | ||
94 | } | ||
95 | |||
96 | static const struct iio_info twl4030_madc_iio_info = { | ||
97 | .read_raw = &twl4030_madc_read, | ||
98 | .driver_module = THIS_MODULE, | ||
99 | }; | ||
100 | |||
101 | #define TWL4030_ADC_CHANNEL(_channel, _type, _name) { \ | ||
102 | .type = _type, \ | ||
103 | .channel = _channel, \ | ||
104 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ | ||
105 | BIT(IIO_CHAN_INFO_AVERAGE_RAW) | \ | ||
106 | BIT(IIO_CHAN_INFO_PROCESSED), \ | ||
107 | .datasheet_name = _name, \ | ||
108 | .indexed = 1, \ | ||
109 | } | ||
110 | |||
111 | static const struct iio_chan_spec twl4030_madc_iio_channels[] = { | ||
112 | TWL4030_ADC_CHANNEL(0, IIO_VOLTAGE, "ADCIN0"), | ||
113 | TWL4030_ADC_CHANNEL(1, IIO_TEMP, "ADCIN1"), | ||
114 | TWL4030_ADC_CHANNEL(2, IIO_VOLTAGE, "ADCIN2"), | ||
115 | TWL4030_ADC_CHANNEL(3, IIO_VOLTAGE, "ADCIN3"), | ||
116 | TWL4030_ADC_CHANNEL(4, IIO_VOLTAGE, "ADCIN4"), | ||
117 | TWL4030_ADC_CHANNEL(5, IIO_VOLTAGE, "ADCIN5"), | ||
118 | TWL4030_ADC_CHANNEL(6, IIO_VOLTAGE, "ADCIN6"), | ||
119 | TWL4030_ADC_CHANNEL(7, IIO_VOLTAGE, "ADCIN7"), | ||
120 | TWL4030_ADC_CHANNEL(8, IIO_VOLTAGE, "ADCIN8"), | ||
121 | TWL4030_ADC_CHANNEL(9, IIO_VOLTAGE, "ADCIN9"), | ||
122 | TWL4030_ADC_CHANNEL(10, IIO_CURRENT, "ADCIN10"), | ||
123 | TWL4030_ADC_CHANNEL(11, IIO_VOLTAGE, "ADCIN11"), | ||
124 | TWL4030_ADC_CHANNEL(12, IIO_VOLTAGE, "ADCIN12"), | ||
125 | TWL4030_ADC_CHANNEL(13, IIO_VOLTAGE, "ADCIN13"), | ||
126 | TWL4030_ADC_CHANNEL(14, IIO_VOLTAGE, "ADCIN14"), | ||
127 | TWL4030_ADC_CHANNEL(15, IIO_VOLTAGE, "ADCIN15"), | ||
128 | }; | ||
129 | |||
130 | static struct twl4030_madc_data *twl4030_madc; | ||
131 | |||
132 | struct twl4030_prescale_divider_ratios { | ||
133 | s16 numerator; | ||
134 | s16 denominator; | ||
135 | }; | ||
136 | |||
137 | static const struct twl4030_prescale_divider_ratios | ||
138 | twl4030_divider_ratios[16] = { | ||
139 | {1, 1}, /* CHANNEL 0 No Prescaler */ | ||
140 | {1, 1}, /* CHANNEL 1 No Prescaler */ | ||
141 | {6, 10}, /* CHANNEL 2 */ | ||
142 | {6, 10}, /* CHANNEL 3 */ | ||
143 | {6, 10}, /* CHANNEL 4 */ | ||
144 | {6, 10}, /* CHANNEL 5 */ | ||
145 | {6, 10}, /* CHANNEL 6 */ | ||
146 | {6, 10}, /* CHANNEL 7 */ | ||
147 | {3, 14}, /* CHANNEL 8 */ | ||
148 | {1, 3}, /* CHANNEL 9 */ | ||
149 | {1, 1}, /* CHANNEL 10 No Prescaler */ | ||
150 | {15, 100}, /* CHANNEL 11 */ | ||
151 | {1, 4}, /* CHANNEL 12 */ | ||
152 | {1, 1}, /* CHANNEL 13 Reserved channels */ | ||
153 | {1, 1}, /* CHANNEL 14 Reseved channels */ | ||
154 | {5, 11}, /* CHANNEL 15 */ | ||
155 | }; | ||
156 | |||
157 | |||
158 | /* Conversion table from -3 to 55 degrees Celcius */ | ||
159 | static int twl4030_therm_tbl[] = { | ||
160 | 30800, 29500, 28300, 27100, | ||
161 | 26000, 24900, 23900, 22900, 22000, 21100, 20300, 19400, 18700, | ||
162 | 17900, 17200, 16500, 15900, 15300, 14700, 14100, 13600, 13100, | ||
163 | 12600, 12100, 11600, 11200, 10800, 10400, 10000, 9630, 9280, | ||
164 | 8950, 8620, 8310, 8020, 7730, 7460, 7200, 6950, 6710, | ||
165 | 6470, 6250, 6040, 5830, 5640, 5450, 5260, 5090, 4920, | ||
166 | 4760, 4600, 4450, 4310, 4170, 4040, 3910, 3790, 3670, | ||
167 | 3550 | ||
168 | }; | ||
169 | |||
170 | /* | ||
171 | * Structure containing the registers | ||
172 | * of different conversion methods supported by MADC. | ||
173 | * Hardware or RT real time conversion request initiated by external host | ||
174 | * processor for RT Signal conversions. | ||
175 | * External host processors can also request for non RT conversions | ||
176 | * SW1 and SW2 software conversions also called asynchronous or GPC request. | ||
177 | */ | ||
178 | static | ||
179 | const struct twl4030_madc_conversion_method twl4030_conversion_methods[] = { | ||
180 | [TWL4030_MADC_RT] = { | ||
181 | .sel = TWL4030_MADC_RTSELECT_LSB, | ||
182 | .avg = TWL4030_MADC_RTAVERAGE_LSB, | ||
183 | .rbase = TWL4030_MADC_RTCH0_LSB, | ||
184 | }, | ||
185 | [TWL4030_MADC_SW1] = { | ||
186 | .sel = TWL4030_MADC_SW1SELECT_LSB, | ||
187 | .avg = TWL4030_MADC_SW1AVERAGE_LSB, | ||
188 | .rbase = TWL4030_MADC_GPCH0_LSB, | ||
189 | .ctrl = TWL4030_MADC_CTRL_SW1, | ||
190 | }, | ||
191 | [TWL4030_MADC_SW2] = { | ||
192 | .sel = TWL4030_MADC_SW2SELECT_LSB, | ||
193 | .avg = TWL4030_MADC_SW2AVERAGE_LSB, | ||
194 | .rbase = TWL4030_MADC_GPCH0_LSB, | ||
195 | .ctrl = TWL4030_MADC_CTRL_SW2, | ||
196 | }, | ||
197 | }; | ||
198 | |||
199 | /** | ||
200 | * twl4030_madc_channel_raw_read() - Function to read a particular channel value | ||
201 | * @madc: pointer to struct twl4030_madc_data | ||
202 | * @reg: lsb of ADC Channel | ||
203 | * | ||
204 | * Return: 0 on success, an error code otherwise. | ||
205 | */ | ||
206 | static int twl4030_madc_channel_raw_read(struct twl4030_madc_data *madc, u8 reg) | ||
207 | { | ||
208 | u16 val; | ||
209 | int ret; | ||
210 | /* | ||
211 | * For each ADC channel, we have MSB and LSB register pair. MSB address | ||
212 | * is always LSB address+1. reg parameter is the address of LSB register | ||
213 | */ | ||
214 | ret = twl_i2c_read_u16(TWL4030_MODULE_MADC, &val, reg); | ||
215 | if (ret) { | ||
216 | dev_err(madc->dev, "unable to read register 0x%X\n", reg); | ||
217 | return ret; | ||
218 | } | ||
219 | |||
220 | return (int)(val >> 6); | ||
221 | } | ||
222 | |||
223 | /* | ||
224 | * Return battery temperature in degrees Celsius | ||
225 | * Or < 0 on failure. | ||
226 | */ | ||
227 | static int twl4030battery_temperature(int raw_volt) | ||
228 | { | ||
229 | u8 val; | ||
230 | int temp, curr, volt, res, ret; | ||
231 | |||
232 | volt = (raw_volt * TEMP_STEP_SIZE) / TEMP_PSR_R; | ||
233 | /* Getting and calculating the supply current in micro amperes */ | ||
234 | ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, &val, | ||
235 | REG_BCICTL2); | ||
236 | if (ret < 0) | ||
237 | return ret; | ||
238 | |||
239 | curr = ((val & TWL4030_BCI_ITHEN) + 1) * 10; | ||
240 | /* Getting and calculating the thermistor resistance in ohms */ | ||
241 | res = volt * 1000 / curr; | ||
242 | /* calculating temperature */ | ||
243 | for (temp = 58; temp >= 0; temp--) { | ||
244 | int actual = twl4030_therm_tbl[temp]; | ||
245 | if ((actual - res) >= 0) | ||
246 | break; | ||
247 | } | ||
248 | |||
249 | return temp + 1; | ||
250 | } | ||
251 | |||
252 | static int twl4030battery_current(int raw_volt) | ||
253 | { | ||
254 | int ret; | ||
255 | u8 val; | ||
256 | |||
257 | ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, &val, | ||
258 | TWL4030_BCI_BCICTL1); | ||
259 | if (ret) | ||
260 | return ret; | ||
261 | if (val & TWL4030_BCI_CGAIN) /* slope of 0.44 mV/mA */ | ||
262 | return (raw_volt * CURR_STEP_SIZE) / CURR_PSR_R1; | ||
263 | else /* slope of 0.88 mV/mA */ | ||
264 | return (raw_volt * CURR_STEP_SIZE) / CURR_PSR_R2; | ||
265 | } | ||
266 | |||
267 | /* | ||
268 | * Function to read channel values | ||
269 | * @madc - pointer to twl4030_madc_data struct | ||
270 | * @reg_base - Base address of the first channel | ||
271 | * @Channels - 16 bit bitmap. If the bit is set, channel's value is read | ||
272 | * @buf - The channel values are stored here. if read fails error | ||
273 | * @raw - Return raw values without conversion | ||
274 | * value is stored | ||
275 | * Returns the number of successfully read channels. | ||
276 | */ | ||
277 | static int twl4030_madc_read_channels(struct twl4030_madc_data *madc, | ||
278 | u8 reg_base, unsigned | ||
279 | long channels, int *buf, | ||
280 | bool raw) | ||
281 | { | ||
282 | int count = 0; | ||
283 | int i; | ||
284 | u8 reg; | ||
285 | |||
286 | for_each_set_bit(i, &channels, TWL4030_MADC_MAX_CHANNELS) { | ||
287 | reg = reg_base + (2 * i); | ||
288 | buf[i] = twl4030_madc_channel_raw_read(madc, reg); | ||
289 | if (buf[i] < 0) { | ||
290 | dev_err(madc->dev, "Unable to read register 0x%X\n", | ||
291 | reg); | ||
292 | return buf[i]; | ||
293 | } | ||
294 | if (raw) { | ||
295 | count++; | ||
296 | continue; | ||
297 | } | ||
298 | switch (i) { | ||
299 | case 10: | ||
300 | buf[i] = twl4030battery_current(buf[i]); | ||
301 | if (buf[i] < 0) { | ||
302 | dev_err(madc->dev, "err reading current\n"); | ||
303 | return buf[i]; | ||
304 | } else { | ||
305 | count++; | ||
306 | buf[i] = buf[i] - 750; | ||
307 | } | ||
308 | break; | ||
309 | case 1: | ||
310 | buf[i] = twl4030battery_temperature(buf[i]); | ||
311 | if (buf[i] < 0) { | ||
312 | dev_err(madc->dev, "err reading temperature\n"); | ||
313 | return buf[i]; | ||
314 | } else { | ||
315 | buf[i] -= 3; | ||
316 | count++; | ||
317 | } | ||
318 | break; | ||
319 | default: | ||
320 | count++; | ||
321 | /* Analog Input (V) = conv_result * step_size / R | ||
322 | * conv_result = decimal value of 10-bit conversion | ||
323 | * result | ||
324 | * step size = 1.5 / (2 ^ 10 -1) | ||
325 | * R = Prescaler ratio for input channels. | ||
326 | * Result given in mV hence multiplied by 1000. | ||
327 | */ | ||
328 | buf[i] = (buf[i] * 3 * 1000 * | ||
329 | twl4030_divider_ratios[i].denominator) | ||
330 | / (2 * 1023 * | ||
331 | twl4030_divider_ratios[i].numerator); | ||
332 | } | ||
333 | } | ||
334 | |||
335 | return count; | ||
336 | } | ||
337 | |||
338 | /* | ||
339 | * Enables irq. | ||
340 | * @madc - pointer to twl4030_madc_data struct | ||
341 | * @id - irq number to be enabled | ||
342 | * can take one of TWL4030_MADC_RT, TWL4030_MADC_SW1, TWL4030_MADC_SW2 | ||
343 | * corresponding to RT, SW1, SW2 conversion requests. | ||
344 | * If the i2c read fails it returns an error else returns 0. | ||
345 | */ | ||
346 | static int twl4030_madc_enable_irq(struct twl4030_madc_data *madc, u8 id) | ||
347 | { | ||
348 | u8 val; | ||
349 | int ret; | ||
350 | |||
351 | ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &val, madc->imr); | ||
352 | if (ret) { | ||
353 | dev_err(madc->dev, "unable to read imr register 0x%X\n", | ||
354 | madc->imr); | ||
355 | return ret; | ||
356 | } | ||
357 | |||
358 | val &= ~(1 << id); | ||
359 | ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, val, madc->imr); | ||
360 | if (ret) { | ||
361 | dev_err(madc->dev, | ||
362 | "unable to write imr register 0x%X\n", madc->imr); | ||
363 | return ret; | ||
364 | } | ||
365 | |||
366 | return 0; | ||
367 | } | ||
368 | |||
369 | /* | ||
370 | * Disables irq. | ||
371 | * @madc - pointer to twl4030_madc_data struct | ||
372 | * @id - irq number to be disabled | ||
373 | * can take one of TWL4030_MADC_RT, TWL4030_MADC_SW1, TWL4030_MADC_SW2 | ||
374 | * corresponding to RT, SW1, SW2 conversion requests. | ||
375 | * Returns error if i2c read/write fails. | ||
376 | */ | ||
377 | static int twl4030_madc_disable_irq(struct twl4030_madc_data *madc, u8 id) | ||
378 | { | ||
379 | u8 val; | ||
380 | int ret; | ||
381 | |||
382 | ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &val, madc->imr); | ||
383 | if (ret) { | ||
384 | dev_err(madc->dev, "unable to read imr register 0x%X\n", | ||
385 | madc->imr); | ||
386 | return ret; | ||
387 | } | ||
388 | val |= (1 << id); | ||
389 | ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, val, madc->imr); | ||
390 | if (ret) { | ||
391 | dev_err(madc->dev, | ||
392 | "unable to write imr register 0x%X\n", madc->imr); | ||
393 | return ret; | ||
394 | } | ||
395 | |||
396 | return 0; | ||
397 | } | ||
398 | |||
399 | static irqreturn_t twl4030_madc_threaded_irq_handler(int irq, void *_madc) | ||
400 | { | ||
401 | struct twl4030_madc_data *madc = _madc; | ||
402 | const struct twl4030_madc_conversion_method *method; | ||
403 | u8 isr_val, imr_val; | ||
404 | int i, len, ret; | ||
405 | struct twl4030_madc_request *r; | ||
406 | |||
407 | mutex_lock(&madc->lock); | ||
408 | ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &isr_val, madc->isr); | ||
409 | if (ret) { | ||
410 | dev_err(madc->dev, "unable to read isr register 0x%X\n", | ||
411 | madc->isr); | ||
412 | goto err_i2c; | ||
413 | } | ||
414 | ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &imr_val, madc->imr); | ||
415 | if (ret) { | ||
416 | dev_err(madc->dev, "unable to read imr register 0x%X\n", | ||
417 | madc->imr); | ||
418 | goto err_i2c; | ||
419 | } | ||
420 | isr_val &= ~imr_val; | ||
421 | for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) { | ||
422 | if (!(isr_val & (1 << i))) | ||
423 | continue; | ||
424 | ret = twl4030_madc_disable_irq(madc, i); | ||
425 | if (ret < 0) | ||
426 | dev_dbg(madc->dev, "Disable interrupt failed %d\n", i); | ||
427 | madc->requests[i].result_pending = 1; | ||
428 | } | ||
429 | for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) { | ||
430 | r = &madc->requests[i]; | ||
431 | /* No pending results for this method, move to next one */ | ||
432 | if (!r->result_pending) | ||
433 | continue; | ||
434 | method = &twl4030_conversion_methods[r->method]; | ||
435 | /* Read results */ | ||
436 | len = twl4030_madc_read_channels(madc, method->rbase, | ||
437 | r->channels, r->rbuf, r->raw); | ||
438 | /* Return results to caller */ | ||
439 | if (r->func_cb != NULL) { | ||
440 | r->func_cb(len, r->channels, r->rbuf); | ||
441 | r->func_cb = NULL; | ||
442 | } | ||
443 | /* Free request */ | ||
444 | r->result_pending = 0; | ||
445 | r->active = 0; | ||
446 | } | ||
447 | mutex_unlock(&madc->lock); | ||
448 | |||
449 | return IRQ_HANDLED; | ||
450 | |||
451 | err_i2c: | ||
452 | /* | ||
453 | * In case of error check whichever request is active | ||
454 | * and service the same. | ||
455 | */ | ||
456 | for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) { | ||
457 | r = &madc->requests[i]; | ||
458 | if (r->active == 0) | ||
459 | continue; | ||
460 | method = &twl4030_conversion_methods[r->method]; | ||
461 | /* Read results */ | ||
462 | len = twl4030_madc_read_channels(madc, method->rbase, | ||
463 | r->channels, r->rbuf, r->raw); | ||
464 | /* Return results to caller */ | ||
465 | if (r->func_cb != NULL) { | ||
466 | r->func_cb(len, r->channels, r->rbuf); | ||
467 | r->func_cb = NULL; | ||
468 | } | ||
469 | /* Free request */ | ||
470 | r->result_pending = 0; | ||
471 | r->active = 0; | ||
472 | } | ||
473 | mutex_unlock(&madc->lock); | ||
474 | |||
475 | return IRQ_HANDLED; | ||
476 | } | ||
477 | |||
478 | static int twl4030_madc_set_irq(struct twl4030_madc_data *madc, | ||
479 | struct twl4030_madc_request *req) | ||
480 | { | ||
481 | struct twl4030_madc_request *p; | ||
482 | int ret; | ||
483 | |||
484 | p = &madc->requests[req->method]; | ||
485 | memcpy(p, req, sizeof(*req)); | ||
486 | ret = twl4030_madc_enable_irq(madc, req->method); | ||
487 | if (ret < 0) { | ||
488 | dev_err(madc->dev, "enable irq failed!!\n"); | ||
489 | return ret; | ||
490 | } | ||
491 | |||
492 | return 0; | ||
493 | } | ||
494 | |||
495 | /* | ||
496 | * Function which enables the madc conversion | ||
497 | * by writing to the control register. | ||
498 | * @madc - pointer to twl4030_madc_data struct | ||
499 | * @conv_method - can be TWL4030_MADC_RT, TWL4030_MADC_SW2, TWL4030_MADC_SW1 | ||
500 | * corresponding to RT SW1 or SW2 conversion methods. | ||
501 | * Returns 0 if succeeds else a negative error value | ||
502 | */ | ||
503 | static int twl4030_madc_start_conversion(struct twl4030_madc_data *madc, | ||
504 | int conv_method) | ||
505 | { | ||
506 | const struct twl4030_madc_conversion_method *method; | ||
507 | int ret = 0; | ||
508 | |||
509 | if (conv_method != TWL4030_MADC_SW1 && conv_method != TWL4030_MADC_SW2) | ||
510 | return -ENOTSUPP; | ||
511 | |||
512 | method = &twl4030_conversion_methods[conv_method]; | ||
513 | ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, TWL4030_MADC_SW_START, | ||
514 | method->ctrl); | ||
515 | if (ret) { | ||
516 | dev_err(madc->dev, "unable to write ctrl register 0x%X\n", | ||
517 | method->ctrl); | ||
518 | return ret; | ||
519 | } | ||
520 | |||
521 | return 0; | ||
522 | } | ||
523 | |||
524 | /* | ||
525 | * Function that waits for conversion to be ready | ||
526 | * @madc - pointer to twl4030_madc_data struct | ||
527 | * @timeout_ms - timeout value in milliseconds | ||
528 | * @status_reg - ctrl register | ||
529 | * returns 0 if succeeds else a negative error value | ||
530 | */ | ||
531 | static int twl4030_madc_wait_conversion_ready(struct twl4030_madc_data *madc, | ||
532 | unsigned int timeout_ms, | ||
533 | u8 status_reg) | ||
534 | { | ||
535 | unsigned long timeout; | ||
536 | int ret; | ||
537 | |||
538 | timeout = jiffies + msecs_to_jiffies(timeout_ms); | ||
539 | do { | ||
540 | u8 reg; | ||
541 | |||
542 | ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, ®, status_reg); | ||
543 | if (ret) { | ||
544 | dev_err(madc->dev, | ||
545 | "unable to read status register 0x%X\n", | ||
546 | status_reg); | ||
547 | return ret; | ||
548 | } | ||
549 | if (!(reg & TWL4030_MADC_BUSY) && (reg & TWL4030_MADC_EOC_SW)) | ||
550 | return 0; | ||
551 | usleep_range(500, 2000); | ||
552 | } while (!time_after(jiffies, timeout)); | ||
553 | dev_err(madc->dev, "conversion timeout!\n"); | ||
554 | |||
555 | return -EAGAIN; | ||
556 | } | ||
557 | |||
558 | /* | ||
559 | * An exported function which can be called from other kernel drivers. | ||
560 | * @req twl4030_madc_request structure | ||
561 | * req->rbuf will be filled with read values of channels based on the | ||
562 | * channel index. If a particular channel reading fails there will | ||
563 | * be a negative error value in the corresponding array element. | ||
564 | * returns 0 if succeeds else error value | ||
565 | */ | ||
566 | int twl4030_madc_conversion(struct twl4030_madc_request *req) | ||
567 | { | ||
568 | const struct twl4030_madc_conversion_method *method; | ||
569 | int ret; | ||
570 | |||
571 | if (!req || !twl4030_madc) | ||
572 | return -EINVAL; | ||
573 | |||
574 | mutex_lock(&twl4030_madc->lock); | ||
575 | if (req->method < TWL4030_MADC_RT || req->method > TWL4030_MADC_SW2) { | ||
576 | ret = -EINVAL; | ||
577 | goto out; | ||
578 | } | ||
579 | /* Do we have a conversion request ongoing */ | ||
580 | if (twl4030_madc->requests[req->method].active) { | ||
581 | ret = -EBUSY; | ||
582 | goto out; | ||
583 | } | ||
584 | method = &twl4030_conversion_methods[req->method]; | ||
585 | /* Select channels to be converted */ | ||
586 | ret = twl_i2c_write_u16(TWL4030_MODULE_MADC, req->channels, method->sel); | ||
587 | if (ret) { | ||
588 | dev_err(twl4030_madc->dev, | ||
589 | "unable to write sel register 0x%X\n", method->sel); | ||
590 | goto out; | ||
591 | } | ||
592 | /* Select averaging for all channels if do_avg is set */ | ||
593 | if (req->do_avg) { | ||
594 | ret = twl_i2c_write_u16(TWL4030_MODULE_MADC, req->channels, | ||
595 | method->avg); | ||
596 | if (ret) { | ||
597 | dev_err(twl4030_madc->dev, | ||
598 | "unable to write avg register 0x%X\n", | ||
599 | method->avg); | ||
600 | goto out; | ||
601 | } | ||
602 | } | ||
603 | if (req->type == TWL4030_MADC_IRQ_ONESHOT && req->func_cb != NULL) { | ||
604 | ret = twl4030_madc_set_irq(twl4030_madc, req); | ||
605 | if (ret < 0) | ||
606 | goto out; | ||
607 | ret = twl4030_madc_start_conversion(twl4030_madc, req->method); | ||
608 | if (ret < 0) | ||
609 | goto out; | ||
610 | twl4030_madc->requests[req->method].active = 1; | ||
611 | ret = 0; | ||
612 | goto out; | ||
613 | } | ||
614 | /* With RT method we should not be here anymore */ | ||
615 | if (req->method == TWL4030_MADC_RT) { | ||
616 | ret = -EINVAL; | ||
617 | goto out; | ||
618 | } | ||
619 | ret = twl4030_madc_start_conversion(twl4030_madc, req->method); | ||
620 | if (ret < 0) | ||
621 | goto out; | ||
622 | twl4030_madc->requests[req->method].active = 1; | ||
623 | /* Wait until conversion is ready (ctrl register returns EOC) */ | ||
624 | ret = twl4030_madc_wait_conversion_ready(twl4030_madc, 5, method->ctrl); | ||
625 | if (ret) { | ||
626 | twl4030_madc->requests[req->method].active = 0; | ||
627 | goto out; | ||
628 | } | ||
629 | ret = twl4030_madc_read_channels(twl4030_madc, method->rbase, | ||
630 | req->channels, req->rbuf, req->raw); | ||
631 | twl4030_madc->requests[req->method].active = 0; | ||
632 | |||
633 | out: | ||
634 | mutex_unlock(&twl4030_madc->lock); | ||
635 | |||
636 | return ret; | ||
637 | } | ||
638 | EXPORT_SYMBOL_GPL(twl4030_madc_conversion); | ||
639 | |||
640 | int twl4030_get_madc_conversion(int channel_no) | ||
641 | { | ||
642 | struct twl4030_madc_request req; | ||
643 | int temp = 0; | ||
644 | int ret; | ||
645 | |||
646 | req.channels = (1 << channel_no); | ||
647 | req.method = TWL4030_MADC_SW2; | ||
648 | req.active = 0; | ||
649 | req.func_cb = NULL; | ||
650 | ret = twl4030_madc_conversion(&req); | ||
651 | if (ret < 0) | ||
652 | return ret; | ||
653 | if (req.rbuf[channel_no] > 0) | ||
654 | temp = req.rbuf[channel_no]; | ||
655 | |||
656 | return temp; | ||
657 | } | ||
658 | EXPORT_SYMBOL_GPL(twl4030_get_madc_conversion); | ||
659 | |||
660 | /** | ||
661 | * twl4030_madc_set_current_generator() - setup bias current | ||
662 | * | ||
663 | * @madc: pointer to twl4030_madc_data struct | ||
664 | * @chan: can be one of the two values: | ||
665 | * TWL4030_BCI_ITHEN | ||
666 | * Enables bias current for main battery type reading | ||
667 | * TWL4030_BCI_TYPEN | ||
668 | * Enables bias current for main battery temperature sensing | ||
669 | * @on: enable or disable chan. | ||
670 | * | ||
671 | * Function to enable or disable bias current for | ||
672 | * main battery type reading or temperature sensing | ||
673 | */ | ||
674 | static int twl4030_madc_set_current_generator(struct twl4030_madc_data *madc, | ||
675 | int chan, int on) | ||
676 | { | ||
677 | int ret; | ||
678 | int regmask; | ||
679 | u8 regval; | ||
680 | |||
681 | ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, | ||
682 | ®val, TWL4030_BCI_BCICTL1); | ||
683 | if (ret) { | ||
684 | dev_err(madc->dev, "unable to read BCICTL1 reg 0x%X", | ||
685 | TWL4030_BCI_BCICTL1); | ||
686 | return ret; | ||
687 | } | ||
688 | |||
689 | regmask = chan ? TWL4030_BCI_ITHEN : TWL4030_BCI_TYPEN; | ||
690 | if (on) | ||
691 | regval |= regmask; | ||
692 | else | ||
693 | regval &= ~regmask; | ||
694 | |||
695 | ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, | ||
696 | regval, TWL4030_BCI_BCICTL1); | ||
697 | if (ret) { | ||
698 | dev_err(madc->dev, "unable to write BCICTL1 reg 0x%X\n", | ||
699 | TWL4030_BCI_BCICTL1); | ||
700 | return ret; | ||
701 | } | ||
702 | |||
703 | return 0; | ||
704 | } | ||
705 | |||
706 | /* | ||
707 | * Function that sets MADC software power on bit to enable MADC | ||
708 | * @madc - pointer to twl4030_madc_data struct | ||
709 | * @on - Enable or disable MADC software power on bit. | ||
710 | * returns error if i2c read/write fails else 0 | ||
711 | */ | ||
712 | static int twl4030_madc_set_power(struct twl4030_madc_data *madc, int on) | ||
713 | { | ||
714 | u8 regval; | ||
715 | int ret; | ||
716 | |||
717 | ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, | ||
718 | ®val, TWL4030_MADC_CTRL1); | ||
719 | if (ret) { | ||
720 | dev_err(madc->dev, "unable to read madc ctrl1 reg 0x%X\n", | ||
721 | TWL4030_MADC_CTRL1); | ||
722 | return ret; | ||
723 | } | ||
724 | if (on) | ||
725 | regval |= TWL4030_MADC_MADCON; | ||
726 | else | ||
727 | regval &= ~TWL4030_MADC_MADCON; | ||
728 | ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, regval, TWL4030_MADC_CTRL1); | ||
729 | if (ret) { | ||
730 | dev_err(madc->dev, "unable to write madc ctrl1 reg 0x%X\n", | ||
731 | TWL4030_MADC_CTRL1); | ||
732 | return ret; | ||
733 | } | ||
734 | |||
735 | return 0; | ||
736 | } | ||
737 | |||
738 | /* | ||
739 | * Initialize MADC and request for threaded irq | ||
740 | */ | ||
741 | static int twl4030_madc_probe(struct platform_device *pdev) | ||
742 | { | ||
743 | struct twl4030_madc_data *madc; | ||
744 | struct twl4030_madc_platform_data *pdata = dev_get_platdata(&pdev->dev); | ||
745 | struct device_node *np = pdev->dev.of_node; | ||
746 | int irq, ret; | ||
747 | u8 regval; | ||
748 | struct iio_dev *iio_dev = NULL; | ||
749 | |||
750 | if (!pdata && !np) { | ||
751 | dev_err(&pdev->dev, "neither platform data nor Device Tree node available\n"); | ||
752 | return -EINVAL; | ||
753 | } | ||
754 | |||
755 | iio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*madc)); | ||
756 | if (!iio_dev) { | ||
757 | dev_err(&pdev->dev, "failed allocating iio device\n"); | ||
758 | return -ENOMEM; | ||
759 | } | ||
760 | |||
761 | madc = iio_priv(iio_dev); | ||
762 | madc->dev = &pdev->dev; | ||
763 | |||
764 | iio_dev->name = dev_name(&pdev->dev); | ||
765 | iio_dev->dev.parent = &pdev->dev; | ||
766 | iio_dev->dev.of_node = pdev->dev.of_node; | ||
767 | iio_dev->info = &twl4030_madc_iio_info; | ||
768 | iio_dev->modes = INDIO_DIRECT_MODE; | ||
769 | iio_dev->channels = twl4030_madc_iio_channels; | ||
770 | iio_dev->num_channels = ARRAY_SIZE(twl4030_madc_iio_channels); | ||
771 | |||
772 | /* | ||
773 | * Phoenix provides 2 interrupt lines. The first one is connected to | ||
774 | * the OMAP. The other one can be connected to the other processor such | ||
775 | * as modem. Hence two separate ISR and IMR registers. | ||
776 | */ | ||
777 | if (pdata) | ||
778 | madc->use_second_irq = (pdata->irq_line != 1); | ||
779 | else | ||
780 | madc->use_second_irq = of_property_read_bool(np, | ||
781 | "ti,system-uses-second-madc-irq"); | ||
782 | |||
783 | madc->imr = madc->use_second_irq ? TWL4030_MADC_IMR2 : | ||
784 | TWL4030_MADC_IMR1; | ||
785 | madc->isr = madc->use_second_irq ? TWL4030_MADC_ISR2 : | ||
786 | TWL4030_MADC_ISR1; | ||
787 | |||
788 | ret = twl4030_madc_set_power(madc, 1); | ||
789 | if (ret < 0) | ||
790 | return ret; | ||
791 | ret = twl4030_madc_set_current_generator(madc, 0, 1); | ||
792 | if (ret < 0) | ||
793 | goto err_current_generator; | ||
794 | |||
795 | ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, | ||
796 | ®val, TWL4030_BCI_BCICTL1); | ||
797 | if (ret) { | ||
798 | dev_err(&pdev->dev, "unable to read reg BCI CTL1 0x%X\n", | ||
799 | TWL4030_BCI_BCICTL1); | ||
800 | goto err_i2c; | ||
801 | } | ||
802 | regval |= TWL4030_BCI_MESBAT; | ||
803 | ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, | ||
804 | regval, TWL4030_BCI_BCICTL1); | ||
805 | if (ret) { | ||
806 | dev_err(&pdev->dev, "unable to write reg BCI Ctl1 0x%X\n", | ||
807 | TWL4030_BCI_BCICTL1); | ||
808 | goto err_i2c; | ||
809 | } | ||
810 | |||
811 | /* Check that MADC clock is on */ | ||
812 | ret = twl_i2c_read_u8(TWL4030_MODULE_INTBR, ®val, TWL4030_REG_GPBR1); | ||
813 | if (ret) { | ||
814 | dev_err(&pdev->dev, "unable to read reg GPBR1 0x%X\n", | ||
815 | TWL4030_REG_GPBR1); | ||
816 | goto err_i2c; | ||
817 | } | ||
818 | |||
819 | /* If MADC clk is not on, turn it on */ | ||
820 | if (!(regval & TWL4030_GPBR1_MADC_HFCLK_EN)) { | ||
821 | dev_info(&pdev->dev, "clk disabled, enabling\n"); | ||
822 | regval |= TWL4030_GPBR1_MADC_HFCLK_EN; | ||
823 | ret = twl_i2c_write_u8(TWL4030_MODULE_INTBR, regval, | ||
824 | TWL4030_REG_GPBR1); | ||
825 | if (ret) { | ||
826 | dev_err(&pdev->dev, "unable to write reg GPBR1 0x%X\n", | ||
827 | TWL4030_REG_GPBR1); | ||
828 | goto err_i2c; | ||
829 | } | ||
830 | } | ||
831 | |||
832 | platform_set_drvdata(pdev, iio_dev); | ||
833 | mutex_init(&madc->lock); | ||
834 | |||
835 | irq = platform_get_irq(pdev, 0); | ||
836 | ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, | ||
837 | twl4030_madc_threaded_irq_handler, | ||
838 | IRQF_TRIGGER_RISING, "twl4030_madc", madc); | ||
839 | if (ret) { | ||
840 | dev_err(&pdev->dev, "could not request irq\n"); | ||
841 | goto err_i2c; | ||
842 | } | ||
843 | twl4030_madc = madc; | ||
844 | |||
845 | ret = iio_device_register(iio_dev); | ||
846 | if (ret) { | ||
847 | dev_err(&pdev->dev, "could not register iio device\n"); | ||
848 | goto err_i2c; | ||
849 | } | ||
850 | |||
851 | return 0; | ||
852 | |||
853 | err_i2c: | ||
854 | twl4030_madc_set_current_generator(madc, 0, 0); | ||
855 | err_current_generator: | ||
856 | twl4030_madc_set_power(madc, 0); | ||
857 | return ret; | ||
858 | } | ||
859 | |||
860 | static int twl4030_madc_remove(struct platform_device *pdev) | ||
861 | { | ||
862 | struct iio_dev *iio_dev = platform_get_drvdata(pdev); | ||
863 | struct twl4030_madc_data *madc = iio_priv(iio_dev); | ||
864 | |||
865 | iio_device_unregister(iio_dev); | ||
866 | |||
867 | twl4030_madc_set_current_generator(madc, 0, 0); | ||
868 | twl4030_madc_set_power(madc, 0); | ||
869 | |||
870 | return 0; | ||
871 | } | ||
872 | |||
873 | #ifdef CONFIG_OF | ||
874 | static const struct of_device_id twl_madc_of_match[] = { | ||
875 | { .compatible = "ti,twl4030-madc", }, | ||
876 | { }, | ||
877 | }; | ||
878 | MODULE_DEVICE_TABLE(of, twl_madc_of_match); | ||
879 | #endif | ||
880 | |||
881 | static struct platform_driver twl4030_madc_driver = { | ||
882 | .probe = twl4030_madc_probe, | ||
883 | .remove = twl4030_madc_remove, | ||
884 | .driver = { | ||
885 | .name = "twl4030_madc", | ||
886 | .owner = THIS_MODULE, | ||
887 | .of_match_table = of_match_ptr(twl_madc_of_match), | ||
888 | }, | ||
889 | }; | ||
890 | |||
891 | module_platform_driver(twl4030_madc_driver); | ||
892 | |||
893 | MODULE_DESCRIPTION("TWL4030 ADC driver"); | ||
894 | MODULE_LICENSE("GPL"); | ||
895 | MODULE_AUTHOR("J Keerthy"); | ||
896 | MODULE_ALIAS("platform:twl4030_madc"); | ||