Loading Documentation/devicetree/bindings/iio/adc/twl4030-madc.txt 0 → 100644 +24 −0 Original line number Diff line number Diff line * 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>; }; }; drivers/iio/adc/Kconfig +10 −0 Original line number Diff line number Diff line Loading @@ -183,6 +183,16 @@ config TI_AM335X_ADC Say yes here to build support for Texas Instruments ADC 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 tristate "TWL6030 GPADC (General Purpose A/D Converter) Support" depends on TWL4030_CORE Loading drivers/iio/adc/Makefile +1 −0 Original line number Diff line number Diff line Loading @@ -20,5 +20,6 @@ obj-$(CONFIG_MCP3422) += mcp3422.o obj-$(CONFIG_NAU7802) += nau7802.o obj-$(CONFIG_TI_ADC081C) += ti-adc081c.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_VIPERBOARD_ADC) += viperboard_adc.o drivers/mfd/twl4030-madc.c→drivers/iio/adc/twl4030-madc.c +199 −121 Original line number Diff line number Diff line Loading @@ -47,20 +47,84 @@ #include <linux/gfp.h> #include <linux/err.h> /* #include <linux/iio/iio.h> /** * struct twl4030_madc_data - a container for madc info * @dev - pointer to device structure for madc * @lock - mutex protecting this data structure * @requests - Array of request struct corresponding to SW1, SW2 and RT * @imr - Interrupt mask register of MADC * @isr - Interrupt status register of MADC * @dev: Pointer to device structure for madc * @lock: Mutex protecting this data structure * @requests: Array of request struct corresponding to SW1, SW2 and RT * @use_second_irq: IRQ selection (main or co-processor) * @imr: Interrupt mask register of MADC * @isr: Interrupt status register of MADC */ struct twl4030_madc_data { struct device *dev; struct mutex lock; /* mutex protecting this data structure */ struct twl4030_madc_request requests[TWL4030_MADC_NUM_METHODS]; int imr; int isr; bool use_second_irq; 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; Loading Loading @@ -91,17 +155,16 @@ twl4030_divider_ratios[16] = { }; /* * Conversion table from -3 to 55 degree Celcius */ static int therm_tbl[] = { /* Conversion table from -3 to 55 degrees Celcius */ static int twl4030_therm_tbl[] = { 30800, 29500, 28300, 27100, 26000, 24900, 23900, 22900, 22000, 21100, 20300, 19400, 18700, 17900, 17200, 16500, 15900, 15300, 14700, 14100, 13600, 13100, 12600, 12100, 11600, 11200, 10800, 10400, 10000, 9630, 9280, 8950, 8620, 8310, 8020, 7730, 7460, 7200, 6950, 6710, 6470, 6250, 6040, 5830, 5640, 5450, 5260, 5090, 4920, 4760, 4600, 4450, 4310, 4170, 4040, 3910, 3790, 3670, 3550 26000, 24900, 23900, 22900, 22000, 21100, 20300, 19400, 18700, 17900, 17200, 16500, 15900, 15300, 14700, 14100, 13600, 13100, 12600, 12100, 11600, 11200, 10800, 10400, 10000, 9630, 9280, 8950, 8620, 8310, 8020, 7730, 7460, 7200, 6950, 6710, 6470, 6250, 6040, 5830, 5640, 5450, 5260, 5090, 4920, 4760, 4600, 4450, 4310, 4170, 4040, 3910, 3790, 3670, 3550 }; /* Loading Loading @@ -133,37 +196,32 @@ const struct twl4030_madc_conversion_method twl4030_conversion_methods[] = { }, }; /* * Function to read a particular channel value. * @madc - pointer to struct twl4030_madc_data * @reg - lsb of ADC Channel * If the i2c read fails it returns an error else returns 0. /** * twl4030_madc_channel_raw_read() - Function to read a particular channel value * @madc: pointer to struct twl4030_madc_data * @reg: lsb of ADC Channel * * Return: 0 on success, an error code otherwise. */ static int twl4030_madc_channel_raw_read(struct twl4030_madc_data *madc, u8 reg) { u8 msb, lsb; u16 val; int ret; /* * 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 */ ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &msb, reg + 1); ret = twl_i2c_read_u16(TWL4030_MODULE_MADC, &val, reg); if (ret) { dev_err(madc->dev, "unable to read MSB register 0x%X\n", 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); dev_err(madc->dev, "unable to read register 0x%X\n", reg); 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. */ static int twl4030battery_temperature(int raw_volt) Loading @@ -172,18 +230,18 @@ static int twl4030battery_temperature(int raw_volt) int temp, curr, volt, res, ret; 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, REG_BCICTL2); if (ret < 0) return ret; curr = ((val & TWL4030_BCI_ITHEN) + 1) * 10; /* Getting and calculating the thermistor resistance in ohms */ res = volt * 1000 / curr; /* calculating temperature */ for (temp = 58; temp >= 0; temp--) { int actual = therm_tbl[temp]; int actual = twl4030_therm_tbl[temp]; if ((actual - res) >= 0) break; } Loading @@ -205,11 +263,12 @@ static int twl4030battery_current(int raw_volt) else /* slope of 0.88 mV/mA */ return (raw_volt * CURR_STEP_SIZE) / CURR_PSR_R2; } /* * Function to read channel values * @madc - pointer to twl4030_madc_data struct * @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 * @raw - Return raw values without conversion * value is stored Loading @@ -220,17 +279,17 @@ static int twl4030_madc_read_channels(struct twl4030_madc_data *madc, long channels, int *buf, bool raw) { int count = 0, count_req = 0, i; int count = 0; int i; u8 reg; 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); if (buf[i] < 0) { dev_err(madc->dev, "Unable to read register 0x%X\n", reg); count_req++; continue; dev_err(madc->dev, "Unable to read register 0x%X\n", reg); return buf[i]; } if (raw) { count++; Loading @@ -241,7 +300,7 @@ static int twl4030_madc_read_channels(struct twl4030_madc_data *madc, buf[i] = twl4030battery_current(buf[i]); if (buf[i] < 0) { dev_err(madc->dev, "err reading current\n"); count_req++; return buf[i]; } else { count++; buf[i] = buf[i] - 750; Loading @@ -251,7 +310,7 @@ static int twl4030_madc_read_channels(struct twl4030_madc_data *madc, buf[i] = twl4030battery_temperature(buf[i]); if (buf[i] < 0) { dev_err(madc->dev, "err reading temperature\n"); count_req++; return buf[i]; } else { buf[i] -= 3; count++; Loading @@ -272,8 +331,6 @@ static int twl4030_madc_read_channels(struct twl4030_madc_data *madc, twl4030_divider_ratios[i].numerator); } } if (count_req) dev_err(madc->dev, "%d channel conversion failed\n", count_req); return count; } Loading @@ -297,13 +354,13 @@ static int twl4030_madc_enable_irq(struct twl4030_madc_data *madc, u8 id) madc->imr); return ret; } val &= ~(1 << id); ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, val, madc->imr); if (ret) { dev_err(madc->dev, "unable to write imr register 0x%X\n", madc->imr); return ret; } return 0; Loading Loading @@ -448,22 +505,18 @@ static int twl4030_madc_start_conversion(struct twl4030_madc_data *madc, { const struct twl4030_madc_conversion_method *method; int ret = 0; if (conv_method != TWL4030_MADC_SW1 && conv_method != TWL4030_MADC_SW2) return -ENOTSUPP; method = &twl4030_conversion_methods[conv_method]; switch (conv_method) { case TWL4030_MADC_SW1: case TWL4030_MADC_SW2: ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, TWL4030_MADC_SW_START, method->ctrl); ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, TWL4030_MADC_SW_START, method->ctrl); if (ret) { dev_err(madc->dev, "unable to write ctrl register 0x%X\n", dev_err(madc->dev, "unable to write ctrl register 0x%X\n", method->ctrl); return ret; } break; default: break; } return 0; } Loading Loading @@ -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) { const struct twl4030_madc_conversion_method *method; u8 ch_msb, ch_lsb; int ret; if (!req || !twl4030_madc) Loading @@ -529,38 +581,22 @@ int twl4030_madc_conversion(struct twl4030_madc_request *req) ret = -EBUSY; goto out; } ch_msb = (req->channels >> 8) & 0xff; ch_lsb = req->channels & 0xff; method = &twl4030_conversion_methods[req->method]; /* Select channels to be converted */ ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, ch_msb, method->sel + 1); if (ret) { dev_err(twl4030_madc->dev, "unable to write sel register 0x%X\n", method->sel + 1); goto out; } ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, ch_lsb, method->sel); ret = twl_i2c_write_u16(TWL4030_MODULE_MADC, req->channels, method->sel); if (ret) { 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; } /* Select averaging for all channels if do_avg is set */ if (req->do_avg) { ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, ch_msb, method->avg + 1); ret = twl_i2c_write_u16(TWL4030_MODULE_MADC, req->channels, method->avg); if (ret) { dev_err(twl4030_madc->dev, "unable to write avg register 0x%X\n", method->avg + 1); 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); method->avg); goto out; } } Loading Loading @@ -601,10 +637,6 @@ int twl4030_madc_conversion(struct twl4030_madc_request *req) } EXPORT_SYMBOL_GPL(twl4030_madc_conversion); /* * Return channel value * Or < 0 on failure. */ int twl4030_get_madc_conversion(int channel_no) { struct twl4030_madc_request req; Loading @@ -625,20 +657,25 @@ int twl4030_get_madc_conversion(int channel_no) } 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 * 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, int chan, int on) { int ret; int regmask; u8 regval; ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, Loading @@ -648,10 +685,13 @@ static int twl4030_madc_set_current_generator(struct twl4030_madc_data *madc, TWL4030_BCI_BCICTL1); return ret; } regmask = chan ? TWL4030_BCI_ITHEN : TWL4030_BCI_TYPEN; if (on) regval |= chan ? TWL4030_BCI_ITHEN : TWL4030_BCI_TYPEN; regval |= regmask; else regval &= chan ? ~TWL4030_BCI_ITHEN : ~TWL4030_BCI_TYPEN; regval &= ~regmask; ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, regval, TWL4030_BCI_BCICTL1); if (ret) { Loading @@ -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 * @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 */ static int twl4030_madc_set_power(struct twl4030_madc_data *madc, int on) Loading Loading @@ -702,31 +742,52 @@ static int twl4030_madc_probe(struct platform_device *pdev) { struct twl4030_madc_data *madc; 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; struct iio_dev *iio_dev = NULL; if (!pdata) { dev_err(&pdev->dev, "platform_data not available\n"); if (!pdata && !np) { dev_err(&pdev->dev, "neither platform data nor Device Tree node available\n"); return -EINVAL; } madc = kzalloc(sizeof(*madc), GFP_KERNEL); if (!madc) 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; 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 * the OMAP. The other one can be connected to the other processor such * as modem. Hence two separate ISR and IMR registers. */ madc->imr = (pdata->irq_line == 1) ? TWL4030_MADC_IMR1 : TWL4030_MADC_IMR2; madc->isr = (pdata->irq_line == 1) ? TWL4030_MADC_ISR1 : TWL4030_MADC_ISR2; if (pdata) madc->use_second_irq = (pdata->irq_line != 1); else 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); if (ret < 0) goto err_power; return ret; ret = twl4030_madc_set_current_generator(madc, 0, 1); if (ret < 0) goto err_current_generator; Loading Loading @@ -768,45 +829,62 @@ static int twl4030_madc_probe(struct platform_device *pdev) } } platform_set_drvdata(pdev, madc); platform_set_drvdata(pdev, iio_dev); 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, IRQF_TRIGGER_RISING, "twl4030_madc", madc); if (ret) { dev_dbg(&pdev->dev, "could not request irq\n"); dev_err(&pdev->dev, "could not request irq\n"); goto err_i2c; } 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; err_i2c: twl4030_madc_set_current_generator(madc, 0, 0); err_current_generator: twl4030_madc_set_power(madc, 0); err_power: kfree(madc); return ret; } 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_power(madc, 0); kfree(madc); 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 = { .probe = twl4030_madc_probe, .remove = twl4030_madc_remove, .driver = { .name = "twl4030_madc", .owner = THIS_MODULE, .of_match_table = of_match_ptr(twl_madc_of_match), }, }; Loading drivers/mfd/Kconfig +0 −10 Original line number Diff line number Diff line Loading @@ -935,16 +935,6 @@ config TWL4030_CORE high speed USB OTG transceiver, an audio codec (on most 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 bool "TI TWL4030 power resources" depends on TWL4030_CORE && ARM Loading Loading
Documentation/devicetree/bindings/iio/adc/twl4030-madc.txt 0 → 100644 +24 −0 Original line number Diff line number Diff line * 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>; }; };
drivers/iio/adc/Kconfig +10 −0 Original line number Diff line number Diff line Loading @@ -183,6 +183,16 @@ config TI_AM335X_ADC Say yes here to build support for Texas Instruments ADC 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 tristate "TWL6030 GPADC (General Purpose A/D Converter) Support" depends on TWL4030_CORE Loading
drivers/iio/adc/Makefile +1 −0 Original line number Diff line number Diff line Loading @@ -20,5 +20,6 @@ obj-$(CONFIG_MCP3422) += mcp3422.o obj-$(CONFIG_NAU7802) += nau7802.o obj-$(CONFIG_TI_ADC081C) += ti-adc081c.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_VIPERBOARD_ADC) += viperboard_adc.o
drivers/mfd/twl4030-madc.c→drivers/iio/adc/twl4030-madc.c +199 −121 Original line number Diff line number Diff line Loading @@ -47,20 +47,84 @@ #include <linux/gfp.h> #include <linux/err.h> /* #include <linux/iio/iio.h> /** * struct twl4030_madc_data - a container for madc info * @dev - pointer to device structure for madc * @lock - mutex protecting this data structure * @requests - Array of request struct corresponding to SW1, SW2 and RT * @imr - Interrupt mask register of MADC * @isr - Interrupt status register of MADC * @dev: Pointer to device structure for madc * @lock: Mutex protecting this data structure * @requests: Array of request struct corresponding to SW1, SW2 and RT * @use_second_irq: IRQ selection (main or co-processor) * @imr: Interrupt mask register of MADC * @isr: Interrupt status register of MADC */ struct twl4030_madc_data { struct device *dev; struct mutex lock; /* mutex protecting this data structure */ struct twl4030_madc_request requests[TWL4030_MADC_NUM_METHODS]; int imr; int isr; bool use_second_irq; 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; Loading Loading @@ -91,17 +155,16 @@ twl4030_divider_ratios[16] = { }; /* * Conversion table from -3 to 55 degree Celcius */ static int therm_tbl[] = { /* Conversion table from -3 to 55 degrees Celcius */ static int twl4030_therm_tbl[] = { 30800, 29500, 28300, 27100, 26000, 24900, 23900, 22900, 22000, 21100, 20300, 19400, 18700, 17900, 17200, 16500, 15900, 15300, 14700, 14100, 13600, 13100, 12600, 12100, 11600, 11200, 10800, 10400, 10000, 9630, 9280, 8950, 8620, 8310, 8020, 7730, 7460, 7200, 6950, 6710, 6470, 6250, 6040, 5830, 5640, 5450, 5260, 5090, 4920, 4760, 4600, 4450, 4310, 4170, 4040, 3910, 3790, 3670, 3550 26000, 24900, 23900, 22900, 22000, 21100, 20300, 19400, 18700, 17900, 17200, 16500, 15900, 15300, 14700, 14100, 13600, 13100, 12600, 12100, 11600, 11200, 10800, 10400, 10000, 9630, 9280, 8950, 8620, 8310, 8020, 7730, 7460, 7200, 6950, 6710, 6470, 6250, 6040, 5830, 5640, 5450, 5260, 5090, 4920, 4760, 4600, 4450, 4310, 4170, 4040, 3910, 3790, 3670, 3550 }; /* Loading Loading @@ -133,37 +196,32 @@ const struct twl4030_madc_conversion_method twl4030_conversion_methods[] = { }, }; /* * Function to read a particular channel value. * @madc - pointer to struct twl4030_madc_data * @reg - lsb of ADC Channel * If the i2c read fails it returns an error else returns 0. /** * twl4030_madc_channel_raw_read() - Function to read a particular channel value * @madc: pointer to struct twl4030_madc_data * @reg: lsb of ADC Channel * * Return: 0 on success, an error code otherwise. */ static int twl4030_madc_channel_raw_read(struct twl4030_madc_data *madc, u8 reg) { u8 msb, lsb; u16 val; int ret; /* * 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 */ ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &msb, reg + 1); ret = twl_i2c_read_u16(TWL4030_MODULE_MADC, &val, reg); if (ret) { dev_err(madc->dev, "unable to read MSB register 0x%X\n", 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); dev_err(madc->dev, "unable to read register 0x%X\n", reg); 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. */ static int twl4030battery_temperature(int raw_volt) Loading @@ -172,18 +230,18 @@ static int twl4030battery_temperature(int raw_volt) int temp, curr, volt, res, ret; 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, REG_BCICTL2); if (ret < 0) return ret; curr = ((val & TWL4030_BCI_ITHEN) + 1) * 10; /* Getting and calculating the thermistor resistance in ohms */ res = volt * 1000 / curr; /* calculating temperature */ for (temp = 58; temp >= 0; temp--) { int actual = therm_tbl[temp]; int actual = twl4030_therm_tbl[temp]; if ((actual - res) >= 0) break; } Loading @@ -205,11 +263,12 @@ static int twl4030battery_current(int raw_volt) else /* slope of 0.88 mV/mA */ return (raw_volt * CURR_STEP_SIZE) / CURR_PSR_R2; } /* * Function to read channel values * @madc - pointer to twl4030_madc_data struct * @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 * @raw - Return raw values without conversion * value is stored Loading @@ -220,17 +279,17 @@ static int twl4030_madc_read_channels(struct twl4030_madc_data *madc, long channels, int *buf, bool raw) { int count = 0, count_req = 0, i; int count = 0; int i; u8 reg; 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); if (buf[i] < 0) { dev_err(madc->dev, "Unable to read register 0x%X\n", reg); count_req++; continue; dev_err(madc->dev, "Unable to read register 0x%X\n", reg); return buf[i]; } if (raw) { count++; Loading @@ -241,7 +300,7 @@ static int twl4030_madc_read_channels(struct twl4030_madc_data *madc, buf[i] = twl4030battery_current(buf[i]); if (buf[i] < 0) { dev_err(madc->dev, "err reading current\n"); count_req++; return buf[i]; } else { count++; buf[i] = buf[i] - 750; Loading @@ -251,7 +310,7 @@ static int twl4030_madc_read_channels(struct twl4030_madc_data *madc, buf[i] = twl4030battery_temperature(buf[i]); if (buf[i] < 0) { dev_err(madc->dev, "err reading temperature\n"); count_req++; return buf[i]; } else { buf[i] -= 3; count++; Loading @@ -272,8 +331,6 @@ static int twl4030_madc_read_channels(struct twl4030_madc_data *madc, twl4030_divider_ratios[i].numerator); } } if (count_req) dev_err(madc->dev, "%d channel conversion failed\n", count_req); return count; } Loading @@ -297,13 +354,13 @@ static int twl4030_madc_enable_irq(struct twl4030_madc_data *madc, u8 id) madc->imr); return ret; } val &= ~(1 << id); ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, val, madc->imr); if (ret) { dev_err(madc->dev, "unable to write imr register 0x%X\n", madc->imr); return ret; } return 0; Loading Loading @@ -448,22 +505,18 @@ static int twl4030_madc_start_conversion(struct twl4030_madc_data *madc, { const struct twl4030_madc_conversion_method *method; int ret = 0; if (conv_method != TWL4030_MADC_SW1 && conv_method != TWL4030_MADC_SW2) return -ENOTSUPP; method = &twl4030_conversion_methods[conv_method]; switch (conv_method) { case TWL4030_MADC_SW1: case TWL4030_MADC_SW2: ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, TWL4030_MADC_SW_START, method->ctrl); ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, TWL4030_MADC_SW_START, method->ctrl); if (ret) { dev_err(madc->dev, "unable to write ctrl register 0x%X\n", dev_err(madc->dev, "unable to write ctrl register 0x%X\n", method->ctrl); return ret; } break; default: break; } return 0; } Loading Loading @@ -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) { const struct twl4030_madc_conversion_method *method; u8 ch_msb, ch_lsb; int ret; if (!req || !twl4030_madc) Loading @@ -529,38 +581,22 @@ int twl4030_madc_conversion(struct twl4030_madc_request *req) ret = -EBUSY; goto out; } ch_msb = (req->channels >> 8) & 0xff; ch_lsb = req->channels & 0xff; method = &twl4030_conversion_methods[req->method]; /* Select channels to be converted */ ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, ch_msb, method->sel + 1); if (ret) { dev_err(twl4030_madc->dev, "unable to write sel register 0x%X\n", method->sel + 1); goto out; } ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, ch_lsb, method->sel); ret = twl_i2c_write_u16(TWL4030_MODULE_MADC, req->channels, method->sel); if (ret) { 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; } /* Select averaging for all channels if do_avg is set */ if (req->do_avg) { ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, ch_msb, method->avg + 1); ret = twl_i2c_write_u16(TWL4030_MODULE_MADC, req->channels, method->avg); if (ret) { dev_err(twl4030_madc->dev, "unable to write avg register 0x%X\n", method->avg + 1); 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); method->avg); goto out; } } Loading Loading @@ -601,10 +637,6 @@ int twl4030_madc_conversion(struct twl4030_madc_request *req) } EXPORT_SYMBOL_GPL(twl4030_madc_conversion); /* * Return channel value * Or < 0 on failure. */ int twl4030_get_madc_conversion(int channel_no) { struct twl4030_madc_request req; Loading @@ -625,20 +657,25 @@ int twl4030_get_madc_conversion(int channel_no) } 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 * 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, int chan, int on) { int ret; int regmask; u8 regval; ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, Loading @@ -648,10 +685,13 @@ static int twl4030_madc_set_current_generator(struct twl4030_madc_data *madc, TWL4030_BCI_BCICTL1); return ret; } regmask = chan ? TWL4030_BCI_ITHEN : TWL4030_BCI_TYPEN; if (on) regval |= chan ? TWL4030_BCI_ITHEN : TWL4030_BCI_TYPEN; regval |= regmask; else regval &= chan ? ~TWL4030_BCI_ITHEN : ~TWL4030_BCI_TYPEN; regval &= ~regmask; ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, regval, TWL4030_BCI_BCICTL1); if (ret) { Loading @@ -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 * @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 */ static int twl4030_madc_set_power(struct twl4030_madc_data *madc, int on) Loading Loading @@ -702,31 +742,52 @@ static int twl4030_madc_probe(struct platform_device *pdev) { struct twl4030_madc_data *madc; 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; struct iio_dev *iio_dev = NULL; if (!pdata) { dev_err(&pdev->dev, "platform_data not available\n"); if (!pdata && !np) { dev_err(&pdev->dev, "neither platform data nor Device Tree node available\n"); return -EINVAL; } madc = kzalloc(sizeof(*madc), GFP_KERNEL); if (!madc) 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; 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 * the OMAP. The other one can be connected to the other processor such * as modem. Hence two separate ISR and IMR registers. */ madc->imr = (pdata->irq_line == 1) ? TWL4030_MADC_IMR1 : TWL4030_MADC_IMR2; madc->isr = (pdata->irq_line == 1) ? TWL4030_MADC_ISR1 : TWL4030_MADC_ISR2; if (pdata) madc->use_second_irq = (pdata->irq_line != 1); else 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); if (ret < 0) goto err_power; return ret; ret = twl4030_madc_set_current_generator(madc, 0, 1); if (ret < 0) goto err_current_generator; Loading Loading @@ -768,45 +829,62 @@ static int twl4030_madc_probe(struct platform_device *pdev) } } platform_set_drvdata(pdev, madc); platform_set_drvdata(pdev, iio_dev); 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, IRQF_TRIGGER_RISING, "twl4030_madc", madc); if (ret) { dev_dbg(&pdev->dev, "could not request irq\n"); dev_err(&pdev->dev, "could not request irq\n"); goto err_i2c; } 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; err_i2c: twl4030_madc_set_current_generator(madc, 0, 0); err_current_generator: twl4030_madc_set_power(madc, 0); err_power: kfree(madc); return ret; } 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_power(madc, 0); kfree(madc); 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 = { .probe = twl4030_madc_probe, .remove = twl4030_madc_remove, .driver = { .name = "twl4030_madc", .owner = THIS_MODULE, .of_match_table = of_match_ptr(twl_madc_of_match), }, }; Loading
drivers/mfd/Kconfig +0 −10 Original line number Diff line number Diff line Loading @@ -935,16 +935,6 @@ config TWL4030_CORE high speed USB OTG transceiver, an audio codec (on most 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 bool "TI TWL4030 power resources" depends on TWL4030_CORE && ARM Loading