blob: 6ce40cc4568a2d75561177ec079eaccdce2fc8cb [file] [log] [blame]
Thomas Gleixner2b27bdc2019-05-29 16:57:50 -07001// SPDX-License-Identifier: GPL-2.0-only
Keerthyf99c1d42011-03-01 19:12:26 +05302/*
3 *
4 * TWL4030 MADC module driver-This driver monitors the real time
5 * conversion of analog signals like battery temperature,
6 * battery type, battery level etc.
7 *
Alexander A. Klimov3593cd52020-07-04 21:27:43 +02008 * Copyright (C) 2011 Texas Instruments Incorporated - https://www.ti.com/
Keerthyf99c1d42011-03-01 19:12:26 +05309 * J Keerthy <j-keerthy@ti.com>
10 *
11 * Based on twl4030-madc.c
12 * Copyright (C) 2008 Nokia Corporation
13 * Mikko Ylinen <mikko.k.ylinen@nokia.com>
14 *
15 * Amit Kucheria <amit.kucheria@canonical.com>
Keerthyf99c1d42011-03-01 19:12:26 +053016 */
17
Keerthyf99c1d42011-03-01 19:12:26 +053018#include <linux/device.h>
19#include <linux/interrupt.h>
20#include <linux/kernel.h>
21#include <linux/delay.h>
22#include <linux/platform_device.h>
23#include <linux/slab.h>
Wolfram Sanga2054252017-08-14 18:34:24 +020024#include <linux/mfd/twl.h>
Keerthyf99c1d42011-03-01 19:12:26 +053025#include <linux/module.h>
26#include <linux/stddef.h>
27#include <linux/mutex.h>
28#include <linux/bitops.h>
29#include <linux/jiffies.h>
30#include <linux/types.h>
31#include <linux/gfp.h>
32#include <linux/err.h>
Adam YH Lee7cc97d72015-08-04 11:15:48 -070033#include <linux/regulator/consumer.h>
Keerthyf99c1d42011-03-01 19:12:26 +053034
Sebastian Reichel2f39b702014-03-16 02:43:26 +010035#include <linux/iio/iio.h>
36
Sebastian Reichel7af1a062017-04-27 17:30:12 +020037#define TWL4030_MADC_MAX_CHANNELS 16
38
39#define TWL4030_MADC_CTRL1 0x00
40#define TWL4030_MADC_CTRL2 0x01
41
42#define TWL4030_MADC_RTSELECT_LSB 0x02
43#define TWL4030_MADC_SW1SELECT_LSB 0x06
44#define TWL4030_MADC_SW2SELECT_LSB 0x0A
45
46#define TWL4030_MADC_RTAVERAGE_LSB 0x04
47#define TWL4030_MADC_SW1AVERAGE_LSB 0x08
48#define TWL4030_MADC_SW2AVERAGE_LSB 0x0C
49
50#define TWL4030_MADC_CTRL_SW1 0x12
51#define TWL4030_MADC_CTRL_SW2 0x13
52
53#define TWL4030_MADC_RTCH0_LSB 0x17
54#define TWL4030_MADC_GPCH0_LSB 0x37
55
56#define TWL4030_MADC_MADCON (1 << 0) /* MADC power on */
57#define TWL4030_MADC_BUSY (1 << 0) /* MADC busy */
58/* MADC conversion completion */
59#define TWL4030_MADC_EOC_SW (1 << 1)
60/* MADC SWx start conversion */
61#define TWL4030_MADC_SW_START (1 << 5)
62#define TWL4030_MADC_ADCIN0 (1 << 0)
63#define TWL4030_MADC_ADCIN1 (1 << 1)
64#define TWL4030_MADC_ADCIN2 (1 << 2)
65#define TWL4030_MADC_ADCIN3 (1 << 3)
66#define TWL4030_MADC_ADCIN4 (1 << 4)
67#define TWL4030_MADC_ADCIN5 (1 << 5)
68#define TWL4030_MADC_ADCIN6 (1 << 6)
69#define TWL4030_MADC_ADCIN7 (1 << 7)
70#define TWL4030_MADC_ADCIN8 (1 << 8)
71#define TWL4030_MADC_ADCIN9 (1 << 9)
72#define TWL4030_MADC_ADCIN10 (1 << 10)
73#define TWL4030_MADC_ADCIN11 (1 << 11)
74#define TWL4030_MADC_ADCIN12 (1 << 12)
75#define TWL4030_MADC_ADCIN13 (1 << 13)
76#define TWL4030_MADC_ADCIN14 (1 << 14)
77#define TWL4030_MADC_ADCIN15 (1 << 15)
78
79/* Fixed channels */
80#define TWL4030_MADC_BTEMP TWL4030_MADC_ADCIN1
81#define TWL4030_MADC_VBUS TWL4030_MADC_ADCIN8
82#define TWL4030_MADC_VBKB TWL4030_MADC_ADCIN9
83#define TWL4030_MADC_ICHG TWL4030_MADC_ADCIN10
84#define TWL4030_MADC_VCHG TWL4030_MADC_ADCIN11
85#define TWL4030_MADC_VBAT TWL4030_MADC_ADCIN12
86
87/* Step size and prescaler ratio */
88#define TEMP_STEP_SIZE 147
89#define TEMP_PSR_R 100
90#define CURR_STEP_SIZE 147
91#define CURR_PSR_R1 44
92#define CURR_PSR_R2 88
93
94#define TWL4030_BCI_BCICTL1 0x23
95#define TWL4030_BCI_CGAIN 0x020
96#define TWL4030_BCI_MESBAT (1 << 1)
97#define TWL4030_BCI_TYPEN (1 << 4)
98#define TWL4030_BCI_ITHEN (1 << 3)
99
100#define REG_BCICTL2 0x024
101#define TWL4030_BCI_ITHSENS 0x007
102
103/* Register and bits for GPBR1 register */
104#define TWL4030_REG_GPBR1 0x0c
105#define TWL4030_GPBR1_MADC_HFCLK_EN (1 << 7)
106
Adam YH Lee7cc97d72015-08-04 11:15:48 -0700107#define TWL4030_USB_SEL_MADC_MCPC (1<<3)
108#define TWL4030_USB_CARKIT_ANA_CTRL 0xBB
109
Sebastian Reichel7af1a062017-04-27 17:30:12 +0200110struct twl4030_madc_conversion_method {
111 u8 sel;
112 u8 avg;
113 u8 rbase;
114 u8 ctrl;
115};
116
117/**
118 * struct twl4030_madc_request - madc request packet for channel conversion
119 * @channels: 16 bit bitmap for individual channels
120 * @do_avg: sample the input channel for 4 consecutive cycles
121 * @method: RT, SW1, SW2
122 * @type: Polling or interrupt based method
123 * @active: Flag if request is active
124 * @result_pending: Flag from irq handler, that result is ready
125 * @raw: Return raw value, do not convert it
126 * @rbuf: Result buffer
127 */
128struct twl4030_madc_request {
129 unsigned long channels;
130 bool do_avg;
131 u16 method;
132 u16 type;
133 bool active;
134 bool result_pending;
135 bool raw;
136 int rbuf[TWL4030_MADC_MAX_CHANNELS];
137};
138
139enum conversion_methods {
140 TWL4030_MADC_RT,
141 TWL4030_MADC_SW1,
142 TWL4030_MADC_SW2,
143 TWL4030_MADC_NUM_METHODS
144};
145
146enum sample_type {
147 TWL4030_MADC_WAIT,
148 TWL4030_MADC_IRQ_ONESHOT,
149 TWL4030_MADC_IRQ_REARM
150};
151
Sebastian Reichel99be0242014-03-16 02:43:27 +0100152/**
Keerthyf99c1d42011-03-01 19:12:26 +0530153 * struct twl4030_madc_data - a container for madc info
Sebastian Reichel99be0242014-03-16 02:43:27 +0100154 * @dev: Pointer to device structure for madc
155 * @lock: Mutex protecting this data structure
Lee Jones2006cf12020-07-17 17:55:11 +0100156 * @usb3v1: Pointer to bias regulator for madc
Sebastian Reichel99be0242014-03-16 02:43:27 +0100157 * @requests: Array of request struct corresponding to SW1, SW2 and RT
158 * @use_second_irq: IRQ selection (main or co-processor)
159 * @imr: Interrupt mask register of MADC
160 * @isr: Interrupt status register of MADC
Keerthyf99c1d42011-03-01 19:12:26 +0530161 */
162struct twl4030_madc_data {
163 struct device *dev;
Lee Jones2006cf12020-07-17 17:55:11 +0100164 struct mutex lock;
Adam YH Lee7cc97d72015-08-04 11:15:48 -0700165 struct regulator *usb3v1;
Keerthyf99c1d42011-03-01 19:12:26 +0530166 struct twl4030_madc_request requests[TWL4030_MADC_NUM_METHODS];
Sebastian Reichel2f39b702014-03-16 02:43:26 +0100167 bool use_second_irq;
Sebastian Reichel99be0242014-03-16 02:43:27 +0100168 u8 imr;
169 u8 isr;
Keerthyf99c1d42011-03-01 19:12:26 +0530170};
171
Sebastian Reichel42ab9272017-04-27 17:30:09 +0200172static int twl4030_madc_conversion(struct twl4030_madc_request *req);
173
Sebastian Reichel2f39b702014-03-16 02:43:26 +0100174static int twl4030_madc_read(struct iio_dev *iio_dev,
175 const struct iio_chan_spec *chan,
176 int *val, int *val2, long mask)
177{
178 struct twl4030_madc_data *madc = iio_priv(iio_dev);
179 struct twl4030_madc_request req;
180 int ret;
181
182 req.method = madc->use_second_irq ? TWL4030_MADC_SW2 : TWL4030_MADC_SW1;
183
184 req.channels = BIT(chan->channel);
185 req.active = false;
Sebastian Reichel2f39b702014-03-16 02:43:26 +0100186 req.type = TWL4030_MADC_WAIT;
187 req.raw = !(mask == IIO_CHAN_INFO_PROCESSED);
188 req.do_avg = (mask == IIO_CHAN_INFO_AVERAGE_RAW);
189
190 ret = twl4030_madc_conversion(&req);
191 if (ret < 0)
192 return ret;
193
194 *val = req.rbuf[chan->channel];
195
196 return IIO_VAL_INT;
197}
198
199static const struct iio_info twl4030_madc_iio_info = {
200 .read_raw = &twl4030_madc_read,
Sebastian Reichel2f39b702014-03-16 02:43:26 +0100201};
202
203#define TWL4030_ADC_CHANNEL(_channel, _type, _name) { \
204 .type = _type, \
205 .channel = _channel, \
206 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
207 BIT(IIO_CHAN_INFO_AVERAGE_RAW) | \
208 BIT(IIO_CHAN_INFO_PROCESSED), \
209 .datasheet_name = _name, \
210 .indexed = 1, \
211}
212
213static const struct iio_chan_spec twl4030_madc_iio_channels[] = {
214 TWL4030_ADC_CHANNEL(0, IIO_VOLTAGE, "ADCIN0"),
215 TWL4030_ADC_CHANNEL(1, IIO_TEMP, "ADCIN1"),
216 TWL4030_ADC_CHANNEL(2, IIO_VOLTAGE, "ADCIN2"),
217 TWL4030_ADC_CHANNEL(3, IIO_VOLTAGE, "ADCIN3"),
218 TWL4030_ADC_CHANNEL(4, IIO_VOLTAGE, "ADCIN4"),
219 TWL4030_ADC_CHANNEL(5, IIO_VOLTAGE, "ADCIN5"),
220 TWL4030_ADC_CHANNEL(6, IIO_VOLTAGE, "ADCIN6"),
221 TWL4030_ADC_CHANNEL(7, IIO_VOLTAGE, "ADCIN7"),
222 TWL4030_ADC_CHANNEL(8, IIO_VOLTAGE, "ADCIN8"),
223 TWL4030_ADC_CHANNEL(9, IIO_VOLTAGE, "ADCIN9"),
224 TWL4030_ADC_CHANNEL(10, IIO_CURRENT, "ADCIN10"),
225 TWL4030_ADC_CHANNEL(11, IIO_VOLTAGE, "ADCIN11"),
226 TWL4030_ADC_CHANNEL(12, IIO_VOLTAGE, "ADCIN12"),
227 TWL4030_ADC_CHANNEL(13, IIO_VOLTAGE, "ADCIN13"),
228 TWL4030_ADC_CHANNEL(14, IIO_VOLTAGE, "ADCIN14"),
229 TWL4030_ADC_CHANNEL(15, IIO_VOLTAGE, "ADCIN15"),
230};
231
Keerthyf99c1d42011-03-01 19:12:26 +0530232static struct twl4030_madc_data *twl4030_madc;
233
234struct twl4030_prescale_divider_ratios {
235 s16 numerator;
236 s16 denominator;
237};
238
239static const struct twl4030_prescale_divider_ratios
240twl4030_divider_ratios[16] = {
241 {1, 1}, /* CHANNEL 0 No Prescaler */
242 {1, 1}, /* CHANNEL 1 No Prescaler */
243 {6, 10}, /* CHANNEL 2 */
244 {6, 10}, /* CHANNEL 3 */
245 {6, 10}, /* CHANNEL 4 */
246 {6, 10}, /* CHANNEL 5 */
247 {6, 10}, /* CHANNEL 6 */
248 {6, 10}, /* CHANNEL 7 */
249 {3, 14}, /* CHANNEL 8 */
250 {1, 3}, /* CHANNEL 9 */
251 {1, 1}, /* CHANNEL 10 No Prescaler */
252 {15, 100}, /* CHANNEL 11 */
253 {1, 4}, /* CHANNEL 12 */
254 {1, 1}, /* CHANNEL 13 Reserved channels */
255 {1, 1}, /* CHANNEL 14 Reseved channels */
256 {5, 11}, /* CHANNEL 15 */
257};
258
259
Sebastian Reichel99be0242014-03-16 02:43:27 +0100260/* Conversion table from -3 to 55 degrees Celcius */
261static int twl4030_therm_tbl[] = {
262 30800, 29500, 28300, 27100,
263 26000, 24900, 23900, 22900, 22000, 21100, 20300, 19400, 18700,
264 17900, 17200, 16500, 15900, 15300, 14700, 14100, 13600, 13100,
265 12600, 12100, 11600, 11200, 10800, 10400, 10000, 9630, 9280,
266 8950, 8620, 8310, 8020, 7730, 7460, 7200, 6950, 6710,
267 6470, 6250, 6040, 5830, 5640, 5450, 5260, 5090, 4920,
268 4760, 4600, 4450, 4310, 4170, 4040, 3910, 3790, 3670,
269 3550
Keerthyf99c1d42011-03-01 19:12:26 +0530270};
271
272/*
273 * Structure containing the registers
274 * of different conversion methods supported by MADC.
275 * Hardware or RT real time conversion request initiated by external host
276 * processor for RT Signal conversions.
277 * External host processors can also request for non RT conversions
278 * SW1 and SW2 software conversions also called asynchronous or GPC request.
279 */
280static
281const struct twl4030_madc_conversion_method twl4030_conversion_methods[] = {
282 [TWL4030_MADC_RT] = {
283 .sel = TWL4030_MADC_RTSELECT_LSB,
284 .avg = TWL4030_MADC_RTAVERAGE_LSB,
285 .rbase = TWL4030_MADC_RTCH0_LSB,
286 },
287 [TWL4030_MADC_SW1] = {
288 .sel = TWL4030_MADC_SW1SELECT_LSB,
289 .avg = TWL4030_MADC_SW1AVERAGE_LSB,
290 .rbase = TWL4030_MADC_GPCH0_LSB,
291 .ctrl = TWL4030_MADC_CTRL_SW1,
292 },
293 [TWL4030_MADC_SW2] = {
294 .sel = TWL4030_MADC_SW2SELECT_LSB,
295 .avg = TWL4030_MADC_SW2AVERAGE_LSB,
296 .rbase = TWL4030_MADC_GPCH0_LSB,
297 .ctrl = TWL4030_MADC_CTRL_SW2,
298 },
299};
300
Sebastian Reichel99be0242014-03-16 02:43:27 +0100301/**
302 * twl4030_madc_channel_raw_read() - Function to read a particular channel value
303 * @madc: pointer to struct twl4030_madc_data
304 * @reg: lsb of ADC Channel
305 *
306 * Return: 0 on success, an error code otherwise.
Keerthyf99c1d42011-03-01 19:12:26 +0530307 */
308static int twl4030_madc_channel_raw_read(struct twl4030_madc_data *madc, u8 reg)
309{
Sebastian Reichel168ae302014-03-16 02:43:29 +0100310 u16 val;
Keerthyf99c1d42011-03-01 19:12:26 +0530311 int ret;
312 /*
313 * For each ADC channel, we have MSB and LSB register pair. MSB address
314 * is always LSB address+1. reg parameter is the address of LSB register
315 */
Sebastian Reichel168ae302014-03-16 02:43:29 +0100316 ret = twl_i2c_read_u16(TWL4030_MODULE_MADC, &val, reg);
Keerthyf99c1d42011-03-01 19:12:26 +0530317 if (ret) {
Sebastian Reichel168ae302014-03-16 02:43:29 +0100318 dev_err(madc->dev, "unable to read register 0x%X\n", reg);
Keerthyf99c1d42011-03-01 19:12:26 +0530319 return ret;
320 }
321
Sebastian Reichel168ae302014-03-16 02:43:29 +0100322 return (int)(val >> 6);
Keerthyf99c1d42011-03-01 19:12:26 +0530323}
324
325/*
Sebastian Reichel99be0242014-03-16 02:43:27 +0100326 * Return battery temperature in degrees Celsius
Keerthyf99c1d42011-03-01 19:12:26 +0530327 * Or < 0 on failure.
328 */
329static int twl4030battery_temperature(int raw_volt)
330{
331 u8 val;
332 int temp, curr, volt, res, ret;
333
334 volt = (raw_volt * TEMP_STEP_SIZE) / TEMP_PSR_R;
Sebastian Reichel99be0242014-03-16 02:43:27 +0100335 /* Getting and calculating the supply current in micro amperes */
Peter Ujfalusie45342f2012-11-13 09:28:51 +0100336 ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, &val,
Keerthyf99c1d42011-03-01 19:12:26 +0530337 REG_BCICTL2);
338 if (ret < 0)
339 return ret;
Sebastian Reichel99be0242014-03-16 02:43:27 +0100340
H. Nikolaus Schaller0cbb39f2015-05-28 21:50:18 +0200341 curr = ((val & TWL4030_BCI_ITHSENS) + 1) * 10;
Keerthyf99c1d42011-03-01 19:12:26 +0530342 /* Getting and calculating the thermistor resistance in ohms */
343 res = volt * 1000 / curr;
344 /* calculating temperature */
345 for (temp = 58; temp >= 0; temp--) {
Sebastian Reichel99be0242014-03-16 02:43:27 +0100346 int actual = twl4030_therm_tbl[temp];
Keerthyf99c1d42011-03-01 19:12:26 +0530347 if ((actual - res) >= 0)
348 break;
349 }
350
351 return temp + 1;
352}
353
354static int twl4030battery_current(int raw_volt)
355{
356 int ret;
357 u8 val;
358
Peter Ujfalusie45342f2012-11-13 09:28:51 +0100359 ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, &val,
Keerthyf99c1d42011-03-01 19:12:26 +0530360 TWL4030_BCI_BCICTL1);
361 if (ret)
362 return ret;
363 if (val & TWL4030_BCI_CGAIN) /* slope of 0.44 mV/mA */
364 return (raw_volt * CURR_STEP_SIZE) / CURR_PSR_R1;
365 else /* slope of 0.88 mV/mA */
366 return (raw_volt * CURR_STEP_SIZE) / CURR_PSR_R2;
367}
Sebastian Reichel99be0242014-03-16 02:43:27 +0100368
Keerthyf99c1d42011-03-01 19:12:26 +0530369/*
370 * Function to read channel values
371 * @madc - pointer to twl4030_madc_data struct
372 * @reg_base - Base address of the first channel
Sebastian Reichel99be0242014-03-16 02:43:27 +0100373 * @Channels - 16 bit bitmap. If the bit is set, channel's value is read
Keerthyf99c1d42011-03-01 19:12:26 +0530374 * @buf - The channel values are stored here. if read fails error
Pali Rohára5055d52013-02-15 23:56:49 +0100375 * @raw - Return raw values without conversion
Keerthyf99c1d42011-03-01 19:12:26 +0530376 * value is stored
377 * Returns the number of successfully read channels.
378 */
379static int twl4030_madc_read_channels(struct twl4030_madc_data *madc,
380 u8 reg_base, unsigned
Pali Rohára5055d52013-02-15 23:56:49 +0100381 long channels, int *buf,
382 bool raw)
Keerthyf99c1d42011-03-01 19:12:26 +0530383{
Sebastian Reichel99be0242014-03-16 02:43:27 +0100384 int count = 0;
385 int i;
Keerthyf99c1d42011-03-01 19:12:26 +0530386 u8 reg;
387
388 for_each_set_bit(i, &channels, TWL4030_MADC_MAX_CHANNELS) {
Sebastian Reichel99be0242014-03-16 02:43:27 +0100389 reg = reg_base + (2 * i);
Keerthyf99c1d42011-03-01 19:12:26 +0530390 buf[i] = twl4030_madc_channel_raw_read(madc, reg);
391 if (buf[i] < 0) {
Sebastian Reichel99be0242014-03-16 02:43:27 +0100392 dev_err(madc->dev, "Unable to read register 0x%X\n",
393 reg);
394 return buf[i];
Keerthyf99c1d42011-03-01 19:12:26 +0530395 }
Pali Rohára5055d52013-02-15 23:56:49 +0100396 if (raw) {
397 count++;
398 continue;
399 }
Keerthyf99c1d42011-03-01 19:12:26 +0530400 switch (i) {
401 case 10:
402 buf[i] = twl4030battery_current(buf[i]);
403 if (buf[i] < 0) {
404 dev_err(madc->dev, "err reading current\n");
Sebastian Reichel99be0242014-03-16 02:43:27 +0100405 return buf[i];
Keerthyf99c1d42011-03-01 19:12:26 +0530406 } else {
407 count++;
408 buf[i] = buf[i] - 750;
409 }
410 break;
411 case 1:
412 buf[i] = twl4030battery_temperature(buf[i]);
413 if (buf[i] < 0) {
414 dev_err(madc->dev, "err reading temperature\n");
Sebastian Reichel99be0242014-03-16 02:43:27 +0100415 return buf[i];
Keerthyf99c1d42011-03-01 19:12:26 +0530416 } else {
417 buf[i] -= 3;
418 count++;
419 }
420 break;
421 default:
422 count++;
423 /* Analog Input (V) = conv_result * step_size / R
424 * conv_result = decimal value of 10-bit conversion
425 * result
426 * step size = 1.5 / (2 ^ 10 -1)
427 * R = Prescaler ratio for input channels.
428 * Result given in mV hence multiplied by 1000.
429 */
430 buf[i] = (buf[i] * 3 * 1000 *
431 twl4030_divider_ratios[i].denominator)
432 / (2 * 1023 *
433 twl4030_divider_ratios[i].numerator);
434 }
435 }
Keerthyf99c1d42011-03-01 19:12:26 +0530436
437 return count;
438}
439
440/*
Keerthyf99c1d42011-03-01 19:12:26 +0530441 * Disables irq.
442 * @madc - pointer to twl4030_madc_data struct
443 * @id - irq number to be disabled
444 * can take one of TWL4030_MADC_RT, TWL4030_MADC_SW1, TWL4030_MADC_SW2
445 * corresponding to RT, SW1, SW2 conversion requests.
446 * Returns error if i2c read/write fails.
447 */
448static int twl4030_madc_disable_irq(struct twl4030_madc_data *madc, u8 id)
449{
450 u8 val;
451 int ret;
452
453 ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &val, madc->imr);
454 if (ret) {
455 dev_err(madc->dev, "unable to read imr register 0x%X\n",
456 madc->imr);
457 return ret;
458 }
459 val |= (1 << id);
460 ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, val, madc->imr);
461 if (ret) {
462 dev_err(madc->dev,
463 "unable to write imr register 0x%X\n", madc->imr);
464 return ret;
465 }
466
467 return 0;
468}
469
470static irqreturn_t twl4030_madc_threaded_irq_handler(int irq, void *_madc)
471{
472 struct twl4030_madc_data *madc = _madc;
473 const struct twl4030_madc_conversion_method *method;
474 u8 isr_val, imr_val;
Lee Jones8856d5c2020-07-17 17:55:10 +0100475 int i, ret;
Keerthyf99c1d42011-03-01 19:12:26 +0530476 struct twl4030_madc_request *r;
477
478 mutex_lock(&madc->lock);
479 ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &isr_val, madc->isr);
480 if (ret) {
481 dev_err(madc->dev, "unable to read isr register 0x%X\n",
482 madc->isr);
483 goto err_i2c;
484 }
485 ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &imr_val, madc->imr);
486 if (ret) {
487 dev_err(madc->dev, "unable to read imr register 0x%X\n",
488 madc->imr);
489 goto err_i2c;
490 }
491 isr_val &= ~imr_val;
492 for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) {
493 if (!(isr_val & (1 << i)))
494 continue;
495 ret = twl4030_madc_disable_irq(madc, i);
496 if (ret < 0)
Sebastian Reichel99be0242014-03-16 02:43:27 +0100497 dev_dbg(madc->dev, "Disable interrupt failed %d\n", i);
Jonathan Cameron54f965d2019-10-13 17:37:54 +0100498 madc->requests[i].result_pending = true;
Keerthyf99c1d42011-03-01 19:12:26 +0530499 }
500 for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) {
501 r = &madc->requests[i];
502 /* No pending results for this method, move to next one */
503 if (!r->result_pending)
504 continue;
505 method = &twl4030_conversion_methods[r->method];
506 /* Read results */
Lee Jones8856d5c2020-07-17 17:55:10 +0100507 twl4030_madc_read_channels(madc, method->rbase,
508 r->channels, r->rbuf, r->raw);
Keerthyf99c1d42011-03-01 19:12:26 +0530509 /* Free request */
Jonathan Cameron54f965d2019-10-13 17:37:54 +0100510 r->result_pending = false;
511 r->active = false;
Keerthyf99c1d42011-03-01 19:12:26 +0530512 }
513 mutex_unlock(&madc->lock);
514
515 return IRQ_HANDLED;
516
517err_i2c:
518 /*
519 * In case of error check whichever request is active
520 * and service the same.
521 */
522 for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) {
523 r = &madc->requests[i];
Jonathan Cameron54f965d2019-10-13 17:37:54 +0100524 if (!r->active)
Keerthyf99c1d42011-03-01 19:12:26 +0530525 continue;
526 method = &twl4030_conversion_methods[r->method];
527 /* Read results */
Lee Jones8856d5c2020-07-17 17:55:10 +0100528 twl4030_madc_read_channels(madc, method->rbase,
529 r->channels, r->rbuf, r->raw);
Keerthyf99c1d42011-03-01 19:12:26 +0530530 /* Free request */
Jonathan Cameron54f965d2019-10-13 17:37:54 +0100531 r->result_pending = false;
532 r->active = false;
Keerthyf99c1d42011-03-01 19:12:26 +0530533 }
534 mutex_unlock(&madc->lock);
535
536 return IRQ_HANDLED;
537}
538
Keerthyf99c1d42011-03-01 19:12:26 +0530539/*
540 * Function which enables the madc conversion
541 * by writing to the control register.
542 * @madc - pointer to twl4030_madc_data struct
543 * @conv_method - can be TWL4030_MADC_RT, TWL4030_MADC_SW2, TWL4030_MADC_SW1
544 * corresponding to RT SW1 or SW2 conversion methods.
545 * Returns 0 if succeeds else a negative error value
546 */
547static int twl4030_madc_start_conversion(struct twl4030_madc_data *madc,
548 int conv_method)
549{
550 const struct twl4030_madc_conversion_method *method;
551 int ret = 0;
Sebastian Reichel99be0242014-03-16 02:43:27 +0100552
553 if (conv_method != TWL4030_MADC_SW1 && conv_method != TWL4030_MADC_SW2)
554 return -ENOTSUPP;
555
Keerthyf99c1d42011-03-01 19:12:26 +0530556 method = &twl4030_conversion_methods[conv_method];
Sebastian Reichel99be0242014-03-16 02:43:27 +0100557 ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, TWL4030_MADC_SW_START,
558 method->ctrl);
559 if (ret) {
560 dev_err(madc->dev, "unable to write ctrl register 0x%X\n",
561 method->ctrl);
562 return ret;
Keerthyf99c1d42011-03-01 19:12:26 +0530563 }
564
565 return 0;
566}
567
568/*
569 * Function that waits for conversion to be ready
570 * @madc - pointer to twl4030_madc_data struct
571 * @timeout_ms - timeout value in milliseconds
572 * @status_reg - ctrl register
573 * returns 0 if succeeds else a negative error value
574 */
575static int twl4030_madc_wait_conversion_ready(struct twl4030_madc_data *madc,
576 unsigned int timeout_ms,
577 u8 status_reg)
578{
579 unsigned long timeout;
580 int ret;
581
582 timeout = jiffies + msecs_to_jiffies(timeout_ms);
583 do {
584 u8 reg;
585
586 ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &reg, status_reg);
587 if (ret) {
588 dev_err(madc->dev,
589 "unable to read status register 0x%X\n",
590 status_reg);
591 return ret;
592 }
593 if (!(reg & TWL4030_MADC_BUSY) && (reg & TWL4030_MADC_EOC_SW))
594 return 0;
595 usleep_range(500, 2000);
596 } while (!time_after(jiffies, timeout));
597 dev_err(madc->dev, "conversion timeout!\n");
598
599 return -EAGAIN;
600}
601
602/*
603 * An exported function which can be called from other kernel drivers.
604 * @req twl4030_madc_request structure
605 * req->rbuf will be filled with read values of channels based on the
606 * channel index. If a particular channel reading fails there will
607 * be a negative error value in the corresponding array element.
608 * returns 0 if succeeds else error value
609 */
Sebastian Reichel42ab9272017-04-27 17:30:09 +0200610static int twl4030_madc_conversion(struct twl4030_madc_request *req)
Keerthyf99c1d42011-03-01 19:12:26 +0530611{
612 const struct twl4030_madc_conversion_method *method;
Keerthyf99c1d42011-03-01 19:12:26 +0530613 int ret;
614
Kyle Mannad0e84ca2011-08-11 22:33:14 -0500615 if (!req || !twl4030_madc)
Keerthyf99c1d42011-03-01 19:12:26 +0530616 return -EINVAL;
Kyle Mannad0e84ca2011-08-11 22:33:14 -0500617
Keerthyf99c1d42011-03-01 19:12:26 +0530618 mutex_lock(&twl4030_madc->lock);
619 if (req->method < TWL4030_MADC_RT || req->method > TWL4030_MADC_SW2) {
620 ret = -EINVAL;
621 goto out;
622 }
623 /* Do we have a conversion request ongoing */
624 if (twl4030_madc->requests[req->method].active) {
625 ret = -EBUSY;
626 goto out;
627 }
Keerthyf99c1d42011-03-01 19:12:26 +0530628 method = &twl4030_conversion_methods[req->method];
629 /* Select channels to be converted */
Sebastian Reichel168ae302014-03-16 02:43:29 +0100630 ret = twl_i2c_write_u16(TWL4030_MODULE_MADC, req->channels, method->sel);
Keerthyf99c1d42011-03-01 19:12:26 +0530631 if (ret) {
632 dev_err(twl4030_madc->dev,
Sebastian Reichel168ae302014-03-16 02:43:29 +0100633 "unable to write sel register 0x%X\n", method->sel);
Sanjeev Premie178ccb2011-07-11 20:50:31 +0530634 goto out;
Keerthyf99c1d42011-03-01 19:12:26 +0530635 }
636 /* Select averaging for all channels if do_avg is set */
637 if (req->do_avg) {
Sebastian Reichel168ae302014-03-16 02:43:29 +0100638 ret = twl_i2c_write_u16(TWL4030_MODULE_MADC, req->channels,
639 method->avg);
Keerthyf99c1d42011-03-01 19:12:26 +0530640 if (ret) {
641 dev_err(twl4030_madc->dev,
642 "unable to write avg register 0x%X\n",
Sebastian Reichel99be0242014-03-16 02:43:27 +0100643 method->avg);
Sanjeev Premie178ccb2011-07-11 20:50:31 +0530644 goto out;
Keerthyf99c1d42011-03-01 19:12:26 +0530645 }
646 }
Keerthyf99c1d42011-03-01 19:12:26 +0530647 /* With RT method we should not be here anymore */
648 if (req->method == TWL4030_MADC_RT) {
649 ret = -EINVAL;
650 goto out;
651 }
652 ret = twl4030_madc_start_conversion(twl4030_madc, req->method);
653 if (ret < 0)
654 goto out;
Jonathan Cameron54f965d2019-10-13 17:37:54 +0100655 twl4030_madc->requests[req->method].active = true;
Keerthyf99c1d42011-03-01 19:12:26 +0530656 /* Wait until conversion is ready (ctrl register returns EOC) */
657 ret = twl4030_madc_wait_conversion_ready(twl4030_madc, 5, method->ctrl);
658 if (ret) {
Jonathan Cameron54f965d2019-10-13 17:37:54 +0100659 twl4030_madc->requests[req->method].active = false;
Keerthyf99c1d42011-03-01 19:12:26 +0530660 goto out;
661 }
662 ret = twl4030_madc_read_channels(twl4030_madc, method->rbase,
Pali Rohára5055d52013-02-15 23:56:49 +0100663 req->channels, req->rbuf, req->raw);
Jonathan Cameron54f965d2019-10-13 17:37:54 +0100664 twl4030_madc->requests[req->method].active = false;
Keerthyf99c1d42011-03-01 19:12:26 +0530665
666out:
667 mutex_unlock(&twl4030_madc->lock);
668
669 return ret;
670}
Keerthyf99c1d42011-03-01 19:12:26 +0530671
Sebastian Reichel99be0242014-03-16 02:43:27 +0100672/**
673 * twl4030_madc_set_current_generator() - setup bias current
674 *
675 * @madc: pointer to twl4030_madc_data struct
676 * @chan: can be one of the two values:
H. Nikolaus Schaller994bda82015-05-28 21:50:19 +0200677 * 0 - Enables bias current for main battery type reading
678 * 1 - Enables bias current for main battery temperature sensing
Sebastian Reichel99be0242014-03-16 02:43:27 +0100679 * @on: enable or disable chan.
680 *
Keerthyf99c1d42011-03-01 19:12:26 +0530681 * Function to enable or disable bias current for
682 * main battery type reading or temperature sensing
Keerthyf99c1d42011-03-01 19:12:26 +0530683 */
684static int twl4030_madc_set_current_generator(struct twl4030_madc_data *madc,
685 int chan, int on)
686{
687 int ret;
Sebastian Reichel99be0242014-03-16 02:43:27 +0100688 int regmask;
Keerthyf99c1d42011-03-01 19:12:26 +0530689 u8 regval;
690
Peter Ujfalusie45342f2012-11-13 09:28:51 +0100691 ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE,
Keerthyf99c1d42011-03-01 19:12:26 +0530692 &regval, TWL4030_BCI_BCICTL1);
693 if (ret) {
694 dev_err(madc->dev, "unable to read BCICTL1 reg 0x%X",
695 TWL4030_BCI_BCICTL1);
696 return ret;
697 }
Sebastian Reichel99be0242014-03-16 02:43:27 +0100698
699 regmask = chan ? TWL4030_BCI_ITHEN : TWL4030_BCI_TYPEN;
Keerthyf99c1d42011-03-01 19:12:26 +0530700 if (on)
Sebastian Reichel99be0242014-03-16 02:43:27 +0100701 regval |= regmask;
Keerthyf99c1d42011-03-01 19:12:26 +0530702 else
Sebastian Reichel99be0242014-03-16 02:43:27 +0100703 regval &= ~regmask;
704
Peter Ujfalusie45342f2012-11-13 09:28:51 +0100705 ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE,
Keerthyf99c1d42011-03-01 19:12:26 +0530706 regval, TWL4030_BCI_BCICTL1);
707 if (ret) {
708 dev_err(madc->dev, "unable to write BCICTL1 reg 0x%X\n",
709 TWL4030_BCI_BCICTL1);
710 return ret;
711 }
712
713 return 0;
714}
715
716/*
717 * Function that sets MADC software power on bit to enable MADC
718 * @madc - pointer to twl4030_madc_data struct
Sebastian Reichel99be0242014-03-16 02:43:27 +0100719 * @on - Enable or disable MADC software power on bit.
Keerthyf99c1d42011-03-01 19:12:26 +0530720 * returns error if i2c read/write fails else 0
721 */
722static int twl4030_madc_set_power(struct twl4030_madc_data *madc, int on)
723{
724 u8 regval;
725 int ret;
726
Peter Ujfalusie45342f2012-11-13 09:28:51 +0100727 ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE,
Keerthyf99c1d42011-03-01 19:12:26 +0530728 &regval, TWL4030_MADC_CTRL1);
729 if (ret) {
730 dev_err(madc->dev, "unable to read madc ctrl1 reg 0x%X\n",
731 TWL4030_MADC_CTRL1);
732 return ret;
733 }
734 if (on)
735 regval |= TWL4030_MADC_MADCON;
736 else
737 regval &= ~TWL4030_MADC_MADCON;
738 ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, regval, TWL4030_MADC_CTRL1);
739 if (ret) {
740 dev_err(madc->dev, "unable to write madc ctrl1 reg 0x%X\n",
741 TWL4030_MADC_CTRL1);
742 return ret;
743 }
744
745 return 0;
746}
747
748/*
749 * Initialize MADC and request for threaded irq
750 */
Bill Pembertonf791be42012-11-19 13:23:04 -0500751static int twl4030_madc_probe(struct platform_device *pdev)
Keerthyf99c1d42011-03-01 19:12:26 +0530752{
753 struct twl4030_madc_data *madc;
Jingoo Han334a41c2013-07-30 17:10:05 +0900754 struct twl4030_madc_platform_data *pdata = dev_get_platdata(&pdev->dev);
Sebastian Reichel2f39b702014-03-16 02:43:26 +0100755 struct device_node *np = pdev->dev.of_node;
Sebastian Reichele7f22b72014-03-16 02:43:25 +0100756 int irq, ret;
Keerthyf99c1d42011-03-01 19:12:26 +0530757 u8 regval;
Sebastian Reichel2f39b702014-03-16 02:43:26 +0100758 struct iio_dev *iio_dev = NULL;
Keerthyf99c1d42011-03-01 19:12:26 +0530759
Sebastian Reichel2f39b702014-03-16 02:43:26 +0100760 if (!pdata && !np) {
761 dev_err(&pdev->dev, "neither platform data nor Device Tree node available\n");
Keerthyf99c1d42011-03-01 19:12:26 +0530762 return -EINVAL;
763 }
Keerthyf99c1d42011-03-01 19:12:26 +0530764
Sebastian Reichel2f39b702014-03-16 02:43:26 +0100765 iio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*madc));
766 if (!iio_dev) {
767 dev_err(&pdev->dev, "failed allocating iio device\n");
768 return -ENOMEM;
769 }
770
771 madc = iio_priv(iio_dev);
Kyle Manna66cc5b82011-08-11 22:33:12 -0500772 madc->dev = &pdev->dev;
773
Sebastian Reichel2f39b702014-03-16 02:43:26 +0100774 iio_dev->name = dev_name(&pdev->dev);
Sebastian Reichel2f39b702014-03-16 02:43:26 +0100775 iio_dev->info = &twl4030_madc_iio_info;
776 iio_dev->modes = INDIO_DIRECT_MODE;
777 iio_dev->channels = twl4030_madc_iio_channels;
778 iio_dev->num_channels = ARRAY_SIZE(twl4030_madc_iio_channels);
779
Keerthyf99c1d42011-03-01 19:12:26 +0530780 /*
781 * Phoenix provides 2 interrupt lines. The first one is connected to
782 * the OMAP. The other one can be connected to the other processor such
783 * as modem. Hence two separate ISR and IMR registers.
784 */
Sebastian Reichel2f39b702014-03-16 02:43:26 +0100785 if (pdata)
786 madc->use_second_irq = (pdata->irq_line != 1);
787 else
788 madc->use_second_irq = of_property_read_bool(np,
789 "ti,system-uses-second-madc-irq");
790
791 madc->imr = madc->use_second_irq ? TWL4030_MADC_IMR2 :
792 TWL4030_MADC_IMR1;
793 madc->isr = madc->use_second_irq ? TWL4030_MADC_ISR2 :
794 TWL4030_MADC_ISR1;
795
Keerthyf99c1d42011-03-01 19:12:26 +0530796 ret = twl4030_madc_set_power(madc, 1);
797 if (ret < 0)
Sebastian Reichele7f22b72014-03-16 02:43:25 +0100798 return ret;
Keerthyf99c1d42011-03-01 19:12:26 +0530799 ret = twl4030_madc_set_current_generator(madc, 0, 1);
800 if (ret < 0)
801 goto err_current_generator;
802
Peter Ujfalusie45342f2012-11-13 09:28:51 +0100803 ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE,
Keerthyf99c1d42011-03-01 19:12:26 +0530804 &regval, TWL4030_BCI_BCICTL1);
805 if (ret) {
806 dev_err(&pdev->dev, "unable to read reg BCI CTL1 0x%X\n",
807 TWL4030_BCI_BCICTL1);
808 goto err_i2c;
809 }
810 regval |= TWL4030_BCI_MESBAT;
Peter Ujfalusie45342f2012-11-13 09:28:51 +0100811 ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE,
Keerthyf99c1d42011-03-01 19:12:26 +0530812 regval, TWL4030_BCI_BCICTL1);
813 if (ret) {
814 dev_err(&pdev->dev, "unable to write reg BCI Ctl1 0x%X\n",
815 TWL4030_BCI_BCICTL1);
816 goto err_i2c;
817 }
Kyle Manna3d6271f2011-08-11 22:33:13 -0500818
819 /* Check that MADC clock is on */
820 ret = twl_i2c_read_u8(TWL4030_MODULE_INTBR, &regval, TWL4030_REG_GPBR1);
821 if (ret) {
822 dev_err(&pdev->dev, "unable to read reg GPBR1 0x%X\n",
823 TWL4030_REG_GPBR1);
824 goto err_i2c;
825 }
826
827 /* If MADC clk is not on, turn it on */
828 if (!(regval & TWL4030_GPBR1_MADC_HFCLK_EN)) {
829 dev_info(&pdev->dev, "clk disabled, enabling\n");
830 regval |= TWL4030_GPBR1_MADC_HFCLK_EN;
831 ret = twl_i2c_write_u8(TWL4030_MODULE_INTBR, regval,
832 TWL4030_REG_GPBR1);
833 if (ret) {
834 dev_err(&pdev->dev, "unable to write reg GPBR1 0x%X\n",
835 TWL4030_REG_GPBR1);
836 goto err_i2c;
837 }
838 }
839
Sebastian Reichel2f39b702014-03-16 02:43:26 +0100840 platform_set_drvdata(pdev, iio_dev);
Keerthyf99c1d42011-03-01 19:12:26 +0530841 mutex_init(&madc->lock);
Sebastian Reichele7f22b72014-03-16 02:43:25 +0100842
843 irq = platform_get_irq(pdev, 0);
844 ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
Keerthyf99c1d42011-03-01 19:12:26 +0530845 twl4030_madc_threaded_irq_handler,
Fabio Estevam6c0d48c2015-05-24 17:39:04 -0300846 IRQF_TRIGGER_RISING | IRQF_ONESHOT,
847 "twl4030_madc", madc);
Keerthyf99c1d42011-03-01 19:12:26 +0530848 if (ret) {
Sebastian Reichel2f39b702014-03-16 02:43:26 +0100849 dev_err(&pdev->dev, "could not request irq\n");
Jingoo Hanc3d6a0a2013-05-06 13:08:08 +0900850 goto err_i2c;
Keerthyf99c1d42011-03-01 19:12:26 +0530851 }
852 twl4030_madc = madc;
Sebastian Reichel2f39b702014-03-16 02:43:26 +0100853
Adam YH Lee7cc97d72015-08-04 11:15:48 -0700854 /* Configure MADC[3:6] */
855 ret = twl_i2c_read_u8(TWL_MODULE_USB, &regval,
856 TWL4030_USB_CARKIT_ANA_CTRL);
857 if (ret) {
858 dev_err(&pdev->dev, "unable to read reg CARKIT_ANA_CTRL 0x%X\n",
859 TWL4030_USB_CARKIT_ANA_CTRL);
860 goto err_i2c;
861 }
862 regval |= TWL4030_USB_SEL_MADC_MCPC;
863 ret = twl_i2c_write_u8(TWL_MODULE_USB, regval,
864 TWL4030_USB_CARKIT_ANA_CTRL);
865 if (ret) {
866 dev_err(&pdev->dev, "unable to write reg CARKIT_ANA_CTRL 0x%X\n",
867 TWL4030_USB_CARKIT_ANA_CTRL);
868 goto err_i2c;
869 }
870
871 /* Enable 3v1 bias regulator for MADC[3:6] */
872 madc->usb3v1 = devm_regulator_get(madc->dev, "vusb3v1");
Christophe JAILLET245a3962017-09-23 08:06:18 +0200873 if (IS_ERR(madc->usb3v1)) {
874 ret = -ENODEV;
875 goto err_i2c;
876 }
Adam YH Lee7cc97d72015-08-04 11:15:48 -0700877
878 ret = regulator_enable(madc->usb3v1);
Christophe JAILLET53063842017-09-23 08:06:20 +0200879 if (ret) {
Adam YH Lee7cc97d72015-08-04 11:15:48 -0700880 dev_err(madc->dev, "could not enable 3v1 bias regulator\n");
Christophe JAILLET53063842017-09-23 08:06:20 +0200881 goto err_i2c;
882 }
Adam YH Lee7cc97d72015-08-04 11:15:48 -0700883
Sebastian Reichel2f39b702014-03-16 02:43:26 +0100884 ret = iio_device_register(iio_dev);
885 if (ret) {
886 dev_err(&pdev->dev, "could not register iio device\n");
Christophe JAILLET7f70be62017-09-23 08:06:19 +0200887 goto err_usb3v1;
Sebastian Reichel2f39b702014-03-16 02:43:26 +0100888 }
889
Keerthyf99c1d42011-03-01 19:12:26 +0530890 return 0;
Sebastian Reichel2f39b702014-03-16 02:43:26 +0100891
Christophe JAILLET7f70be62017-09-23 08:06:19 +0200892err_usb3v1:
893 regulator_disable(madc->usb3v1);
Keerthyf99c1d42011-03-01 19:12:26 +0530894err_i2c:
895 twl4030_madc_set_current_generator(madc, 0, 0);
896err_current_generator:
897 twl4030_madc_set_power(madc, 0);
Keerthyf99c1d42011-03-01 19:12:26 +0530898 return ret;
899}
900
Bill Pemberton4740f732012-11-19 13:26:01 -0500901static int twl4030_madc_remove(struct platform_device *pdev)
Keerthyf99c1d42011-03-01 19:12:26 +0530902{
Sebastian Reichel2f39b702014-03-16 02:43:26 +0100903 struct iio_dev *iio_dev = platform_get_drvdata(pdev);
904 struct twl4030_madc_data *madc = iio_priv(iio_dev);
905
906 iio_device_unregister(iio_dev);
Keerthyf99c1d42011-03-01 19:12:26 +0530907
Keerthyf99c1d42011-03-01 19:12:26 +0530908 twl4030_madc_set_current_generator(madc, 0, 0);
909 twl4030_madc_set_power(madc, 0);
Keerthyf99c1d42011-03-01 19:12:26 +0530910
Adam YH Lee7cc97d72015-08-04 11:15:48 -0700911 regulator_disable(madc->usb3v1);
912
Keerthyf99c1d42011-03-01 19:12:26 +0530913 return 0;
914}
915
Sebastian Reichel2f39b702014-03-16 02:43:26 +0100916#ifdef CONFIG_OF
917static const struct of_device_id twl_madc_of_match[] = {
918 { .compatible = "ti,twl4030-madc", },
919 { },
920};
921MODULE_DEVICE_TABLE(of, twl_madc_of_match);
922#endif
923
Keerthyf99c1d42011-03-01 19:12:26 +0530924static struct platform_driver twl4030_madc_driver = {
925 .probe = twl4030_madc_probe,
Arnd Bergmann03715412013-03-14 22:56:38 +0100926 .remove = twl4030_madc_remove,
Keerthyf99c1d42011-03-01 19:12:26 +0530927 .driver = {
928 .name = "twl4030_madc",
Sebastian Reichel2f39b702014-03-16 02:43:26 +0100929 .of_match_table = of_match_ptr(twl_madc_of_match),
Sebastian Reichel99be0242014-03-16 02:43:27 +0100930 },
Keerthyf99c1d42011-03-01 19:12:26 +0530931};
932
Mark Brown65349d62011-11-23 22:58:34 +0000933module_platform_driver(twl4030_madc_driver);
Keerthyf99c1d42011-03-01 19:12:26 +0530934
935MODULE_DESCRIPTION("TWL4030 ADC driver");
936MODULE_LICENSE("GPL");
937MODULE_AUTHOR("J Keerthy");
Axel Lin0ea3e832011-03-07 11:02:29 +0800938MODULE_ALIAS("platform:twl4030_madc");