This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Am4378 ADC1 如何设定为默认单端模式?

Other Parts Discussed in Thread: AM4372, SYSCONFIG, DRV2667

目前使用SDK版本03.03.00.04安装了0001-HACK-Add-ADC1-support-to-the-AM437x-GP-EVM.patch,重新编译内核后发现ADC1的8个口默认配置为差分模式,如何修改成单端模式。

  • 请到ADC1驱动源码里找到STEPCONFIGx寄存器配置DIFF_CNTRL位为0 -> Single Ended,重新编译内核。
  • /* Additional StepConfigs for MAGADC IP */
    #define STEPCONFIG_GAIN_CTRL4_MASK (0x30000000)
    #define STEPCONFIG_GAIN_CTRL3_MASK (0x600)
    #define STEPCONFIG_GAIN_CTRL2_MASK (0x180)
    #define STEPCONFIG_GAIN_CTRL1_MASK (0x60)
    #define STEPCONFIG_THRES_CMP_EN ((0x1) << 11)
    #define STEPCONFIG_THRES_PTR_MASK (0xC0000000)
    #define STEPCONFIG_THRES_PTR(val) ((val) << 30)
    #define STEPCONFIG_THRESREG2_TRIGGER STEPCONFIG_THRES_PTR(2)
    #define STEPCONFIG_THRESREG3_TRIGGER STEPCONFIG_THRES_PTR(3)
    #define STEPCONFIG_THRESREG4_TRIGGER STEPCONFIG_THRES_PTR(4)
    #define STEPCONFIG_DIFFCTRL(val) ((val) << 25)
    #define STEPCONFIG_DIFFCTRL_SELC STEPCONFIG_DIFFCTRL(1)
    #define STEPCONFIG_MODE_HWSYNCCONT STEPCONFIG_MODE(3)
    #define STEPCONFIG_MODE_SWSYNCCONT STEPCONFIG_MODE(1)
    #define STEPCONFIG_RFM_ADCREFM ((3) << 23)
    #define STEPCONFIG_RFP_VDD (0)
    #define STEPCONFIG_AVG_4 STEPCONFIG_AVG(2)



    是否是将STEPCONFIG_DIFFCTRL(1) 中 1改为0
  • 已经试过改成0, 0x0,0xFFFFFFFE都没有用,前置放大器已经设定为bypass,整个patch如下,不知道是哪里设置问题。
    From c5a5e6ab95668af11c33bbc08a685233be8f84ba Mon Sep 17 00:00:00 2001
    From: Jason Reeder <jreeder@ti.com>
    Date: Tue, 17 Jan 2017 14:17:06 -0600
    Subject: [PATCH] [HACK] Add ADC1 support to the AM437x GP EVM

    This commit is a hack that adds ADC1 support to the AM437x
    GP EVM. This should patch directly on the Linux Processor
    SDK v3.2.0.5.

    Official support for ADC1 will be added in a later version
    of the Linux Processor SDK.

    Signed-off-by: Jason Reeder <jreeder@ti.com>
    ---
    .../bindings/input/magnetic-stripes/ti-mag-adc.txt | 34 +
    arch/arm/boot/dts/am4372.dtsi | 26 +-
    arch/arm/boot/dts/am437x-gp-evm.dts | 12 +
    arch/arm/boot/dts/am43xx-clocks.dtsi | 8 +
    arch/arm/mach-omap2/omap_hwmod_43xx_data.c | 38 +
    arch/arm/mach-omap2/prcm43xx.h | 1 +
    drivers/clk/ti/clk-43xx.c | 1 +
    drivers/iio/adc/ti_am335x_adc.c | 119 +--
    drivers/input/misc/Kconfig | 6 +
    drivers/input/misc/Makefile | 1 +
    drivers/input/misc/ti_magadc.c | 936 +++++++++++++++++++++
    drivers/input/touchscreen/ti_am335x_tsc.c | 152 ++--
    drivers/mfd/ti_am335x_tscadc.c | 260 ++++--
    include/linux/mfd/ti_am335x_tscadc.h | 91 +-
    include/linux/ti_magadc.h | 111 +++
    include/uapi/linux/Kbuild | 1 +
    16 files changed, 1553 insertions(+), 244 deletions(-)
    create mode 100644 Documentation/devicetree/bindings/input/magnetic-stripes/ti-mag-adc.txt
    create mode 100644 drivers/input/misc/ti_magadc.c
    create mode 100644 include/linux/ti_magadc.h

    diff --git a/Documentation/devicetree/bindings/input/magnetic-stripes/ti-mag-adc.txt b/Documentation/devicetree/bindings/input/magnetic-stripes/ti-mag-adc.txt
    new file mode 100644
    index 0000000..7e17f74
    --- /dev/null
    +++ b/Documentation/devicetree/bindings/input/magnetic-stripes/ti-mag-adc.txt
    @@ -0,0 +1,34 @@
    +* TI - MAG ADC (Magnetic Stripe Reader/ADC)
    +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    +
    +Required properties:
    +- child "mag"
    + ti,tracks: Refers to magnetic stripe's tracks.1/2/3 number of tracks
    + of the Magnetic stripe read support on the platform is
    + provided using the variable.
    +- child "adc"
    + ti,adc-channels: List of analog inputs available for ADC.
    + AIN0 = 0, AIN1 = 1 and so on till AIN7 = 7.
    +
    +Example:
    + magadc: magadc@4834c000 {
    + compatible = "ti,am4372-magadc";
    + reg = <0x4834c000 0x2000>;
    + dmas = <&edma 54>;
    + dma-names = "rx";
    + interrupts = <GIC_SPI 115 IRQ_TYPE_LEVEL_HIGH>;
    + ti,hwmods = "adc_mag";
    + clocks = <&adc_mag_fck>;
    + clock-names = "fck";
    + status = "disabled";
    +
    + mag {
    + ti,tracks = <3>;
    + compatible = "ti,am4372-mag";
    + };
    +
    + adc {
    + ti,adc-channels = <2>;
    + compatible ="ti,am4372-adc";
    + };
    + };
    diff --git a/arch/arm/boot/dts/am4372.dtsi b/arch/arm/boot/dts/am4372.dtsi
    index 0f296c1..df0e4a8 100644
    --- a/arch/arm/boot/dts/am4372.dtsi
    +++ b/arch/arm/boot/dts/am4372.dtsi
    @@ -890,7 +890,7 @@
    };

    tscadc: tscadc@44e0d000 {
    - compatible = "ti,am3359-tscadc";
    + compatible = "ti,am4372-tscadc","ti,am3359-tscadc";
    reg = <0x44e0d000 0x1000>;
    ti,hwmods = "adc_tsc";
    interrupts = <GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH>;
    @@ -899,16 +899,36 @@
    status = "disabled";

    tsc {
    - compatible = "ti,am3359-tsc";
    + compatible = "ti,am4372-tsc","ti,am3359-tsc";
    };

    adc {
    #io-channel-cells = <1>;
    - compatible = "ti,am3359-adc";
    + compatible = "ti,am4372-adc","ti,am3359-adc";
    };

    };

    + magadc: magadc@4834c000 {
    + compatible = "ti,am4372-magadc";
    + reg = <0x4834c000 0x2000>;
    + ti,hwmods = "adc_mag";
    + dmas = <&edma 54>;
    + dma-names = "rx";
    + interrupts = <GIC_SPI 115 IRQ_TYPE_LEVEL_HIGH>;
    + clocks = <&adc_mag_fck>;
    + clock-names = "fck";
    + status = "disabled";
    +
    + mag {
    + compatible = "ti,am4372-mag";
    + };
    +
    + adc {
    + compatible ="ti,am4372-adc";
    + };
    + };
    +
    sham: sham@53100000 {
    compatible = "ti,omap5-sham";
    ti,hwmods = "sham";
    diff --git a/arch/arm/boot/dts/am437x-gp-evm.dts b/arch/arm/boot/dts/am437x-gp-evm.dts
    index 615f580..5b1341c 100644
    --- a/arch/arm/boot/dts/am437x-gp-evm.dts
    +++ b/arch/arm/boot/dts/am437x-gp-evm.dts
    @@ -841,6 +841,18 @@
    };
    };

    +&magadc {
    + status = "okay";
    +
    + mag {
    + ti,tracks = <0>;
    + };
    +
    + adc {
    + ti,adc-channels = <0 1 2 3 4 5 6 7>;
    + };
    +};
    +
    &ecap0 {
    status = "okay";
    pinctrl-names = "default", "sleep";
    diff --git a/arch/arm/boot/dts/am43xx-clocks.dtsi b/arch/arm/boot/dts/am43xx-clocks.dtsi
    index 627746a..6093214 100644
    --- a/arch/arm/boot/dts/am43xx-clocks.dtsi
    +++ b/arch/arm/boot/dts/am43xx-clocks.dtsi
    @@ -40,6 +40,14 @@
    clock-div = <1>;
    };

    + adc_mag_fck: adc_mag_fck {
    + #clock-cells = <0>;
    + compatible = "fixed-factor-clock";
    + clocks = <&sys_clkin_ck>;
    + clock-mult = <1>;
    + clock-div = <1>;
    + };
    +
    dcan0_fck: dcan0_fck {
    #clock-cells = <0>;
    compatible = "fixed-factor-clock";
    diff --git a/arch/arm/mach-omap2/omap_hwmod_43xx_data.c b/arch/arm/mach-omap2/omap_hwmod_43xx_data.c
    index 2a58010..0174238 100644
    --- a/arch/arm/mach-omap2/omap_hwmod_43xx_data.c
    +++ b/arch/arm/mach-omap2/omap_hwmod_43xx_data.c
    @@ -442,6 +442,36 @@ static struct omap_hwmod am43xx_adc_tsc_hwmod = {
    },
    };

    +/*
    + * 'adc/mag' class
    + * MagCard Controller (Anolog-To-Digital Converter)
    + */
    +static struct omap_hwmod_class_sysconfig am43xx_adc_mag_sysc = {
    + .rev_offs = 0x00,
    + .sysc_offs = 0x10,
    + .sysc_flags = SYSC_HAS_SIDLEMODE,
    + .idlemodes = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART),
    + .sysc_fields = &omap_hwmod_sysc_type2,
    +};
    +
    +static struct omap_hwmod_class am43xx_adc_mag_hwmod_class = {
    + .name = "adc_mag",
    + .sysc = &am43xx_adc_mag_sysc,
    +};
    +
    +static struct omap_hwmod am43xx_adc_mag_hwmod = {
    + .name = "adc_mag",
    + .class = &am43xx_adc_mag_hwmod_class,
    + .clkdm_name = "l3s_clkdm",
    + .main_clk = "adc_mag_fck",
    + .prcm = {
    + .omap4 = {
    + .clkctrl_offs = AM43XX_CM_PER_MAGADC_CLKCTRL_OFFSET,
    + .modulemode = MODULEMODE_SWCTRL,
    + },
    + },
    +};
    +
    static struct omap_hwmod_class_sysconfig am43xx_des_sysc = {
    .rev_offs = 0x30,
    .sysc_offs = 0x34,
    @@ -678,6 +708,13 @@ static struct omap_hwmod_ocp_if am43xx_l4_wkup__adc_tsc = {
    .user = OCP_USER_MPU,
    };

    +static struct omap_hwmod_ocp_if am43xx_l4_ls__adc_mag = {
    + .master = &am33xx_l4_ls_hwmod,
    + .slave = &am43xx_adc_mag_hwmod,
    + .clk = "dpll_core_m4_div2_ck",
    + .user = OCP_USER_MPU,
    +};
    +
    static struct omap_hwmod_ocp_if am43xx_l4_hs__cpgmac0 = {
    .master = &am43xx_l4_hs_hwmod,
    .slave = &am33xx_cpgmac0_hwmod,
    @@ -997,6 +1034,7 @@ static struct omap_hwmod_ocp_if *am43xx_hwmod_ocp_ifs[] __initdata = {
    &am43xx_l3__vpfe1,
    &am43xx_l4_ls__vpfe0,
    &am43xx_l4_ls__vpfe1,
    + &am43xx_l4_ls__adc_mag,
    NULL,
    };

    diff --git a/arch/arm/mach-omap2/prcm43xx.h b/arch/arm/mach-omap2/prcm43xx.h
    index e2ad14e..27cd9d0 100644
    --- a/arch/arm/mach-omap2/prcm43xx.h
    +++ b/arch/arm/mach-omap2/prcm43xx.h
    @@ -117,6 +117,7 @@
    #define AM43XX_CM_PER_MMC2_CLKCTRL_OFFSET 0x0248
    #define AM43XX_CM_PER_QSPI_CLKCTRL_OFFSET 0x0258
    #define AM43XX_CM_PER_GPMC_CLKCTRL_OFFSET 0x0220
    +#define AM43XX_CM_PER_MAGADC_CLKCTRL_OFFSET 0x0230
    #define AM43XX_CM_PER_MCASP0_CLKCTRL_OFFSET 0x0238
    #define AM43XX_CM_PER_MCASP1_CLKCTRL_OFFSET 0x0240
    #define AM43XX_CM_PER_L4LS_CLKCTRL_OFFSET 0x0420
    diff --git a/drivers/clk/ti/clk-43xx.c b/drivers/clk/ti/clk-43xx.c
    index 3f157a4..d7dca9b 100644
    --- a/drivers/clk/ti/clk-43xx.c
    +++ b/drivers/clk/ti/clk-43xx.c
    @@ -47,6 +47,7 @@ static struct ti_dt_clk am43xx_clks[] = {
    DT_CLK(NULL, "dpll_per_m2_div4_wkupdm_ck", "dpll_per_m2_div4_wkupdm_ck"),
    DT_CLK(NULL, "dpll_per_m2_div4_ck", "dpll_per_m2_div4_ck"),
    DT_CLK(NULL, "adc_tsc_fck", "adc_tsc_fck"),
    + DT_CLK(NULL, "adc_mag_fck", "adc_mag_fck"),
    DT_CLK(NULL, "clkdiv32k_ck", "clkdiv32k_ck"),
    DT_CLK(NULL, "clkdiv32k_ick", "clkdiv32k_ick"),
    DT_CLK(NULL, "dcan0_fck", "dcan0_fck"),
    diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c
    index 0470fc8..ecb2294 100644
    --- a/drivers/iio/adc/ti_am335x_adc.c
    +++ b/drivers/iio/adc/ti_am335x_adc.c
    @@ -13,6 +13,7 @@
    * GNU General Public License for more details.
    */

    +#include <linux/init.h>
    #include <linux/kernel.h>
    #include <linux/err.h>
    #include <linux/module.h>
    @@ -32,13 +33,11 @@

    struct tiadc_device {
    struct ti_tscadc_dev *mfd_tscadc;
    - struct mutex fifo1_lock; /* to protect fifo access */
    int channels;
    u8 channel_line[8];
    u8 channel_step[8];
    int buffer_en_ch_steps;
    u16 data[8];
    - u32 open_delay[8], sample_delay[8], step_avg[8];
    };

    static unsigned int tiadc_readl(struct tiadc_device *adc, unsigned int reg)
    @@ -87,61 +86,34 @@ static u32 get_adc_step_bit(struct tiadc_device *adc_dev, int chan)
    static void tiadc_step_config(struct iio_dev *indio_dev)
    {
    struct tiadc_device *adc_dev = iio_priv(indio_dev);
    - struct device *dev = adc_dev->mfd_tscadc->dev;
    unsigned int stepconfig;
    - int i, steps = 0;
    + int i, steps;

    /*
    * There are 16 configurable steps and 8 analog input
    * lines available which are shared between Touchscreen and ADC.
    *
    - * Steps forwards i.e. from 0 towards 16 are used by 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 = 0;
    + if (iio_buffer_enabled(indio_dev))
    + stepconfig = STEPCONFIG_AVG_16 | STEPCONFIG_FIFO1
    + | STEPCONFIG_MODE_SWCNT;
    + else
    + stepconfig = STEPCONFIG_AVG_16 | STEPCONFIG_FIFO1;

    for (i = 0; i < adc_dev->channels; i++) {
    int chan;

    chan = adc_dev->channel_line[i];
    -
    - if (adc_dev->step_avg[i] > STEPCONFIG_AVG_16) {
    - dev_warn(dev, "chan %d step_avg truncating to %d\n",
    - chan, STEPCONFIG_AVG_16);
    - adc_dev->step_avg[i] = STEPCONFIG_AVG_16;
    - }
    -
    - if (adc_dev->step_avg[i])
    - stepconfig =
    - STEPCONFIG_AVG(ffs(adc_dev->step_avg[i]) - 1) |
    - STEPCONFIG_FIFO1;
    - else
    - stepconfig = STEPCONFIG_FIFO1;
    -
    - if (iio_buffer_enabled(indio_dev))
    - stepconfig |= STEPCONFIG_MODE_SWCNT;
    -
    tiadc_writel(adc_dev, REG_STEPCONFIG(steps),
    stepconfig | STEPCONFIG_INP(chan));
    -
    - if (adc_dev->open_delay[i] > STEPDELAY_OPEN_MASK) {
    - dev_warn(dev, "chan %d open delay truncating to 0x3FFFF\n",
    - chan);
    - adc_dev->open_delay[i] = STEPDELAY_OPEN_MASK;
    - }
    -
    - if (adc_dev->sample_delay[i] > 0xFF) {
    - dev_warn(dev, "chan %d sample delay truncating to 0xFF\n",
    - chan);
    - adc_dev->sample_delay[i] = 0xFF;
    - }
    -
    tiadc_writel(adc_dev, REG_STEPDELAY(steps),
    - STEPDELAY_OPEN(adc_dev->open_delay[i]) |
    - STEPDELAY_SAMPLE(adc_dev->sample_delay[i]));
    -
    + STEPCONFIG_OPENDLY);
    adc_dev->channel_step[i] = steps;
    steps++;
    }
    @@ -218,11 +190,12 @@ static int tiadc_buffer_preenable(struct iio_dev *indio_dev)
    static int tiadc_buffer_postenable(struct iio_dev *indio_dev)
    {
    struct tiadc_device *adc_dev = iio_priv(indio_dev);
    + struct iio_buffer *buffer = indio_dev->buffer;
    unsigned int enb = 0;
    u8 bit;

    tiadc_step_config(indio_dev);
    - for_each_set_bit(bit, indio_dev->active_scan_mask, adc_dev->channels)
    + for_each_set_bit(bit, buffer->scan_mask, adc_dev->channels)
    enb |= (get_adc_step_bit(adc_dev, bit) << 1);
    adc_dev->buffer_en_ch_steps = enb;

    @@ -290,10 +263,15 @@ static int tiadc_iio_buffered_hardware_setup(struct iio_dev *indio_dev,
    goto error_kfifo_free;

    indio_dev->setup_ops = setup_ops;
    - indio_dev->modes |= INDIO_BUFFER_SOFTWARE;
    + indio_dev->modes |= INDIO_BUFFER_HARDWARE;
    +
    + if (ret)
    + goto error_free_irq;

    return 0;

    +error_free_irq:
    + free_irq(irq, indio_dev);
    error_kfifo_free:
    iio_kfifo_free(indio_dev->buffer);
    return ret;
    @@ -361,7 +339,6 @@ 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 ret = IIO_VAL_INT;
    int i, map_val;
    unsigned int fifo1count, read, stepid;
    bool found = false;
    @@ -375,14 +352,13 @@ static int tiadc_read_raw(struct iio_dev *indio_dev,
    if (!step_en)
    return -EINVAL;

    - mutex_lock(&adc_dev->fifo1_lock);
    fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT);
    while (fifo1count--)
    tiadc_readl(adc_dev, REG_FIFO1);

    am335x_tsc_se_set_once(adc_dev->mfd_tscadc, step_en);

    - timeout = jiffies + msecs_to_jiffies
    + timeout = jiffies + usecs_to_jiffies
    (IDLE_TIMEOUT * adc_dev->channels);
    /* Wait for Fifo threshold interrupt */
    while (1) {
    @@ -392,8 +368,7 @@ static int tiadc_read_raw(struct iio_dev *indio_dev,

    if (time_after(jiffies, timeout)) {
    am335x_tsc_se_adc_done(adc_dev->mfd_tscadc);
    - ret = -EAGAIN;
    - goto err_unlock;
    + return -EAGAIN;
    }
    }
    map_val = adc_dev->channel_step[chan->scan_index];
    @@ -419,11 +394,8 @@ static int tiadc_read_raw(struct iio_dev *indio_dev,
    am335x_tsc_se_adc_done(adc_dev->mfd_tscadc);

    if (found == false)
    - ret = -EBUSY;
    -
    -err_unlock:
    - mutex_unlock(&adc_dev->fifo1_lock);
    - return ret;
    + return -EBUSY;
    + return IIO_VAL_INT;
    }

    static const struct iio_info tiadc_info = {
    @@ -431,43 +403,16 @@ static const struct iio_info tiadc_info = {
    .driver_module = THIS_MODULE,
    };

    -static int tiadc_parse_dt(struct platform_device *pdev,
    - struct tiadc_device *adc_dev)
    -{
    - struct device_node *node = pdev->dev.of_node;
    - struct property *prop;
    - const __be32 *cur;
    - int channels = 0;
    - u32 val;
    -
    - of_property_for_each_u32(node, "ti,adc-channels", prop, cur, val) {
    - adc_dev->channel_line[channels] = val;
    -
    - /* Set Default values for optional DT parameters */
    - adc_dev->open_delay[channels] = STEPCONFIG_OPENDLY;
    - adc_dev->sample_delay[channels] = STEPCONFIG_SAMPLEDLY;
    - adc_dev->step_avg[channels] = 16;
    -
    - channels++;
    - }
    -
    - of_property_read_u32_array(node, "ti,chan-step-avg",
    - adc_dev->step_avg, channels);
    - of_property_read_u32_array(node, "ti,chan-step-opendelay",
    - adc_dev->open_delay, channels);
    - of_property_read_u32_array(node, "ti,chan-step-sampledelay",
    - adc_dev->sample_delay, channels);
    -
    - adc_dev->channels = channels;
    - return 0;
    -}
    -
    static int tiadc_probe(struct platform_device *pdev)
    {
    struct iio_dev *indio_dev;
    struct tiadc_device *adc_dev;
    struct device_node *node = pdev->dev.of_node;
    + struct property *prop;
    + const __be32 *cur;
    int err;
    + u32 val;
    + int channels = 0;

    if (!node) {
    dev_err(&pdev->dev, "Could not find valid DT data.\n");
    @@ -483,7 +428,12 @@ static int tiadc_probe(struct platform_device *pdev)
    adc_dev = iio_priv(indio_dev);

    adc_dev->mfd_tscadc = ti_tscadc_dev_get(pdev);
    - tiadc_parse_dt(pdev, adc_dev);
    +
    + 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);
    @@ -492,8 +442,6 @@ static int tiadc_probe(struct platform_device *pdev)

    tiadc_step_config(indio_dev);
    tiadc_writel(adc_dev, REG_FIFO1THR, FIFO1_THRESHOLD);
    - mutex_init(&adc_dev->fifo1_lock);
    -
    err = tiadc_channel_init(indio_dev, adc_dev->channels);
    if (err < 0)
    return err;
    @@ -513,7 +461,6 @@ static int tiadc_probe(struct platform_device *pdev)
    goto err_buffer_unregister;

    platform_set_drvdata(pdev, indio_dev);
    -
    return 0;

    err_buffer_unregister:
    @@ -586,6 +533,7 @@ static const struct dev_pm_ops tiadc_pm_ops = {

    static const struct of_device_id ti_adc_dt_ids[] = {
    { .compatible = "ti,am3359-adc", },
    + { .compatible = "ti,am4372-adc", },
    { }
    };
    MODULE_DEVICE_TABLE(of, ti_adc_dt_ids);
    @@ -593,6 +541,7 @@ MODULE_DEVICE_TABLE(of, ti_adc_dt_ids);
    static struct platform_driver tiadc_driver = {
    .driver = {
    .name = "TI-am335x-adc",
    + .owner = THIS_MODULE,
    .pm = TIADC_PM_OPS,
    .of_match_table = ti_adc_dt_ids,
    },
    diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
    index 4134c11..c2d6791 100644
    --- a/drivers/input/misc/Kconfig
    +++ b/drivers/input/misc/Kconfig
    @@ -807,4 +807,10 @@ config INPUT_DRV2667_HAPTICS
    To compile this driver as a module, choose M here: the
    module will be called drv2667-haptics.

    +config INPUT_MAG_ADC
    + tristate "Magnetic Card Reader Driver for AM43xx"
    + depends on SOC_AM43XX
    + help
    + Say Y here if you want to enable MSR driver for AM43XX
    +
    endif
    diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
    index 0357a08..f65bc37 100644
    --- a/drivers/input/misc/Makefile
    +++ b/drivers/input/misc/Makefile
    @@ -75,3 +75,4 @@ obj-$(CONFIG_INPUT_WM831X_ON) += wm831x-on.o
    obj-$(CONFIG_INPUT_XEN_KBDDEV_FRONTEND) += xen-kbdfront.o
    obj-$(CONFIG_INPUT_YEALINK) += yealink.o
    obj-$(CONFIG_INPUT_IDEAPAD_SLIDEBAR) += ideapad_slidebar.o
    +obj-$(CONFIG_INPUT_MAG_ADC) += ti_magadc.o
    diff --git a/drivers/input/misc/ti_magadc.c b/drivers/input/misc/ti_magadc.c
    new file mode 100644
    index 0000000..37d6339
    --- /dev/null
    +++ b/drivers/input/misc/ti_magadc.c
    @@ -0,0 +1,936 @@
    +/*
    + * TI Magnetic Stripe Reader driver
    + *
    + * This is a driver for TI's MAGADC IP.
    + *
    + * Copyright (C) 2013 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/firmware.h>
    +#include <linux/init.h>
    +#include <linux/kernel.h>
    +#include <linux/module.h>
    +#include <linux/input.h>
    +#include <linux/dma-mapping.h>
    +#include <linux/pm_runtime.h>
    +#include <linux/dmaengine.h>
    +#include <linux/edma.h>
    +#include <linux/interrupt.h>
    +#include <linux/clk.h>
    +#include <linux/platform_device.h>
    +#include <linux/of_device.h>
    +#include <linux/mfd/ti_am335x_tscadc.h>
    +#include <linux/uaccess.h>
    +#include <linux/ctype.h>
    +#include <linux/types.h>
    +#include <linux/export.h>
    +#include <linux/slab.h>
    +#include <linux/string.h>
    +#include <linux/miscdevice.h>
    +#include <linux/ti_magadc.h>
    +
    +struct timag {
    + struct input_dev *input;
    + struct ti_tscadc_dev *mfd_magadc;
    + struct platform_device *pdev;
    + struct timag_track mag_track;
    + struct work_struct decode_work;
    + spinlock_t lock;
    + atomic_t shutting_down;
    + int dma_rx_chnum;
    + struct dma_chan *dma_rx;
    + long int phy_base;
    + dma_addr_t dma_address;
    + int len;
    + int irq;
    + unsigned int tracks;
    +};
    +/*
    + * Miscellaneous device structre
    + */
    +static struct miscdevice magadc_cdev;
    +
    +/*
    + * variable for storing pid of Application
    + */
    +int u_pid = 0;
    +/* Variable used for validating number of applications opening the node file */
    +static int node_open;
    +/*
    + * Local variables used for interaction with application
    + */
    +unsigned char *var;
    +struct timag_track *trackData = NULL;
    +struct timag_track *tData = NULL;
    +unsigned int roaddr = 0;
    +unsigned int rwaddr = 0;
    +unsigned int phys_addr;
    +unsigned int readCnt = 1;
    +unsigned int addrStored = 0;
    +unsigned char rwAddrStr[9] = { '\0'};
    +unsigned char roAddrStr[9] = { '\0'};
    +static int timag_dma_config(struct timag *mag_dev,
    + struct platform_device *pdev);
    +/*
    + * Read magadc register
    + */
    +static inline unsigned int timag_readl(struct timag *mag_dev, unsigned int reg)
    +{
    + return readl(mag_dev->mfd_magadc->tscadc_base + reg);
    +}
    +
    +/*
    + * Write the data to magadc register
    + */
    +static inline void timag_writel(struct timag *mag_dev, unsigned int reg,
    + unsigned int val)
    +{
    + writel(val, mag_dev->mfd_magadc->tscadc_base + reg);
    +}
    +
    +/*
    + * Enable the step
    + */
    +void timag_step_enable(struct timag *mag_dev, int stepid, bool enable)
    +{
    + int val;
    +
    + val = timag_readl(mag_dev, REG_SE);
    + if (enable)
    + val |= (0x1 << (stepid+1));
    + else
    + val &= ~(0x1 << (stepid+1));
    + timag_writel(mag_dev, REG_SE, val);
    +}
    +
    +/*
    + * Configure the magadc steps
    + */
    +static void timag_step_config(struct timag *mag_dev)
    +{
    + unsigned int analog_input;
    + unsigned int total_tracks;
    + unsigned int config;
    + unsigned int inm_val;
    + unsigned int inp_val;
    + int i;
    +
    + total_tracks = mag_dev->tracks;
    +
    + for (i = 0; i < total_tracks; i++) {
    + analog_input = i;
    + if (i > 0)
    + analog_input = (analog_input * 2);
    +
    + inm_val = analog_input + 1;
    + inp_val = analog_input;
    +
    + /* STEP delay config : sample and open delay */
    + config = 0;
    + config = MAGADC_SAMPLEDELAY | MAGADC_OPENDELAY;
    + timag_writel(mag_dev, REG_STEPDELAY(i), config);
    +
    + /* Setting up other configs for each STEP */
    + config = 0;
    + config = (STEPCONFIG_DIFFCTRL_SELC |
    + STEPCONFIG_AVG_4 |
    + STEPCONFIG_RFM_ADCREFM |
    + STEPCONFIG_INM(inm_val) |
    + STEPCONFIG_INP(inp_val));
    +
    + /* GAIN is set to 12 , i.e 00 */
    + config &= ~STEPCONFIG_GAIN_CTRL4_MASK;
    + config &= ~STEPCONFIG_GAIN_CTRL3_MASK;
    + config &= ~STEPCONFIG_GAIN_CTRL2_MASK;
    + config &= ~STEPCONFIG_GAIN_CTRL1_MASK;
    +
    + /*
    + * The first step is SW sync continuous and all
    + * other steps are hardware sync
    + */
    + config &= ~STEPCONFIG_MODE_MASK;
    +
    + /* only the track1 is software sync continuous
    + * all other tracks are hardware sync continuous.
    + */
    + if (i == MAGADC_TRACK1) {
    + config |= (STEPCONFIG_MODE_SWSYNCCONT |
    + STEPCONFIG_THRES_CMP_EN);
    + } else {
    + config = config | STEPCONFIG_MODE_HWSYNCCONT;
    + config &= ~STEPCONFIG_THRES_CMP_EN;
    + }
    + /* Select FIFO0 for each step */
    + config &= ~STEPCONFIG_FIFO1;
    +
    + /* Trigger using swipe threshold reg1 for all steps */
    + config &= ~STEPCONFIG_THRES_PTR_MASK;
    + timag_writel(mag_dev, REG_STEPCONFIG(i), config);
    + }
    +}
    +
    +/*
    + * Set the swipe threshold value
    + */
    +static int timag_swipethreshold_set(struct timag *mag_dev,
    + enum timag_thresdata type,
    + unsigned int val)
    +{
    + unsigned int thres;
    +
    + switch (type) {
    + case MAGADC_SWIPE_THRESDATA1:
    + thres = timag_readl(mag_dev, REG_SWIPECOMPARE12);
    + thres &= ~SWIPETHRES_DATA1_VAL_MASK;
    + thres |= SWIPETHRES_DATA1_VAL(val);
    + timag_writel(mag_dev, REG_SWIPECOMPARE12, thres);
    + break;
    + case MAGADC_SWIPE_THRESDATA2:
    + thres = timag_readl(mag_dev, REG_SWIPECOMPARE12);
    + thres &= ~SWIPETHRES_DATA2_VAL_MASK;
    + thres |= SWIPETHRES_DATA2_VAL(val);
    + timag_writel(mag_dev, REG_SWIPECOMPARE12, thres);
    + break;
    + case MAGADC_SWIPE_THRESDATA3:
    + thres = timag_readl(mag_dev, REG_SWIPECOMPARE34);
    + thres &= ~SWIPETHRES_DATA3_VAL_MASK;
    + thres |= SWIPETHRES_DATA3_VAL(val);
    + timag_writel(mag_dev, REG_SWIPECOMPARE34, thres);
    + break;
    + case MAGADC_SWIPE_THRESDATA4:
    + thres = timag_readl(mag_dev, REG_SWIPECOMPARE34);
    + thres &= ~SWIPETHRES_DATA4_VAL_MASK;
    + thres |= SWIPETHRES_DATA4_VAL(val);
    + timag_writel(mag_dev, REG_SWIPECOMPARE34, thres);
    + break;
    + default:
    + return -EINVAL;
    + }
    + return 0;
    +}
    +/*
    + * Track the input report
    + */
    +static void timag_track_inputreport(struct timag *mag_dev, u8 stepid)
    +{
    + input_event(mag_dev->input, EV_MSC, MSC_RAW, stepid);
    +}
    +/*
    + * power down the magadc
    + */
    +static void timag_powerdown(struct timag *mag_dev, bool enable)
    +{
    + unsigned int ctrl;
    +
    + ctrl = timag_readl(mag_dev, REG_CTRL);
    + /*
    + * Write '0' to power on the AFE, '1' to power down
    + * The MAGADC should be disabled before this.
    + */
    + if (enable)
    + ctrl &= ~CNTRLREG_POWERDOWN;
    + else
    + ctrl |= CNTRLREG_POWERDOWN;
    +
    + timag_writel(mag_dev, REG_CTRL, ctrl);
    +}
    +
    +/*
    + * power down the pre - amplifier
    + */
    +static void timag_preamp_powerdown(struct timag *mag_dev, bool enable)
    +{
    + unsigned int ctrl;
    +
    + ctrl = timag_readl(mag_dev, REG_CTRL);
    + /*
    + * Write '0' to power on the Preamp, '1' to power down
    + * The MAGADC should be disabled before this.
    + */
    + if (enable)
    + ctrl &= ~CNTRLREG_PREAMP_PWRDOWN;
    + else
    + ctrl |= CNTRLREG_PREAMP_PWRDOWN;
    +
    + timag_writel(mag_dev, REG_CTRL, ctrl);
    +}
    +
    +/*
    + * Set/reset the pre amplifier bypass
    + */
    +static void timag_preamp_bypass(struct timag *mag_dev, bool enable)
    +{
    + unsigned int ctrl;
    +
    + ctrl = timag_readl(mag_dev, REG_CTRL);
    + /*
    + * 0 - Preamp is enabled by default
    + * 1 - Preamp is bypassed.
    + */
    + if (enable)
    + ctrl |= CNTRLREG_PREAMP_BYPASS;
    + else
    + ctrl &= ~CNTRLREG_PREAMP_BYPASS;
    +
    + timag_writel(mag_dev, REG_CTRL, ctrl);
    +}
    +
    +/*
    + * Enable the magadc
    + */
    +static void timag_enable(struct timag *mag_dev, bool enable)
    +{
    + unsigned int ctrl;
    +
    + ctrl = timag_readl(mag_dev, REG_CTRL);
    +
    + /* Write '1' to enable the MAGADC and '0' to disable */
    + if (enable)
    + ctrl |= CNTRLREG_MAGADCENB;
    + else
    + ctrl &= ~CNTRLREG_MAGADCENB;
    +
    + timag_writel(mag_dev, REG_CTRL, ctrl);
    +}
    +
    +/*
    + * Configure the step id
    + */
    +static void timag_stepid_config(struct timag *mag_dev, bool enable)
    +{
    + unsigned int ctrl;
    +
    + ctrl = timag_readl(mag_dev, REG_CTRL);
    +
    + /* Write '1' to enable the MAGADC and '0' to disable */
    + if (enable)
    + ctrl |= CNTRLREG_STEPID;
    + else
    + ctrl &= ~CNTRLREG_STEPID;
    +
    + timag_writel(mag_dev, REG_CTRL, ctrl);
    +}
    +
    +/*
    + * Enable DMA fifo
    + */
    +void timag_dmafifo_enable(struct timag *mag_dev, bool fifosel, bool enable)
    +{
    + unsigned int val;
    +
    + if (enable) {
    + val = timag_readl(mag_dev, REG_DMAENABLESET);
    + val |= (0x1 << fifosel);
    + timag_writel(mag_dev, REG_DMAENABLESET, val);
    + } else {
    + val = timag_readl(mag_dev, REG_DMAENABLECLR);
    + val |= (0x1 << fifosel);
    + timag_writel(mag_dev, REG_DMAENABLECLR, val);
    + }
    +}
    +
    +/*
    + * Release the dma function
    + */
    +static void timag_release_dma(struct device *dev, struct timag *mag_dev)
    +{
    + /* Release RX resources */
    + dmaengine_terminate_all(mag_dev->dma_rx);
    +
    + dma_unmap_single(dev, mag_dev->dma_address,
    + mag_dev->len, DMA_FROM_DEVICE);
    +
    + dma_release_channel(mag_dev->dma_rx);
    + mag_dev->dma_rx = NULL;
    +}
    +/*
    + * Api to request dma
    + */
    +static int timag_request_dma(struct timag *mag_dev,
    + struct platform_device *pdev)
    +{
    + dma_cap_mask_t mask;
    + struct device *sdev = &pdev->dev;
    +
    + dma_cap_zero(mask);
    + dma_cap_set(DMA_SLAVE, mask);
    +
    + mag_dev->dma_rx = dma_request_slave_channel_compat(mask,
    + edma_filter_fn,
    + &mag_dev->dma_rx_chnum,
    + mag_dev->mfd_magadc->dev, "rx");
    + if (!mag_dev->dma_rx) {
    + dev_err(sdev, "request RX DMA channel failed\n");
    + return -ENODEV;
    + }
    +
    + mag_dev->dma_address = dma_map_single(sdev,
    + &mag_dev->mag_track.adc_data[0],
    + mag_dev->len, DMA_FROM_DEVICE);
    + if (dma_mapping_error(sdev, mag_dev->dma_address)) {
    + dev_err(sdev, "dma %u bytes error\n", mag_dev->len);
    + return -EINVAL;
    + }
    + return 0;
    +}
    +
    +/*
    + * Callback function after completion of dma operation
    + */
    +static void timag_dma_rx_callback(void *dev)
    +{
    + struct timag *mag_dev;
    +
    + mag_dev = (struct timag *)dev;
    +
    + if (atomic_read(&mag_dev->shutting_down))
    + return;
    +
    + /* Disable DMA mode of MAG ADC*/
    + timag_dmafifo_enable(mag_dev, 0, false);
    + timag_enable(mag_dev, false);
    +
    + spin_lock(&mag_dev->lock);
    + mag_dev->mag_track.is_swiped = true;
    + mag_dev->mag_track.total_samples = MAX_ADC_DATA_RAW;
    + spin_unlock(&mag_dev->lock);
    +
    + schedule_work(&mag_dev->decode_work);
    +}
    +
    +/*
    + * Configure dma
    + */
    +static int timag_dma_config(struct timag *mag_dev,
    + struct platform_device *pdev)
    +{
    + struct dma_slave_config dma_rx_conf = {
    + .direction = DMA_DEV_TO_MEM,
    + .src_addr = (unsigned long)(mag_dev->phy_base + REG_FIFO0),
    + .src_addr_width = (32 * sizeof(unsigned int)),
    + .src_maxburst = 1,
    + };
    +
    + int dmathreshold = MAGADC_DMA0_THRESHOLD;
    + struct dma_async_tx_descriptor *rxdesc;
    +
    + mag_dev->len = (MAX_ADC_DATA_RAW * sizeof(unsigned int));
    +
    + dmaengine_slave_config(mag_dev->dma_rx, &dma_rx_conf);
    +
    + rxdesc = dmaengine_prep_slave_single(mag_dev->dma_rx,
    + mag_dev->dma_address,
    + mag_dev->len,
    + DMA_DEV_TO_MEM,
    + DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
    + if (!rxdesc)
    + return -EINVAL;
    +
    + rxdesc->callback = timag_dma_rx_callback;
    + rxdesc->callback_param = (void *)mag_dev;
    +
    + dmaengine_submit(rxdesc);
    + dma_async_issue_pending(mag_dev->dma_rx);
    +
    + timag_writel(mag_dev, REG_DMA0REQ, dmathreshold);
    + /* Enable DMA for MAGADC */
    + timag_dmafifo_enable(mag_dev, 0, true);
    +
    + return 0;
    +}
    +
    +/*
    + * initialize the magadc configuration
    + */
    +static void timag_init_config(struct timag *mag_dev)
    +{
    + timag_powerdown(mag_dev, true);
    +
    + timag_preamp_powerdown(mag_dev, true);
    +
    + timag_preamp_bypass(mag_dev, false);
    +
    + timag_swipethreshold_set(mag_dev,
    + MAGADC_SWIPE_THRESDATA1,
    + MAGADC_SWIPETHRES_VAL);
    +
    + /* STEPID is enabled in control register */
    + timag_stepid_config(mag_dev, true);
    + timag_step_config(mag_dev);
    +}
    +
    +/*
    + * magadc interrupt handler
    + */
    +static irqreturn_t timag_irq(int irq, void *dev)
    +{
    + struct timag *mag_dev = dev;
    + bool over_or_under_flow = false;
    + unsigned int status, irqclr = 0;
    +
    + status = timag_readl(mag_dev, REG_IRQSTATUS);
    +
    + if ((status & IRQENB_FIFO0_OVERRUN) ||
    + (status & IRQENB_FIFO0_UNDERFLOW) ||
    + (status & IRQENB_FIFO1_OVERRUN) ||
    + (status & IRQENB_FIFO1_UNDERFLOW)) {
    + timag_enable(mag_dev, false);
    + over_or_under_flow = true;
    + }
    +
    + if (status & IRQENB_END_OF_SEQ)
    + irqclr |= IRQENB_END_OF_SEQ;
    +
    + if (status & IRQENB_FIFO0_OVERRUN)
    + irqclr |= IRQENB_FIFO0_OVERRUN;
    +
    + if (status & IRQENB_FIFO0_UNDERFLOW)
    + irqclr |= IRQENB_FIFO0_UNDERFLOW;
    +
    + if (status & IRQENB_FIFO0THRES)
    + irqclr |= IRQENB_FIFO0THRES;
    +
    + if (status & IRQENB_FIFO1THRES)
    + irqclr |= IRQENB_FIFO1THRES;
    +
    + if (status & IRQENB_FIFO1_OVERRUN)
    + irqclr |= IRQENB_FIFO1_OVERRUN;
    +
    + if (status & IRQENB_FIFO1_OVERRUN)
    + irqclr |= IRQENB_FIFO1_OVERRUN;
    +
    + if (status & IRQENB_FIFO1_UNDERFLOW)
    + irqclr |= IRQENB_FIFO1_UNDERFLOW;
    +
    + if (status & IRQENB_OUT_OF_RANGE)
    + irqclr |= IRQENB_OUT_OF_RANGE;
    +
    + if (status & IRQENB_START_OF_SWIPE)
    + irqclr |= IRQENB_START_OF_SWIPE;
    +
    + if (irqclr) {
    + timag_writel(mag_dev, REG_IRQSTATUS, irqclr);
    + return IRQ_HANDLED;
    + }
    +
    + /* Re-enable the magadc module , in case of overrun/underflow */
    + if (over_or_under_flow)
    + timag_enable(mag_dev, true);
    +
    + return IRQ_NONE;
    +}
    +
    +/*
    + * Parse dt file
    + */
    +static int timag_parse_dt(struct timag *mag_dev)
    +{
    + struct device_node *node = mag_dev->pdev->dev.of_node;
    + int err;
    +
    + if (!node)
    + return -EINVAL;
    +
    + err = of_property_read_u32(node, "ti,tracks", &mag_dev->tracks);
    + if (err < 0) {
    + dev_err(&mag_dev->pdev->dev, "failed to find tracks in DT.\n");
    + return err;
    + }
    + return 0;
    +}
    +
    +/*
    + * Bottom half of work queue
    + */
    +static void timag_decode_work(struct work_struct *work)
    +{
    + int i, ret = 0;
    + struct siginfo info;
    + struct task_struct *tid;
    +
    +
    + struct timag *mag_dev = container_of(work, struct timag,
    + decode_work);
    + struct platform_device *pdev = mag_dev->pdev;
    + const struct device *dev = &pdev->dev;
    +
    + /* If any public application is running then send the signal to app */
    + if (u_pid != 0) {
    + /* find task structure associated with this pid */
    + tid = pid_task(find_vpid(u_pid), PIDTYPE_PID);
    + info.si_signo = MAGADC_SIGID;
    + info.si_code = SI_QUEUE;
    + info.si_int = MAGADC_SIGINT;
    + mag_dev->mag_track.num_tracks = mag_dev->tracks;
    + ret = send_sig_info(MAGADC_SIGID, &info, tid);
    + if (ret < 0) {
    + dev_err(dev, "error sending signal to public app:%d\n",
    + ret);
    + return;
    + }
    + }
    + for (i = 0; i < mag_dev->tracks; i++)
    + timag_track_inputreport(mag_dev, i);
    +
    + /* The end of swipe report is informed to the input subsystem */
    + input_sync(mag_dev->input);
    + /*
    + * After sending signal to user app, Re-configure the DMA to
    + * process another card swipe. Hence this is a
    + * support for multiple swipes.
    + */
    + timag_dma_config(mag_dev, mag_dev->pdev);
    + for (i = 0; i < mag_dev->tracks; i++)
    + timag_step_enable(mag_dev, i, true);
    +
    + /* Enable the Magadc Module */
    + timag_enable(mag_dev, true);
    + return;
    +}
    +static int magadc_open(struct inode *inode, struct file *file)
    +{
    + if (node_open != 0)
    + return -EBUSY;
    + node_open++;
    + try_module_get(THIS_MODULE);
    + return 0;
    +}
    +static int magadc_release(struct inode *inode, struct file *file)
    +{
    + if (node_open <= 0){
    + return -1;
    + }
    + node_open--;
    + module_put(THIS_MODULE);
    + return 0;
    +}
    +
    +static long magadc_ioctl(struct file *file,
    + unsigned int cmd, unsigned long arg)
    +{
    + int ret;
    + void *ptr = NULL;
    + unsigned int phyAddr = 0;
    +
    + ret = copy_from_user(&phyAddr, (unsigned int *)arg, sizeof(int));
    + if (ret != 0)
    + return -EFAULT;
    +
    + if (cmd == MAGADC_STORE_RW_ADDR) {
    + rwaddr = phyAddr;
    + sprintf(rwAddrStr, "%x", rwaddr);
    + ptr = ioremap_cache(rwaddr, sizeof(struct timag_track));
    + tData = (struct timag_track *)ptr;
    +
    + } else if (cmd == MAGADC_STORE_RO_ADDR) {
    + roaddr = phyAddr;
    + sprintf(roAddrStr, "%x", roaddr);
    +
    + } else if (cmd == MAGADC_READ_RW_ADDR) {
    + if (rwaddr == 0)
    + return -1;
    + if (copy_to_user((unsigned int *)arg, &rwaddr,
    + sizeof(unsigned int)))
    + return -EFAULT;
    + } else if (cmd == MAGADC_READ_RO_ADDR) {
    + if (roaddr == 0)
    + return -1;
    + if (copy_to_user((unsigned int *)arg,
    + &roaddr, sizeof(unsigned int)))
    + return -EFAULT;
    + } else if (cmd == MAGADC_COPY_RAW_DATA) {
    + memcpy((unsigned char *)tData,
    + (unsigned char *)trackData,
    + sizeof(struct timag_track));
    + }
    + return 0;
    +}
    +static ssize_t magadc_write(struct file *file,
    + const char __user *buffer, size_t length, loff_t *offset)
    +{
    + int ret;
    +
    + ret = copy_from_user(&u_pid, (int *)buffer, length);
    + if (ret != 0)
    + return -EFAULT;
    + return 0;
    +
    +}
    +static ssize_t magadc_read(struct file *file, char __user *buf,
    + size_t count, loff_t * ppos)
    +{
    + if (copy_to_user(buf, trackData, count)) {
    + return -EFAULT;
    + }
    + return 0;
    +}
    +
    +static const struct file_operations magadc_fops = {
    + .owner = THIS_MODULE,
    + .open = magadc_open,
    + .write = magadc_write,
    + .read = magadc_read,
    + .unlocked_ioctl = magadc_ioctl,
    + .release = magadc_release,
    +};
    +
    +
    +/*
    + * The functions for inserting/removing driver as a module.
    + */
    +static int timag_probe(struct platform_device *pdev)
    +{
    + struct timag *mag_dev;
    + struct input_dev *input_dev;
    + struct ti_tscadc_dev *magadc_dev = ti_tscadc_dev_get(pdev);
    + int err;
    + int irq_mask;
    + int i;
    + int ret;
    +
    + /* Allocate memory for device */
    + mag_dev = devm_kzalloc(&pdev->dev, sizeof(struct timag), GFP_KERNEL);
    + input_dev = input_allocate_device();
    + if (!mag_dev || !input_dev) {
    + dev_err(&pdev->dev, "failed to allocate memory.\n");
    + err = -ENOMEM;
    + goto err_free_mem;
    + }
    +
    + trackData = &(mag_dev->mag_track);
    + magadc_dev->mag = mag_dev;
    + mag_dev->mfd_magadc = magadc_dev;
    + mag_dev->input = input_dev;
    + mag_dev->irq = magadc_dev->magirq;
    + mag_dev->dma_rx_chnum = magadc_dev->dma_rx_chnum;
    + mag_dev->phy_base = magadc_dev->phy_base;
    + mag_dev->len = MAX_ADC_DATA_RAW;
    + mag_dev->pdev = pdev;
    + var = (unsigned char *)mag_dev;
    + input_set_capability(input_dev, EV_MSC, MSC_RAW);
    + err = timag_parse_dt(mag_dev);
    + if (err) {
    + dev_err(&pdev->dev, "Could not find valid DT data.\n");
    + goto err_free_mem;
    + }
    +
    + if (mag_dev->tracks > MAX_NO_OF_TRACKS) {
    + dev_err(&pdev->dev, "Invalid number of magnetic track .\n");
    + goto err_free_mem;
    + }
    +
    + err = devm_request_irq(&pdev->dev, mag_dev->irq, timag_irq,
    + IRQF_TRIGGER_HIGH, pdev->dev.driver->name,
    + mag_dev);
    + if (err) {
    + dev_err(&pdev->dev, "failed to allocate irq.\n");
    + goto err_free_mem;
    + }
    +
    + spin_lock_init(&mag_dev->lock);
    +
    + /* Enable clock */
    + pm_runtime_enable(&pdev->dev);
    + pm_runtime_set_active(&pdev->dev);
    +
    + magadc_cdev.minor = MISC_DYNAMIC_MINOR;
    + magadc_cdev.name = "magadc";
    + magadc_cdev.fops = &magadc_fops;
    + magadc_cdev.parent = &(pdev->dev);
    +
    + ret = misc_register(&magadc_cdev);
    + if (ret)
    + goto err_free_mem;
    +
    + INIT_WORK(&mag_dev->decode_work, timag_decode_work);
    + atomic_set(&mag_dev->shutting_down, false);
    +
    + /* Perform initial configuration and enable required interrupts */
    + timag_init_config(mag_dev);
    +
    + /* Enable the interrupt */
    + irq_mask = (IRQENB_FIFO0_OVERRUN | IRQENB_FIFO0THRES |
    + IRQENB_FIFO1THRES |
    + IRQENB_FIFO0_UNDERFLOW |
    + IRQENB_FIFO1_OVERRUN |
    + IRQENB_FIFO1_UNDERFLOW);
    +
    + timag_writel(mag_dev, REG_IRQENABLE, irq_mask);
    +
    + input_dev->name = "ti-mag";
    + input_dev->dev.parent = &pdev->dev;
    +
    + /* register to the input system */
    + err = input_register_device(input_dev);
    + if (err)
    + goto err_free_irq;
    +
    + platform_set_drvdata(pdev, mag_dev);
    +
    + timag_request_dma(mag_dev, pdev);
    +
    + timag_dma_config(mag_dev, pdev);
    +
    + for (i = 0; i < mag_dev->tracks; i++)
    + timag_step_enable(mag_dev, i, true);
    +
    + /* Enable the MAGModule */
    + timag_enable(mag_dev, true);
    +
    + return 0;
    +
    +err_free_irq:
    + devm_free_irq(&pdev->dev, mag_dev->irq, mag_dev);
    + /* Disable clock */
    + pm_runtime_disable(&pdev->dev);
    + misc_deregister(&magadc_cdev);
    +err_free_mem:
    + if (input_dev)
    + input_free_device(input_dev);
    + if (mag_dev)
    + devm_kfree(&pdev->dev, mag_dev);
    + return err;
    +}
    +
    +/*
    + * Remove driver function
    + */
    +static int timag_remove(struct platform_device *pdev)
    +{
    + struct timag *mag_dev = platform_get_drvdata(pdev);
    +
    + atomic_set(&mag_dev->shutting_down, true);
    + cancel_work_sync(&mag_dev->decode_work);
    +
    + /* Disable the MAGADC Module */
    + timag_enable(mag_dev, false);
    +
    + timag_release_dma(&pdev->dev, mag_dev);
    +
    + devm_free_irq(&pdev->dev, mag_dev->irq, mag_dev);
    +
    + timag_preamp_powerdown(mag_dev, false);
    +
    + timag_powerdown(mag_dev, false);
    +
    + input_unregister_device(mag_dev->input);
    +
    + input_free_device(mag_dev->input);
    +
    + /* deallocate mag_dev */
    + devm_kfree(&pdev->dev, mag_dev);
    +
    + /* Disable clock */
    + pm_runtime_set_suspended(&pdev->dev);
    + pm_runtime_disable(&pdev->dev);
    + misc_deregister(&magadc_cdev);
    + return 0;
    +}
    +static const struct of_device_id ti_mag_dt_ids[] = {
    + { .compatible = "ti,am4372-mag", },
    + { }
    +};
    +MODULE_DEVICE_TABLE(of, ti_mag_dt_ids);
    +
    +#ifdef CONFIG_PM
    +/*
    + * Suspend function to suspend the magadc
    + */
    +static int timag_suspend(struct device *dev)
    +{
    + struct timag *mag_dev = dev_get_drvdata(dev);
    +
    + atomic_set(&mag_dev->shutting_down, true);
    +
    + /* Clear the user application pid */
    + u_pid = 0;
    +
    + /* Disable the MAGADC Module */
    + timag_enable(mag_dev, false);
    +
    + /* To decrement the device's usage count */
    + pm_runtime_put_sync_autosuspend(dev);
    +
    + timag_release_dma(dev, mag_dev);
    +
    + timag_preamp_powerdown(mag_dev, false);
    +
    + timag_powerdown(mag_dev, false);
    +
    + /* Disable clock */
    + pm_runtime_set_suspended(dev);
    + pm_runtime_disable(dev);
    + return 0;
    +
    +}
    +
    +/*
    + * Function to resume from sleep
    + */
    +static int timag_resume(struct device *dev)
    +{
    + struct timag *mag_dev = dev_get_drvdata(dev);
    + struct platform_device *pdev;
    + struct ti_tscadc_dev *magadc_dev;
    + int i;
    +
    + pdev = to_platform_device(dev);
    + magadc_dev = ti_tscadc_dev_get(pdev);
    +
    + /* Enable clock */
    + /* To increment the device's usage count */
    + pm_runtime_get_sync(dev);
    + pm_runtime_enable(dev);
    + pm_runtime_set_active(dev);
    +
    + atomic_set(&mag_dev->shutting_down, false);
    +
    + /* Perform initial configuration and enable required interrupts */
    + timag_init_config(mag_dev);
    +
    + timag_request_dma(mag_dev, pdev);
    +
    + timag_dma_config(mag_dev, pdev);
    +
    + for (i = 0; i < mag_dev->tracks; i++)
    + timag_step_enable(mag_dev, i, true);
    +
    + /* Enable the MAGModule */
    + timag_enable(mag_dev, true);
    +
    + return 0;
    +}
    +
    +static const struct dev_pm_ops timag_pm_ops = {
    + .suspend = timag_suspend,
    + .resume = timag_resume,
    +};
    +#define TIMAG_PM_OPS (&timag_pm_ops)
    +#else
    +#define TIMAG_PM_OPS NULL
    +#endif
    +
    +static struct platform_driver ti_mag_driver = {
    + .probe = timag_probe,
    + .remove = timag_remove,
    + .driver = {
    + .name = "TI-am43xx-mag",
    + .owner = THIS_MODULE,
    + .pm = TIMAG_PM_OPS,
    + .of_match_table = of_match_ptr(ti_mag_dt_ids),
    + },
    +};
    +
    +module_platform_driver(ti_mag_driver);
    +
    +MODULE_DESCRIPTION("TI Magnetic Card Reader driver");
    +MODULE_AUTHOR("Priyaranjan Das <priyaranjandas@ti.com>");
    +MODULE_LICENSE("GPL v2");
    diff --git a/drivers/input/touchscreen/ti_am335x_tsc.c b/drivers/input/touchscreen/ti_am335x_tsc.c
    index d50cf6f..5345b4d 100644
    --- a/drivers/input/touchscreen/ti_am335x_tsc.c
    +++ b/drivers/input/touchscreen/ti_am335x_tsc.c
    @@ -26,8 +26,6 @@
    #include <linux/delay.h>
    #include <linux/of.h>
    #include <linux/of_device.h>
    -#include <linux/sort.h>
    -#include <linux/pm_wakeirq.h>

    #include <linux/mfd/ti_am335x_tscadc.h>

    @@ -55,6 +53,8 @@ struct *** {
    u32 inp_xp, inp_xn, inp_yp, inp_yn;
    u32 step_mask;
    u32 charge_delay;
    + u32 prev_x, prev_y, prev_z;
    + bool event_pending;
    };

    static unsigned int ***(struct *** *ts, unsigned int reg)
    @@ -206,68 +206,63 @@ static void ***(struct *** *ts_dev)
    am335x_tsc_se_set_cache(ts_dev->mfd_tscadc, ts_dev->step_mask);
    }

    -static int ***(const void *a, const void *b)
    -{
    - return *(int *)a - *(int *)b;
    -}
    -
    static void ***(struct *** *ts_dev,
    u32 *x, u32 *y, u32 *z1, u32 *z2)
    {
    - unsigned int yvals[7], xvals[7];
    - unsigned int i, xsum = 0, ysum = 0;
    + unsigned int fifocount = ***(ts_dev, REG_FIFO0CNT);
    + unsigned int prev_val_x = ~0, prev_val_y = ~0;
    + unsigned int prev_diff_x = ~0, prev_diff_y = ~0;
    + unsigned int read, diff;
    + unsigned int i, channel;
    unsigned int creads = ts_dev->coordinate_readouts;
    + unsigned int first_step = TOTAL_STEPS - (creads * 2 + 2);

    - for (i = 0; i < creads; i++) {
    - yvals[i] = ***(ts_dev, REG_FIFO0);
    - yvals[i] &= 0xfff;
    - }
    + *z1 = *z2 = 0;
    + if (fifocount % (creads * 2 + 2))
    + fifocount -= fifocount % (creads * 2 + 2);
    + /*
    + * Delta filter is used to remove large variations in sampled
    + * values from ADC. The filter tries to predict where the next
    + * coordinate could be. This is done by taking a previous
    + * coordinate and subtracting it form current one. Further the
    + * algorithm compares the difference with that of a present value,
    + * if true the value is reported to the sub system.
    + */
    + for (i = 0; i < fifocount; i++) {
    + read = ***(ts_dev, REG_FIFO0);
    +
    + channel = (read & 0xf0000) >> 16;
    + read &= 0xfff;
    + if (channel > first_step + creads + 2) {
    + diff = abs(read - prev_val_x);
    + if (diff < prev_diff_x) {
    + prev_diff_x = diff;
    + *x = read;
    + }
    + prev_val_x = read;

    - *z1 = ***(ts_dev, REG_FIFO0);
    - *z1 &= 0xfff;
    - *z2 = ***(ts_dev, REG_FIFO0);
    - *z2 &= 0xfff;
    + } else if (channel == first_step + creads + 1) {
    + *z1 = read;

    - for (i = 0; i < creads; i++) {
    - xvals[i] = ***(ts_dev, REG_FIFO0);
    - xvals[i] &= 0xfff;
    - }
    + } else if (channel == first_step + creads + 2) {
    + *z2 = read;

    - /*
    - * If co-ordinates readouts is less than 4 then
    - * report the average. In case of 4 or more
    - * readouts, sort the co-ordinate samples, drop
    - * min and max values and report the average of
    - * remaining values.
    - */
    - if (creads <= 3) {
    - for (i = 0; i < creads; i++) {
    - ysum += yvals[i];
    - xsum += xvals[i];
    - }
    - ysum /= creads;
    - xsum /= creads;
    - } else {
    - sort(yvals, creads, sizeof(unsigned int),
    - ***, NULL);
    - sort(xvals, creads, sizeof(unsigned int),
    - ***, NULL);
    - for (i = 1; i < creads - 1; i++) {
    - ysum += yvals[i];
    - xsum += xvals[i];
    + } else if (channel > first_step) {
    + diff = abs(read - prev_val_y);
    + if (diff < prev_diff_y) {
    + prev_diff_y = diff;
    + *y = read;
    + }
    + prev_val_y = read;
    }
    - ysum /= creads - 2;
    - xsum /= creads - 2;
    }
    - *y = ysum;
    - *x = xsum;
    }

    static irqreturn_t ***(int irq, void *dev)
    {
    struct *** *ts_dev = dev;
    struct input_dev *input_dev = ts_dev->input;
    - unsigned int fsm, status, irqclr = 0;
    + unsigned int status, irqclr = 0;
    unsigned int x = 0, y = 0;
    unsigned int z1, z2, z;

    @@ -275,21 +270,22 @@ static irqreturn_t ***(int irq, void *dev)
    if (status & IRQENB_HW_PEN) {
    ts_dev->pen_down = true;
    irqclr |= IRQENB_HW_PEN;
    - pm_stay_awake(ts_dev->mfd_tscadc->dev);
    }

    if (status & IRQENB_PENUP) {
    - fsm = ***(ts_dev, REG_ADCFSM);
    - if (fsm == ADCFSM_STEPID) {
    - ts_dev->pen_down = false;
    - input_report_key(input_dev, BTN_TOUCH, 0);
    - input_report_abs(input_dev, ABS_PRESSURE, 0);
    - input_sync(input_dev);
    - pm_relax(ts_dev->mfd_tscadc->dev);
    - } else {
    - ts_dev->pen_down = true;
    - }
    + ts_dev->pen_down = false;
    + input_report_key(input_dev, BTN_TOUCH, 0);
    + input_report_abs(input_dev, ABS_PRESSURE, 0);
    + input_sync(input_dev);
    irqclr |= IRQENB_PENUP;
    + ts_dev->event_pending = false;
    + } else if (ts_dev->event_pending == true) {
    + input_report_abs(input_dev, ABS_X, ts_dev->prev_x);
    + input_report_abs(input_dev, ABS_Y, ts_dev->prev_y);
    + input_report_abs(input_dev, ABS_PRESSURE, ts_dev->prev_z);
    + input_report_key(input_dev, BTN_TOUCH, 1);
    + input_sync(input_dev);
    + ts_dev->event_pending = false;
    }

    if (status & IRQENB_EOS)
    @@ -316,20 +312,17 @@ static irqreturn_t ***(int irq, void *dev)
    z = (z + 2047) >> 12;

    if (z <= MAX_12BIT) {
    - input_report_abs(input_dev, ABS_X, x);
    - input_report_abs(input_dev, ABS_Y, y);
    - input_report_abs(input_dev, ABS_PRESSURE, z);
    - input_report_key(input_dev, BTN_TOUCH, 1);
    - input_sync(input_dev);
    + ts_dev->prev_x = x;
    + ts_dev->prev_y = y;
    + ts_dev->prev_z = z;
    + ts_dev->event_pending = true;
    }
    }
    irqclr |= IRQENB_FIFO0THRES;
    }
    if (irqclr) {
    ***(ts_dev, REG_IRQSTATUS, irqclr);
    - if (status & IRQENB_EOS)
    - am335x_tsc_se_set_cache(ts_dev->mfd_tscadc,
    - ts_dev->step_mask);
    + am335x_tsc_se_set_cache(ts_dev->mfd_tscadc, ts_dev->step_mask);
    return IRQ_HANDLED;
    }
    return IRQ_NONE;
    @@ -367,21 +360,12 @@ static int ***(struct platform_device *pdev,
    */
    err = of_property_read_u32(node, "ti,coordinate-readouts",
    &ts_dev->coordinate_readouts);
    - if (err < 0) {
    - dev_warn(&pdev->dev, "please use 'ti,coordinate-readouts' instead\n");
    + if (err < 0)
    err = of_property_read_u32(node, "ti,coordiante-readouts",
    &ts_dev->coordinate_readouts);
    - }
    -
    if (err < 0)
    return err;

    - if (ts_dev->coordinate_readouts <= 0) {
    - dev_warn(&pdev->dev,
    - "invalid co-ordinate readouts, resetting it to 5\n");
    - ts_dev->coordinate_readouts = 5;
    - }
    -
    err = of_property_read_u32(node, "ti,charge-delay",
    &ts_dev->charge_delay);
    /*
    @@ -421,6 +405,7 @@ static int ***(struct platform_device *pdev)
    ts_dev->mfd_tscadc = tscadc_dev;
    ts_dev->input = input_dev;
    ts_dev->irq = tscadc_dev->irq;
    + ts_dev->event_pending = false;

    err = ***(pdev, ts_dev);
    if (err) {
    @@ -435,13 +420,6 @@ static int ***(struct platform_device *pdev)
    goto err_free_mem;
    }

    - if (device_may_wakeup(tscadc_dev->dev)) {
    - err = dev_pm_set_wake_irq(tscadc_dev->dev, ts_dev->irq);
    - if (err)
    - dev_err(&pdev->dev, "irq wake enable failed.\n");
    - }
    -
    - ***(ts_dev, REG_IRQSTATUS, IRQENB_MASK);
    ***(ts_dev, REG_IRQENABLE, IRQENB_FIFO0THRES);
    ***(ts_dev, REG_IRQENABLE, IRQENB_EOS);
    err = ***(ts_dev);
    @@ -472,7 +450,6 @@ static int ***(struct platform_device *pdev)
    return 0;

    err_free_irq:
    - dev_pm_clear_wake_irq(tscadc_dev->dev);
    free_irq(ts_dev->irq, ts_dev);
    err_free_mem:
    input_free_device(input_dev);
    @@ -485,7 +462,6 @@ static int ***(struct platform_device *pdev)
    struct *** *ts_dev = platform_get_drvdata(pdev);
    u32 steps;

    - dev_pm_clear_wake_irq(ts_dev->mfd_tscadc->dev);
    free_irq(ts_dev->irq, ts_dev);

    /* total steps followed by the enable mask */
    @@ -506,9 +482,9 @@ static int ***(struct device *dev)
    struct ti_tscadc_dev *tscadc_dev;
    unsigned int idle;

    + ts_dev->event_pending = false;
    tscadc_dev = ti_tscadc_dev_get(to_platform_device(dev));
    if (device_may_wakeup(tscadc_dev->dev)) {
    - ***(ts_dev, REG_IRQSTATUS, IRQENB_MASK);
    idle = ***(ts_dev, REG_IRQENABLE);
    ***(ts_dev, REG_IRQENABLE,
    (idle | IRQENB_HW_PEN));
    @@ -527,7 +503,6 @@ static int ***(struct device *dev)
    ***(ts_dev, REG_IRQWAKEUP,
    0x00);
    ***(ts_dev, REG_IRQCLR, IRQENB_HW_PEN);
    - pm_relax(ts_dev->mfd_tscadc->dev);
    }
    ***(ts_dev);
    ***(ts_dev, REG_FIFO0THR,
    @@ -555,6 +530,7 @@ static struct platform_driver ti_tsc_driver = {
    .remove = ***,
    .driver = {
    .name = "TI-am335x-tsc",
    + .owner = THIS_MODULE,
    .pm = ***,
    .of_match_table = ti_tsc_dt_ids,
    },
    diff --git a/drivers/mfd/ti_am335x_tscadc.c b/drivers/mfd/ti_am335x_tscadc.c
    index e4e4b22..5bc29d8 100644
    --- a/drivers/mfd/ti_am335x_tscadc.c
    +++ b/drivers/mfd/ti_am335x_tscadc.c
    @@ -14,6 +14,7 @@
    */

    #include <linux/module.h>
    +#include <linux/init.h>
    #include <linux/slab.h>
    #include <linux/err.h>
    #include <linux/io.h>
    @@ -24,9 +25,48 @@
    #include <linux/of.h>
    #include <linux/of_device.h>
    #include <linux/sched.h>
    -
    +#include <linux/of_irq.h>
    #include <linux/mfd/ti_am335x_tscadc.h>

    +static struct ti_mfdcell_prop tscadc_prop = {
    + .name_fr = "TI-am335x-tsc",
    + .compatible_fr = "ti,am3359-tsc",
    + .name_sc = "TI-am335x-adc",
    + .compatible_sc = "ti,am3359-adc"
    +};
    +
    +static struct ti_mfdcell_prop magadc_prop = {
    + .name_fr = "TI-am43xx-mag",
    + .compatible_fr = "ti,am4372-mag",
    + .name_sc = "TI-am43xx-adc",
    + .compatible_sc = "ti,am4372-adc"
    +};
    +
    +static const struct ti_tscadc_data tscdata = {
    + .use_mag = 0,
    + .clk_name = "adc_tsc_fck",
    + .clk_div = ADC_CLK,
    + .cell_prop = &tscadc_prop
    +};
    +
    +static const struct ti_tscadc_data magdata = {
    + .use_mag = 1,
    + .clk_name = "adc_mag_fck",
    + .clk_div = MAG_ADC_CLK,
    + .cell_prop = &magadc_prop
    +};
    +
    +static const struct of_device_id ti_tscadc_dt_ids[] = {
    + { .compatible = "ti,am3359-tscadc",
    + .data = &tscdata,
    + },
    + { .compatible = "ti,am4372-magadc",
    + .data = &magdata,
    + },
    + { }
    +};
    +MODULE_DEVICE_TABLE(of, ti_tscadc_dt_ids);
    +
    static unsigned int tscadc_readl(struct ti_tscadc_dev *tsadc, unsigned int reg)
    {
    unsigned int val;
    @@ -68,6 +108,12 @@ static void am335x_tscadc_need_adc(struct ti_tscadc_dev *tsadc)
    DEFINE_WAIT(wait);
    u32 reg;

    + /*
    + * disable TSC steps so it does not run while the ADC is using it. If
    + * write 0 while it is running (it just started or was already running)
    + * then it completes all steps that were enabled and stops then.
    + */
    + tscadc_writel(tsadc, REG_SE, 0);
    reg = tscadc_readl(tsadc, REG_ADCFSM);
    if (reg & SEQ_STATUS) {
    tsadc->adc_waiting = true;
    @@ -85,7 +131,7 @@ static void am335x_tscadc_need_adc(struct ti_tscadc_dev *tsadc)
    * busy applying the charge step.
    */
    reg = tscadc_readl(tsadc, REG_ADCFSM);
    - WARN_ON((reg & SEQ_STATUS) && !(reg & CHARGE_STEP));
    + WARN_ON(reg & SEQ_STATUS & (!CHARGE_STEP));
    tsadc->adc_waiting = false;
    }
    tsadc->adc_in_use = true;
    @@ -94,6 +140,7 @@ static void am335x_tscadc_need_adc(struct ti_tscadc_dev *tsadc)
    void am335x_tsc_se_set_once(struct ti_tscadc_dev *tsadc, u32 val)
    {
    spin_lock_irq(&tsadc->reg_lock);
    + tsadc->reg_se_cache |= val;
    am335x_tscadc_need_adc(tsadc);

    tscadc_writel(tsadc, REG_SE, val);
    @@ -123,12 +170,13 @@ void am335x_tsc_se_clr(struct ti_tscadc_dev *tsadc, u32 val)
    }
    EXPORT_SYMBOL_GPL(am335x_tsc_se_clr);

    -static void tscadc_idle_config(struct ti_tscadc_dev *config)
    +static void tscadc_idle_config(struct ti_tscadc_dev *config, bool use_mag)
    {
    - unsigned int idleconfig;
    + unsigned int idleconfig = 0;

    - idleconfig = STEPCONFIG_YNN | STEPCONFIG_INM_ADCREFM |
    - STEPCONFIG_INP_ADCREFM | STEPCONFIG_YPN;
    + if (!use_mag)
    + idleconfig = STEPCONFIG_YNN | STEPCONFIG_INM_ADCREFM |
    + STEPCONFIG_INP_ADCREFM | STEPCONFIG_YPN;

    tscadc_writel(config, REG_IDLECONFIG, idleconfig);
    }
    @@ -143,19 +191,31 @@ static int ti_tscadc_probe(struct platform_device *pdev)
    struct property *prop;
    const __be32 *cur;
    u32 val;
    - int err, ctrl;
    + int err = -EINVAL, ctrl;
    int clock_rate;
    int tsc_wires = 0, adc_channels = 0, total_channels;
    int readouts = 0;
    + int mag_tracks;
    + const struct ti_tscadc_data *data;
    + const struct of_device_id *id;

    if (!pdev->dev.of_node) {
    dev_err(&pdev->dev, "Could not find valid DT data.\n");
    return -EINVAL;
    }

    - node = of_get_child_by_name(pdev->dev.of_node, "tsc");
    - of_property_read_u32(node, "ti,wires", &tsc_wires);
    - of_property_read_u32(node, "ti,coordiante-readouts", &readouts);
    + id = of_match_device(ti_tscadc_dt_ids, &pdev->dev);
    + data = id->data;
    +
    + if (data->use_mag) {
    + node = of_get_child_by_name(pdev->dev.of_node, "mag");
    + of_property_read_u32(node, "ti,tracks", &mag_tracks);
    + tsc_wires = mag_tracks * 2;
    + } else {
    + node = of_get_child_by_name(pdev->dev.of_node, "tsc");
    + of_property_read_u32(node, "ti,wires", &tsc_wires);
    + of_property_read_u32(node, "ti,coordiante-readouts", &readouts);
    + }

    node = of_get_child_by_name(pdev->dev.of_node, "adc");
    of_property_for_each_u32(node, "ti,adc-channels", prop, cur, val) {
    @@ -176,11 +236,17 @@ static int ti_tscadc_probe(struct platform_device *pdev)
    return -EINVAL;
    }

    - if (readouts * 2 + 2 + adc_channels > 16) {
    + if (!data->use_mag && (readouts * 2 + 2 + adc_channels > 16)) {
    dev_err(&pdev->dev, "Too many step configurations requested\n");
    return -EINVAL;
    }

    + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    + if (!res) {
    + dev_err(&pdev->dev, "no memory resource defined.\n");
    + return -EINVAL;
    + }
    +
    /* Allocate memory for device */
    tscadc = devm_kzalloc(&pdev->dev,
    sizeof(struct ti_tscadc_dev), GFP_KERNEL);
    @@ -190,17 +256,38 @@ static int ti_tscadc_probe(struct platform_device *pdev)
    }
    tscadc->dev = &pdev->dev;

    - err = platform_get_irq(pdev, 0);
    - if (err < 0) {
    - dev_err(&pdev->dev, "no irq ID is specified.\n");
    - goto ret;
    - } else
    - tscadc->irq = err;
    + /* JR: modified below logic to allow magadc to get a valid IRQ */
    + /*if (!data->use_mag) {*/
    + err = platform_get_irq(pdev, 0);
    + if (err < 0) {
    + dev_err(&pdev->dev, "no irq ID is specified.\n");
    + goto ret;
    + } else
    + tscadc->irq = err;
    + /*} else { */
    + if (data->use_mag) {
    + tscadc->magirq = of_irq_to_resource(pdev->dev.of_node, 0, NULL);
    + if (!tscadc->magirq) {
    + dev_err(&pdev->dev, "can't translate OF irq value\n");
    + goto ret;
    + }
    + }
    + /*}*/

    - res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    - tscadc->tscadc_base = devm_ioremap_resource(&pdev->dev, res);
    - if (IS_ERR(tscadc->tscadc_base))
    - return PTR_ERR(tscadc->tscadc_base);
    + res = devm_request_mem_region(&pdev->dev,
    + res->start, resource_size(res), pdev->name);
    + if (!res) {
    + dev_err(&pdev->dev, "failed to reserve registers.\n");
    + return -EBUSY;
    + }
    +
    + tscadc->tscadc_base = devm_ioremap(&pdev->dev,
    + res->start, resource_size(res));
    + if (!tscadc->tscadc_base) {
    + dev_err(&pdev->dev, "failed to map registers.\n");
    + return -ENOMEM;
    + }
    + tscadc->phy_base = res->start;

    tscadc->regmap_tscadc = devm_regmap_init_mmio(&pdev->dev,
    tscadc->tscadc_base, &tscadc_regmap_config);
    @@ -224,7 +311,7 @@ static int ti_tscadc_probe(struct platform_device *pdev)
    * The TSC_ADC_SS controller design assumes the OCP clock is
    * at least 6x faster than the ADC clock.
    */
    - clk = clk_get(&pdev->dev, "adc_tsc_fck");
    + clk = clk_get(&pdev->dev, data->clk_name);
    if (IS_ERR(clk)) {
    dev_err(&pdev->dev, "failed to get TSC fck\n");
    err = PTR_ERR(clk);
    @@ -239,33 +326,44 @@ static int ti_tscadc_probe(struct platform_device *pdev)
    tscadc_writel(tscadc, REG_CLKDIV, tscadc->clk_div);

    /* Set the control register bits */
    - ctrl = CNTRLREG_STEPCONFIGWRT | CNTRLREG_STEPID;
    - tscadc_writel(tscadc, REG_CTRL, ctrl);
    + if (!data->use_mag && (tsc_wires > 0))
    + ctrl = CNTRLREG_STEPCONFIGWRT | CNTRLREG_STEPID;

    - /* Set register bits for Idle Config Mode */
    - if (tsc_wires > 0) {
    - tscadc->tsc_wires = tsc_wires;
    - if (tsc_wires == 5)
    - ctrl |= CNTRLREG_5WIRE | CNTRLREG_TSCENB;
    - else
    - ctrl |= CNTRLREG_4WIRE | CNTRLREG_TSCENB;
    - tscadc_idle_config(tscadc);
    + if (!data->use_mag) {
    + ctrl |= CNTRLREG_4WIRE | CNTRLREG_TSCENB;
    + tscadc_writel(tscadc, REG_CTRL, ctrl);
    }
    + /* JR added to make magadc iio devices to work */
    + if (data->use_mag && (adc_channels > 0)) {
    + ctrl = CNTRLREG_STEPID;
    + /* uncomment next line to power down and bypass preamp */
    + //ctrl |= CNTRLREG_PREAMP_PWRDOWN | CNTRLREG_PREAMP_BYPASS;
    + tscadc_writel(tscadc, REG_CTRL, ctrl);
    + }
    +
    + /* Set register bits for Idle Config Mode for MAGADC or TSCADC */
    + if (tsc_wires > 0)
    + tscadc_idle_config(tscadc, data->use_mag);

    /* Enable the TSC module enable bit */
    + ctrl = tscadc_readl(tscadc, REG_CTRL);
    ctrl |= CNTRLREG_TSCSSENB;
    tscadc_writel(tscadc, REG_CTRL, ctrl);

    tscadc->used_cells = 0;
    tscadc->tsc_cell = -1;
    tscadc->adc_cell = -1;
    + tscadc->mag_cell = -1;

    - /* TSC Cell */
    + /* TSC or MAG Cell */
    if (tsc_wires > 0) {
    - tscadc->tsc_cell = tscadc->used_cells;
    + if (data->use_mag)
    + tscadc->mag_cell = tscadc->used_cells;
    + else
    + tscadc->tsc_cell = tscadc->used_cells;
    cell = &tscadc->cells[tscadc->used_cells++];
    - cell->name = "TI-am335x-tsc";
    - cell->of_compatible = "ti,am3359-tsc";
    + cell->name = data->cell_prop->name_fr;
    + cell->of_compatible = data->cell_prop->compatible_fr;
    cell->platform_data = &tscadc;
    cell->pdata_size = sizeof(tscadc);
    }
    @@ -274,8 +372,8 @@ static int ti_tscadc_probe(struct platform_device *pdev)
    if (adc_channels > 0) {
    tscadc->adc_cell = tscadc->used_cells;
    cell = &tscadc->cells[tscadc->used_cells++];
    - cell->name = "TI-am335x-adc";
    - cell->of_compatible = "ti,am3359-adc";
    + cell->name = data->cell_prop->name_sc;
    + cell->of_compatible = data->cell_prop->compatible_sc;
    cell->platform_data = &tscadc;
    cell->pdata_size = sizeof(tscadc);
    }
    @@ -302,49 +400,92 @@ static int ti_tscadc_remove(struct platform_device *pdev)

    tscadc_writel(tscadc, REG_SE, 0x00);

    + mfd_remove_devices(tscadc->dev);
    +
    pm_runtime_put_sync(&pdev->dev);
    pm_runtime_disable(&pdev->dev);

    - mfd_remove_devices(tscadc->dev);
    -
    return 0;
    }

    #ifdef CONFIG_PM
    static int tscadc_suspend(struct device *dev)
    {
    - struct ti_tscadc_dev *tscadc_dev = dev_get_drvdata(dev);
    + struct platform_device *pdev = to_platform_device(dev);
    + struct ti_tscadc_dev *tscadc;
    +
    + const struct ti_tscadc_data *data;
    + const struct of_device_id *id;
    + id = of_match_device(ti_tscadc_dt_ids, dev);
    + data = id->data;

    - tscadc_writel(tscadc_dev, REG_SE, 0x00);
    + /* To decrement the device's usage count */
    pm_runtime_put_sync(dev);

    + tscadc = platform_get_drvdata(pdev);
    +
    + tscadc_writel(tscadc, REG_SE, 0x00);
    +
    + pm_runtime_put_sync(&pdev->dev);
    + pm_runtime_disable(&pdev->dev);
    +
    return 0;
    }

    static int tscadc_resume(struct device *dev)
    {
    - struct ti_tscadc_dev *tscadc_dev = dev_get_drvdata(dev);
    - u32 ctrl;
    + struct platform_device *pdev = to_platform_device(dev);
    + struct ti_tscadc_dev *tscadc;
    + struct clk *clk;
    + int err = -EINVAL;
    + int clk_value, clock_rate;
    + int clkVal = 0;
    + const struct ti_tscadc_data *data;
    + const struct of_device_id *id;

    + /* To increment the device's usage count */
    pm_runtime_get_sync(dev);

    - /* context restore */
    - ctrl = CNTRLREG_STEPCONFIGWRT | CNTRLREG_STEPID;
    - tscadc_writel(tscadc_dev, REG_CTRL, ctrl);
    + tscadc = platform_get_drvdata(pdev);
    + if (!pdev->dev.of_node) {
    + dev_err(&pdev->dev, "Could not find valid DT data.\n");
    + return -EINVAL;
    + }

    - if (tscadc_dev->tsc_cell != -1) {
    - if (tscadc_dev->tsc_wires == 5)
    - ctrl |= CNTRLREG_5WIRE | CNTRLREG_TSCENB;
    - else
    - ctrl |= CNTRLREG_4WIRE | CNTRLREG_TSCENB;
    - tscadc_idle_config(tscadc_dev);
    + id = of_match_device(ti_tscadc_dt_ids, &pdev->dev);
    + data = id->data;
    +
    + tscadc->dev = &pdev->dev;
    +
    + /* Enable the clocks */
    + pm_runtime_enable(&pdev->dev);
    + pm_runtime_get_sync(&pdev->dev);
    +
    + /*
    + * The TSC_ADC_Subsystem has 2 clock domains
    + * OCP_CLK and ADC_CLK.
    + */
    + clk = devm_clk_get(&pdev->dev, data->clk_name);
    + if (IS_ERR(clk)) {
    + dev_err(&pdev->dev, "failed to get TSC fck\n");
    + err = PTR_ERR(clk);
    + goto err_disable_clk;
    }
    - ctrl |= CNTRLREG_TSCSSENB;
    - tscadc_writel(tscadc_dev, REG_CTRL, ctrl);
    + clock_rate = clk_get_rate(clk);
    + clk_value = clock_rate / data->clk_div;

    - tscadc_writel(tscadc_dev, REG_CLKDIV, tscadc_dev->clk_div);
    + /* TSCADC_CLKDIV needs to be configured to the value minus 1 */
    + clk_value = clk_value - 1;
    + tscadc_writel(tscadc, REG_CLKDIV, clk_value);
    + clkVal = tscadc_readl(tscadc, REG_CLKDIV);

    + platform_set_drvdata(pdev, tscadc);
    return 0;
    +
    +err_disable_clk:
    + pm_runtime_put_sync(&pdev->dev);
    + pm_runtime_disable(&pdev->dev);
    + return -EINVAL;
    }

    static const struct dev_pm_ops tscadc_pm_ops = {
    @@ -356,15 +497,10 @@ static const struct dev_pm_ops tscadc_pm_ops = {
    #define TSCADC_PM_OPS NULL
    #endif

    -static const struct of_device_id ti_tscadc_dt_ids[] = {
    - { .compatible = "ti,am3359-tscadc", },
    - { }
    -};
    -MODULE_DEVICE_TABLE(of, ti_tscadc_dt_ids);
    -
    static struct platform_driver ti_tscadc_driver = {
    .driver = {
    .name = "ti_am3359-tscadc",
    + .owner = THIS_MODULE,
    .pm = TSCADC_PM_OPS,
    .of_match_table = ti_tscadc_dt_ids,
    },
    diff --git a/include/linux/mfd/ti_am335x_tscadc.h b/include/linux/mfd/ti_am335x_tscadc.h
    index 05ce1d1..04e731f 100644
    --- a/include/linux/mfd/ti_am335x_tscadc.h
    +++ b/include/linux/mfd/ti_am335x_tscadc.h
    @@ -39,6 +39,17 @@
    #define REG_FIFO0 0x100
    #define REG_FIFO1 0x200

    +/* Register specific for MAGADC, Which is a reuse IP of TSCADC */
    +#define REG_ADCREVISION 0x000
    +#define REG_SYSCONFIG 0x010
    +#define REG_EOI 0x020
    +#define REG_ADCRANGE 0x048
    +#define REG_SWIPECOMPARE12 0x05C
    +#define REG_SWIPECOMPARE34 0x060
    +#define REG_DMAENABLESET 0x038
    +#define REG_DMAENABLECLR 0x03C
    +#define REG_DMA0REQ 0x0EC
    +
    /* Register Bitfields */
    /* IRQ wakeup enable */
    #define IRQWKUP_ENB BIT(0)
    @@ -60,7 +71,15 @@
    #define IRQENB_FIFO1OVRRUN BIT(6)
    #define IRQENB_FIFO1UNDRFLW BIT(7)
    #define IRQENB_PENUP BIT(9)
    -#define IRQENB_MASK (0x7FF)
    +
    +/* IRQ enable for MAGADC */
    +#define IRQENB_END_OF_SEQ BIT(1)
    +#define IRQENB_FIFO0_OVERRUN BIT(3)
    +#define IRQENB_FIFO0_UNDERFLOW BIT(4)
    +#define IRQENB_FIFO1_OVERRUN BIT(6)
    +#define IRQENB_FIFO1_UNDERFLOW BIT(7)
    +#define IRQENB_OUT_OF_RANGE BIT(8)
    +#define IRQENB_START_OF_SWIPE BIT(9)

    /* Step Configuration */
    #define STEPCONFIG_MODE_MASK (3 << 0)
    @@ -85,6 +104,35 @@
    #define STEPCONFIG_INP_ADCREFM STEPCONFIG_INP(8)
    #define STEPCONFIG_FIFO1 BIT(26)

    +/* Additional StepConfigs for MAGADC IP */
    +#define STEPCONFIG_GAIN_CTRL4_MASK (0x30000000)
    +#define STEPCONFIG_GAIN_CTRL3_MASK (0x600)
    +#define STEPCONFIG_GAIN_CTRL2_MASK (0x180)
    +#define STEPCONFIG_GAIN_CTRL1_MASK (0x60)
    +#define STEPCONFIG_THRES_CMP_EN ((0x1) << 11)
    +#define STEPCONFIG_THRES_PTR_MASK (0xC0000000)
    +#define STEPCONFIG_THRES_PTR(val) ((val) << 30)
    +#define STEPCONFIG_THRESREG2_TRIGGER STEPCONFIG_THRES_PTR(2)
    +#define STEPCONFIG_THRESREG3_TRIGGER STEPCONFIG_THRES_PTR(3)
    +#define STEPCONFIG_THRESREG4_TRIGGER STEPCONFIG_THRES_PTR(4)
    +#define STEPCONFIG_DIFFCTRL(val) ((val) << 25)
    +#define STEPCONFIG_DIFFCTRL_SELC STEPCONFIG_DIFFCTRL(1)
    +#define STEPCONFIG_MODE_HWSYNCCONT STEPCONFIG_MODE(3)
    +#define STEPCONFIG_MODE_SWSYNCCONT STEPCONFIG_MODE(1)
    +#define STEPCONFIG_RFM_ADCREFM ((3) << 23)
    +#define STEPCONFIG_RFP_VDD (0)
    +#define STEPCONFIG_AVG_4 STEPCONFIG_AVG(2)
    +
    +/* Swipe threshold Masks for MAGADC */
    +#define SWIPETHRES_DATA1_VAL(val) ((val) << 16)
    +#define SWIPETHRES_DATA2_VAL(val) ((val) << 0)
    +#define SWIPETHRES_DATA3_VAL(val) ((val) << 16)
    +#define SWIPETHRES_DATA4_VAL(val) ((val) << 0)
    +#define SWIPETHRES_DATA1_VAL_MASK (0x0FFF0000U)
    +#define SWIPETHRES_DATA2_VAL_MASK (0x00000FFFU)
    +#define SWIPETHRES_DATA3_VAL_MASK (0x0FFF0000U)
    +#define SWIPETHRES_DATA4_VAL_MASK (0x00000FFFU)
    +
    /* Delay register */
    #define STEPDELAY_OPEN_MASK (0x3FFFF << 0)
    #define STEPDELAY_OPEN(val) ((val) << 0)
    @@ -93,6 +141,10 @@
    #define STEPDELAY_SAMPLE(val) ((val) << 24)
    #define STEPCONFIG_SAMPLEDLY STEPDELAY_SAMPLE(0)

    +/* Delay added for MAGADC */
    +#define MAGADC_SAMPLEDELAY (STEPDELAY_SAMPLE(1))
    +#define MAGADC_OPENDELAY (STEPDELAY_OPEN(0))
    +
    /* Charge Config */
    #define STEPCHARGE_RFP_MASK (7 << 12)
    #define STEPCHARGE_RFP(val) ((val) << 12)
    @@ -123,6 +175,11 @@
    #define CNTRLREG_8WIRE CNTRLREG_AFE_CTRL(3)
    #define CNTRLREG_TSCENB BIT(7)

    +/*Control registers bitfields for MAGADC IP */
    +#define CNTRLREG_MAGADCENB BIT(0)
    +#define CNTRLREG_PREAMP_PWRDOWN BIT(5)
    +#define CNTRLREG_PREAMP_BYPASS BIT(6)
    +
    /* FIFO READ Register */
    #define FIFOREAD_DATA_MASK (0xfff << 0)
    #define FIFOREAD_CHNLID_MASK (0xf << 16)
    @@ -132,6 +189,7 @@
    #define CHARGE_STEP 0x11

    #define ADC_CLK 3000000
    +#define MAG_ADC_CLK 1600000
    #define TOTAL_STEPS 16
    #define TOTAL_CHANNELS 8
    #define FIFO1_THRESHOLD 19
    @@ -139,16 +197,16 @@
    /*
    * time in us for processing a single channel, calculated as follows:
    *
    - * max num cycles = open delay + (sample delay + conv time) * averaging
    + * num cycles = open delay + (sample delay + conv time) * averaging
    *
    - * max num cycles: 262143 + (255 + 13) * 16 = 266431
    + * num cycles: 152 + (1 + 13) * 16 = 376
    *
    * clock frequency: 26MHz / 8 = 3.25MHz
    * clock period: 1 / 3.25MHz = 308ns
    *
    - * max processing time: 266431 * 308ns = 83ms(approx)
    + * processing time: 376 * 308ns = 116us
    */
    -#define IDLE_TIMEOUT 83 /* milliseconds */
    +#define IDLE_TIMEOUT 116 /* microsec */

    #define TSCADC_CELLS 2

    @@ -156,11 +214,14 @@ struct ti_tscadc_dev {
    struct device *dev;
    struct regmap *regmap_tscadc;
    void __iomem *tscadc_base;
    + unsigned long phy_base;
    int irq;
    + int magirq;
    int used_cells; /* 1-2 */
    - int tsc_wires;
    int tsc_cell; /* -1 if not used */
    int adc_cell; /* -1 if not used */
    + int mag_cell; /* -1 if not used */
    + int use_mag; /* 1 for magadc instance, 0 for tsc instance */
    struct mfd_cell cells[TSCADC_CELLS];
    u32 reg_se_cache;
    bool adc_waiting;
    @@ -172,8 +233,26 @@ struct ti_tscadc_dev {
    /* tsc device */
    struct *** *tsc;

    + /* mag device */
    + struct timag *mag;
    +
    /* adc device */
    struct adc_device *adc;
    + int dma_rx_chnum;
    +};
    +
    +struct ti_mfdcell_prop {
    + const char *name_fr;
    + const char *compatible_fr;
    + const char *name_sc;
    + const char *compatible_sc;
    +};
    +
    +struct ti_tscadc_data {
    + bool use_mag;
    + const char *clk_name;
    + int clk_div;
    + struct ti_mfdcell_prop *cell_prop;
    };

    static inline struct ti_tscadc_dev *ti_tscadc_dev_get(struct platform_device *p)
    diff --git a/include/linux/ti_magadc.h b/include/linux/ti_magadc.h
    new file mode 100644
    index 0000000..5c1e94c
    --- /dev/null
    +++ b/include/linux/ti_magadc.h
    @@ -0,0 +1,111 @@
    +/*
    + * ti_magadc.h - Header file for magadc driver
    + *
    + * Copyright (C) 2013 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.
    + */
    +
    +#ifndef __TI_MAGADC_H__
    +#define __TI_MAGADC_H__
    +
    +#include <linux/types.h>
    +#include <linux/ioctl.h>
    +#include <stdbool.h>
    +
    +#define MAGADC_IOCTL 0xAE
    +#define MAGADC_SIGID 50
    +#define MAGADC_SIGINT 99
    +
    +#define MAGADC_STORE_RW_ADDR _IOR(MAGADC_IOCTL, 0, int)
    +#define MAGADC_STORE_RO_ADDR _IO(MAGADC_IOCTL, 1)
    +#define MAGADC_READ_RW_ADDR _IO(MAGADC_IOCTL, 2)
    +#define MAGADC_READ_RO_ADDR _IO(MAGADC_IOCTL, 3)
    +#define MAGADC_COPY_RAW_DATA _IO(MAGADC_IOCTL, 4)
    +
    +
    +/* This denote raw data for each track from ADC */
    +#define MAX_TRACK_DATA (30000)
    +#define MAX_NO_OF_TRACKS (3)
    +#define MAX_TRACK_DATA_BITS (1500)
    +#define MAX_TRACK_DATA_CHARS (250)
    +#define MAX_NO_OF_PEAKS (1500 * 2)
    +#define MAX_CHARS_REPORT (100)
    +#define MAX_INPUT_EVENTS (300)
    +/* Size of total Raw data collected from ADC */
    +#define MAX_ADC_DATA_RAW (MAX_TRACK_DATA * MAX_NO_OF_TRACKS)
    +
    +#define MAGADC_TRACK1 (2)
    +#define MAGADC_TRACK2 (1)
    +#define MAGADC_TRACK3 (0)
    +#define MAGADC_SWIPETHRES_VAL (2200)
    +#define MAGADC_DMA0_THRESHOLD (31)
    +#define MAGADC_ZERO_VAL (2048)
    +#define MAGADC_TRACK1_OFFSET (32)
    +#define MAGADC_TRACK2_OFFSET (48)
    +#define MAGADC_TRACK1_BITPERCHAR (6)
    +#define MAGADC_TRACK2_BITPERCHAR (4)
    +#define MAGADC_IOCTL_REGISTER_PID 1
    +#define MAGADC_IOCTL_COPY_DATA 2
    +
    +enum timag_thresdata {
    + MAGADC_SWIPE_THRESDATA1 = 0,
    + MAGADC_SWIPE_THRESDATA2,
    + MAGADC_SWIPE_THRESDATA3,
    + MAGADC_SWIPE_THRESDATA4
    +};
    +/*
    + * F2F decoding support.
    + *
    + * 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.
    + */
    +
    +struct peak_data {
    + unsigned short value;
    + int index;
    +};
    +struct timag_decode_data {
    + unsigned char asciiData[MAX_NO_OF_TRACKS][MAX_TRACK_DATA_CHARS];
    +};
    +struct timag_track_data {
    + unsigned int num_samples;
    + unsigned int num_peaks;
    + unsigned int num_bits;
    + unsigned int num_chars;
    + unsigned int badchars;
    + unsigned short rawsample[MAX_TRACK_DATA *
    + sizeof(unsigned int)];
    + unsigned char bitdata[MAX_TRACK_DATA_BITS];
    + struct peak_data peakdata[MAX_NO_OF_PEAKS *
    + sizeof(struct peak_data)];
    +};
    +
    +struct bit_data_prop {
    + int bitperchar;
    + int bitcount;
    + unsigned char offset;
    + bool paritycheck;
    + char endchar;
    +};
    +
    +struct timag_track {
    + struct timag_track_data tracks[MAX_NO_OF_TRACKS];
    + bool sentinel_pass[MAX_NO_OF_TRACKS];
    + struct bit_data_prop bitprop[MAX_NO_OF_TRACKS];
    + bool is_swiped;
    + unsigned int adc_data[MAX_ADC_DATA_RAW * sizeof(int)];
    + unsigned int total_samples;
    + unsigned int num_tracks;
    +};
    +
    +
    +#endif /* __TI_MAGADC_H__ */
    diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild
    index 224be60..599375f 100644
    --- a/include/uapi/linux/Kbuild
    +++ b/include/uapi/linux/Kbuild
    @@ -468,3 +468,4 @@ header-y += xilinx-v4l2-controls.h
    header-y += zorro.h
    header-y += zorro_ids.h
    header-y += userfaultfd.h
    +header-y += ti_magadc.h
    --
    1.9.1
  • 改为 0,0x0或者 0xFFFFFFFE也没有用,前置放大器已经设置为 bypass。还有哪里有可能没设置好。




    From c5a5e6ab95668af11c33bbc08a685233be8f84ba Mon Sep 17 00:00:00 2001
    From: Jason Reeder <jreeder@ti.com>
    Date: Tue, 17 Jan 2017 14:17:06 -0600
    Subject: [PATCH] [HACK] Add ADC1 support to the AM437x GP EVM

    This commit is a hack that adds ADC1 support to the AM437x
    GP EVM. This should patch directly on the Linux Processor
    SDK v3.2.0.5.

    Official support for ADC1 will be added in a later version
    of the Linux Processor SDK.

    Signed-off-by: Jason Reeder <jreeder@ti.com>
    ---
    .../bindings/input/magnetic-stripes/ti-mag-adc.txt | 34 +
    arch/arm/boot/dts/am4372.dtsi | 26 +-
    arch/arm/boot/dts/am437x-gp-evm.dts | 12 +
    arch/arm/boot/dts/am43xx-clocks.dtsi | 8 +
    arch/arm/mach-omap2/omap_hwmod_43xx_data.c | 38 +
    arch/arm/mach-omap2/prcm43xx.h | 1 +
    drivers/clk/ti/clk-43xx.c | 1 +
    drivers/iio/adc/ti_am335x_adc.c | 119 +--
    drivers/input/misc/Kconfig | 6 +
    drivers/input/misc/Makefile | 1 +
    drivers/input/misc/ti_magadc.c | 936 +++++++++++++++++++++
    drivers/input/touchscreen/ti_am335x_tsc.c | 152 ++--
    drivers/mfd/ti_am335x_tscadc.c | 260 ++++--
    include/linux/mfd/ti_am335x_tscadc.h | 91 +-
    include/linux/ti_magadc.h | 111 +++
    include/uapi/linux/Kbuild | 1 +
    16 files changed, 1553 insertions(+), 244 deletions(-)
    create mode 100644 Documentation/devicetree/bindings/input/magnetic-stripes/ti-mag-adc.txt
    create mode 100644 drivers/input/misc/ti_magadc.c
    create mode 100644 include/linux/ti_magadc.h

    diff --git a/Documentation/devicetree/bindings/input/magnetic-stripes/ti-mag-adc.txt b/Documentation/devicetree/bindings/input/magnetic-stripes/ti-mag-adc.txt
    new file mode 100644
    index 0000000..7e17f74
    --- /dev/null
    +++ b/Documentation/devicetree/bindings/input/magnetic-stripes/ti-mag-adc.txt
    @@ -0,0 +1,34 @@
    +* TI - MAG ADC (Magnetic Stripe Reader/ADC)
    +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    +
    +Required properties:
    +- child "mag"
    + ti,tracks: Refers to magnetic stripe's tracks.1/2/3 number of tracks
    + of the Magnetic stripe read support on the platform is
    + provided using the variable.
    +- child "adc"
    + ti,adc-channels: List of analog inputs available for ADC.
    + AIN0 = 0, AIN1 = 1 and so on till AIN7 = 7.
    +
    +Example:
    + magadc: magadc@4834c000 {
    + compatible = "ti,am4372-magadc";
    + reg = <0x4834c000 0x2000>;
    + dmas = <&edma 54>;
    + dma-names = "rx";
    + interrupts = <GIC_SPI 115 IRQ_TYPE_LEVEL_HIGH>;
    + ti,hwmods = "adc_mag";
    + clocks = <&adc_mag_fck>;
    + clock-names = "fck";
    + status = "disabled";
    +
    + mag {
    + ti,tracks = <3>;
    + compatible = "ti,am4372-mag";
    + };
    +
    + adc {
    + ti,adc-channels = <2>;
    + compatible ="ti,am4372-adc";
    + };
    + };
    diff --git a/arch/arm/boot/dts/am4372.dtsi b/arch/arm/boot/dts/am4372.dtsi
    index 0f296c1..df0e4a8 100644
    --- a/arch/arm/boot/dts/am4372.dtsi
    +++ b/arch/arm/boot/dts/am4372.dtsi
    @@ -890,7 +890,7 @@
    };

    tscadc: tscadc@44e0d000 {
    - compatible = "ti,am3359-tscadc";
    + compatible = "ti,am4372-tscadc","ti,am3359-tscadc";
    reg = <0x44e0d000 0x1000>;
    ti,hwmods = "adc_tsc";
    interrupts = <GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH>;
    @@ -899,16 +899,36 @@
    status = "disabled";

    tsc {
    - compatible = "ti,am3359-tsc";
    + compatible = "ti,am4372-tsc","ti,am3359-tsc";
    };

    adc {
    #io-channel-cells = <1>;
    - compatible = "ti,am3359-adc";
    + compatible = "ti,am4372-adc","ti,am3359-adc";
    };

    };

    + magadc: magadc@4834c000 {
    + compatible = "ti,am4372-magadc";
    + reg = <0x4834c000 0x2000>;
    + ti,hwmods = "adc_mag";
    + dmas = <&edma 54>;
    + dma-names = "rx";
    + interrupts = <GIC_SPI 115 IRQ_TYPE_LEVEL_HIGH>;
    + clocks = <&adc_mag_fck>;
    + clock-names = "fck";
    + status = "disabled";
    +
    + mag {
    + compatible = "ti,am4372-mag";
    + };
    +
    + adc {
    + compatible ="ti,am4372-adc";
    + };
    + };
    +
    sham: sham@53100000 {
    compatible = "ti,omap5-sham";
    ti,hwmods = "sham";
    diff --git a/arch/arm/boot/dts/am437x-gp-evm.dts b/arch/arm/boot/dts/am437x-gp-evm.dts
    index 615f580..5b1341c 100644
    --- a/arch/arm/boot/dts/am437x-gp-evm.dts
    +++ b/arch/arm/boot/dts/am437x-gp-evm.dts
    @@ -841,6 +841,18 @@
    };
    };

    +&magadc {
    + status = "okay";
    +
    + mag {
    + ti,tracks = <0>;
    + };
    +
    + adc {
    + ti,adc-channels = <0 1 2 3 4 5 6 7>;
    + };
    +};
    +
    &ecap0 {
    status = "okay";
    pinctrl-names = "default", "sleep";
    diff --git a/arch/arm/boot/dts/am43xx-clocks.dtsi b/arch/arm/boot/dts/am43xx-clocks.dtsi
    index 627746a..6093214 100644
    --- a/arch/arm/boot/dts/am43xx-clocks.dtsi
    +++ b/arch/arm/boot/dts/am43xx-clocks.dtsi
    @@ -40,6 +40,14 @@
    clock-div = <1>;
    };

    + adc_mag_fck: adc_mag_fck {
    + #clock-cells = <0>;
    + compatible = "fixed-factor-clock";
    + clocks = <&sys_clkin_ck>;
    + clock-mult = <1>;
    + clock-div = <1>;
    + };
    +
    dcan0_fck: dcan0_fck {
    #clock-cells = <0>;
    compatible = "fixed-factor-clock";
    diff --git a/arch/arm/mach-omap2/omap_hwmod_43xx_data.c b/arch/arm/mach-omap2/omap_hwmod_43xx_data.c
    index 2a58010..0174238 100644
    --- a/arch/arm/mach-omap2/omap_hwmod_43xx_data.c
    +++ b/arch/arm/mach-omap2/omap_hwmod_43xx_data.c
    @@ -442,6 +442,36 @@ static struct omap_hwmod am43xx_adc_tsc_hwmod = {
    },
    };

    +/*
    + * 'adc/mag' class
    + * MagCard Controller (Anolog-To-Digital Converter)
    + */
    +static struct omap_hwmod_class_sysconfig am43xx_adc_mag_sysc = {
    + .rev_offs = 0x00,
    + .sysc_offs = 0x10,
    + .sysc_flags = SYSC_HAS_SIDLEMODE,
    + .idlemodes = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART),
    + .sysc_fields = &omap_hwmod_sysc_type2,
    +};
    +
    +static struct omap_hwmod_class am43xx_adc_mag_hwmod_class = {
    + .name = "adc_mag",
    + .sysc = &am43xx_adc_mag_sysc,
    +};
    +
    +static struct omap_hwmod am43xx_adc_mag_hwmod = {
    + .name = "adc_mag",
    + .class = &am43xx_adc_mag_hwmod_class,
    + .clkdm_name = "l3s_clkdm",
    + .main_clk = "adc_mag_fck",
    + .prcm = {
    + .omap4 = {
    + .clkctrl_offs = AM43XX_CM_PER_MAGADC_CLKCTRL_OFFSET,
    + .modulemode = MODULEMODE_SWCTRL,
    + },
    + },
    +};
    +
    static struct omap_hwmod_class_sysconfig am43xx_des_sysc = {
    .rev_offs = 0x30,
    .sysc_offs = 0x34,
    @@ -678,6 +708,13 @@ static struct omap_hwmod_ocp_if am43xx_l4_wkup__adc_tsc = {
    .user = OCP_USER_MPU,
    };

    +static struct omap_hwmod_ocp_if am43xx_l4_ls__adc_mag = {
    + .master = &am33xx_l4_ls_hwmod,
    + .slave = &am43xx_adc_mag_hwmod,
    + .clk = "dpll_core_m4_div2_ck",
    + .user = OCP_USER_MPU,
    +};
    +
    static struct omap_hwmod_ocp_if am43xx_l4_hs__cpgmac0 = {
    .master = &am43xx_l4_hs_hwmod,
    .slave = &am33xx_cpgmac0_hwmod,
    @@ -997,6 +1034,7 @@ static struct omap_hwmod_ocp_if *am43xx_hwmod_ocp_ifs[] __initdata = {
    &am43xx_l3__vpfe1,
    &am43xx_l4_ls__vpfe0,
    &am43xx_l4_ls__vpfe1,
    + &am43xx_l4_ls__adc_mag,
    NULL,
    };

    diff --git a/arch/arm/mach-omap2/prcm43xx.h b/arch/arm/mach-omap2/prcm43xx.h
    index e2ad14e..27cd9d0 100644
    --- a/arch/arm/mach-omap2/prcm43xx.h
    +++ b/arch/arm/mach-omap2/prcm43xx.h
    @@ -117,6 +117,7 @@
    #define AM43XX_CM_PER_MMC2_CLKCTRL_OFFSET 0x0248
    #define AM43XX_CM_PER_QSPI_CLKCTRL_OFFSET 0x0258
    #define AM43XX_CM_PER_GPMC_CLKCTRL_OFFSET 0x0220
    +#define AM43XX_CM_PER_MAGADC_CLKCTRL_OFFSET 0x0230
    #define AM43XX_CM_PER_MCASP0_CLKCTRL_OFFSET 0x0238
    #define AM43XX_CM_PER_MCASP1_CLKCTRL_OFFSET 0x0240
    #define AM43XX_CM_PER_L4LS_CLKCTRL_OFFSET 0x0420
    diff --git a/drivers/clk/ti/clk-43xx.c b/drivers/clk/ti/clk-43xx.c
    index 3f157a4..d7dca9b 100644
    --- a/drivers/clk/ti/clk-43xx.c
    +++ b/drivers/clk/ti/clk-43xx.c
    @@ -47,6 +47,7 @@ static struct ti_dt_clk am43xx_clks[] = {
    DT_CLK(NULL, "dpll_per_m2_div4_wkupdm_ck", "dpll_per_m2_div4_wkupdm_ck"),
    DT_CLK(NULL, "dpll_per_m2_div4_ck", "dpll_per_m2_div4_ck"),
    DT_CLK(NULL, "adc_tsc_fck", "adc_tsc_fck"),
    + DT_CLK(NULL, "adc_mag_fck", "adc_mag_fck"),
    DT_CLK(NULL, "clkdiv32k_ck", "clkdiv32k_ck"),
    DT_CLK(NULL, "clkdiv32k_ick", "clkdiv32k_ick"),
    DT_CLK(NULL, "dcan0_fck", "dcan0_fck"),
    diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c
    index 0470fc8..ecb2294 100644
    --- a/drivers/iio/adc/ti_am335x_adc.c
    +++ b/drivers/iio/adc/ti_am335x_adc.c
    @@ -13,6 +13,7 @@
    * GNU General Public License for more details.
    */

    +#include <linux/init.h>
    #include <linux/kernel.h>
    #include <linux/err.h>
    #include <linux/module.h>
    @@ -32,13 +33,11 @@

    struct tiadc_device {
    struct ti_tscadc_dev *mfd_tscadc;
    - struct mutex fifo1_lock; /* to protect fifo access */
    int channels;
    u8 channel_line[8];
    u8 channel_step[8];
    int buffer_en_ch_steps;
    u16 data[8];
    - u32 open_delay[8], sample_delay[8], step_avg[8];
    };

    static unsigned int tiadc_readl(struct tiadc_device *adc, unsigned int reg)
    @@ -87,61 +86,34 @@ static u32 get_adc_step_bit(struct tiadc_device *adc_dev, int chan)
    static void tiadc_step_config(struct iio_dev *indio_dev)
    {
    struct tiadc_device *adc_dev = iio_priv(indio_dev);
    - struct device *dev = adc_dev->mfd_tscadc->dev;
    unsigned int stepconfig;
    - int i, steps = 0;
    + int i, steps;

    /*
    * There are 16 configurable steps and 8 analog input
    * lines available which are shared between Touchscreen and ADC.
    *
    - * Steps forwards i.e. from 0 towards 16 are used by 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 = 0;
    + if (iio_buffer_enabled(indio_dev))
    + stepconfig = STEPCONFIG_AVG_16 | STEPCONFIG_FIFO1
    + | STEPCONFIG_MODE_SWCNT;
    + else
    + stepconfig = STEPCONFIG_AVG_16 | STEPCONFIG_FIFO1;

    for (i = 0; i < adc_dev->channels; i++) {
    int chan;

    chan = adc_dev->channel_line[i];
    -
    - if (adc_dev->step_avg[i] > STEPCONFIG_AVG_16) {
    - dev_warn(dev, "chan %d step_avg truncating to %d\n",
    - chan, STEPCONFIG_AVG_16);
    - adc_dev->step_avg[i] = STEPCONFIG_AVG_16;
    - }
    -
    - if (adc_dev->step_avg[i])
    - stepconfig =
    - STEPCONFIG_AVG(ffs(adc_dev->step_avg[i]) - 1) |
    - STEPCONFIG_FIFO1;
    - else
    - stepconfig = STEPCONFIG_FIFO1;
    -
    - if (iio_buffer_enabled(indio_dev))
    - stepconfig |= STEPCONFIG_MODE_SWCNT;
    -
    tiadc_writel(adc_dev, REG_STEPCONFIG(steps),
    stepconfig | STEPCONFIG_INP(chan));
    -
    - if (adc_dev->open_delay[i] > STEPDELAY_OPEN_MASK) {
    - dev_warn(dev, "chan %d open delay truncating to 0x3FFFF\n",
    - chan);
    - adc_dev->open_delay[i] = STEPDELAY_OPEN_MASK;
    - }
    -
    - if (adc_dev->sample_delay[i] > 0xFF) {
    - dev_warn(dev, "chan %d sample delay truncating to 0xFF\n",
    - chan);
    - adc_dev->sample_delay[i] = 0xFF;
    - }
    -
    tiadc_writel(adc_dev, REG_STEPDELAY(steps),
    - STEPDELAY_OPEN(adc_dev->open_delay[i]) |
    - STEPDELAY_SAMPLE(adc_dev->sample_delay[i]));
    -
    + STEPCONFIG_OPENDLY);
    adc_dev->channel_step[i] = steps;
    steps++;
    }
    @@ -218,11 +190,12 @@ static int tiadc_buffer_preenable(struct iio_dev *indio_dev)
    static int tiadc_buffer_postenable(struct iio_dev *indio_dev)
    {
    struct tiadc_device *adc_dev = iio_priv(indio_dev);
    + struct iio_buffer *buffer = indio_dev->buffer;
    unsigned int enb = 0;
    u8 bit;

    tiadc_step_config(indio_dev);
    - for_each_set_bit(bit, indio_dev->active_scan_mask, adc_dev->channels)
    + for_each_set_bit(bit, buffer->scan_mask, adc_dev->channels)
    enb |= (get_adc_step_bit(adc_dev, bit) << 1);
    adc_dev->buffer_en_ch_steps = enb;

    @@ -290,10 +263,15 @@ static int tiadc_iio_buffered_hardware_setup(struct iio_dev *indio_dev,
    goto error_kfifo_free;

    indio_dev->setup_ops = setup_ops;
    - indio_dev->modes |= INDIO_BUFFER_SOFTWARE;
    + indio_dev->modes |= INDIO_BUFFER_HARDWARE;
    +
    + if (ret)
    + goto error_free_irq;

    return 0;

    +error_free_irq:
    + free_irq(irq, indio_dev);
    error_kfifo_free:
    iio_kfifo_free(indio_dev->buffer);
    return ret;
    @@ -361,7 +339,6 @@ 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 ret = IIO_VAL_INT;
    int i, map_val;
    unsigned int fifo1count, read, stepid;
    bool found = false;
    @@ -375,14 +352,13 @@ static int tiadc_read_raw(struct iio_dev *indio_dev,
    if (!step_en)
    return -EINVAL;

    - mutex_lock(&adc_dev->fifo1_lock);
    fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT);
    while (fifo1count--)
    tiadc_readl(adc_dev, REG_FIFO1);

    am335x_tsc_se_set_once(adc_dev->mfd_tscadc, step_en);

    - timeout = jiffies + msecs_to_jiffies
    + timeout = jiffies + usecs_to_jiffies
    (IDLE_TIMEOUT * adc_dev->channels);
    /* Wait for Fifo threshold interrupt */
    while (1) {
    @@ -392,8 +368,7 @@ static int tiadc_read_raw(struct iio_dev *indio_dev,

    if (time_after(jiffies, timeout)) {
    am335x_tsc_se_adc_done(adc_dev->mfd_tscadc);
    - ret = -EAGAIN;
    - goto err_unlock;
    + return -EAGAIN;
    }
    }
    map_val = adc_dev->channel_step[chan->scan_index];
    @@ -419,11 +394,8 @@ static int tiadc_read_raw(struct iio_dev *indio_dev,
    am335x_tsc_se_adc_done(adc_dev->mfd_tscadc);

    if (found == false)
    - ret = -EBUSY;
    -
    -err_unlock:
    - mutex_unlock(&adc_dev->fifo1_lock);
    - return ret;
    + return -EBUSY;
    + return IIO_VAL_INT;
    }

    static const struct iio_info tiadc_info = {
    @@ -431,43 +403,16 @@ static const struct iio_info tiadc_info = {
    .driver_module = THIS_MODULE,
    };

    -static int tiadc_parse_dt(struct platform_device *pdev,
    - struct tiadc_device *adc_dev)
    -{
    - struct device_node *node = pdev->dev.of_node;
    - struct property *prop;
    - const __be32 *cur;
    - int channels = 0;
    - u32 val;
    -
    - of_property_for_each_u32(node, "ti,adc-channels", prop, cur, val) {
    - adc_dev->channel_line[channels] = val;
    -
    - /* Set Default values for optional DT parameters */
    - adc_dev->open_delay[channels] = STEPCONFIG_OPENDLY;
    - adc_dev->sample_delay[channels] = STEPCONFIG_SAMPLEDLY;
    - adc_dev->step_avg[channels] = 16;
    -
    - channels++;
    - }
    -
    - of_property_read_u32_array(node, "ti,chan-step-avg",
    - adc_dev->step_avg, channels);
    - of_property_read_u32_array(node, "ti,chan-step-opendelay",
    - adc_dev->open_delay, channels);
    - of_property_read_u32_array(node, "ti,chan-step-sampledelay",
    - adc_dev->sample_delay, channels);
    -
    - adc_dev->channels = channels;
    - return 0;
    -}
    -
    static int tiadc_probe(struct platform_device *pdev)
    {
    struct iio_dev *indio_dev;
    struct tiadc_device *adc_dev;
    struct device_node *node = pdev->dev.of_node;
    + struct property *prop;
    + const __be32 *cur;
    int err;
    + u32 val;
    + int channels = 0;

    if (!node) {
    dev_err(&pdev->dev, "Could not find valid DT data.\n");
    @@ -483,7 +428,12 @@ static int tiadc_probe(struct platform_device *pdev)
    adc_dev = iio_priv(indio_dev);

    adc_dev->mfd_tscadc = ti_tscadc_dev_get(pdev);
    - tiadc_parse_dt(pdev, adc_dev);
    +
    + 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);
    @@ -492,8 +442,6 @@ static int tiadc_probe(struct platform_device *pdev)

    tiadc_step_config(indio_dev);
    tiadc_writel(adc_dev, REG_FIFO1THR, FIFO1_THRESHOLD);
    - mutex_init(&adc_dev->fifo1_lock);
    -
    err = tiadc_channel_init(indio_dev, adc_dev->channels);
    if (err < 0)
    return err;
    @@ -513,7 +461,6 @@ static int tiadc_probe(struct platform_device *pdev)
    goto err_buffer_unregister;

    platform_set_drvdata(pdev, indio_dev);
    -
    return 0;

    err_buffer_unregister:
    @@ -586,6 +533,7 @@ static const struct dev_pm_ops tiadc_pm_ops = {

    static const struct of_device_id ti_adc_dt_ids[] = {
    { .compatible = "ti,am3359-adc", },
    + { .compatible = "ti,am4372-adc", },
    { }
    };
    MODULE_DEVICE_TABLE(of, ti_adc_dt_ids);
    @@ -593,6 +541,7 @@ MODULE_DEVICE_TABLE(of, ti_adc_dt_ids);
    static struct platform_driver tiadc_driver = {
    .driver = {
    .name = "TI-am335x-adc",
    + .owner = THIS_MODULE,
    .pm = TIADC_PM_OPS,
    .of_match_table = ti_adc_dt_ids,
    },
    diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
    index 4134c11..c2d6791 100644
    --- a/drivers/input/misc/Kconfig
    +++ b/drivers/input/misc/Kconfig
    @@ -807,4 +807,10 @@ config INPUT_DRV2667_HAPTICS
    To compile this driver as a module, choose M here: the
    module will be called drv2667-haptics.

    +config INPUT_MAG_ADC
    + tristate "Magnetic Card Reader Driver for AM43xx"
    + depends on SOC_AM43XX
    + help
    + Say Y here if you want to enable MSR driver for AM43XX
    +
    endif
    diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
    index 0357a08..f65bc37 100644
    --- a/drivers/input/misc/Makefile
    +++ b/drivers/input/misc/Makefile
    @@ -75,3 +75,4 @@ obj-$(CONFIG_INPUT_WM831X_ON) += wm831x-on.o
    obj-$(CONFIG_INPUT_XEN_KBDDEV_FRONTEND) += xen-kbdfront.o
    obj-$(CONFIG_INPUT_YEALINK) += yealink.o
    obj-$(CONFIG_INPUT_IDEAPAD_SLIDEBAR) += ideapad_slidebar.o
    +obj-$(CONFIG_INPUT_MAG_ADC) += ti_magadc.o
    diff --git a/drivers/input/misc/ti_magadc.c b/drivers/input/misc/ti_magadc.c
    new file mode 100644
    index 0000000..37d6339
    --- /dev/null
    +++ b/drivers/input/misc/ti_magadc.c
    @@ -0,0 +1,936 @@
    +/*
    + * TI Magnetic Stripe Reader driver
    + *
    + * This is a driver for TI's MAGADC IP.
    + *
    + * Copyright (C) 2013 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/firmware.h>
    +#include <linux/init.h>
    +#include <linux/kernel.h>
    +#include <linux/module.h>
    +#include <linux/input.h>
    +#include <linux/dma-mapping.h>
    +#include <linux/pm_runtime.h>
    +#include <linux/dmaengine.h>
    +#include <linux/edma.h>
    +#include <linux/interrupt.h>
    +#include <linux/clk.h>
    +#include <linux/platform_device.h>
    +#include <linux/of_device.h>
    +#include <linux/mfd/ti_am335x_tscadc.h>
    +#include <linux/uaccess.h>
    +#include <linux/ctype.h>
    +#include <linux/types.h>
    +#include <linux/export.h>
    +#include <linux/slab.h>
    +#include <linux/string.h>
    +#include <linux/miscdevice.h>
    +#include <linux/ti_magadc.h>
    +
    +struct timag {
    + struct input_dev *input;
    + struct ti_tscadc_dev *mfd_magadc;
    + struct platform_device *pdev;
    + struct timag_track mag_track;
    + struct work_struct decode_work;
    + spinlock_t lock;
    + atomic_t shutting_down;
    + int dma_rx_chnum;
    + struct dma_chan *dma_rx;
    + long int phy_base;
    + dma_addr_t dma_address;
    + int len;
    + int irq;
    + unsigned int tracks;
    +};
    +/*
    + * Miscellaneous device structre
    + */
    +static struct miscdevice magadc_cdev;
    +
    +/*
    + * variable for storing pid of Application
    + */
    +int u_pid = 0;
    +/* Variable used for validating number of applications opening the node file */
    +static int node_open;
    +/*
    + * Local variables used for interaction with application
    + */
    +unsigned char *var;
    +struct timag_track *trackData = NULL;
    +struct timag_track *tData = NULL;
    +unsigned int roaddr = 0;
    +unsigned int rwaddr = 0;
    +unsigned int phys_addr;
    +unsigned int readCnt = 1;
    +unsigned int addrStored = 0;
    +unsigned char rwAddrStr[9] = { '\0'};
    +unsigned char roAddrStr[9] = { '\0'};
    +static int timag_dma_config(struct timag *mag_dev,
    + struct platform_device *pdev);
    +/*
    + * Read magadc register
    + */
    +static inline unsigned int timag_readl(struct timag *mag_dev, unsigned int reg)
    +{
    + return readl(mag_dev->mfd_magadc->tscadc_base + reg);
    +}
    +
    +/*
    + * Write the data to magadc register
    + */
    +static inline void timag_writel(struct timag *mag_dev, unsigned int reg,
    + unsigned int val)
    +{
    + writel(val, mag_dev->mfd_magadc->tscadc_base + reg);
    +}
    +
    +/*
    + * Enable the step
    + */
    +void timag_step_enable(struct timag *mag_dev, int stepid, bool enable)
    +{
    + int val;
    +
    + val = timag_readl(mag_dev, REG_SE);
    + if (enable)
    + val |= (0x1 << (stepid+1));
    + else
    + val &= ~(0x1 << (stepid+1));
    + timag_writel(mag_dev, REG_SE, val);
    +}
    +
    +/*
    + * Configure the magadc steps
    + */
    +static void timag_step_config(struct timag *mag_dev)
    +{
    + unsigned int analog_input;
    + unsigned int total_tracks;
    + unsigned int config;
    + unsigned int inm_val;
    + unsigned int inp_val;
    + int i;
    +
    + total_tracks = mag_dev->tracks;
    +
    + for (i = 0; i < total_tracks; i++) {
    + analog_input = i;
    + if (i > 0)
    + analog_input = (analog_input * 2);
    +
    + inm_val = analog_input + 1;
    + inp_val = analog_input;
    +
    + /* STEP delay config : sample and open delay */
    + config = 0;
    + config = MAGADC_SAMPLEDELAY | MAGADC_OPENDELAY;
    + timag_writel(mag_dev, REG_STEPDELAY(i), config);
    +
    + /* Setting up other configs for each STEP */
    + config = 0;
    + config = (STEPCONFIG_DIFFCTRL_SELC |
    + STEPCONFIG_AVG_4 |
    + STEPCONFIG_RFM_ADCREFM |
    + STEPCONFIG_INM(inm_val) |
    + STEPCONFIG_INP(inp_val));
    +
    + /* GAIN is set to 12 , i.e 00 */
    + config &= ~STEPCONFIG_GAIN_CTRL4_MASK;
    + config &= ~STEPCONFIG_GAIN_CTRL3_MASK;
    + config &= ~STEPCONFIG_GAIN_CTRL2_MASK;
    + config &= ~STEPCONFIG_GAIN_CTRL1_MASK;
    +
    + /*
    + * The first step is SW sync continuous and all
    + * other steps are hardware sync
    + */
    + config &= ~STEPCONFIG_MODE_MASK;
    +
    + /* only the track1 is software sync continuous
    + * all other tracks are hardware sync continuous.
    + */
    + if (i == MAGADC_TRACK1) {
    + config |= (STEPCONFIG_MODE_SWSYNCCONT |
    + STEPCONFIG_THRES_CMP_EN);
    + } else {
    + config = config | STEPCONFIG_MODE_HWSYNCCONT;
    + config &= ~STEPCONFIG_THRES_CMP_EN;
    + }
    + /* Select FIFO0 for each step */
    + config &= ~STEPCONFIG_FIFO1;
    +
    + /* Trigger using swipe threshold reg1 for all steps */
    + config &= ~STEPCONFIG_THRES_PTR_MASK;
    + timag_writel(mag_dev, REG_STEPCONFIG(i), config);
    + }
    +}
    +
    +/*
    + * Set the swipe threshold value
    + */
    +static int timag_swipethreshold_set(struct timag *mag_dev,
    + enum timag_thresdata type,
    + unsigned int val)
    +{
    + unsigned int thres;
    +
    + switch (type) {
    + case MAGADC_SWIPE_THRESDATA1:
    + thres = timag_readl(mag_dev, REG_SWIPECOMPARE12);
    + thres &= ~SWIPETHRES_DATA1_VAL_MASK;
    + thres |= SWIPETHRES_DATA1_VAL(val);
    + timag_writel(mag_dev, REG_SWIPECOMPARE12, thres);
    + break;
    + case MAGADC_SWIPE_THRESDATA2:
    + thres = timag_readl(mag_dev, REG_SWIPECOMPARE12);
    + thres &= ~SWIPETHRES_DATA2_VAL_MASK;
    + thres |= SWIPETHRES_DATA2_VAL(val);
    + timag_writel(mag_dev, REG_SWIPECOMPARE12, thres);
    + break;
    + case MAGADC_SWIPE_THRESDATA3:
    + thres = timag_readl(mag_dev, REG_SWIPECOMPARE34);
    + thres &= ~SWIPETHRES_DATA3_VAL_MASK;
    + thres |= SWIPETHRES_DATA3_VAL(val);
    + timag_writel(mag_dev, REG_SWIPECOMPARE34, thres);
    + break;
    + case MAGADC_SWIPE_THRESDATA4:
    + thres = timag_readl(mag_dev, REG_SWIPECOMPARE34);
    + thres &= ~SWIPETHRES_DATA4_VAL_MASK;
    + thres |= SWIPETHRES_DATA4_VAL(val);
    + timag_writel(mag_dev, REG_SWIPECOMPARE34, thres);
    + break;
    + default:
    + return -EINVAL;
    + }
    + return 0;
    +}
    +/*
    + * Track the input report
    + */
    +static void timag_track_inputreport(struct timag *mag_dev, u8 stepid)
    +{
    + input_event(mag_dev->input, EV_MSC, MSC_RAW, stepid);
    +}
    +/*
    + * power down the magadc
    + */
    +static void timag_powerdown(struct timag *mag_dev, bool enable)
    +{
    + unsigned int ctrl;
    +
    + ctrl = timag_readl(mag_dev, REG_CTRL);
    + /*
    + * Write '0' to power on the AFE, '1' to power down
    + * The MAGADC should be disabled before this.
    + */
    + if (enable)
    + ctrl &= ~CNTRLREG_POWERDOWN;
    + else
    + ctrl |= CNTRLREG_POWERDOWN;
    +
    + timag_writel(mag_dev, REG_CTRL, ctrl);
    +}
    +
    +/*
    + * power down the pre - amplifier
    + */
    +static void timag_preamp_powerdown(struct timag *mag_dev, bool enable)
    +{
    + unsigned int ctrl;
    +
    + ctrl = timag_readl(mag_dev, REG_CTRL);
    + /*
    + * Write '0' to power on the Preamp, '1' to power down
    + * The MAGADC should be disabled before this.
    + */
    + if (enable)
    + ctrl &= ~CNTRLREG_PREAMP_PWRDOWN;
    + else
    + ctrl |= CNTRLREG_PREAMP_PWRDOWN;
    +
    + timag_writel(mag_dev, REG_CTRL, ctrl);
    +}
    +
    +/*
    + * Set/reset the pre amplifier bypass
    + */
    +static void timag_preamp_bypass(struct timag *mag_dev, bool enable)
    +{
    + unsigned int ctrl;
    +
    + ctrl = timag_readl(mag_dev, REG_CTRL);
    + /*
    + * 0 - Preamp is enabled by default
    + * 1 - Preamp is bypassed.
    + */
    + if (enable)
    + ctrl |= CNTRLREG_PREAMP_BYPASS;
    + else
    + ctrl &= ~CNTRLREG_PREAMP_BYPASS;
    +
    + timag_writel(mag_dev, REG_CTRL, ctrl);
    +}
    +
    +/*
    + * Enable the magadc
    + */
    +static void timag_enable(struct timag *mag_dev, bool enable)
    +{
    + unsigned int ctrl;
    +
    + ctrl = timag_readl(mag_dev, REG_CTRL);
    +
    + /* Write '1' to enable the MAGADC and '0' to disable */
    + if (enable)
    + ctrl |= CNTRLREG_MAGADCENB;
    + else
    + ctrl &= ~CNTRLREG_MAGADCENB;
    +
    + timag_writel(mag_dev, REG_CTRL, ctrl);
    +}
    +
    +/*
    + * Configure the step id
    + */
    +static void timag_stepid_config(struct timag *mag_dev, bool enable)
    +{
    + unsigned int ctrl;
    +
    + ctrl = timag_readl(mag_dev, REG_CTRL);
    +
    + /* Write '1' to enable the MAGADC and '0' to disable */
    + if (enable)
    + ctrl |= CNTRLREG_STEPID;
    + else
    + ctrl &= ~CNTRLREG_STEPID;
    +
    + timag_writel(mag_dev, REG_CTRL, ctrl);
    +}
    +
    +/*
    + * Enable DMA fifo
    + */
    +void timag_dmafifo_enable(struct timag *mag_dev, bool fifosel, bool enable)
    +{
    + unsigned int val;
    +
    + if (enable) {
    + val = timag_readl(mag_dev, REG_DMAENABLESET);
    + val |= (0x1 << fifosel);
    + timag_writel(mag_dev, REG_DMAENABLESET, val);
    + } else {
    + val = timag_readl(mag_dev, REG_DMAENABLECLR);
    + val |= (0x1 << fifosel);
    + timag_writel(mag_dev, REG_DMAENABLECLR, val);
    + }
    +}
    +
    +/*
    + * Release the dma function
    + */
    +static void timag_release_dma(struct device *dev, struct timag *mag_dev)
    +{
    + /* Release RX resources */
    + dmaengine_terminate_all(mag_dev->dma_rx);
    +
    + dma_unmap_single(dev, mag_dev->dma_address,
    + mag_dev->len, DMA_FROM_DEVICE);
    +
    + dma_release_channel(mag_dev->dma_rx);
    + mag_dev->dma_rx = NULL;
    +}
    +/*
    + * Api to request dma
    + */
    +static int timag_request_dma(struct timag *mag_dev,
    + struct platform_device *pdev)
    +{
    + dma_cap_mask_t mask;
    + struct device *sdev = &pdev->dev;
    +
    + dma_cap_zero(mask);
    + dma_cap_set(DMA_SLAVE, mask);
    +
    + mag_dev->dma_rx = dma_request_slave_channel_compat(mask,
    + edma_filter_fn,
    + &mag_dev->dma_rx_chnum,
    + mag_dev->mfd_magadc->dev, "rx");
    + if (!mag_dev->dma_rx) {
    + dev_err(sdev, "request RX DMA channel failed\n");
    + return -ENODEV;
    + }
    +
    + mag_dev->dma_address = dma_map_single(sdev,
    + &mag_dev->mag_track.adc_data[0],
    + mag_dev->len, DMA_FROM_DEVICE);
    + if (dma_mapping_error(sdev, mag_dev->dma_address)) {
    + dev_err(sdev, "dma %u bytes error\n", mag_dev->len);
    + return -EINVAL;
    + }
    + return 0;
    +}
    +
    +/*
    + * Callback function after completion of dma operation
    + */
    +static void timag_dma_rx_callback(void *dev)
    +{
    + struct timag *mag_dev;
    +
    + mag_dev = (struct timag *)dev;
    +
    + if (atomic_read(&mag_dev->shutting_down))
    + return;
    +
    + /* Disable DMA mode of MAG ADC*/
    + timag_dmafifo_enable(mag_dev, 0, false);
    + timag_enable(mag_dev, false);
    +
    + spin_lock(&mag_dev->lock);
    + mag_dev->mag_track.is_swiped = true;
    + mag_dev->mag_track.total_samples = MAX_ADC_DATA_RAW;
    + spin_unlock(&mag_dev->lock);
    +
    + schedule_work(&mag_dev->decode_work);
    +}
    +
    +/*
    + * Configure dma
    + */
    +static int timag_dma_config(struct timag *mag_dev,
    + struct platform_device *pdev)
    +{
    + struct dma_slave_config dma_rx_conf = {
    + .direction = DMA_DEV_TO_MEM,
    + .src_addr = (unsigned long)(mag_dev->phy_base + REG_FIFO0),
    + .src_addr_width = (32 * sizeof(unsigned int)),
    + .src_maxburst = 1,
    + };
    +
    + int dmathreshold = MAGADC_DMA0_THRESHOLD;
    + struct dma_async_tx_descriptor *rxdesc;
    +
    + mag_dev->len = (MAX_ADC_DATA_RAW * sizeof(unsigned int));
    +
    + dmaengine_slave_config(mag_dev->dma_rx, &dma_rx_conf);
    +
    + rxdesc = dmaengine_prep_slave_single(mag_dev->dma_rx,
    + mag_dev->dma_address,
    + mag_dev->len,
    + DMA_DEV_TO_MEM,
    + DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
    + if (!rxdesc)
    + return -EINVAL;
    +
    + rxdesc->callback = timag_dma_rx_callback;
    + rxdesc->callback_param = (void *)mag_dev;
    +
    + dmaengine_submit(rxdesc);
    + dma_async_issue_pending(mag_dev->dma_rx);
    +
    + timag_writel(mag_dev, REG_DMA0REQ, dmathreshold);
    + /* Enable DMA for MAGADC */
    + timag_dmafifo_enable(mag_dev, 0, true);
    +
    + return 0;
    +}
    +
    +/*
    + * initialize the magadc configuration
    + */
    +static void timag_init_config(struct timag *mag_dev)
    +{
    + timag_powerdown(mag_dev, true);
    +
    + timag_preamp_powerdown(mag_dev, true);
    +
    + timag_preamp_bypass(mag_dev, false);
    +
    + timag_swipethreshold_set(mag_dev,
    + MAGADC_SWIPE_THRESDATA1,
    + MAGADC_SWIPETHRES_VAL);
    +
    + /* STEPID is enabled in control register */
    + timag_stepid_config(mag_dev, true);
    + timag_step_config(mag_dev);
    +}
    +
    +/*
    + * magadc interrupt handler
    + */
    +static irqreturn_t timag_irq(int irq, void *dev)
    +{
    + struct timag *mag_dev = dev;
    + bool over_or_under_flow = false;
    + unsigned int status, irqclr = 0;
    +
    + status = timag_readl(mag_dev, REG_IRQSTATUS);
    +
    + if ((status & IRQENB_FIFO0_OVERRUN) ||
    + (status & IRQENB_FIFO0_UNDERFLOW) ||
    + (status & IRQENB_FIFO1_OVERRUN) ||
    + (status & IRQENB_FIFO1_UNDERFLOW)) {
    + timag_enable(mag_dev, false);
    + over_or_under_flow = true;
    + }
    +
    + if (status & IRQENB_END_OF_SEQ)
    + irqclr |= IRQENB_END_OF_SEQ;
    +
    + if (status & IRQENB_FIFO0_OVERRUN)
    + irqclr |= IRQENB_FIFO0_OVERRUN;
    +
    + if (status & IRQENB_FIFO0_UNDERFLOW)
    + irqclr |= IRQENB_FIFO0_UNDERFLOW;
    +
    + if (status & IRQENB_FIFO0THRES)
    + irqclr |= IRQENB_FIFO0THRES;
    +
    + if (status & IRQENB_FIFO1THRES)
    + irqclr |= IRQENB_FIFO1THRES;
    +
    + if (status & IRQENB_FIFO1_OVERRUN)
    + irqclr |= IRQENB_FIFO1_OVERRUN;
    +
    + if (status & IRQENB_FIFO1_OVERRUN)
    + irqclr |= IRQENB_FIFO1_OVERRUN;
    +
    + if (status & IRQENB_FIFO1_UNDERFLOW)
    + irqclr |= IRQENB_FIFO1_UNDERFLOW;
    +
    + if (status & IRQENB_OUT_OF_RANGE)
    + irqclr |= IRQENB_OUT_OF_RANGE;
    +
    + if (status & IRQENB_START_OF_SWIPE)
    + irqclr |= IRQENB_START_OF_SWIPE;
    +
    + if (irqclr) {
    + timag_writel(mag_dev, REG_IRQSTATUS, irqclr);
    + return IRQ_HANDLED;
    + }
    +
    + /* Re-enable the magadc module , in case of overrun/underflow */
    + if (over_or_under_flow)
    + timag_enable(mag_dev, true);
    +
    + return IRQ_NONE;
    +}
    +
    +/*
    + * Parse dt file
    + */
    +static int timag_parse_dt(struct timag *mag_dev)
    +{
    + struct device_node *node = mag_dev->pdev->dev.of_node;
    + int err;
    +
    + if (!node)
    + return -EINVAL;
    +
    + err = of_property_read_u32(node, "ti,tracks", &mag_dev->tracks);
    + if (err < 0) {
    + dev_err(&mag_dev->pdev->dev, "failed to find tracks in DT.\n");
    + return err;
    + }
    + return 0;
    +}
    +
    +/*
    + * Bottom half of work queue
    + */
    +static void timag_decode_work(struct work_struct *work)
    +{
    + int i, ret = 0;
    + struct siginfo info;
    + struct task_struct *tid;
    +
    +
    + struct timag *mag_dev = container_of(work, struct timag,
    + decode_work);
    + struct platform_device *pdev = mag_dev->pdev;
    + const struct device *dev = &pdev->dev;
    +
    + /* If any public application is running then send the signal to app */
    + if (u_pid != 0) {
    + /* find task structure associated with this pid */
    + tid = pid_task(find_vpid(u_pid), PIDTYPE_PID);
    + info.si_signo = MAGADC_SIGID;
    + info.si_code = SI_QUEUE;
    + info.si_int = MAGADC_SIGINT;
    + mag_dev->mag_track.num_tracks = mag_dev->tracks;
    + ret = send_sig_info(MAGADC_SIGID, &info, tid);
    + if (ret < 0) {
    + dev_err(dev, "error sending signal to public app:%d\n",
    + ret);
    + return;
    + }
    + }
    + for (i = 0; i < mag_dev->tracks; i++)
    + timag_track_inputreport(mag_dev, i);
    +
    + /* The end of swipe report is informed to the input subsystem */
    + input_sync(mag_dev->input);
    + /*
    + * After sending signal to user app, Re-configure the DMA to
    + * process another card swipe. Hence this is a
    + * support for multiple swipes.
    + */
    + timag_dma_config(mag_dev, mag_dev->pdev);
    + for (i = 0; i < mag_dev->tracks; i++)
    + timag_step_enable(mag_dev, i, true);
    +
    + /* Enable the Magadc Module */
    + timag_enable(mag_dev, true);
    + return;
    +}
    +static int magadc_open(struct inode *inode, struct file *file)
    +{
    + if (node_open != 0)
    + return -EBUSY;
    + node_open++;
    + try_module_get(THIS_MODULE);
    + return 0;
    +}
    +static int magadc_release(struct inode *inode, struct file *file)
    +{
    + if (node_open <= 0){
    + return -1;
    + }
    + node_open--;
    + module_put(THIS_MODULE);
    + return 0;
    +}
    +
    +static long magadc_ioctl(struct file *file,
    + unsigned int cmd, unsigned long arg)
    +{
    + int ret;
    + void *ptr = NULL;
    + unsigned int phyAddr = 0;
    +
    + ret = copy_from_user(&phyAddr, (unsigned int *)arg, sizeof(int));
    + if (ret != 0)
    + return -EFAULT;
    +
    + if (cmd == MAGADC_STORE_RW_ADDR) {
    + rwaddr = phyAddr;
    + sprintf(rwAddrStr, "%x", rwaddr);
    + ptr = ioremap_cache(rwaddr, sizeof(struct timag_track));
    + tData = (struct timag_track *)ptr;
    +
    + } else if (cmd == MAGADC_STORE_RO_ADDR) {
    + roaddr = phyAddr;
    + sprintf(roAddrStr, "%x", roaddr);
    +
    + } else if (cmd == MAGADC_READ_RW_ADDR) {
    + if (rwaddr == 0)
    + return -1;
    + if (copy_to_user((unsigned int *)arg, &rwaddr,
    + sizeof(unsigned int)))
    + return -EFAULT;
    + } else if (cmd == MAGADC_READ_RO_ADDR) {
    + if (roaddr == 0)
    + return -1;
    + if (copy_to_user((unsigned int *)arg,
    + &roaddr, sizeof(unsigned int)))
    + return -EFAULT;
    + } else if (cmd == MAGADC_COPY_RAW_DATA) {
    + memcpy((unsigned char *)tData,
    + (unsigned char *)trackData,
    + sizeof(struct timag_track));
    + }
    + return 0;
    +}
    +static ssize_t magadc_write(struct file *file,
    + const char __user *buffer, size_t length, loff_t *offset)
    +{
    + int ret;
    +
    + ret = copy_from_user(&u_pid, (int *)buffer, length);
    + if (ret != 0)
    + return -EFAULT;
    + return 0;
    +
    +}
    +static ssize_t magadc_read(struct file *file, char __user *buf,
    + size_t count, loff_t * ppos)
    +{
    + if (copy_to_user(buf, trackData, count)) {
    + return -EFAULT;
    + }
    + return 0;
    +}
    +
    +static const struct file_operations magadc_fops = {
    + .owner = THIS_MODULE,
    + .open = magadc_open,
    + .write = magadc_write,
    + .read = magadc_read,
    + .unlocked_ioctl = magadc_ioctl,
    + .release = magadc_release,
    +};
    +
    +
    +/*
    + * The functions for inserting/removing driver as a module.
    + */
    +static int timag_probe(struct platform_device *pdev)
    +{
    + struct timag *mag_dev;
    + struct input_dev *input_dev;
    + struct ti_tscadc_dev *magadc_dev = ti_tscadc_dev_get(pdev);
    + int err;
    + int irq_mask;
    + int i;
    + int ret;
    +
    + /* Allocate memory for device */
    + mag_dev = devm_kzalloc(&pdev->dev, sizeof(struct timag), GFP_KERNEL);
    + input_dev = input_allocate_device();
    + if (!mag_dev || !input_dev) {
    + dev_err(&pdev->dev, "failed to allocate memory.\n");
    + err = -ENOMEM;
    + goto err_free_mem;
    + }
    +
    + trackData = &(mag_dev->mag_track);
    + magadc_dev->mag = mag_dev;
    + mag_dev->mfd_magadc = magadc_dev;
    + mag_dev->input = input_dev;
    + mag_dev->irq = magadc_dev->magirq;
    + mag_dev->dma_rx_chnum = magadc_dev->dma_rx_chnum;
    + mag_dev->phy_base = magadc_dev->phy_base;
    + mag_dev->len = MAX_ADC_DATA_RAW;
    + mag_dev->pdev = pdev;
    + var = (unsigned char *)mag_dev;
    + input_set_capability(input_dev, EV_MSC, MSC_RAW);
    + err = timag_parse_dt(mag_dev);
    + if (err) {
    + dev_err(&pdev->dev, "Could not find valid DT data.\n");
    + goto err_free_mem;
    + }
    +
    + if (mag_dev->tracks > MAX_NO_OF_TRACKS) {
    + dev_err(&pdev->dev, "Invalid number of magnetic track .\n");
    + goto err_free_mem;
    + }
    +
    + err = devm_request_irq(&pdev->dev, mag_dev->irq, timag_irq,
    + IRQF_TRIGGER_HIGH, pdev->dev.driver->name,
    + mag_dev);
    + if (err) {
    + dev_err(&pdev->dev, "failed to allocate irq.\n");
    + goto err_free_mem;
    + }
    +
    + spin_lock_init(&mag_dev->lock);
    +
    + /* Enable clock */
    + pm_runtime_enable(&pdev->dev);
    + pm_runtime_set_active(&pdev->dev);
    +
    + magadc_cdev.minor = MISC_DYNAMIC_MINOR;
    + magadc_cdev.name = "magadc";
    + magadc_cdev.fops = &magadc_fops;
    + magadc_cdev.parent = &(pdev->dev);
    +
    + ret = misc_register(&magadc_cdev);
    + if (ret)
    + goto err_free_mem;
    +
    + INIT_WORK(&mag_dev->decode_work, timag_decode_work);
    + atomic_set(&mag_dev->shutting_down, false);
    +
    + /* Perform initial configuration and enable required interrupts */
    + timag_init_config(mag_dev);
    +
    + /* Enable the interrupt */
    + irq_mask = (IRQENB_FIFO0_OVERRUN | IRQENB_FIFO0THRES |
    + IRQENB_FIFO1THRES |
    + IRQENB_FIFO0_UNDERFLOW |
    + IRQENB_FIFO1_OVERRUN |
    + IRQENB_FIFO1_UNDERFLOW);
    +
    + timag_writel(mag_dev, REG_IRQENABLE, irq_mask);
    +
    + input_dev->name = "ti-mag";
    + input_dev->dev.parent = &pdev->dev;
    +
    + /* register to the input system */
    + err = input_register_device(input_dev);
    + if (err)
    + goto err_free_irq;
    +
    + platform_set_drvdata(pdev, mag_dev);
    +
    + timag_request_dma(mag_dev, pdev);
    +
    + timag_dma_config(mag_dev, pdev);
    +
    + for (i = 0; i < mag_dev->tracks; i++)
    + timag_step_enable(mag_dev, i, true);
    +
    + /* Enable the MAGModule */
    + timag_enable(mag_dev, true);
    +
    + return 0;
    +
    +err_free_irq:
    + devm_free_irq(&pdev->dev, mag_dev->irq, mag_dev);
    + /* Disable clock */
    + pm_runtime_disable(&pdev->dev);
    + misc_deregister(&magadc_cdev);
    +err_free_mem:
    + if (input_dev)
    + input_free_device(input_dev);
    + if (mag_dev)
    + devm_kfree(&pdev->dev, mag_dev);
    + return err;
    +}
    +
    +/*
    + * Remove driver function
    + */
    +static int timag_remove(struct platform_device *pdev)
    +{
    + struct timag *mag_dev = platform_get_drvdata(pdev);
    +
    + atomic_set(&mag_dev->shutting_down, true);
    + cancel_work_sync(&mag_dev->decode_work);
    +
    + /* Disable the MAGADC Module */
    + timag_enable(mag_dev, false);
    +
    + timag_release_dma(&pdev->dev, mag_dev);
    +
    + devm_free_irq(&pdev->dev, mag_dev->irq, mag_dev);
    +
    + timag_preamp_powerdown(mag_dev, false);
    +
    + timag_powerdown(mag_dev, false);
    +
    + input_unregister_device(mag_dev->input);
    +
    + input_free_device(mag_dev->input);
    +
    + /* deallocate mag_dev */
    + devm_kfree(&pdev->dev, mag_dev);
    +
    + /* Disable clock */
    + pm_runtime_set_suspended(&pdev->dev);
    + pm_runtime_disable(&pdev->dev);
    + misc_deregister(&magadc_cdev);
    + return 0;
    +}
    +static const struct of_device_id ti_mag_dt_ids[] = {
    + { .compatible = "ti,am4372-mag", },
    + { }
    +};
    +MODULE_DEVICE_TABLE(of, ti_mag_dt_ids);
    +
    +#ifdef CONFIG_PM
    +/*
    + * Suspend function to suspend the magadc
    + */
    +static int timag_suspend(struct device *dev)
    +{
    + struct timag *mag_dev = dev_get_drvdata(dev);
    +
    + atomic_set(&mag_dev->shutting_down, true);
    +
    + /* Clear the user application pid */
    + u_pid = 0;
    +
    + /* Disable the MAGADC Module */
    + timag_enable(mag_dev, false);
    +
    + /* To decrement the device's usage count */
    + pm_runtime_put_sync_autosuspend(dev);
    +
    + timag_release_dma(dev, mag_dev);
    +
    + timag_preamp_powerdown(mag_dev, false);
    +
    + timag_powerdown(mag_dev, false);
    +
    + /* Disable clock */
    + pm_runtime_set_suspended(dev);
    + pm_runtime_disable(dev);
    + return 0;
    +
    +}
    +
    +/*
    + * Function to resume from sleep
    + */
    +static int timag_resume(struct device *dev)
    +{
    + struct timag *mag_dev = dev_get_drvdata(dev);
    + struct platform_device *pdev;
    + struct ti_tscadc_dev *magadc_dev;
    + int i;
    +
    + pdev = to_platform_device(dev);
    + magadc_dev = ti_tscadc_dev_get(pdev);
    +
    + /* Enable clock */
    + /* To increment the device's usage count */
    + pm_runtime_get_sync(dev);
    + pm_runtime_enable(dev);
    + pm_runtime_set_active(dev);
    +
    + atomic_set(&mag_dev->shutting_down, false);
    +
    + /* Perform initial configuration and enable required interrupts */
    + timag_init_config(mag_dev);
    +
    + timag_request_dma(mag_dev, pdev);
    +
    + timag_dma_config(mag_dev, pdev);
    +
    + for (i = 0; i < mag_dev->tracks; i++)
    + timag_step_enable(mag_dev, i, true);
    +
    + /* Enable the MAGModule */
    + timag_enable(mag_dev, true);
    +
    + return 0;
    +}
    +
    +static const struct dev_pm_ops timag_pm_ops = {
    + .suspend = timag_suspend,
    + .resume = timag_resume,
    +};
    +#define TIMAG_PM_OPS (&timag_pm_ops)
    +#else
    +#define TIMAG_PM_OPS NULL
    +#endif
    +
    +static struct platform_driver ti_mag_driver = {
    + .probe = timag_probe,
    + .remove = timag_remove,
    + .driver = {
    + .name = "TI-am43xx-mag",
    + .owner = THIS_MODULE,
    + .pm = TIMAG_PM_OPS,
    + .of_match_table = of_match_ptr(ti_mag_dt_ids),
    + },
    +};
    +
    +module_platform_driver(ti_mag_driver);
    +
    +MODULE_DESCRIPTION("TI Magnetic Card Reader driver");
    +MODULE_AUTHOR("Priyaranjan Das <priyaranjandas@ti.com>");
    +MODULE_LICENSE("GPL v2");
    diff --git a/drivers/input/touchscreen/ti_am335x_tsc.c b/drivers/input/touchscreen/ti_am335x_tsc.c
    index d50cf6f..5345b4d 100644
    --- a/drivers/input/touchscreen/ti_am335x_tsc.c
    +++ b/drivers/input/touchscreen/ti_am335x_tsc.c
    @@ -26,8 +26,6 @@
    #include <linux/delay.h>
    #include <linux/of.h>
    #include <linux/of_device.h>
    -#include <linux/sort.h>
    -#include <linux/pm_wakeirq.h>

    #include <linux/mfd/ti_am335x_tscadc.h>

    @@ -55,6 +53,8 @@ struct *** {
    u32 inp_xp, inp_xn, inp_yp, inp_yn;
    u32 step_mask;
    u32 charge_delay;
    + u32 prev_x, prev_y, prev_z;
    + bool event_pending;
    };

    static unsigned int ***(struct *** *ts, unsigned int reg)
    @@ -206,68 +206,63 @@ static void ***(struct *** *ts_dev)
    am335x_tsc_se_set_cache(ts_dev->mfd_tscadc, ts_dev->step_mask);
    }

    -static int ***(const void *a, const void *b)
    -{
    - return *(int *)a - *(int *)b;
    -}
    -
    static void ***(struct *** *ts_dev,
    u32 *x, u32 *y, u32 *z1, u32 *z2)
    {
    - unsigned int yvals[7], xvals[7];
    - unsigned int i, xsum = 0, ysum = 0;
    + unsigned int fifocount = ***(ts_dev, REG_FIFO0CNT);
    + unsigned int prev_val_x = ~0, prev_val_y = ~0;
    + unsigned int prev_diff_x = ~0, prev_diff_y = ~0;
    + unsigned int read, diff;
    + unsigned int i, channel;
    unsigned int creads = ts_dev->coordinate_readouts;
    + unsigned int first_step = TOTAL_STEPS - (creads * 2 + 2);

    - for (i = 0; i < creads; i++) {
    - yvals[i] = ***(ts_dev, REG_FIFO0);
    - yvals[i] &= 0xfff;
    - }
    + *z1 = *z2 = 0;
    + if (fifocount % (creads * 2 + 2))
    + fifocount -= fifocount % (creads * 2 + 2);
    + /*
    + * Delta filter is used to remove large variations in sampled
    + * values from ADC. The filter tries to predict where the next
    + * coordinate could be. This is done by taking a previous
    + * coordinate and subtracting it form current one. Further the
    + * algorithm compares the difference with that of a present value,
    + * if true the value is reported to the sub system.
    + */
    + for (i = 0; i < fifocount; i++) {
    + read = ***(ts_dev, REG_FIFO0);
    +
    + channel = (read & 0xf0000) >> 16;
    + read &= 0xfff;
    + if (channel > first_step + creads + 2) {
    + diff = abs(read - prev_val_x);
    + if (diff < prev_diff_x) {
    + prev_diff_x = diff;
    + *x = read;
    + }
    + prev_val_x = read;

    - *z1 = ***(ts_dev, REG_FIFO0);
    - *z1 &= 0xfff;
    - *z2 = ***(ts_dev, REG_FIFO0);
    - *z2 &= 0xfff;
    + } else if (channel == first_step + creads + 1) {
    + *z1 = read;

    - for (i = 0; i < creads; i++) {
    - xvals[i] = ***(ts_dev, REG_FIFO0);
    - xvals[i] &= 0xfff;
    - }
    + } else if (channel == first_step + creads + 2) {
    + *z2 = read;

    - /*
    - * If co-ordinates readouts is less than 4 then
    - * report the average. In case of 4 or more
    - * readouts, sort the co-ordinate samples, drop
    - * min and max values and report the average of
    - * remaining values.
    - */
    - if (creads <= 3) {
    - for (i = 0; i < creads; i++) {
    - ysum += yvals[i];
    - xsum += xvals[i];
    - }
    - ysum /= creads;
    - xsum /= creads;
    - } else {
    - sort(yvals, creads, sizeof(unsigned int),
    - ***, NULL);
    - sort(xvals, creads, sizeof(unsigned int),
    - ***, NULL);
    - for (i = 1; i < creads - 1; i++) {
    - ysum += yvals[i];
    - xsum += xvals[i];
    + } else if (channel > first_step) {
    + diff = abs(read - prev_val_y);
    + if (diff < prev_diff_y) {
    + prev_diff_y = diff;
    + *y = read;
    + }
    + prev_val_y = read;
    }
    - ysum /= creads - 2;
    - xsum /= creads - 2;
    }
    - *y = ysum;
    - *x = xsum;
    }

    static irqreturn_t ***(int irq, void *dev)
    {
    struct *** *ts_dev = dev;
    struct input_dev *input_dev = ts_dev->input;
    - unsigned int fsm, status, irqclr = 0;
    + unsigned int status, irqclr = 0;
    unsigned int x = 0, y = 0;
    unsigned int z1, z2, z;

    @@ -275,21 +270,22 @@ static irqreturn_t ***(int irq, void *dev)
    if (status & IRQENB_HW_PEN) {
    ts_dev->pen_down = true;
    irqclr |= IRQENB_HW_PEN;
    - pm_stay_awake(ts_dev->mfd_tscadc->dev);
    }

    if (status & IRQENB_PENUP) {
    - fsm = ***(ts_dev, REG_ADCFSM);
    - if (fsm == ADCFSM_STEPID) {
    - ts_dev->pen_down = false;
    - input_report_key(input_dev, BTN_TOUCH, 0);
    - input_report_abs(input_dev, ABS_PRESSURE, 0);
    - input_sync(input_dev);
    - pm_relax(ts_dev->mfd_tscadc->dev);
    - } else {
    - ts_dev->pen_down = true;
    - }
    + ts_dev->pen_down = false;
    + input_report_key(input_dev, BTN_TOUCH, 0);
    + input_report_abs(input_dev, ABS_PRESSURE, 0);
    + input_sync(input_dev);
    irqclr |= IRQENB_PENUP;
    + ts_dev->event_pending = false;
    + } else if (ts_dev->event_pending == true) {
    + input_report_abs(input_dev, ABS_X, ts_dev->prev_x);
    + input_report_abs(input_dev, ABS_Y, ts_dev->prev_y);
    + input_report_abs(input_dev, ABS_PRESSURE, ts_dev->prev_z);
    + input_report_key(input_dev, BTN_TOUCH, 1);
    + input_sync(input_dev);
    + ts_dev->event_pending = false;
    }

    if (status & IRQENB_EOS)
    @@ -316,20 +312,17 @@ static irqreturn_t ***(int irq, void *dev)
    z = (z + 2047) >> 12;

    if (z <= MAX_12BIT) {
    - input_report_abs(input_dev, ABS_X, x);
    - input_report_abs(input_dev, ABS_Y, y);
    - input_report_abs(input_dev, ABS_PRESSURE, z);
    - input_report_key(input_dev, BTN_TOUCH, 1);
    - input_sync(input_dev);
    + ts_dev->prev_x = x;
    + ts_dev->prev_y = y;
    + ts_dev->prev_z = z;
    + ts_dev->event_pending = true;
    }
    }
    irqclr |= IRQENB_FIFO0THRES;
    }
    if (irqclr) {
    ***(ts_dev, REG_IRQSTATUS, irqclr);
    - if (status & IRQENB_EOS)
    - am335x_tsc_se_set_cache(ts_dev->mfd_tscadc,
    - ts_dev->step_mask);
    + am335x_tsc_se_set_cache(ts_dev->mfd_tscadc, ts_dev->step_mask);
    return IRQ_HANDLED;
    }
    return IRQ_NONE;
    @@ -367,21 +360,12 @@ static int ***(struct platform_device *pdev,
    */
    err = of_property_read_u32(node, "ti,coordinate-readouts",
    &ts_dev->coordinate_readouts);
    - if (err < 0) {
    - dev_warn(&pdev->dev, "please use 'ti,coordinate-readouts' instead\n");
    + if (err < 0)
    err = of_property_read_u32(node, "ti,coordiante-readouts",
    &ts_dev->coordinate_readouts);
    - }
    -
    if (err < 0)
    return err;

    - if (ts_dev->coordinate_readouts <= 0) {
    - dev_warn(&pdev->dev,
    - "invalid co-ordinate readouts, resetting it to 5\n");
    - ts_dev->coordinate_readouts = 5;
    - }
    -
    err = of_property_read_u32(node, "ti,charge-delay",
    &ts_dev->charge_delay);
    /*
    @@ -421,6 +405,7 @@ static int ***(struct platform_device *pdev)
    ts_dev->mfd_tscadc = tscadc_dev;
    ts_dev->input = input_dev;
    ts_dev->irq = tscadc_dev->irq;
    + ts_dev->event_pending = false;

    err = ***(pdev, ts_dev);
    if (err) {
    @@ -435,13 +420,6 @@ static int ***(struct platform_device *pdev)
    goto err_free_mem;
    }

    - if (device_may_wakeup(tscadc_dev->dev)) {
    - err = dev_pm_set_wake_irq(tscadc_dev->dev, ts_dev->irq);
    - if (err)
    - dev_err(&pdev->dev, "irq wake enable failed.\n");
    - }
    -
    - ***(ts_dev, REG_IRQSTATUS, IRQENB_MASK);
    ***(ts_dev, REG_IRQENABLE, IRQENB_FIFO0THRES);
    ***(ts_dev, REG_IRQENABLE, IRQENB_EOS);
    err = ***(ts_dev);
    @@ -472,7 +450,6 @@ static int ***(struct platform_device *pdev)
    return 0;

    err_free_irq:
    - dev_pm_clear_wake_irq(tscadc_dev->dev);
    free_irq(ts_dev->irq, ts_dev);
    err_free_mem:
    input_free_device(input_dev);
    @@ -485,7 +462,6 @@ static int ***(struct platform_device *pdev)
    struct *** *ts_dev = platform_get_drvdata(pdev);
    u32 steps;

    - dev_pm_clear_wake_irq(ts_dev->mfd_tscadc->dev);
    free_irq(ts_dev->irq, ts_dev);

    /* total steps followed by the enable mask */
    @@ -506,9 +482,9 @@ static int ***(struct device *dev)
    struct ti_tscadc_dev *tscadc_dev;
    unsigned int idle;

    + ts_dev->event_pending = false;
    tscadc_dev = ti_tscadc_dev_get(to_platform_device(dev));
    if (device_may_wakeup(tscadc_dev->dev)) {
    - ***(ts_dev, REG_IRQSTATUS, IRQENB_MASK);
    idle = ***(ts_dev, REG_IRQENABLE);
    ***(ts_dev, REG_IRQENABLE,
    (idle | IRQENB_HW_PEN));
    @@ -527,7 +503,6 @@ static int ***(struct device *dev)
    ***(ts_dev, REG_IRQWAKEUP,
    0x00);
    ***(ts_dev, REG_IRQCLR, IRQENB_HW_PEN);
    - pm_relax(ts_dev->mfd_tscadc->dev);
    }
    ***(ts_dev);
    ***(ts_dev, REG_FIFO0THR,
    @@ -555,6 +530,7 @@ static struct platform_driver ti_tsc_driver = {
    .remove = ***,
    .driver = {
    .name = "TI-am335x-tsc",
    + .owner = THIS_MODULE,
    .pm = ***,
    .of_match_table = ti_tsc_dt_ids,
    },
    diff --git a/drivers/mfd/ti_am335x_tscadc.c b/drivers/mfd/ti_am335x_tscadc.c
    index e4e4b22..5bc29d8 100644
    --- a/drivers/mfd/ti_am335x_tscadc.c
    +++ b/drivers/mfd/ti_am335x_tscadc.c
    @@ -14,6 +14,7 @@
    */

    #include <linux/module.h>
    +#include <linux/init.h>
    #include <linux/slab.h>
    #include <linux/err.h>
    #include <linux/io.h>
    @@ -24,9 +25,48 @@
    #include <linux/of.h>
    #include <linux/of_device.h>
    #include <linux/sched.h>
    -
    +#include <linux/of_irq.h>
    #include <linux/mfd/ti_am335x_tscadc.h>

    +static struct ti_mfdcell_prop tscadc_prop = {
    + .name_fr = "TI-am335x-tsc",
    + .compatible_fr = "ti,am3359-tsc",
    + .name_sc = "TI-am335x-adc",
    + .compatible_sc = "ti,am3359-adc"
    +};
    +
    +static struct ti_mfdcell_prop magadc_prop = {
    + .name_fr = "TI-am43xx-mag",
    + .compatible_fr = "ti,am4372-mag",
    + .name_sc = "TI-am43xx-adc",
    + .compatible_sc = "ti,am4372-adc"
    +};
    +
    +static const struct ti_tscadc_data tscdata = {
    + .use_mag = 0,
    + .clk_name = "adc_tsc_fck",
    + .clk_div = ADC_CLK,
    + .cell_prop = &tscadc_prop
    +};
    +
    +static const struct ti_tscadc_data magdata = {
    + .use_mag = 1,
    + .clk_name = "adc_mag_fck",
    + .clk_div = MAG_ADC_CLK,
    + .cell_prop = &magadc_prop
    +};
    +
    +static const struct of_device_id ti_tscadc_dt_ids[] = {
    + { .compatible = "ti,am3359-tscadc",
    + .data = &tscdata,
    + },
    + { .compatible = "ti,am4372-magadc",
    + .data = &magdata,
    + },
    + { }
    +};
    +MODULE_DEVICE_TABLE(of, ti_tscadc_dt_ids);
    +
    static unsigned int tscadc_readl(struct ti_tscadc_dev *tsadc, unsigned int reg)
    {
    unsigned int val;
    @@ -68,6 +108,12 @@ static void am335x_tscadc_need_adc(struct ti_tscadc_dev *tsadc)
    DEFINE_WAIT(wait);
    u32 reg;

    + /*
    + * disable TSC steps so it does not run while the ADC is using it. If
    + * write 0 while it is running (it just started or was already running)
    + * then it completes all steps that were enabled and stops then.
    + */
    + tscadc_writel(tsadc, REG_SE, 0);
    reg = tscadc_readl(tsadc, REG_ADCFSM);
    if (reg & SEQ_STATUS) {
    tsadc->adc_waiting = true;
    @@ -85,7 +131,7 @@ static void am335x_tscadc_need_adc(struct ti_tscadc_dev *tsadc)
    * busy applying the charge step.
    */
    reg = tscadc_readl(tsadc, REG_ADCFSM);
    - WARN_ON((reg & SEQ_STATUS) && !(reg & CHARGE_STEP));
    + WARN_ON(reg & SEQ_STATUS & (!CHARGE_STEP));
    tsadc->adc_waiting = false;
    }
    tsadc->adc_in_use = true;
    @@ -94,6 +140,7 @@ static void am335x_tscadc_need_adc(struct ti_tscadc_dev *tsadc)
    void am335x_tsc_se_set_once(struct ti_tscadc_dev *tsadc, u32 val)
    {
    spin_lock_irq(&tsadc->reg_lock);
    + tsadc->reg_se_cache |= val;
    am335x_tscadc_need_adc(tsadc);

    tscadc_writel(tsadc, REG_SE, val);
    @@ -123,12 +170,13 @@ void am335x_tsc_se_clr(struct ti_tscadc_dev *tsadc, u32 val)
    }
    EXPORT_SYMBOL_GPL(am335x_tsc_se_clr);

    -static void tscadc_idle_config(struct ti_tscadc_dev *config)
    +static void tscadc_idle_config(struct ti_tscadc_dev *config, bool use_mag)
    {
    - unsigned int idleconfig;
    + unsigned int idleconfig = 0;

    - idleconfig = STEPCONFIG_YNN | STEPCONFIG_INM_ADCREFM |
    - STEPCONFIG_INP_ADCREFM | STEPCONFIG_YPN;
    + if (!use_mag)
    + idleconfig = STEPCONFIG_YNN | STEPCONFIG_INM_ADCREFM |
    + STEPCONFIG_INP_ADCREFM | STEPCONFIG_YPN;

    tscadc_writel(config, REG_IDLECONFIG, idleconfig);
    }
    @@ -143,19 +191,31 @@ static int ti_tscadc_probe(struct platform_device *pdev)
    struct property *prop;
    const __be32 *cur;
    u32 val;
    - int err, ctrl;
    + int err = -EINVAL, ctrl;
    int clock_rate;
    int tsc_wires = 0, adc_channels = 0, total_channels;
    int readouts = 0;
    + int mag_tracks;
    + const struct ti_tscadc_data *data;
    + const struct of_device_id *id;

    if (!pdev->dev.of_node) {
    dev_err(&pdev->dev, "Could not find valid DT data.\n");
    return -EINVAL;
    }

    - node = of_get_child_by_name(pdev->dev.of_node, "tsc");
    - of_property_read_u32(node, "ti,wires", &tsc_wires);
    - of_property_read_u32(node, "ti,coordiante-readouts", &readouts);
    + id = of_match_device(ti_tscadc_dt_ids, &pdev->dev);
    + data = id->data;
    +
    + if (data->use_mag) {
    + node = of_get_child_by_name(pdev->dev.of_node, "mag");
    + of_property_read_u32(node, "ti,tracks", &mag_tracks);
    + tsc_wires = mag_tracks * 2;
    + } else {
    + node = of_get_child_by_name(pdev->dev.of_node, "tsc");
    + of_property_read_u32(node, "ti,wires", &tsc_wires);
    + of_property_read_u32(node, "ti,coordiante-readouts", &readouts);
    + }

    node = of_get_child_by_name(pdev->dev.of_node, "adc");
    of_property_for_each_u32(node, "ti,adc-channels", prop, cur, val) {
    @@ -176,11 +236,17 @@ static int ti_tscadc_probe(struct platform_device *pdev)
    return -EINVAL;
    }

    - if (readouts * 2 + 2 + adc_channels > 16) {
    + if (!data->use_mag && (readouts * 2 + 2 + adc_channels > 16)) {
    dev_err(&pdev->dev, "Too many step configurations requested\n");
    return -EINVAL;
    }

    + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    + if (!res) {
    + dev_err(&pdev->dev, "no memory resource defined.\n");
    + return -EINVAL;
    + }
    +
    /* Allocate memory for device */
    tscadc = devm_kzalloc(&pdev->dev,
    sizeof(struct ti_tscadc_dev), GFP_KERNEL);
    @@ -190,17 +256,38 @@ static int ti_tscadc_probe(struct platform_device *pdev)
    }
    tscadc->dev = &pdev->dev;

    - err = platform_get_irq(pdev, 0);
    - if (err < 0) {
    - dev_err(&pdev->dev, "no irq ID is specified.\n");
    - goto ret;
    - } else
    - tscadc->irq = err;
    + /* JR: modified below logic to allow magadc to get a valid IRQ */
    + /*if (!data->use_mag) {*/
    + err = platform_get_irq(pdev, 0);
    + if (err < 0) {
    + dev_err(&pdev->dev, "no irq ID is specified.\n");
    + goto ret;
    + } else
    + tscadc->irq = err;
    + /*} else { */
    + if (data->use_mag) {
    + tscadc->magirq = of_irq_to_resource(pdev->dev.of_node, 0, NULL);
    + if (!tscadc->magirq) {
    + dev_err(&pdev->dev, "can't translate OF irq value\n");
    + goto ret;
    + }
    + }
    + /*}*/

    - res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    - tscadc->tscadc_base = devm_ioremap_resource(&pdev->dev, res);
    - if (IS_ERR(tscadc->tscadc_base))
    - return PTR_ERR(tscadc->tscadc_base);
    + res = devm_request_mem_region(&pdev->dev,
    + res->start, resource_size(res), pdev->name);
    + if (!res) {
    + dev_err(&pdev->dev, "failed to reserve registers.\n");
    + return -EBUSY;
    + }
    +
    + tscadc->tscadc_base = devm_ioremap(&pdev->dev,
    + res->start, resource_size(res));
    + if (!tscadc->tscadc_base) {
    + dev_err(&pdev->dev, "failed to map registers.\n");
    + return -ENOMEM;
    + }
    + tscadc->phy_base = res->start;

    tscadc->regmap_tscadc = devm_regmap_init_mmio(&pdev->dev,
    tscadc->tscadc_base, &tscadc_regmap_config);
    @@ -224,7 +311,7 @@ static int ti_tscadc_probe(struct platform_device *pdev)
    * The TSC_ADC_SS controller design assumes the OCP clock is
    * at least 6x faster than the ADC clock.
    */
    - clk = clk_get(&pdev->dev, "adc_tsc_fck");
    + clk = clk_get(&pdev->dev, data->clk_name);
    if (IS_ERR(clk)) {
    dev_err(&pdev->dev, "failed to get TSC fck\n");
    err = PTR_ERR(clk);
    @@ -239,33 +326,44 @@ static int ti_tscadc_probe(struct platform_device *pdev)
    tscadc_writel(tscadc, REG_CLKDIV, tscadc->clk_div);

    /* Set the control register bits */
    - ctrl = CNTRLREG_STEPCONFIGWRT | CNTRLREG_STEPID;
    - tscadc_writel(tscadc, REG_CTRL, ctrl);
    + if (!data->use_mag && (tsc_wires > 0))
    + ctrl = CNTRLREG_STEPCONFIGWRT | CNTRLREG_STEPID;

    - /* Set register bits for Idle Config Mode */
    - if (tsc_wires > 0) {
    - tscadc->tsc_wires = tsc_wires;
    - if (tsc_wires == 5)
    - ctrl |= CNTRLREG_5WIRE | CNTRLREG_TSCENB;
    - else
    - ctrl |= CNTRLREG_4WIRE | CNTRLREG_TSCENB;
    - tscadc_idle_config(tscadc);
    + if (!data->use_mag) {
    + ctrl |= CNTRLREG_4WIRE | CNTRLREG_TSCENB;
    + tscadc_writel(tscadc, REG_CTRL, ctrl);
    }
    + /* JR added to make magadc iio devices to work */
    + if (data->use_mag && (adc_channels > 0)) {
    + ctrl = CNTRLREG_STEPID;
    + /* uncomment next line to power down and bypass preamp */
    + //ctrl |= CNTRLREG_PREAMP_PWRDOWN | CNTRLREG_PREAMP_BYPASS;
    + tscadc_writel(tscadc, REG_CTRL, ctrl);
    + }
    +
    + /* Set register bits for Idle Config Mode for MAGADC or TSCADC */
    + if (tsc_wires > 0)
    + tscadc_idle_config(tscadc, data->use_mag);

    /* Enable the TSC module enable bit */
    + ctrl = tscadc_readl(tscadc, REG_CTRL);
    ctrl |= CNTRLREG_TSCSSENB;
    tscadc_writel(tscadc, REG_CTRL, ctrl);

    tscadc->used_cells = 0;
    tscadc->tsc_cell = -1;
    tscadc->adc_cell = -1;
    + tscadc->mag_cell = -1;

    - /* TSC Cell */
    + /* TSC or MAG Cell */
    if (tsc_wires > 0) {
    - tscadc->tsc_cell = tscadc->used_cells;
    + if (data->use_mag)
    + tscadc->mag_cell = tscadc->used_cells;
    + else
    + tscadc->tsc_cell = tscadc->used_cells;
    cell = &tscadc->cells[tscadc->used_cells++];
    - cell->name = "TI-am335x-tsc";
    - cell->of_compatible = "ti,am3359-tsc";
    + cell->name = data->cell_prop->name_fr;
    + cell->of_compatible = data->cell_prop->compatible_fr;
    cell->platform_data = &tscadc;
    cell->pdata_size = sizeof(tscadc);
    }
    @@ -274,8 +372,8 @@ static int ti_tscadc_probe(struct platform_device *pdev)
    if (adc_channels > 0) {
    tscadc->adc_cell = tscadc->used_cells;
    cell = &tscadc->cells[tscadc->used_cells++];
    - cell->name = "TI-am335x-adc";
    - cell->of_compatible = "ti,am3359-adc";
    + cell->name = data->cell_prop->name_sc;
    + cell->of_compatible = data->cell_prop->compatible_sc;
    cell->platform_data = &tscadc;
    cell->pdata_size = sizeof(tscadc);
    }
    @@ -302,49 +400,92 @@ static int ti_tscadc_remove(struct platform_device *pdev)

    tscadc_writel(tscadc, REG_SE, 0x00);

    + mfd_remove_devices(tscadc->dev);
    +
    pm_runtime_put_sync(&pdev->dev);
    pm_runtime_disable(&pdev->dev);

    - mfd_remove_devices(tscadc->dev);
    -
    return 0;
    }

    #ifdef CONFIG_PM
    static int tscadc_suspend(struct device *dev)
    {
    - struct ti_tscadc_dev *tscadc_dev = dev_get_drvdata(dev);
    + struct platform_device *pdev = to_platform_device(dev);
    + struct ti_tscadc_dev *tscadc;
    +
    + const struct ti_tscadc_data *data;
    + const struct of_device_id *id;
    + id = of_match_device(ti_tscadc_dt_ids, dev);
    + data = id->data;

    - tscadc_writel(tscadc_dev, REG_SE, 0x00);
    + /* To decrement the device's usage count */
    pm_runtime_put_sync(dev);

    + tscadc = platform_get_drvdata(pdev);
    +
    + tscadc_writel(tscadc, REG_SE, 0x00);
    +
    + pm_runtime_put_sync(&pdev->dev);
    + pm_runtime_disable(&pdev->dev);
    +
    return 0;
    }

    static int tscadc_resume(struct device *dev)
    {
    - struct ti_tscadc_dev *tscadc_dev = dev_get_drvdata(dev);
    - u32 ctrl;
    + struct platform_device *pdev = to_platform_device(dev);
    + struct ti_tscadc_dev *tscadc;
    + struct clk *clk;
    + int err = -EINVAL;
    + int clk_value, clock_rate;
    + int clkVal = 0;
    + const struct ti_tscadc_data *data;
    + const struct of_device_id *id;

    + /* To increment the device's usage count */
    pm_runtime_get_sync(dev);

    - /* context restore */
    - ctrl = CNTRLREG_STEPCONFIGWRT | CNTRLREG_STEPID;
    - tscadc_writel(tscadc_dev, REG_CTRL, ctrl);
    + tscadc = platform_get_drvdata(pdev);
    + if (!pdev->dev.of_node) {
    + dev_err(&pdev->dev, "Could not find valid DT data.\n");
    + return -EINVAL;
    + }

    - if (tscadc_dev->tsc_cell != -1) {
    - if (tscadc_dev->tsc_wires == 5)
    - ctrl |= CNTRLREG_5WIRE | CNTRLREG_TSCENB;
    - else
    - ctrl |= CNTRLREG_4WIRE | CNTRLREG_TSCENB;
    - tscadc_idle_config(tscadc_dev);
    + id = of_match_device(ti_tscadc_dt_ids, &pdev->dev);
    + data = id->data;
    +
    + tscadc->dev = &pdev->dev;
    +
    + /* Enable the clocks */
    + pm_runtime_enable(&pdev->dev);
    + pm_runtime_get_sync(&pdev->dev);
    +
    + /*
    + * The TSC_ADC_Subsystem has 2 clock domains
    + * OCP_CLK and ADC_CLK.
    + */
    + clk = devm_clk_get(&pdev->dev, data->clk_name);
    + if (IS_ERR(clk)) {
    + dev_err(&pdev->dev, "failed to get TSC fck\n");
    + err = PTR_ERR(clk);
    + goto err_disable_clk;
    }
    - ctrl |= CNTRLREG_TSCSSENB;
    - tscadc_writel(tscadc_dev, REG_CTRL, ctrl);
    + clock_rate = clk_get_rate(clk);
    + clk_value = clock_rate / data->clk_div;

    - tscadc_writel(tscadc_dev, REG_CLKDIV, tscadc_dev->clk_div);
    + /* TSCADC_CLKDIV needs to be configured to the value minus 1 */
    + clk_value = clk_value - 1;
    + tscadc_writel(tscadc, REG_CLKDIV, clk_value);
    + clkVal = tscadc_readl(tscadc, REG_CLKDIV);

    + platform_set_drvdata(pdev, tscadc);
    return 0;
    +
    +err_disable_clk:
    + pm_runtime_put_sync(&pdev->dev);
    + pm_runtime_disable(&pdev->dev);
    + return -EINVAL;
    }

    static const struct dev_pm_ops tscadc_pm_ops = {
    @@ -356,15 +497,10 @@ static const struct dev_pm_ops tscadc_pm_ops = {
    #define TSCADC_PM_OPS NULL
    #endif

    -static const struct of_device_id ti_tscadc_dt_ids[] = {
    - { .compatible = "ti,am3359-tscadc", },
    - { }
    -};
    -MODULE_DEVICE_TABLE(of, ti_tscadc_dt_ids);
    -
    static struct platform_driver ti_tscadc_driver = {
    .driver = {
    .name = "ti_am3359-tscadc",
    + .owner = THIS_MODULE,
    .pm = TSCADC_PM_OPS,
    .of_match_table = ti_tscadc_dt_ids,
    },
    diff --git a/include/linux/mfd/ti_am335x_tscadc.h b/include/linux/mfd/ti_am335x_tscadc.h
    index 05ce1d1..04e731f 100644
    --- a/include/linux/mfd/ti_am335x_tscadc.h
    +++ b/include/linux/mfd/ti_am335x_tscadc.h
    @@ -39,6 +39,17 @@
    #define REG_FIFO0 0x100
    #define REG_FIFO1 0x200

    +/* Register specific for MAGADC, Which is a reuse IP of TSCADC */
    +#define REG_ADCREVISION 0x000
    +#define REG_SYSCONFIG 0x010
    +#define REG_EOI 0x020
    +#define REG_ADCRANGE 0x048
    +#define REG_SWIPECOMPARE12 0x05C
    +#define REG_SWIPECOMPARE34 0x060
    +#define REG_DMAENABLESET 0x038
    +#define REG_DMAENABLECLR 0x03C
    +#define REG_DMA0REQ 0x0EC
    +
    /* Register Bitfields */
    /* IRQ wakeup enable */
    #define IRQWKUP_ENB BIT(0)
    @@ -60,7 +71,15 @@
    #define IRQENB_FIFO1OVRRUN BIT(6)
    #define IRQENB_FIFO1UNDRFLW BIT(7)
    #define IRQENB_PENUP BIT(9)
    -#define IRQENB_MASK (0x7FF)
    +
    +/* IRQ enable for MAGADC */
    +#define IRQENB_END_OF_SEQ BIT(1)
    +#define IRQENB_FIFO0_OVERRUN BIT(3)
    +#define IRQENB_FIFO0_UNDERFLOW BIT(4)
    +#define IRQENB_FIFO1_OVERRUN BIT(6)
    +#define IRQENB_FIFO1_UNDERFLOW BIT(7)
    +#define IRQENB_OUT_OF_RANGE BIT(8)
    +#define IRQENB_START_OF_SWIPE BIT(9)

    /* Step Configuration */
    #define STEPCONFIG_MODE_MASK (3 << 0)
    @@ -85,6 +104,35 @@
    #define STEPCONFIG_INP_ADCREFM STEPCONFIG_INP(8)
    #define STEPCONFIG_FIFO1 BIT(26)

    +/* Additional StepConfigs for MAGADC IP */
    +#define STEPCONFIG_GAIN_CTRL4_MASK (0x30000000)
    +#define STEPCONFIG_GAIN_CTRL3_MASK (0x600)
    +#define STEPCONFIG_GAIN_CTRL2_MASK (0x180)
    +#define STEPCONFIG_GAIN_CTRL1_MASK (0x60)
    +#define STEPCONFIG_THRES_CMP_EN ((0x1) << 11)
    +#define STEPCONFIG_THRES_PTR_MASK (0xC0000000)
    +#define STEPCONFIG_THRES_PTR(val) ((val) << 30)
    +#define STEPCONFIG_THRESREG2_TRIGGER STEPCONFIG_THRES_PTR(2)
    +#define STEPCONFIG_THRESREG3_TRIGGER STEPCONFIG_THRES_PTR(3)
    +#define STEPCONFIG_THRESREG4_TRIGGER STEPCONFIG_THRES_PTR(4)
    +#define STEPCONFIG_DIFFCTRL(val) ((val) << 25)
    +#define STEPCONFIG_DIFFCTRL_SELC STEPCONFIG_DIFFCTRL(1)
    +#define STEPCONFIG_MODE_HWSYNCCONT STEPCONFIG_MODE(3)
    +#define STEPCONFIG_MODE_SWSYNCCONT STEPCONFIG_MODE(1)
    +#define STEPCONFIG_RFM_ADCREFM ((3) << 23)
    +#define STEPCONFIG_RFP_VDD (0)
    +#define STEPCONFIG_AVG_4 STEPCONFIG_AVG(2)
    +
    +/* Swipe threshold Masks for MAGADC */
    +#define SWIPETHRES_DATA1_VAL(val) ((val) << 16)
    +#define SWIPETHRES_DATA2_VAL(val) ((val) << 0)
    +#define SWIPETHRES_DATA3_VAL(val) ((val) << 16)
    +#define SWIPETHRES_DATA4_VAL(val) ((val) << 0)
    +#define SWIPETHRES_DATA1_VAL_MASK (0x0FFF0000U)
    +#define SWIPETHRES_DATA2_VAL_MASK (0x00000FFFU)
    +#define SWIPETHRES_DATA3_VAL_MASK (0x0FFF0000U)
    +#define SWIPETHRES_DATA4_VAL_MASK (0x00000FFFU)
    +
    /* Delay register */
    #define STEPDELAY_OPEN_MASK (0x3FFFF << 0)
    #define STEPDELAY_OPEN(val) ((val) << 0)
    @@ -93,6 +141,10 @@
    #define STEPDELAY_SAMPLE(val) ((val) << 24)
    #define STEPCONFIG_SAMPLEDLY STEPDELAY_SAMPLE(0)

    +/* Delay added for MAGADC */
    +#define MAGADC_SAMPLEDELAY (STEPDELAY_SAMPLE(1))
    +#define MAGADC_OPENDELAY (STEPDELAY_OPEN(0))
    +
    /* Charge Config */
    #define STEPCHARGE_RFP_MASK (7 << 12)
    #define STEPCHARGE_RFP(val) ((val) << 12)
    @@ -123,6 +175,11 @@
    #define CNTRLREG_8WIRE CNTRLREG_AFE_CTRL(3)
    #define CNTRLREG_TSCENB BIT(7)

    +/*Control registers bitfields for MAGADC IP */
    +#define CNTRLREG_MAGADCENB BIT(0)
    +#define CNTRLREG_PREAMP_PWRDOWN BIT(5)
    +#define CNTRLREG_PREAMP_BYPASS BIT(6)
    +
    /* FIFO READ Register */
    #define FIFOREAD_DATA_MASK (0xfff << 0)
    #define FIFOREAD_CHNLID_MASK (0xf << 16)
    @@ -132,6 +189,7 @@
    #define CHARGE_STEP 0x11

    #define ADC_CLK 3000000
    +#define MAG_ADC_CLK 1600000
    #define TOTAL_STEPS 16
    #define TOTAL_CHANNELS 8
    #define FIFO1_THRESHOLD 19
    @@ -139,16 +197,16 @@
    /*
    * time in us for processing a single channel, calculated as follows:
    *
    - * max num cycles = open delay + (sample delay + conv time) * averaging
    + * num cycles = open delay + (sample delay + conv time) * averaging
    *
    - * max num cycles: 262143 + (255 + 13) * 16 = 266431
    + * num cycles: 152 + (1 + 13) * 16 = 376
    *
    * clock frequency: 26MHz / 8 = 3.25MHz
    * clock period: 1 / 3.25MHz = 308ns
    *
    - * max processing time: 266431 * 308ns = 83ms(approx)
    + * processing time: 376 * 308ns = 116us
    */
    -#define IDLE_TIMEOUT 83 /* milliseconds */
    +#define IDLE_TIMEOUT 116 /* microsec */

    #define TSCADC_CELLS 2

    @@ -156,11 +214,14 @@ struct ti_tscadc_dev {
    struct device *dev;
    struct regmap *regmap_tscadc;
    void __iomem *tscadc_base;
    + unsigned long phy_base;
    int irq;
    + int magirq;
    int used_cells; /* 1-2 */
    - int tsc_wires;
    int tsc_cell; /* -1 if not used */
    int adc_cell; /* -1 if not used */
    + int mag_cell; /* -1 if not used */
    + int use_mag; /* 1 for magadc instance, 0 for tsc instance */
    struct mfd_cell cells[TSCADC_CELLS];
    u32 reg_se_cache;
    bool adc_waiting;
    @@ -172,8 +233,26 @@ struct ti_tscadc_dev {
    /* tsc device */
    struct *** *tsc;

    + /* mag device */
    + struct timag *mag;
    +
    /* adc device */
    struct adc_device *adc;
    + int dma_rx_chnum;
    +};
    +
    +struct ti_mfdcell_prop {
    + const char *name_fr;
    + const char *compatible_fr;
    + const char *name_sc;
    + const char *compatible_sc;
    +};
    +
    +struct ti_tscadc_data {
    + bool use_mag;
    + const char *clk_name;
    + int clk_div;
    + struct ti_mfdcell_prop *cell_prop;
    };

    static inline struct ti_tscadc_dev *ti_tscadc_dev_get(struct platform_device *p)
    diff --git a/include/linux/ti_magadc.h b/include/linux/ti_magadc.h
    new file mode 100644
    index 0000000..5c1e94c
    --- /dev/null
    +++ b/include/linux/ti_magadc.h
    @@ -0,0 +1,111 @@
    +/*
    + * ti_magadc.h - Header file for magadc driver
    + *
    + * Copyright (C) 2013 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.
    + */
    +
    +#ifndef __TI_MAGADC_H__
    +#define __TI_MAGADC_H__
    +
    +#include <linux/types.h>
    +#include <linux/ioctl.h>
    +#include <stdbool.h>
    +
    +#define MAGADC_IOCTL 0xAE
    +#define MAGADC_SIGID 50
    +#define MAGADC_SIGINT 99
    +
    +#define MAGADC_STORE_RW_ADDR _IOR(MAGADC_IOCTL, 0, int)
    +#define MAGADC_STORE_RO_ADDR _IO(MAGADC_IOCTL, 1)
    +#define MAGADC_READ_RW_ADDR _IO(MAGADC_IOCTL, 2)
    +#define MAGADC_READ_RO_ADDR _IO(MAGADC_IOCTL, 3)
    +#define MAGADC_COPY_RAW_DATA _IO(MAGADC_IOCTL, 4)
    +
    +
    +/* This denote raw data for each track from ADC */
    +#define MAX_TRACK_DATA (30000)
    +#define MAX_NO_OF_TRACKS (3)
    +#define MAX_TRACK_DATA_BITS (1500)
    +#define MAX_TRACK_DATA_CHARS (250)
    +#define MAX_NO_OF_PEAKS (1500 * 2)
    +#define MAX_CHARS_REPORT (100)
    +#define MAX_INPUT_EVENTS (300)
    +/* Size of total Raw data collected from ADC */
    +#define MAX_ADC_DATA_RAW (MAX_TRACK_DATA * MAX_NO_OF_TRACKS)
    +
    +#define MAGADC_TRACK1 (2)
    +#define MAGADC_TRACK2 (1)
    +#define MAGADC_TRACK3 (0)
    +#define MAGADC_SWIPETHRES_VAL (2200)
    +#define MAGADC_DMA0_THRESHOLD (31)
    +#define MAGADC_ZERO_VAL (2048)
    +#define MAGADC_TRACK1_OFFSET (32)
    +#define MAGADC_TRACK2_OFFSET (48)
    +#define MAGADC_TRACK1_BITPERCHAR (6)
    +#define MAGADC_TRACK2_BITPERCHAR (4)
    +#define MAGADC_IOCTL_REGISTER_PID 1
    +#define MAGADC_IOCTL_COPY_DATA 2
    +
    +enum timag_thresdata {
    + MAGADC_SWIPE_THRESDATA1 = 0,
    + MAGADC_SWIPE_THRESDATA2,
    + MAGADC_SWIPE_THRESDATA3,
    + MAGADC_SWIPE_THRESDATA4
    +};
    +/*
    + * F2F decoding support.
    + *
    + * 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.
    + */
    +
    +struct peak_data {
    + unsigned short value;
    + int index;
    +};
    +struct timag_decode_data {
    + unsigned char asciiData[MAX_NO_OF_TRACKS][MAX_TRACK_DATA_CHARS];
    +};
    +struct timag_track_data {
    + unsigned int num_samples;
    + unsigned int num_peaks;
    + unsigned int num_bits;
    + unsigned int num_chars;
    + unsigned int badchars;
    + unsigned short rawsample[MAX_TRACK_DATA *
    + sizeof(unsigned int)];
    + unsigned char bitdata[MAX_TRACK_DATA_BITS];
    + struct peak_data peakdata[MAX_NO_OF_PEAKS *
    + sizeof(struct peak_data)];
    +};
    +
    +struct bit_data_prop {
    + int bitperchar;
    + int bitcount;
    + unsigned char offset;
    + bool paritycheck;
    + char endchar;
    +};
    +
    +struct timag_track {
    + struct timag_track_data tracks[MAX_NO_OF_TRACKS];
    + bool sentinel_pass[MAX_NO_OF_TRACKS];
    + struct bit_data_prop bitprop[MAX_NO_OF_TRACKS];
    + bool is_swiped;
    + unsigned int adc_data[MAX_ADC_DATA_RAW * sizeof(int)];
    + unsigned int total_samples;
    + unsigned int num_tracks;
    +};
    +
    +
    +#endif /* __TI_MAGADC_H__ */
    diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild
    index 224be60..599375f 100644
    --- a/include/uapi/linux/Kbuild
    +++ b/include/uapi/linux/Kbuild
    @@ -468,3 +468,4 @@ header-y += xilinx-v4l2-controls.h
    header-y += zorro.h
    header-y += zorro_ids.h
    header-y += userfaultfd.h
    +header-y += ti_magadc.h
    --
    1.9.1
  • 在差分模式抗干扰不是更好吗