TI中文支持网
TI专业的中文技术问题搜集分享网站

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

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

Shine:

请到ADC1驱动源码里找到STEPCONFIGx寄存器配置DIFF_CNTRL位为0 -> Single Ended,重新编译内核。

user4844379:

回复 Shine:

/* 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_4STEPCONFIG_AVG(2)

是否是将STEPCONFIG_DIFFCTRL(1) 中 1改为0

Shine:

回复 user4844379:

是的。

user4844379:

回复 Shine:

已经试过改成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.txtcreate mode 100644 drivers/input/misc/ti_magadc.ccreate 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_OFFSET0x0248#define AM43XX_CM_PER_QSPI_CLKCTRL_OFFSET0x0258#define AM43XX_CM_PER_GPMC_CLKCTRL_OFFSET0x0220
+#define AM43XX_CM_PER_MAGADC_CLKCTRL_OFFSET0x0230#define AM43XX_CM_PER_MCASP0_CLKCTRL_OFFSET0x0238#define AM43XX_CM_PER_MCASP1_CLKCTRL_OFFSET0x0240#define AM43XX_CM_PER_L4LS_CLKCTRL_OFFSET0x0420
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;interr;
+ u32val;
+ intchannels = 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_HAPTICSTo compile this driver as a module, choose M here: themodule 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.oobj-$(CONFIG_INPUT_XEN_KBDDEV_FRONTEND) += xen-kbdfront.oobj-$(CONFIG_INPUT_YEALINK)+= yealink.oobj-$(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 {
+ structinput_dev *input;
+ structti_tscadc_dev *mfd_magadc;
+ struct platform_device *pdev;
+ structtimag_track mag_track;
+ struct work_struct decode_work;
+ spinlock_tlock;
+ atomic_tshutting_down;
+ intdma_rx_chnum;
+ struct dma_chan*dma_rx;
+ long intphy_base;
+ dma_addr_tdma_address;
+ intlen;
+ intirq;
+ unsigned inttracks;
+};
+/*
+ * 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 *** {u32inp_xp, inp_xn, inp_yp, inp_yn;u32step_mask;u32charge_delay;
+ u32prev_x, prev_y, prev_z;
+ boolevent_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;u32val;
– interr, ctrl;
+ interr = -EINVAL, ctrl;intclock_rate;inttsc_wires = 0, adc_channels = 0, total_channels;intreadouts = 0;
+ intmag_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_PMstatic 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;
+ interr = -EINVAL;
+ intclk_value, clock_rate;
+ intclkVal = 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_FIFO00x100#define REG_FIFO10x200
+/* Register specific for MAGADC, Which is a reuse IP of TSCADC */
+#define REG_ADCREVISION0x000
+#define REG_SYSCONFIG0x010
+#define REG_EOI0x020
+#define REG_ADCRANGE0x048
+#define REG_SWIPECOMPARE120x05C
+#define REG_SWIPECOMPARE340x060
+#define REG_DMAENABLESET0x038
+#define REG_DMAENABLECLR0x03C
+#define REG_DMA0REQ0x0EC
+/* Register Bitfields *//* IRQ wakeup enable */#define IRQWKUP_ENBBIT(0)
@@ -60,7 +71,15 @@#define IRQENB_FIFO1OVRRUN BIT(6)#define IRQENB_FIFO1UNDRFLW BIT(7)#define IRQENB_PENUPBIT(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_4STEPCONFIG_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_8WIRECNTRLREG_AFE_CTRL(3)#define CNTRLREG_TSCENBBIT(7)
+/*Control registers bitfieldsfor MAGADC IP */
+#define CNTRLREG_MAGADCENBBIT(0)
+#define CNTRLREG_PREAMP_PWRDOWN BIT(5)
+#define CNTRLREG_PREAMP_BYPASSBIT(6)
+/* FIFO READ Register */#define FIFOREAD_DATA_MASK (0xfff << 0)#define FIFOREAD_CHNLID_MASK (0xf << 16)
@@ -132,6 +189,7 @@#define CHARGE_STEP0x11#define ADC_CLK3000000
+#define MAG_ADC_CLK1600000#define TOTAL_STEPS16#define TOTAL_CHANNELS8#define FIFO1_THRESHOLD19
@@ -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_CELLS2
@@ -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 {
+ booluse_mag;
+ const char*clk_name;
+ intclk_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_SIGINT99
+
+#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_PID1
+#define MAGADC_IOCTL_COPY_DATA2
+
+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 intnum_samples;
+ unsigned intnum_peaks;
+ unsigned intnum_bits;
+ unsigned intnum_chars;
+ unsigned intbadchars;
+ unsigned shortrawsample[MAX_TRACK_DATA *
+sizeof(unsigned int)];
+ unsigned charbitdata[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];
+ boolsentinel_pass[MAX_NO_OF_TRACKS];
+ struct bit_data_prop bitprop[MAX_NO_OF_TRACKS];
+ boolis_swiped;
+ unsigned intadc_data[MAX_ADC_DATA_RAW * sizeof(int)];
+ unsigned inttotal_samples;
+ unsigned intnum_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.hheader-y += zorro.hheader-y += zorro_ids.hheader-y += userfaultfd.h
+header-y += ti_magadc.h

1.9.1

user4844379:

回复 Shine:

改为 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.txtcreate mode 100644 drivers/input/misc/ti_magadc.ccreate 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_OFFSET0x0248#define AM43XX_CM_PER_QSPI_CLKCTRL_OFFSET0x0258#define AM43XX_CM_PER_GPMC_CLKCTRL_OFFSET0x0220
+#define AM43XX_CM_PER_MAGADC_CLKCTRL_OFFSET0x0230#define AM43XX_CM_PER_MCASP0_CLKCTRL_OFFSET0x0238#define AM43XX_CM_PER_MCASP1_CLKCTRL_OFFSET0x0240#define AM43XX_CM_PER_L4LS_CLKCTRL_OFFSET0x0420
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;interr;
+ u32val;
+ intchannels = 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_HAPTICSTo compile this driver as a module, choose M here: themodule 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.oobj-$(CONFIG_INPUT_XEN_KBDDEV_FRONTEND) += xen-kbdfront.oobj-$(CONFIG_INPUT_YEALINK)+= yealink.oobj-$(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 {
+ structinput_dev *input;
+ structti_tscadc_dev *mfd_magadc;
+ struct platform_device *pdev;
+ structtimag_track mag_track;
+ struct work_struct decode_work;
+ spinlock_tlock;
+ atomic_tshutting_down;
+ intdma_rx_chnum;
+ struct dma_chan*dma_rx;
+ long intphy_base;
+ dma_addr_tdma_address;
+ intlen;
+ intirq;
+ unsigned inttracks;
+};
+/*
+ * 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 *** {u32inp_xp, inp_xn, inp_yp, inp_yn;u32step_mask;u32charge_delay;
+ u32prev_x, prev_y, prev_z;
+ boolevent_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;u32val;
– interr, ctrl;
+ interr = -EINVAL, ctrl;intclock_rate;inttsc_wires = 0, adc_channels = 0, total_channels;intreadouts = 0;
+ intmag_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_PMstatic 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;
+ interr = -EINVAL;
+ intclk_value, clock_rate;
+ intclkVal = 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_FIFO00x100#define REG_FIFO10x200
+/* Register specific for MAGADC, Which is a reuse IP of TSCADC */
+#define REG_ADCREVISION0x000
+#define REG_SYSCONFIG0x010
+#define REG_EOI0x020
+#define REG_ADCRANGE0x048
+#define REG_SWIPECOMPARE120x05C
+#define REG_SWIPECOMPARE340x060
+#define REG_DMAENABLESET0x038
+#define REG_DMAENABLECLR0x03C
+#define REG_DMA0REQ0x0EC
+/* Register Bitfields *//* IRQ wakeup enable */#define IRQWKUP_ENBBIT(0)
@@ -60,7 +71,15 @@#define IRQENB_FIFO1OVRRUN BIT(6)#define IRQENB_FIFO1UNDRFLW BIT(7)#define IRQENB_PENUPBIT(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_4STEPCONFIG_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_8WIRECNTRLREG_AFE_CTRL(3)#define CNTRLREG_TSCENBBIT(7)
+/*Control registers bitfieldsfor MAGADC IP */
+#define CNTRLREG_MAGADCENBBIT(0)
+#define CNTRLREG_PREAMP_PWRDOWN BIT(5)
+#define CNTRLREG_PREAMP_BYPASSBIT(6)
+/* FIFO READ Register */#define FIFOREAD_DATA_MASK (0xfff << 0)#define FIFOREAD_CHNLID_MASK (0xf << 16)
@@ -132,6 +189,7 @@#define CHARGE_STEP0x11#define ADC_CLK3000000
+#define MAG_ADC_CLK1600000#define TOTAL_STEPS16#define TOTAL_CHANNELS8#define FIFO1_THRESHOLD19
@@ -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_CELLS2
@@ -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 {
+ booluse_mag;
+ const char*clk_name;
+ intclk_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_SIGINT99
+
+#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_PID1
+#define MAGADC_IOCTL_COPY_DATA2
+
+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 intnum_samples;
+ unsigned intnum_peaks;
+ unsigned intnum_bits;
+ unsigned intnum_chars;
+ unsigned intbadchars;
+ unsigned shortrawsample[MAX_TRACK_DATA *
+sizeof(unsigned int)];
+ unsigned charbitdata[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];
+ boolsentinel_pass[MAX_NO_OF_TRACKS];
+ struct bit_data_prop bitprop[MAX_NO_OF_TRACKS];
+ boolis_swiped;
+ unsigned intadc_data[MAX_ADC_DATA_RAW * sizeof(int)];
+ unsigned inttotal_samples;
+ unsigned intnum_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.hheader-y += zorro.hheader-y += zorro_ids.hheader-y += userfaultfd.h
+header-y += ti_magadc.h

1.9.1

yongqing wang:

回复 user4844379:

在差分模式抗干扰不是更好吗

赞(0)
未经允许不得转载:TI中文支持网 » Am4378 ADC1 如何设定为默认单端模式?
分享到: 更多 (0)