summaryrefslogtreecommitdiffstats
path: root/patches/linux-3.7-rc6/0052-IIO-ADC-tiadc-Add-support-of-TI-s-ADC-driver.patch
diff options
context:
space:
mode:
Diffstat (limited to 'patches/linux-3.7-rc6/0052-IIO-ADC-tiadc-Add-support-of-TI-s-ADC-driver.patch')
-rw-r--r--patches/linux-3.7-rc6/0052-IIO-ADC-tiadc-Add-support-of-TI-s-ADC-driver.patch420
1 files changed, 420 insertions, 0 deletions
diff --git a/patches/linux-3.7-rc6/0052-IIO-ADC-tiadc-Add-support-of-TI-s-ADC-driver.patch b/patches/linux-3.7-rc6/0052-IIO-ADC-tiadc-Add-support-of-TI-s-ADC-driver.patch
new file mode 100644
index 0000000..55e3b66
--- /dev/null
+++ b/patches/linux-3.7-rc6/0052-IIO-ADC-tiadc-Add-support-of-TI-s-ADC-driver.patch
@@ -0,0 +1,420 @@
+From 1c87d22d7a5671164abd42a6478cf536a3637fee Mon Sep 17 00:00:00 2001
+From: "Patil, Rachna" <rachna@ti.com>
+Date: Tue, 16 Oct 2012 07:25:45 +0000
+Subject: [PATCH] IIO : ADC: tiadc: Add support of TI's ADC driver
+
+This patch adds support for TI's ADC driver.
+This is a multifunctional device.
+Analog input lines are provided on which
+voltage measurements can be carried out.
+You can have upto 8 input lines.
+
+Signed-off-by: Patil, Rachna <rachna@ti.com>
+Acked-by: Jonathan Cameron <jic23@kernel.org>
+---
+ drivers/iio/adc/Kconfig | 7 +
+ drivers/iio/adc/Makefile | 1 +
+ drivers/iio/adc/ti_am335x_adc.c | 260 +++++++++++++++++++++++++++
+ drivers/mfd/ti_am335x_tscadc.c | 18 +-
+ include/linux/mfd/ti_am335x_tscadc.h | 9 +-
+ include/linux/platform_data/ti_am335x_adc.h | 14 ++
+ 6 files changed, 307 insertions(+), 2 deletions(-)
+ create mode 100644 drivers/iio/adc/ti_am335x_adc.c
+ create mode 100644 include/linux/platform_data/ti_am335x_adc.h
+
+diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
+index 4927581..1401ed1 100644
+--- a/drivers/iio/adc/Kconfig
++++ b/drivers/iio/adc/Kconfig
+@@ -60,4 +60,11 @@ config LP8788_ADC
+ help
+ Say yes here to build support for TI LP8788 ADC.
+
++config TI_AM335X_ADC
++ tristate "TI's ADC driver"
++ depends on MFD_TI_AM335X_TSCADC
++ help
++ Say yes here to build support for Texas Instruments ADC
++ driver which is also a MFD client.
++
+ endmenu
+diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
+index 900995d..4410a90 100644
+--- a/drivers/iio/adc/Makefile
++++ b/drivers/iio/adc/Makefile
+@@ -8,3 +8,4 @@ obj-$(CONFIG_AD7476) += ad7476.o
+ obj-$(CONFIG_AD7791) += ad7791.o
+ obj-$(CONFIG_AT91_ADC) += at91_adc.o
+ obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
++obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o
+diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c
+new file mode 100644
+index 0000000..02a43c8
+--- /dev/null
++++ b/drivers/iio/adc/ti_am335x_adc.c
+@@ -0,0 +1,260 @@
++/*
++ * TI ADC MFD driver
++ *
++ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation version 2.
++ *
++ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
++ * kind, whether express or implied; without even the implied warranty
++ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ */
++
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/err.h>
++#include <linux/module.h>
++#include <linux/slab.h>
++#include <linux/interrupt.h>
++#include <linux/platform_device.h>
++#include <linux/io.h>
++#include <linux/iio/iio.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;
++};
++
++static unsigned int tiadc_readl(struct tiadc_device *adc, unsigned int reg)
++{
++ return readl(adc->mfd_tscadc->tscadc_base + reg);
++}
++
++static void tiadc_writel(struct tiadc_device *adc, unsigned int reg,
++ unsigned int val)
++{
++ writel(val, adc->mfd_tscadc->tscadc_base + reg);
++}
++
++static void tiadc_step_config(struct tiadc_device *adc_dev)
++{
++ unsigned int stepconfig;
++ int i, channels = 0, steps;
++
++ /*
++ * There are 16 configurable steps and 8 analog input
++ * lines available which are shared between Touchscreen and ADC.
++ *
++ * Steps backwards i.e. from 16 towards 0 are used by ADC
++ * depending on number of input lines needed.
++ * Channel would represent which analog input
++ * needs to be given to ADC to digitalize data.
++ */
++
++ 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),
++ STEPCONFIG_OPENDLY);
++ channels++;
++ }
++ tiadc_writel(adc_dev, REG_SE, STPENB_STEPENB);
++}
++
++static int tiadc_channel_init(struct iio_dev *indio_dev, int channels)
++{
++ struct iio_chan_spec *chan_array;
++ int i;
++
++ indio_dev->num_channels = channels;
++ chan_array = kcalloc(indio_dev->num_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->type = IIO_VOLTAGE;
++ chan->indexed = 1;
++ chan->channel = i;
++ chan->info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT;
++ }
++
++ indio_dev->channels = chan_array;
++
++ return indio_dev->num_channels;
++}
++
++static void tiadc_channels_remove(struct iio_dev *indio_dev)
++{
++ kfree(indio_dev->channels);
++}
++
++static int tiadc_read_raw(struct iio_dev *indio_dev,
++ struct iio_chan_spec const *chan,
++ int *val, int *val2, long mask)
++{
++ struct tiadc_device *adc_dev = iio_priv(indio_dev);
++ int i;
++ unsigned int fifo1count, readx1;
++
++ /*
++ * When the sub-system is first enabled,
++ * the sequencer will always start with the
++ * lowest step (1) and continue until step (16).
++ * For ex: If we have enabled 4 ADC channels and
++ * currently use only 1 out of them, the
++ * sequencer still configures all the 4 steps,
++ * leading to 3 unwanted data.
++ * Hence we need to flush out this data.
++ */
++
++ 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;
++ }
++ tiadc_writel(adc_dev, REG_SE, STPENB_STEPENB);
++
++ return IIO_VAL_INT;
++}
++
++static const struct iio_info tiadc_info = {
++ .read_raw = &tiadc_read_raw,
++};
++
++static int __devinit 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;
++ int err;
++
++ pdata = tscadc_dev->dev->platform_data;
++ if (!pdata || !pdata->adc_init) {
++ dev_err(&pdev->dev, "Could not find platform data\n");
++ return -EINVAL;
++ }
++
++ indio_dev = iio_device_alloc(sizeof(struct tiadc_device));
++ if (indio_dev == NULL) {
++ dev_err(&pdev->dev, "failed to allocate iio device\n");
++ err = -ENOMEM;
++ goto err_ret;
++ }
++ adc_dev = iio_priv(indio_dev);
++
++ adc_dev->mfd_tscadc = tscadc_dev;
++ adc_dev->channels = pdata->adc_init->adc_channels;
++
++ indio_dev->dev.parent = &pdev->dev;
++ indio_dev->name = dev_name(&pdev->dev);
++ indio_dev->modes = INDIO_DIRECT_MODE;
++ indio_dev->info = &tiadc_info;
++
++ tiadc_step_config(adc_dev);
++
++ err = tiadc_channel_init(indio_dev, adc_dev->channels);
++ if (err < 0)
++ goto err_free_device;
++
++ err = iio_device_register(indio_dev);
++ if (err)
++ goto err_free_channels;
++
++ platform_set_drvdata(pdev, indio_dev);
++
++ return 0;
++
++err_free_channels:
++ tiadc_channels_remove(indio_dev);
++err_free_device:
++ iio_device_free(indio_dev);
++err_ret:
++ return err;
++}
++
++static int __devexit tiadc_remove(struct platform_device *pdev)
++{
++ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
++
++ iio_device_unregister(indio_dev);
++ tiadc_channels_remove(indio_dev);
++
++ iio_device_free(indio_dev);
++
++ return 0;
++}
++
++#ifdef CONFIG_PM
++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;
++ unsigned int idle;
++
++ if (!device_may_wakeup(tscadc_dev->dev)) {
++ idle = tiadc_readl(adc_dev, REG_CTRL);
++ idle &= ~(CNTRLREG_TSCSSENB);
++ tiadc_writel(adc_dev, REG_CTRL, (idle |
++ CNTRLREG_POWERDOWN));
++ }
++
++ return 0;
++}
++
++static int tiadc_resume(struct device *dev)
++{
++ struct iio_dev *indio_dev = dev_get_drvdata(dev);
++ struct tiadc_device *adc_dev = iio_priv(indio_dev);
++ unsigned int restore;
++
++ /* Make sure ADC is powered up */
++ restore = tiadc_readl(adc_dev, REG_CTRL);
++ restore &= ~(CNTRLREG_POWERDOWN);
++ tiadc_writel(adc_dev, REG_CTRL, restore);
++
++ tiadc_step_config(adc_dev);
++
++ return 0;
++}
++
++static const struct dev_pm_ops tiadc_pm_ops = {
++ .suspend = tiadc_suspend,
++ .resume = tiadc_resume,
++};
++#define TIADC_PM_OPS (&tiadc_pm_ops)
++#else
++#define TIADC_PM_OPS NULL
++#endif
++
++static struct platform_driver tiadc_driver = {
++ .driver = {
++ .name = "tiadc",
++ .owner = THIS_MODULE,
++ .pm = TIADC_PM_OPS,
++ },
++ .probe = tiadc_probe,
++ .remove = __devexit_p(tiadc_remove),
++};
++
++module_platform_driver(tiadc_driver);
++
++MODULE_DESCRIPTION("TI ADC controller driver");
++MODULE_AUTHOR("Rachna Patil <rachna@ti.com>");
++MODULE_LICENSE("GPL");
+diff --git a/drivers/mfd/ti_am335x_tscadc.c b/drivers/mfd/ti_am335x_tscadc.c
+index d812be4..e947dd8 100644
+--- a/drivers/mfd/ti_am335x_tscadc.c
++++ b/drivers/mfd/ti_am335x_tscadc.c
+@@ -25,6 +25,7 @@
+
+ #include <linux/mfd/ti_am335x_tscadc.h>
+ #include <linux/input/ti_am335x_tsc.h>
++#include <linux/platform_data/ti_am335x_adc.h>
+
+ static unsigned int tscadc_readl(struct ti_tscadc_dev *tsadc, unsigned int reg)
+ {
+@@ -67,14 +68,23 @@ static int __devinit ti_tscadc_probe(struct platform_device *pdev)
+ int irq;
+ int err, ctrl;
+ int clk_value, clock_rate;
+- int tsc_wires;
++ int tsc_wires, adc_channels = 0, total_channels;
+
+ if (!pdata) {
+ dev_err(&pdev->dev, "Could not find platform data\n");
+ return -EINVAL;
+ }
+
++ if (pdata->adc_init)
++ adc_channels = pdata->adc_init->adc_channels;
++
+ tsc_wires = pdata->tsc_init->wires;
++ total_channels = tsc_wires + adc_channels;
++
++ if (total_channels > 8) {
++ dev_err(&pdev->dev, "Number of i/p channels more than 8\n");
++ return -EINVAL;
++ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+@@ -172,6 +182,12 @@ static int __devinit ti_tscadc_probe(struct platform_device *pdev)
+ cell->platform_data = tscadc;
+ cell->pdata_size = sizeof(*tscadc);
+
++ /* ADC Cell */
++ cell = &tscadc->cells[ADC_CELL];
++ cell->name = "tiadc";
++ cell->platform_data = tscadc;
++ cell->pdata_size = sizeof(*tscadc);
++
+ err = mfd_add_devices(&pdev->dev, pdev->id, tscadc->cells,
+ TSCADC_CELLS, NULL, 0, NULL);
+ if (err < 0)
+diff --git a/include/linux/mfd/ti_am335x_tscadc.h b/include/linux/mfd/ti_am335x_tscadc.h
+index fc18b2e..c79ad5d 100644
+--- a/include/linux/mfd/ti_am335x_tscadc.h
++++ b/include/linux/mfd/ti_am335x_tscadc.h
+@@ -120,15 +120,19 @@
+
+ #define ADC_CLK 3000000
+ #define MAX_CLK_DIV 7
++#define TOTAL_STEPS 16
++#define TOTAL_CHANNELS 8
+
+-#define TSCADC_CELLS 1
++#define TSCADC_CELLS 2
+
+ enum tscadc_cells {
+ TSC_CELL,
++ ADC_CELL,
+ };
+
+ struct mfd_tscadc_board {
+ struct tsc_data *tsc_init;
++ struct adc_data *adc_init;
+ };
+
+ struct ti_tscadc_dev {
+@@ -140,6 +144,9 @@ struct ti_tscadc_dev {
+
+ /* tsc device */
+ struct titsc *tsc;
++
++ /* adc device */
++ struct adc_device *adc;
+ };
+
+ #endif
+diff --git a/include/linux/platform_data/ti_am335x_adc.h b/include/linux/platform_data/ti_am335x_adc.h
+new file mode 100644
+index 0000000..e41d583
+--- /dev/null
++++ b/include/linux/platform_data/ti_am335x_adc.h
+@@ -0,0 +1,14 @@
++#ifndef __LINUX_TI_AM335X_ADC_H
++#define __LINUX_TI_AM335X_ADC_H
++
++/**
++ * struct adc_data ADC Input information
++ * @adc_channels: Number of analog inputs
++ * available for ADC.
++ */
++
++struct adc_data {
++ unsigned int adc_channels;
++};
++
++#endif