diff options
author | Sage Weil <sage@inktank.com> | 2013-08-15 11:11:45 -0700 |
---|---|---|
committer | Sage Weil <sage@inktank.com> | 2013-08-15 11:11:45 -0700 |
commit | ee3e542fec6e69bc9fb668698889a37d93950ddf (patch) | |
tree | e74ee766a4764769ef1d3d45d266b4dea64101d3 /drivers/iio/adc | |
parent | fe2a801b50c0bb8039d627e5ae1fec249d10ff39 (diff) | |
parent | f1d6e17f540af37bb1891480143669ba7636c4cf (diff) |
Merge remote-tracking branch 'linus/master' into testing
Diffstat (limited to 'drivers/iio/adc')
-rw-r--r-- | drivers/iio/adc/Kconfig | 10 | ||||
-rw-r--r-- | drivers/iio/adc/Makefile | 1 | ||||
-rw-r--r-- | drivers/iio/adc/at91_adc.c | 2 | ||||
-rw-r--r-- | drivers/iio/adc/exynos_adc.c | 12 | ||||
-rw-r--r-- | drivers/iio/adc/max1363.c | 2 | ||||
-rw-r--r-- | drivers/iio/adc/mcp320x.c | 257 | ||||
-rw-r--r-- | drivers/iio/adc/ti_am335x_adc.c | 147 |
7 files changed, 392 insertions, 39 deletions
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index ab0767e6727..93129ec4b64 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -133,6 +133,16 @@ config MAX1363 max11646, max11647) Provides direct access via sysfs and buffered data via the iio dev interface. +config MCP320X + tristate "Microchip Technology MCP3204/08" + depends on SPI + help + Say yes here to build support for Microchip Technology's MCP3204 or + MCP3208 analog to digital converter. + + This driver can also be built as a module. If so, the module will be + called mcp320x. + config TI_ADC081C tristate "Texas Instruments ADC081C021/027" depends on I2C diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 0a825bed43f..8f475d31fe4 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_AT91_ADC) += at91_adc.o obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o obj-$(CONFIG_MAX1363) += max1363.o +obj-$(CONFIG_MCP320X) += mcp320x.o obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o obj-$(CONFIG_VIPERBOARD_ADC) += viperboard_adc.o diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c index e5b88d5d3b5..b6db6a0e09c 100644 --- a/drivers/iio/adc/at91_adc.c +++ b/drivers/iio/adc/at91_adc.c @@ -774,11 +774,13 @@ static int at91_adc_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_OF static const struct of_device_id at91_adc_dt_ids[] = { { .compatible = "atmel,at91sam9260-adc" }, {}, }; MODULE_DEVICE_TABLE(of, at91_adc_dt_ids); +#endif static struct platform_driver at91_adc_driver = { .probe = at91_adc_probe, diff --git a/drivers/iio/adc/exynos_adc.c b/drivers/iio/adc/exynos_adc.c index b3d03d33594..9809fc9a35d 100644 --- a/drivers/iio/adc/exynos_adc.c +++ b/drivers/iio/adc/exynos_adc.c @@ -270,16 +270,16 @@ static int exynos_adc_probe(struct platform_device *pdev) info = iio_priv(indio_dev); mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - info->regs = devm_request_and_ioremap(&pdev->dev, mem); - if (!info->regs) { - ret = -ENOMEM; + info->regs = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(info->regs)) { + ret = PTR_ERR(info->regs); goto err_iio; } mem = platform_get_resource(pdev, IORESOURCE_MEM, 1); - info->enable_reg = devm_request_and_ioremap(&pdev->dev, mem); - if (!info->enable_reg) { - ret = -ENOMEM; + info->enable_reg = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(info->enable_reg)) { + ret = PTR_ERR(info->enable_reg); goto err_iio; } diff --git a/drivers/iio/adc/max1363.c b/drivers/iio/adc/max1363.c index 9e6da72ad82..f148d00b83f 100644 --- a/drivers/iio/adc/max1363.c +++ b/drivers/iio/adc/max1363.c @@ -660,7 +660,7 @@ static ssize_t max1363_monitor_store_freq(struct device *dev, unsigned long val; bool found = false; - ret = strict_strtoul(buf, 10, &val); + ret = kstrtoul(buf, 10, &val); if (ret) return -EINVAL; for (i = 0; i < ARRAY_SIZE(max1363_monitor_speeds); i++) diff --git a/drivers/iio/adc/mcp320x.c b/drivers/iio/adc/mcp320x.c new file mode 100644 index 00000000000..ebc015922a7 --- /dev/null +++ b/drivers/iio/adc/mcp320x.c @@ -0,0 +1,257 @@ +/* + * Copyright (C) 2013 Oskar Andero <oskar.andero@gmail.com> + * + * Driver for Microchip Technology's MCP3204 and MCP3208 ADC chips. + * Datasheet can be found here: + * http://ww1.microchip.com/downloads/en/devicedoc/21298c.pdf + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/err.h> +#include <linux/spi/spi.h> +#include <linux/module.h> +#include <linux/iio/iio.h> +#include <linux/regulator/consumer.h> + +#define MCP_SINGLE_ENDED (1 << 3) +#define MCP_START_BIT (1 << 4) + +enum { + mcp3204, + mcp3208, +}; + +struct mcp320x { + struct spi_device *spi; + struct spi_message msg; + struct spi_transfer transfer[2]; + + u8 tx_buf; + u8 rx_buf[2]; + + struct regulator *reg; + struct mutex lock; +}; + +static int mcp320x_adc_conversion(struct mcp320x *adc, u8 msg) +{ + int ret; + + adc->tx_buf = msg; + ret = spi_sync(adc->spi, &adc->msg); + if (ret < 0) + return ret; + + return ((adc->rx_buf[0] & 0x3f) << 6) | + (adc->rx_buf[1] >> 2); +} + +static int mcp320x_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *channel, int *val, + int *val2, long mask) +{ + struct mcp320x *adc = iio_priv(indio_dev); + int ret = -EINVAL; + + mutex_lock(&adc->lock); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (channel->differential) + ret = mcp320x_adc_conversion(adc, + MCP_START_BIT | channel->address); + else + ret = mcp320x_adc_conversion(adc, + MCP_START_BIT | MCP_SINGLE_ENDED | + channel->address); + if (ret < 0) + goto out; + + *val = ret; + ret = IIO_VAL_INT; + break; + + case IIO_CHAN_INFO_SCALE: + /* Digital output code = (4096 * Vin) / Vref */ + ret = regulator_get_voltage(adc->reg); + if (ret < 0) + goto out; + + *val = ret / 1000; + *val2 = 12; + ret = IIO_VAL_FRACTIONAL_LOG2; + break; + + default: + break; + } + +out: + mutex_unlock(&adc->lock); + + return ret; +} + +#define MCP320X_VOLTAGE_CHANNEL(num) \ + { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (num), \ + .address = (num), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \ + } + +#define MCP320X_VOLTAGE_CHANNEL_DIFF(num) \ + { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (num * 2), \ + .channel2 = (num * 2 + 1), \ + .address = (num * 2), \ + .differential = 1, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \ + } + +static const struct iio_chan_spec mcp3204_channels[] = { + MCP320X_VOLTAGE_CHANNEL(0), + MCP320X_VOLTAGE_CHANNEL(1), + MCP320X_VOLTAGE_CHANNEL(2), + MCP320X_VOLTAGE_CHANNEL(3), + MCP320X_VOLTAGE_CHANNEL_DIFF(0), + MCP320X_VOLTAGE_CHANNEL_DIFF(1), +}; + +static const struct iio_chan_spec mcp3208_channels[] = { + MCP320X_VOLTAGE_CHANNEL(0), + MCP320X_VOLTAGE_CHANNEL(1), + MCP320X_VOLTAGE_CHANNEL(2), + MCP320X_VOLTAGE_CHANNEL(3), + MCP320X_VOLTAGE_CHANNEL(4), + MCP320X_VOLTAGE_CHANNEL(5), + MCP320X_VOLTAGE_CHANNEL(6), + MCP320X_VOLTAGE_CHANNEL(7), + MCP320X_VOLTAGE_CHANNEL_DIFF(0), + MCP320X_VOLTAGE_CHANNEL_DIFF(1), + MCP320X_VOLTAGE_CHANNEL_DIFF(2), + MCP320X_VOLTAGE_CHANNEL_DIFF(3), +}; + +static const struct iio_info mcp320x_info = { + .read_raw = mcp320x_read_raw, + .driver_module = THIS_MODULE, +}; + +struct mcp3208_chip_info { + const struct iio_chan_spec *channels; + unsigned int num_channels; +}; + +static const struct mcp3208_chip_info mcp3208_chip_infos[] = { + [mcp3204] = { + .channels = mcp3204_channels, + .num_channels = ARRAY_SIZE(mcp3204_channels) + }, + [mcp3208] = { + .channels = mcp3208_channels, + .num_channels = ARRAY_SIZE(mcp3208_channels) + }, +}; + +static int mcp320x_probe(struct spi_device *spi) +{ + struct iio_dev *indio_dev; + struct mcp320x *adc; + const struct mcp3208_chip_info *chip_info; + int ret; + + indio_dev = iio_device_alloc(sizeof(*adc)); + if (!indio_dev) + return -ENOMEM; + + adc = iio_priv(indio_dev); + adc->spi = spi; + + indio_dev->dev.parent = &spi->dev; + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &mcp320x_info; + + chip_info = &mcp3208_chip_infos[spi_get_device_id(spi)->driver_data]; + indio_dev->channels = chip_info->channels; + indio_dev->num_channels = chip_info->num_channels; + + adc->transfer[0].tx_buf = &adc->tx_buf; + adc->transfer[0].len = sizeof(adc->tx_buf); + adc->transfer[1].rx_buf = adc->rx_buf; + adc->transfer[1].len = sizeof(adc->rx_buf); + + spi_message_init_with_transfers(&adc->msg, adc->transfer, + ARRAY_SIZE(adc->transfer)); + + adc->reg = regulator_get(&spi->dev, "vref"); + if (IS_ERR(adc->reg)) { + ret = PTR_ERR(adc->reg); + goto iio_free; + } + + ret = regulator_enable(adc->reg); + if (ret < 0) + goto reg_free; + + mutex_init(&adc->lock); + + ret = iio_device_register(indio_dev); + if (ret < 0) + goto reg_disable; + + return 0; + +reg_disable: + regulator_disable(adc->reg); +reg_free: + regulator_put(adc->reg); +iio_free: + iio_device_free(indio_dev); + + return ret; +} + +static int mcp320x_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct mcp320x *adc = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + regulator_disable(adc->reg); + regulator_put(adc->reg); + iio_device_free(indio_dev); + + return 0; +} + +static const struct spi_device_id mcp320x_id[] = { + { "mcp3204", mcp3204 }, + { "mcp3208", mcp3208 }, + { } +}; +MODULE_DEVICE_TABLE(spi, mcp320x_id); + +static struct spi_driver mcp320x_driver = { + .driver = { + .name = "mcp320x", + .owner = THIS_MODULE, + }, + .probe = mcp320x_probe, + .remove = mcp320x_remove, + .id_table = mcp320x_id, +}; +module_spi_driver(mcp320x_driver); + +MODULE_AUTHOR("Oskar Andero <oskar.andero@gmail.com>"); +MODULE_DESCRIPTION("Microchip Technology MCP3204/08"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c index 5f9a7e7d313..3ceac3e91dd 100644 --- a/drivers/iio/adc/ti_am335x_adc.c +++ b/drivers/iio/adc/ti_am335x_adc.c @@ -22,13 +22,18 @@ #include <linux/platform_device.h> #include <linux/io.h> #include <linux/iio/iio.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/iio/machine.h> +#include <linux/iio/driver.h> #include <linux/mfd/ti_am335x_tscadc.h> -#include <linux/platform_data/ti_am335x_adc.h> struct tiadc_device { struct ti_tscadc_dev *mfd_tscadc; int channels; + u8 channel_line[8]; + u8 channel_step[8]; }; static unsigned int tiadc_readl(struct tiadc_device *adc, unsigned int reg) @@ -42,10 +47,19 @@ static void tiadc_writel(struct tiadc_device *adc, unsigned int reg, writel(val, adc->mfd_tscadc->tscadc_base + reg); } +static u32 get_adc_step_mask(struct tiadc_device *adc_dev) +{ + u32 step_en; + + step_en = ((1 << adc_dev->channels) - 1); + step_en <<= TOTAL_STEPS - adc_dev->channels + 1; + return step_en; +} + static void tiadc_step_config(struct tiadc_device *adc_dev) { unsigned int stepconfig; - int i, channels = 0, steps; + int i, steps; /* * There are 16 configurable steps and 8 analog input @@ -58,43 +72,62 @@ static void tiadc_step_config(struct tiadc_device *adc_dev) */ steps = TOTAL_STEPS - adc_dev->channels; - channels = TOTAL_CHANNELS - adc_dev->channels; - stepconfig = STEPCONFIG_AVG_16 | STEPCONFIG_FIFO1; - for (i = (steps + 1); i <= TOTAL_STEPS; i++) { - tiadc_writel(adc_dev, REG_STEPCONFIG(i), - stepconfig | STEPCONFIG_INP(channels)); - tiadc_writel(adc_dev, REG_STEPDELAY(i), + for (i = 0; i < adc_dev->channels; i++) { + int chan; + + chan = adc_dev->channel_line[i]; + tiadc_writel(adc_dev, REG_STEPCONFIG(steps), + stepconfig | STEPCONFIG_INP(chan)); + tiadc_writel(adc_dev, REG_STEPDELAY(steps), STEPCONFIG_OPENDLY); - channels++; + adc_dev->channel_step[i] = steps; + steps++; } - tiadc_writel(adc_dev, REG_SE, STPENB_STEPENB); + } +static const char * const chan_name_ain[] = { + "AIN0", + "AIN1", + "AIN2", + "AIN3", + "AIN4", + "AIN5", + "AIN6", + "AIN7", +}; + static int tiadc_channel_init(struct iio_dev *indio_dev, int channels) { + struct tiadc_device *adc_dev = iio_priv(indio_dev); struct iio_chan_spec *chan_array; + struct iio_chan_spec *chan; int i; indio_dev->num_channels = channels; - chan_array = kcalloc(indio_dev->num_channels, + chan_array = kcalloc(channels, sizeof(struct iio_chan_spec), GFP_KERNEL); - if (chan_array == NULL) return -ENOMEM; - for (i = 0; i < (indio_dev->num_channels); i++) { - struct iio_chan_spec *chan = chan_array + i; + chan = chan_array; + for (i = 0; i < channels; i++, chan++) { + chan->type = IIO_VOLTAGE; chan->indexed = 1; - chan->channel = i; + chan->channel = adc_dev->channel_line[i]; chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW); + chan->datasheet_name = chan_name_ain[chan->channel]; + chan->scan_type.sign = 'u'; + chan->scan_type.realbits = 12; + chan->scan_type.storagebits = 32; } indio_dev->channels = chan_array; - return indio_dev->num_channels; + return 0; } static void tiadc_channels_remove(struct iio_dev *indio_dev) @@ -107,8 +140,22 @@ static int tiadc_read_raw(struct iio_dev *indio_dev, int *val, int *val2, long mask) { struct tiadc_device *adc_dev = iio_priv(indio_dev); - int i; - unsigned int fifo1count, readx1; + int i, map_val; + unsigned int fifo1count, read, stepid; + u32 step = UINT_MAX; + bool found = false; + u32 step_en; + unsigned long timeout = jiffies + usecs_to_jiffies + (IDLE_TIMEOUT * adc_dev->channels); + step_en = get_adc_step_mask(adc_dev); + am335x_tsc_se_set(adc_dev->mfd_tscadc, step_en); + + /* Wait for ADC sequencer to complete sampling */ + while (tiadc_readl(adc_dev, REG_ADCFSM) & SEQ_STATUS) { + if (time_after(jiffies, timeout)) + return -EAGAIN; + } + map_val = chan->channel + TOTAL_CHANNELS; /* * When the sub-system is first enabled, @@ -121,32 +168,51 @@ static int tiadc_read_raw(struct iio_dev *indio_dev, * Hence we need to flush out this data. */ + for (i = 0; i < ARRAY_SIZE(adc_dev->channel_step); i++) { + if (chan->channel == adc_dev->channel_line[i]) { + step = adc_dev->channel_step[i]; + break; + } + } + if (WARN_ON_ONCE(step == UINT_MAX)) + return -EINVAL; + fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT); for (i = 0; i < fifo1count; i++) { - readx1 = tiadc_readl(adc_dev, REG_FIFO1); - if (i == chan->channel) - *val = readx1 & 0xfff; + read = tiadc_readl(adc_dev, REG_FIFO1); + stepid = read & FIFOREAD_CHNLID_MASK; + stepid = stepid >> 0x10; + + if (stepid == map_val) { + read = read & FIFOREAD_DATA_MASK; + found = true; + *val = read; + } } - tiadc_writel(adc_dev, REG_SE, STPENB_STEPENB); + if (found == false) + return -EBUSY; return IIO_VAL_INT; } static const struct iio_info tiadc_info = { .read_raw = &tiadc_read_raw, + .driver_module = THIS_MODULE, }; static int tiadc_probe(struct platform_device *pdev) { struct iio_dev *indio_dev; struct tiadc_device *adc_dev; - struct ti_tscadc_dev *tscadc_dev = pdev->dev.platform_data; - struct mfd_tscadc_board *pdata; + struct device_node *node = pdev->dev.of_node; + struct property *prop; + const __be32 *cur; int err; + u32 val; + int channels = 0; - pdata = tscadc_dev->dev->platform_data; - if (!pdata || !pdata->adc_init) { - dev_err(&pdev->dev, "Could not find platform data\n"); + if (!node) { + dev_err(&pdev->dev, "Could not find valid DT data.\n"); return -EINVAL; } @@ -158,8 +224,13 @@ static int tiadc_probe(struct platform_device *pdev) } adc_dev = iio_priv(indio_dev); - adc_dev->mfd_tscadc = tscadc_dev; - adc_dev->channels = pdata->adc_init->adc_channels; + adc_dev->mfd_tscadc = ti_tscadc_dev_get(pdev); + + of_property_for_each_u32(node, "ti,adc-channels", prop, cur, val) { + adc_dev->channel_line[channels] = val; + channels++; + } + adc_dev->channels = channels; indio_dev->dev.parent = &pdev->dev; indio_dev->name = dev_name(&pdev->dev); @@ -191,10 +262,15 @@ err_ret: static int tiadc_remove(struct platform_device *pdev) { struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct tiadc_device *adc_dev = iio_priv(indio_dev); + u32 step_en; iio_device_unregister(indio_dev); tiadc_channels_remove(indio_dev); + step_en = get_adc_step_mask(adc_dev); + am335x_tsc_se_clr(adc_dev->mfd_tscadc, step_en); + iio_device_free(indio_dev); return 0; @@ -205,9 +281,10 @@ static int tiadc_suspend(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); struct tiadc_device *adc_dev = iio_priv(indio_dev); - struct ti_tscadc_dev *tscadc_dev = dev->platform_data; + struct ti_tscadc_dev *tscadc_dev; unsigned int idle; + tscadc_dev = ti_tscadc_dev_get(to_platform_device(dev)); if (!device_may_wakeup(tscadc_dev->dev)) { idle = tiadc_readl(adc_dev, REG_CTRL); idle &= ~(CNTRLREG_TSCSSENB); @@ -243,16 +320,22 @@ static const struct dev_pm_ops tiadc_pm_ops = { #define TIADC_PM_OPS NULL #endif +static const struct of_device_id ti_adc_dt_ids[] = { + { .compatible = "ti,am3359-adc", }, + { } +}; +MODULE_DEVICE_TABLE(of, ti_adc_dt_ids); + static struct platform_driver tiadc_driver = { .driver = { - .name = "tiadc", + .name = "TI-am335x-adc", .owner = THIS_MODULE, .pm = TIADC_PM_OPS, + .of_match_table = of_match_ptr(ti_adc_dt_ids), }, .probe = tiadc_probe, .remove = tiadc_remove, }; - module_platform_driver(tiadc_driver); MODULE_DESCRIPTION("TI ADC controller driver"); |