Merge branch 'ib-mfd-io-3.15' into HEAD

This commit is contained in:
Lee Jones 2014-03-19 08:57:53 +00:00
commit 0caeaede0b
8 changed files with 248 additions and 134 deletions

View file

@ -0,0 +1,24 @@
* TWL4030 Monitoring Analog to Digital Converter (MADC)
The MADC subsystem in the TWL4030 consists of a 10-bit ADC
combined with a 16-input analog multiplexer.
Required properties:
- compatible: Should contain "ti,twl4030-madc".
- interrupts: IRQ line for the MADC submodule.
- #io-channel-cells: Should be set to <1>.
Optional properties:
- ti,system-uses-second-madc-irq: boolean, set if the second madc irq register
should be used, which is intended to be used
by Co-Processors (e.g. a modem).
Example:
&twl {
madc {
compatible = "ti,twl4030-madc";
interrupts = <3>;
#io-channel-cells = <1>;
};
};

View file

@ -183,6 +183,16 @@ config TI_AM335X_ADC
Say yes here to build support for Texas Instruments ADC Say yes here to build support for Texas Instruments ADC
driver which is also a MFD client. driver which is also a MFD client.
config TWL4030_MADC
tristate "TWL4030 MADC (Monitoring A/D Converter)"
depends on TWL4030_CORE
help
This driver provides support for Triton TWL4030-MADC. The
driver supports both RT and SW conversion methods.
This driver can also be built as a module. If so, the module will be
called twl4030-madc.
config TWL6030_GPADC config TWL6030_GPADC
tristate "TWL6030 GPADC (General Purpose A/D Converter) Support" tristate "TWL6030 GPADC (General Purpose A/D Converter) Support"
depends on TWL4030_CORE depends on TWL4030_CORE

View file

@ -20,5 +20,6 @@ obj-$(CONFIG_MCP3422) += mcp3422.o
obj-$(CONFIG_NAU7802) += nau7802.o obj-$(CONFIG_NAU7802) += nau7802.o
obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o
obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o
obj-$(CONFIG_TWL6030_GPADC) += twl6030-gpadc.o obj-$(CONFIG_TWL6030_GPADC) += twl6030-gpadc.o
obj-$(CONFIG_VIPERBOARD_ADC) += viperboard_adc.o obj-$(CONFIG_VIPERBOARD_ADC) += viperboard_adc.o

View file

@ -47,20 +47,84 @@
#include <linux/gfp.h> #include <linux/gfp.h>
#include <linux/err.h> #include <linux/err.h>
/* #include <linux/iio/iio.h>
/**
* struct twl4030_madc_data - a container for madc info * struct twl4030_madc_data - a container for madc info
* @dev - pointer to device structure for madc * @dev: Pointer to device structure for madc
* @lock - mutex protecting this data structure * @lock: Mutex protecting this data structure
* @requests - Array of request struct corresponding to SW1, SW2 and RT * @requests: Array of request struct corresponding to SW1, SW2 and RT
* @imr - Interrupt mask register of MADC * @use_second_irq: IRQ selection (main or co-processor)
* @isr - Interrupt status register of MADC * @imr: Interrupt mask register of MADC
* @isr: Interrupt status register of MADC
*/ */
struct twl4030_madc_data { struct twl4030_madc_data {
struct device *dev; struct device *dev;
struct mutex lock; /* mutex protecting this data structure */ struct mutex lock; /* mutex protecting this data structure */
struct twl4030_madc_request requests[TWL4030_MADC_NUM_METHODS]; struct twl4030_madc_request requests[TWL4030_MADC_NUM_METHODS];
int imr; bool use_second_irq;
int isr; u8 imr;
u8 isr;
};
static int twl4030_madc_read(struct iio_dev *iio_dev,
const struct iio_chan_spec *chan,
int *val, int *val2, long mask)
{
struct twl4030_madc_data *madc = iio_priv(iio_dev);
struct twl4030_madc_request req;
int ret;
req.method = madc->use_second_irq ? TWL4030_MADC_SW2 : TWL4030_MADC_SW1;
req.channels = BIT(chan->channel);
req.active = false;
req.func_cb = NULL;
req.type = TWL4030_MADC_WAIT;
req.raw = !(mask == IIO_CHAN_INFO_PROCESSED);
req.do_avg = (mask == IIO_CHAN_INFO_AVERAGE_RAW);
ret = twl4030_madc_conversion(&req);
if (ret < 0)
return ret;
*val = req.rbuf[chan->channel];
return IIO_VAL_INT;
}
static const struct iio_info twl4030_madc_iio_info = {
.read_raw = &twl4030_madc_read,
.driver_module = THIS_MODULE,
};
#define TWL4030_ADC_CHANNEL(_channel, _type, _name) { \
.type = _type, \
.channel = _channel, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
BIT(IIO_CHAN_INFO_AVERAGE_RAW) | \
BIT(IIO_CHAN_INFO_PROCESSED), \
.datasheet_name = _name, \
.indexed = 1, \
}
static const struct iio_chan_spec twl4030_madc_iio_channels[] = {
TWL4030_ADC_CHANNEL(0, IIO_VOLTAGE, "ADCIN0"),
TWL4030_ADC_CHANNEL(1, IIO_TEMP, "ADCIN1"),
TWL4030_ADC_CHANNEL(2, IIO_VOLTAGE, "ADCIN2"),
TWL4030_ADC_CHANNEL(3, IIO_VOLTAGE, "ADCIN3"),
TWL4030_ADC_CHANNEL(4, IIO_VOLTAGE, "ADCIN4"),
TWL4030_ADC_CHANNEL(5, IIO_VOLTAGE, "ADCIN5"),
TWL4030_ADC_CHANNEL(6, IIO_VOLTAGE, "ADCIN6"),
TWL4030_ADC_CHANNEL(7, IIO_VOLTAGE, "ADCIN7"),
TWL4030_ADC_CHANNEL(8, IIO_VOLTAGE, "ADCIN8"),
TWL4030_ADC_CHANNEL(9, IIO_VOLTAGE, "ADCIN9"),
TWL4030_ADC_CHANNEL(10, IIO_CURRENT, "ADCIN10"),
TWL4030_ADC_CHANNEL(11, IIO_VOLTAGE, "ADCIN11"),
TWL4030_ADC_CHANNEL(12, IIO_VOLTAGE, "ADCIN12"),
TWL4030_ADC_CHANNEL(13, IIO_VOLTAGE, "ADCIN13"),
TWL4030_ADC_CHANNEL(14, IIO_VOLTAGE, "ADCIN14"),
TWL4030_ADC_CHANNEL(15, IIO_VOLTAGE, "ADCIN15"),
}; };
static struct twl4030_madc_data *twl4030_madc; static struct twl4030_madc_data *twl4030_madc;
@ -91,17 +155,16 @@ twl4030_divider_ratios[16] = {
}; };
/* /* Conversion table from -3 to 55 degrees Celcius */
* Conversion table from -3 to 55 degree Celcius static int twl4030_therm_tbl[] = {
*/ 30800, 29500, 28300, 27100,
static int therm_tbl[] = { 26000, 24900, 23900, 22900, 22000, 21100, 20300, 19400, 18700,
30800, 29500, 28300, 27100, 17900, 17200, 16500, 15900, 15300, 14700, 14100, 13600, 13100,
26000, 24900, 23900, 22900, 22000, 21100, 20300, 19400, 18700, 17900, 12600, 12100, 11600, 11200, 10800, 10400, 10000, 9630, 9280,
17200, 16500, 15900, 15300, 14700, 14100, 13600, 13100, 12600, 12100, 8950, 8620, 8310, 8020, 7730, 7460, 7200, 6950, 6710,
11600, 11200, 10800, 10400, 10000, 9630, 9280, 8950, 8620, 8310, 6470, 6250, 6040, 5830, 5640, 5450, 5260, 5090, 4920,
8020, 7730, 7460, 7200, 6950, 6710, 6470, 6250, 6040, 5830, 4760, 4600, 4450, 4310, 4170, 4040, 3910, 3790, 3670,
5640, 5450, 5260, 5090, 4920, 4760, 4600, 4450, 4310, 4170, 3550
4040, 3910, 3790, 3670, 3550
}; };
/* /*
@ -133,37 +196,32 @@ const struct twl4030_madc_conversion_method twl4030_conversion_methods[] = {
}, },
}; };
/* /**
* Function to read a particular channel value. * twl4030_madc_channel_raw_read() - Function to read a particular channel value
* @madc - pointer to struct twl4030_madc_data * @madc: pointer to struct twl4030_madc_data
* @reg - lsb of ADC Channel * @reg: lsb of ADC Channel
* If the i2c read fails it returns an error else returns 0. *
* Return: 0 on success, an error code otherwise.
*/ */
static int twl4030_madc_channel_raw_read(struct twl4030_madc_data *madc, u8 reg) static int twl4030_madc_channel_raw_read(struct twl4030_madc_data *madc, u8 reg)
{ {
u8 msb, lsb; u16 val;
int ret; int ret;
/* /*
* For each ADC channel, we have MSB and LSB register pair. MSB address * For each ADC channel, we have MSB and LSB register pair. MSB address
* is always LSB address+1. reg parameter is the address of LSB register * is always LSB address+1. reg parameter is the address of LSB register
*/ */
ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &msb, reg + 1); ret = twl_i2c_read_u16(TWL4030_MODULE_MADC, &val, reg);
if (ret) { if (ret) {
dev_err(madc->dev, "unable to read MSB register 0x%X\n", dev_err(madc->dev, "unable to read register 0x%X\n", reg);
reg + 1);
return ret;
}
ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &lsb, reg);
if (ret) {
dev_err(madc->dev, "unable to read LSB register 0x%X\n", reg);
return ret; return ret;
} }
return (int)(((msb << 8) | lsb) >> 6); return (int)(val >> 6);
} }
/* /*
* Return battery temperature * Return battery temperature in degrees Celsius
* Or < 0 on failure. * Or < 0 on failure.
*/ */
static int twl4030battery_temperature(int raw_volt) static int twl4030battery_temperature(int raw_volt)
@ -172,18 +230,18 @@ static int twl4030battery_temperature(int raw_volt)
int temp, curr, volt, res, ret; int temp, curr, volt, res, ret;
volt = (raw_volt * TEMP_STEP_SIZE) / TEMP_PSR_R; volt = (raw_volt * TEMP_STEP_SIZE) / TEMP_PSR_R;
/* Getting and calculating the supply current in micro ampers */ /* Getting and calculating the supply current in micro amperes */
ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, &val, ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, &val,
REG_BCICTL2); REG_BCICTL2);
if (ret < 0) if (ret < 0)
return ret; return ret;
curr = ((val & TWL4030_BCI_ITHEN) + 1) * 10; curr = ((val & TWL4030_BCI_ITHEN) + 1) * 10;
/* Getting and calculating the thermistor resistance in ohms */ /* Getting and calculating the thermistor resistance in ohms */
res = volt * 1000 / curr; res = volt * 1000 / curr;
/* calculating temperature */ /* calculating temperature */
for (temp = 58; temp >= 0; temp--) { for (temp = 58; temp >= 0; temp--) {
int actual = therm_tbl[temp]; int actual = twl4030_therm_tbl[temp];
if ((actual - res) >= 0) if ((actual - res) >= 0)
break; break;
} }
@ -205,11 +263,12 @@ static int twl4030battery_current(int raw_volt)
else /* slope of 0.88 mV/mA */ else /* slope of 0.88 mV/mA */
return (raw_volt * CURR_STEP_SIZE) / CURR_PSR_R2; return (raw_volt * CURR_STEP_SIZE) / CURR_PSR_R2;
} }
/* /*
* Function to read channel values * Function to read channel values
* @madc - pointer to twl4030_madc_data struct * @madc - pointer to twl4030_madc_data struct
* @reg_base - Base address of the first channel * @reg_base - Base address of the first channel
* @Channels - 16 bit bitmap. If the bit is set, channel value is read * @Channels - 16 bit bitmap. If the bit is set, channel's value is read
* @buf - The channel values are stored here. if read fails error * @buf - The channel values are stored here. if read fails error
* @raw - Return raw values without conversion * @raw - Return raw values without conversion
* value is stored * value is stored
@ -220,17 +279,17 @@ static int twl4030_madc_read_channels(struct twl4030_madc_data *madc,
long channels, int *buf, long channels, int *buf,
bool raw) bool raw)
{ {
int count = 0, count_req = 0, i; int count = 0;
int i;
u8 reg; u8 reg;
for_each_set_bit(i, &channels, TWL4030_MADC_MAX_CHANNELS) { for_each_set_bit(i, &channels, TWL4030_MADC_MAX_CHANNELS) {
reg = reg_base + 2 * i; reg = reg_base + (2 * i);
buf[i] = twl4030_madc_channel_raw_read(madc, reg); buf[i] = twl4030_madc_channel_raw_read(madc, reg);
if (buf[i] < 0) { if (buf[i] < 0) {
dev_err(madc->dev, dev_err(madc->dev, "Unable to read register 0x%X\n",
"Unable to read register 0x%X\n", reg); reg);
count_req++; return buf[i];
continue;
} }
if (raw) { if (raw) {
count++; count++;
@ -241,7 +300,7 @@ static int twl4030_madc_read_channels(struct twl4030_madc_data *madc,
buf[i] = twl4030battery_current(buf[i]); buf[i] = twl4030battery_current(buf[i]);
if (buf[i] < 0) { if (buf[i] < 0) {
dev_err(madc->dev, "err reading current\n"); dev_err(madc->dev, "err reading current\n");
count_req++; return buf[i];
} else { } else {
count++; count++;
buf[i] = buf[i] - 750; buf[i] = buf[i] - 750;
@ -251,7 +310,7 @@ static int twl4030_madc_read_channels(struct twl4030_madc_data *madc,
buf[i] = twl4030battery_temperature(buf[i]); buf[i] = twl4030battery_temperature(buf[i]);
if (buf[i] < 0) { if (buf[i] < 0) {
dev_err(madc->dev, "err reading temperature\n"); dev_err(madc->dev, "err reading temperature\n");
count_req++; return buf[i];
} else { } else {
buf[i] -= 3; buf[i] -= 3;
count++; count++;
@ -272,8 +331,6 @@ static int twl4030_madc_read_channels(struct twl4030_madc_data *madc,
twl4030_divider_ratios[i].numerator); twl4030_divider_ratios[i].numerator);
} }
} }
if (count_req)
dev_err(madc->dev, "%d channel conversion failed\n", count_req);
return count; return count;
} }
@ -297,13 +354,13 @@ static int twl4030_madc_enable_irq(struct twl4030_madc_data *madc, u8 id)
madc->imr); madc->imr);
return ret; return ret;
} }
val &= ~(1 << id); val &= ~(1 << id);
ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, val, madc->imr); ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, val, madc->imr);
if (ret) { if (ret) {
dev_err(madc->dev, dev_err(madc->dev,
"unable to write imr register 0x%X\n", madc->imr); "unable to write imr register 0x%X\n", madc->imr);
return ret; return ret;
} }
return 0; return 0;
@ -366,7 +423,7 @@ static irqreturn_t twl4030_madc_threaded_irq_handler(int irq, void *_madc)
continue; continue;
ret = twl4030_madc_disable_irq(madc, i); ret = twl4030_madc_disable_irq(madc, i);
if (ret < 0) if (ret < 0)
dev_dbg(madc->dev, "Disable interrupt failed%d\n", i); dev_dbg(madc->dev, "Disable interrupt failed %d\n", i);
madc->requests[i].result_pending = 1; madc->requests[i].result_pending = 1;
} }
for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) { for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) {
@ -448,21 +505,17 @@ static int twl4030_madc_start_conversion(struct twl4030_madc_data *madc,
{ {
const struct twl4030_madc_conversion_method *method; const struct twl4030_madc_conversion_method *method;
int ret = 0; int ret = 0;
if (conv_method != TWL4030_MADC_SW1 && conv_method != TWL4030_MADC_SW2)
return -ENOTSUPP;
method = &twl4030_conversion_methods[conv_method]; method = &twl4030_conversion_methods[conv_method];
switch (conv_method) { ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, TWL4030_MADC_SW_START,
case TWL4030_MADC_SW1: method->ctrl);
case TWL4030_MADC_SW2: if (ret) {
ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, dev_err(madc->dev, "unable to write ctrl register 0x%X\n",
TWL4030_MADC_SW_START, method->ctrl); method->ctrl);
if (ret) { return ret;
dev_err(madc->dev,
"unable to write ctrl register 0x%X\n",
method->ctrl);
return ret;
}
break;
default:
break;
} }
return 0; return 0;
@ -513,7 +566,6 @@ static int twl4030_madc_wait_conversion_ready(struct twl4030_madc_data *madc,
int twl4030_madc_conversion(struct twl4030_madc_request *req) int twl4030_madc_conversion(struct twl4030_madc_request *req)
{ {
const struct twl4030_madc_conversion_method *method; const struct twl4030_madc_conversion_method *method;
u8 ch_msb, ch_lsb;
int ret; int ret;
if (!req || !twl4030_madc) if (!req || !twl4030_madc)
@ -529,38 +581,22 @@ int twl4030_madc_conversion(struct twl4030_madc_request *req)
ret = -EBUSY; ret = -EBUSY;
goto out; goto out;
} }
ch_msb = (req->channels >> 8) & 0xff;
ch_lsb = req->channels & 0xff;
method = &twl4030_conversion_methods[req->method]; method = &twl4030_conversion_methods[req->method];
/* Select channels to be converted */ /* Select channels to be converted */
ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, ch_msb, method->sel + 1); ret = twl_i2c_write_u16(TWL4030_MODULE_MADC, req->channels, method->sel);
if (ret) { if (ret) {
dev_err(twl4030_madc->dev, dev_err(twl4030_madc->dev,
"unable to write sel register 0x%X\n", method->sel + 1); "unable to write sel register 0x%X\n", method->sel);
goto out;
}
ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, ch_lsb, method->sel);
if (ret) {
dev_err(twl4030_madc->dev,
"unable to write sel register 0x%X\n", method->sel + 1);
goto out; goto out;
} }
/* Select averaging for all channels if do_avg is set */ /* Select averaging for all channels if do_avg is set */
if (req->do_avg) { if (req->do_avg) {
ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, ret = twl_i2c_write_u16(TWL4030_MODULE_MADC, req->channels,
ch_msb, method->avg + 1); method->avg);
if (ret) { if (ret) {
dev_err(twl4030_madc->dev, dev_err(twl4030_madc->dev,
"unable to write avg register 0x%X\n", "unable to write avg register 0x%X\n",
method->avg + 1); method->avg);
goto out;
}
ret = twl_i2c_write_u8(TWL4030_MODULE_MADC,
ch_lsb, method->avg);
if (ret) {
dev_err(twl4030_madc->dev,
"unable to write sel reg 0x%X\n",
method->sel + 1);
goto out; goto out;
} }
} }
@ -601,10 +637,6 @@ out:
} }
EXPORT_SYMBOL_GPL(twl4030_madc_conversion); EXPORT_SYMBOL_GPL(twl4030_madc_conversion);
/*
* Return channel value
* Or < 0 on failure.
*/
int twl4030_get_madc_conversion(int channel_no) int twl4030_get_madc_conversion(int channel_no)
{ {
struct twl4030_madc_request req; struct twl4030_madc_request req;
@ -625,20 +657,25 @@ int twl4030_get_madc_conversion(int channel_no)
} }
EXPORT_SYMBOL_GPL(twl4030_get_madc_conversion); EXPORT_SYMBOL_GPL(twl4030_get_madc_conversion);
/* /**
* twl4030_madc_set_current_generator() - setup bias current
*
* @madc: pointer to twl4030_madc_data struct
* @chan: can be one of the two values:
* TWL4030_BCI_ITHEN
* Enables bias current for main battery type reading
* TWL4030_BCI_TYPEN
* Enables bias current for main battery temperature sensing
* @on: enable or disable chan.
*
* Function to enable or disable bias current for * Function to enable or disable bias current for
* main battery type reading or temperature sensing * main battery type reading or temperature sensing
* @madc - pointer to twl4030_madc_data struct
* @chan - can be one of the two values
* TWL4030_BCI_ITHEN - Enables bias current for main battery type reading
* TWL4030_BCI_TYPEN - Enables bias current for main battery temperature
* sensing
* @on - enable or disable chan.
*/ */
static int twl4030_madc_set_current_generator(struct twl4030_madc_data *madc, static int twl4030_madc_set_current_generator(struct twl4030_madc_data *madc,
int chan, int on) int chan, int on)
{ {
int ret; int ret;
int regmask;
u8 regval; u8 regval;
ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE,
@ -648,10 +685,13 @@ static int twl4030_madc_set_current_generator(struct twl4030_madc_data *madc,
TWL4030_BCI_BCICTL1); TWL4030_BCI_BCICTL1);
return ret; return ret;
} }
regmask = chan ? TWL4030_BCI_ITHEN : TWL4030_BCI_TYPEN;
if (on) if (on)
regval |= chan ? TWL4030_BCI_ITHEN : TWL4030_BCI_TYPEN; regval |= regmask;
else else
regval &= chan ? ~TWL4030_BCI_ITHEN : ~TWL4030_BCI_TYPEN; regval &= ~regmask;
ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE,
regval, TWL4030_BCI_BCICTL1); regval, TWL4030_BCI_BCICTL1);
if (ret) { if (ret) {
@ -666,7 +706,7 @@ static int twl4030_madc_set_current_generator(struct twl4030_madc_data *madc,
/* /*
* Function that sets MADC software power on bit to enable MADC * Function that sets MADC software power on bit to enable MADC
* @madc - pointer to twl4030_madc_data struct * @madc - pointer to twl4030_madc_data struct
* @on - Enable or disable MADC software powen on bit. * @on - Enable or disable MADC software power on bit.
* returns error if i2c read/write fails else 0 * returns error if i2c read/write fails else 0
*/ */
static int twl4030_madc_set_power(struct twl4030_madc_data *madc, int on) static int twl4030_madc_set_power(struct twl4030_madc_data *madc, int on)
@ -702,31 +742,52 @@ static int twl4030_madc_probe(struct platform_device *pdev)
{ {
struct twl4030_madc_data *madc; struct twl4030_madc_data *madc;
struct twl4030_madc_platform_data *pdata = dev_get_platdata(&pdev->dev); struct twl4030_madc_platform_data *pdata = dev_get_platdata(&pdev->dev);
int ret; struct device_node *np = pdev->dev.of_node;
int irq, ret;
u8 regval; u8 regval;
struct iio_dev *iio_dev = NULL;
if (!pdata) { if (!pdata && !np) {
dev_err(&pdev->dev, "platform_data not available\n"); dev_err(&pdev->dev, "neither platform data nor Device Tree node available\n");
return -EINVAL; return -EINVAL;
} }
madc = kzalloc(sizeof(*madc), GFP_KERNEL);
if (!madc)
return -ENOMEM;
iio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*madc));
if (!iio_dev) {
dev_err(&pdev->dev, "failed allocating iio device\n");
return -ENOMEM;
}
madc = iio_priv(iio_dev);
madc->dev = &pdev->dev; madc->dev = &pdev->dev;
iio_dev->name = dev_name(&pdev->dev);
iio_dev->dev.parent = &pdev->dev;
iio_dev->dev.of_node = pdev->dev.of_node;
iio_dev->info = &twl4030_madc_iio_info;
iio_dev->modes = INDIO_DIRECT_MODE;
iio_dev->channels = twl4030_madc_iio_channels;
iio_dev->num_channels = ARRAY_SIZE(twl4030_madc_iio_channels);
/* /*
* Phoenix provides 2 interrupt lines. The first one is connected to * Phoenix provides 2 interrupt lines. The first one is connected to
* the OMAP. The other one can be connected to the other processor such * the OMAP. The other one can be connected to the other processor such
* as modem. Hence two separate ISR and IMR registers. * as modem. Hence two separate ISR and IMR registers.
*/ */
madc->imr = (pdata->irq_line == 1) ? if (pdata)
TWL4030_MADC_IMR1 : TWL4030_MADC_IMR2; madc->use_second_irq = (pdata->irq_line != 1);
madc->isr = (pdata->irq_line == 1) ? else
TWL4030_MADC_ISR1 : TWL4030_MADC_ISR2; madc->use_second_irq = of_property_read_bool(np,
"ti,system-uses-second-madc-irq");
madc->imr = madc->use_second_irq ? TWL4030_MADC_IMR2 :
TWL4030_MADC_IMR1;
madc->isr = madc->use_second_irq ? TWL4030_MADC_ISR2 :
TWL4030_MADC_ISR1;
ret = twl4030_madc_set_power(madc, 1); ret = twl4030_madc_set_power(madc, 1);
if (ret < 0) if (ret < 0)
goto err_power; return ret;
ret = twl4030_madc_set_current_generator(madc, 0, 1); ret = twl4030_madc_set_current_generator(madc, 0, 1);
if (ret < 0) if (ret < 0)
goto err_current_generator; goto err_current_generator;
@ -768,46 +829,63 @@ static int twl4030_madc_probe(struct platform_device *pdev)
} }
} }
platform_set_drvdata(pdev, madc); platform_set_drvdata(pdev, iio_dev);
mutex_init(&madc->lock); mutex_init(&madc->lock);
ret = request_threaded_irq(platform_get_irq(pdev, 0), NULL,
irq = platform_get_irq(pdev, 0);
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
twl4030_madc_threaded_irq_handler, twl4030_madc_threaded_irq_handler,
IRQF_TRIGGER_RISING, "twl4030_madc", madc); IRQF_TRIGGER_RISING, "twl4030_madc", madc);
if (ret) { if (ret) {
dev_dbg(&pdev->dev, "could not request irq\n"); dev_err(&pdev->dev, "could not request irq\n");
goto err_i2c; goto err_i2c;
} }
twl4030_madc = madc; twl4030_madc = madc;
ret = iio_device_register(iio_dev);
if (ret) {
dev_err(&pdev->dev, "could not register iio device\n");
goto err_i2c;
}
return 0; return 0;
err_i2c: err_i2c:
twl4030_madc_set_current_generator(madc, 0, 0); twl4030_madc_set_current_generator(madc, 0, 0);
err_current_generator: err_current_generator:
twl4030_madc_set_power(madc, 0); twl4030_madc_set_power(madc, 0);
err_power:
kfree(madc);
return ret; return ret;
} }
static int twl4030_madc_remove(struct platform_device *pdev) static int twl4030_madc_remove(struct platform_device *pdev)
{ {
struct twl4030_madc_data *madc = platform_get_drvdata(pdev); struct iio_dev *iio_dev = platform_get_drvdata(pdev);
struct twl4030_madc_data *madc = iio_priv(iio_dev);
iio_device_unregister(iio_dev);
free_irq(platform_get_irq(pdev, 0), madc);
twl4030_madc_set_current_generator(madc, 0, 0); twl4030_madc_set_current_generator(madc, 0, 0);
twl4030_madc_set_power(madc, 0); twl4030_madc_set_power(madc, 0);
kfree(madc);
return 0; return 0;
} }
#ifdef CONFIG_OF
static const struct of_device_id twl_madc_of_match[] = {
{ .compatible = "ti,twl4030-madc", },
{ },
};
MODULE_DEVICE_TABLE(of, twl_madc_of_match);
#endif
static struct platform_driver twl4030_madc_driver = { static struct platform_driver twl4030_madc_driver = {
.probe = twl4030_madc_probe, .probe = twl4030_madc_probe,
.remove = twl4030_madc_remove, .remove = twl4030_madc_remove,
.driver = { .driver = {
.name = "twl4030_madc", .name = "twl4030_madc",
.owner = THIS_MODULE, .owner = THIS_MODULE,
}, .of_match_table = of_match_ptr(twl_madc_of_match),
},
}; };
module_platform_driver(twl4030_madc_driver); module_platform_driver(twl4030_madc_driver);

View file

@ -935,16 +935,6 @@ config TWL4030_CORE
high speed USB OTG transceiver, an audio codec (on most high speed USB OTG transceiver, an audio codec (on most
versions) and many other features. versions) and many other features.
config TWL4030_MADC
tristate "TI TWL4030 MADC"
depends on TWL4030_CORE
help
This driver provides support for triton TWL4030-MADC. The
driver supports both RT and SW conversion methods.
This driver can be built as a module. If so it will be
named twl4030-madc
config TWL4030_POWER config TWL4030_POWER
bool "TI TWL4030 power resources" bool "TI TWL4030 power resources"
depends on TWL4030_CORE && ARM depends on TWL4030_CORE && ARM

View file

@ -71,7 +71,6 @@ obj-$(CONFIG_MFD_TPS80031) += tps80031.o
obj-$(CONFIG_MENELAUS) += menelaus.o obj-$(CONFIG_MENELAUS) += menelaus.o
obj-$(CONFIG_TWL4030_CORE) += twl-core.o twl4030-irq.o twl6030-irq.o obj-$(CONFIG_TWL4030_CORE) += twl-core.o twl4030-irq.o twl6030-irq.o
obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o
obj-$(CONFIG_TWL4030_POWER) += twl4030-power.o obj-$(CONFIG_TWL4030_POWER) += twl4030-power.o
obj-$(CONFIG_MFD_TWL4030_AUDIO) += twl4030-audio.o obj-$(CONFIG_MFD_TWL4030_AUDIO) += twl4030-audio.o
obj-$(CONFIG_TWL6040_CORE) += twl6040.o obj-$(CONFIG_TWL6040_CORE) += twl6040.o

View file

@ -195,6 +195,18 @@ static inline int twl_i2c_read_u8(u8 mod_no, u8 *val, u8 reg) {
return twl_i2c_read(mod_no, val, reg, 1); return twl_i2c_read(mod_no, val, reg, 1);
} }
static inline int twl_i2c_write_u16(u8 mod_no, u16 val, u8 reg) {
val = cpu_to_le16(val);
return twl_i2c_write(mod_no, (u8*) &val, reg, 2);
}
static inline int twl_i2c_read_u16(u8 mod_no, u16 *val, u8 reg) {
int ret;
ret = twl_i2c_read(mod_no, (u8*) val, reg, 2);
*val = le16_to_cpu(*val);
return ret;
}
int twl_get_type(void); int twl_get_type(void);
int twl_get_version(void); int twl_get_version(void);
int twl_get_hfclk_rate(void); int twl_get_hfclk_rate(void);

View file

@ -44,7 +44,7 @@ struct twl4030_madc_conversion_method {
struct twl4030_madc_request { struct twl4030_madc_request {
unsigned long channels; unsigned long channels;
u16 do_avg; bool do_avg;
u16 method; u16 method;
u16 type; u16 type;
bool active; bool active;