mfd: ti_tscadc: Add support for TI's TSC/ADC MFDevice
authorPatil, Rachna <rachna@ti.com>
Tue, 16 Oct 2012 07:25:43 +0000 (12:55 +0530)
committerSamuel Ortiz <sameo@linux.intel.com>
Mon, 5 Nov 2012 22:50:27 +0000 (23:50 +0100)
Add the mfd core driver which supports touchscreen
and ADC.
With this patch we are only adding infrastructure to
support the MFD clients.

Signed-off-by: Patil, Rachna <rachna@ti.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
drivers/mfd/Kconfig
drivers/mfd/Makefile
drivers/mfd/ti_am335x_tscadc.c [new file with mode: 0644]
include/linux/mfd/ti_am335x_tscadc.h [new file with mode: 0644]

index acab3ef8a310efb611ccb30868c7f85474807d86..9bba7f78ff36c95e7eb6f4e4683b0755e5d67ab5 100644 (file)
@@ -94,6 +94,17 @@ config MFD_TI_SSP
          To compile this driver as a module, choose M here: the
          module will be called ti-ssp.
 
+config MFD_TI_AM335X_TSCADC
+       tristate "TI ADC / Touch Screen chip support"
+       select MFD_CORE
+       select REGMAP
+       select REGMAP_MMIO
+       help
+         If you say yes here you get support for Texas Instruments series
+         of Touch Screen /ADC chips.
+         To compile this driver as a module, choose M here: the
+         module will be called ti_am335x_tscadc.
+
 config HTC_EGPIO
        bool "HTC EGPIO support"
        depends on GENERIC_HARDIRQS && GPIOLIB && ARM
index d8ccb630ddb07f6fbdf821661181f2c5fe67bbce..442c17e407417c2cdf7de22c229cd8bd23965ce4 100644 (file)
@@ -16,6 +16,7 @@ obj-$(CONFIG_HTC_I2CPLD)      += htc-i2cpld.o
 obj-$(CONFIG_MFD_DAVINCI_VOICECODEC)   += davinci_voicecodec.o
 obj-$(CONFIG_MFD_DM355EVM_MSP) += dm355evm_msp.o
 obj-$(CONFIG_MFD_TI_SSP)       += ti-ssp.o
+obj-$(CONFIG_MFD_TI_AM335X_TSCADC)     += ti_am335x_tscadc.o
 
 obj-$(CONFIG_MFD_STA2X11)      += sta2x11-mfd.o
 obj-$(CONFIG_MFD_STMPE)                += stmpe.o
diff --git a/drivers/mfd/ti_am335x_tscadc.c b/drivers/mfd/ti_am335x_tscadc.c
new file mode 100644 (file)
index 0000000..14df67b
--- /dev/null
@@ -0,0 +1,250 @@
+/*
+ * TI Touch Screen / ADC MFD driver
+ *
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/regmap.h>
+#include <linux/mfd/core.h>
+#include <linux/pm_runtime.h>
+
+#include <linux/mfd/ti_am335x_tscadc.h>
+
+static unsigned int tscadc_readl(struct ti_tscadc_dev *tsadc, unsigned int reg)
+{
+       unsigned int val;
+
+       regmap_read(tsadc->regmap_tscadc, reg, &val);
+       return val;
+}
+
+static void tscadc_writel(struct ti_tscadc_dev *tsadc, unsigned int reg,
+                                       unsigned int val)
+{
+       regmap_write(tsadc->regmap_tscadc, reg, val);
+}
+
+static const struct regmap_config tscadc_regmap_config = {
+       .name = "ti_tscadc",
+       .reg_bits = 32,
+       .reg_stride = 4,
+       .val_bits = 32,
+};
+
+static void tscadc_idle_config(struct ti_tscadc_dev *config)
+{
+       unsigned int idleconfig;
+
+       idleconfig = STEPCONFIG_YNN | STEPCONFIG_INM_ADCREFM |
+                       STEPCONFIG_INP_ADCREFM | STEPCONFIG_YPN;
+
+       tscadc_writel(config, REG_IDLECONFIG, idleconfig);
+}
+
+static int __devinit ti_tscadc_probe(struct platform_device *pdev)
+{
+       struct ti_tscadc_dev    *tscadc;
+       struct resource         *res;
+       struct clk              *clk;
+       struct mfd_tscadc_board *pdata = pdev->dev.platform_data;
+       int                     irq;
+       int                     err, ctrl;
+       int                     clk_value, clock_rate;
+
+       if (!pdata) {
+               dev_err(&pdev->dev, "Could not find platform data\n");
+               return -EINVAL;
+       }
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res) {
+               dev_err(&pdev->dev, "no memory resource defined.\n");
+               return -EINVAL;
+       }
+
+       irq = platform_get_irq(pdev, 0);
+       if (irq < 0) {
+               dev_err(&pdev->dev, "no irq ID is specified.\n");
+               return -EINVAL;
+       }
+
+       /* Allocate memory for device */
+       tscadc = devm_kzalloc(&pdev->dev,
+                       sizeof(struct ti_tscadc_dev), GFP_KERNEL);
+       if (!tscadc) {
+               dev_err(&pdev->dev, "failed to allocate memory.\n");
+               return -ENOMEM;
+       }
+       tscadc->dev = &pdev->dev;
+       tscadc->irq = irq;
+
+       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");
+               err = -EBUSY;
+               goto err;
+       }
+
+       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");
+               err = -ENOMEM;
+               goto err;
+       }
+
+       tscadc->regmap_tscadc = devm_regmap_init_mmio(&pdev->dev,
+                       tscadc->tscadc_base, &tscadc_regmap_config);
+       if (IS_ERR(tscadc->regmap_tscadc)) {
+               dev_err(&pdev->dev, "regmap init failed\n");
+               err = PTR_ERR(tscadc->regmap_tscadc);
+               goto err;
+       }
+
+       pm_runtime_enable(&pdev->dev);
+       pm_runtime_get_sync(&pdev->dev);
+
+       /*
+        * The TSC_ADC_Subsystem has 2 clock domains
+        * OCP_CLK and ADC_CLK.
+        * The ADC clock is expected to run at target of 3MHz,
+        * and expected to capture 12-bit data at a rate of 200 KSPS.
+        * 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");
+       if (IS_ERR(clk)) {
+               dev_err(&pdev->dev, "failed to get TSC fck\n");
+               err = PTR_ERR(clk);
+               goto err_disable_clk;
+       }
+       clock_rate = clk_get_rate(clk);
+       clk_put(clk);
+       clk_value = clock_rate / ADC_CLK;
+       if (clk_value < MAX_CLK_DIV) {
+               dev_err(&pdev->dev, "clock input less than min clock requirement\n");
+               err = -EINVAL;
+               goto err_disable_clk;
+       }
+       /* TSCADC_CLKDIV needs to be configured to the value minus 1 */
+       clk_value = clk_value - 1;
+       tscadc_writel(tscadc, REG_CLKDIV, clk_value);
+
+       /* Set the control register bits */
+       ctrl = CNTRLREG_STEPCONFIGWRT |
+                       CNTRLREG_TSCENB |
+                       CNTRLREG_STEPID |
+                       CNTRLREG_4WIRE;
+       tscadc_writel(tscadc, REG_CTRL, ctrl);
+
+       /* Set register bits for Idle Config Mode */
+       tscadc_idle_config(tscadc);
+
+       /* Enable the TSC module enable bit */
+       ctrl = tscadc_readl(tscadc, REG_CTRL);
+       ctrl |= CNTRLREG_TSCSSENB;
+       tscadc_writel(tscadc, REG_CTRL, ctrl);
+
+       err = mfd_add_devices(&pdev->dev, pdev->id, tscadc->cells,
+                       TSCADC_CELLS, NULL, 0, NULL);
+       if (err < 0)
+               goto err_disable_clk;
+
+       device_init_wakeup(&pdev->dev, true);
+       platform_set_drvdata(pdev, tscadc);
+
+       return 0;
+
+err_disable_clk:
+       pm_runtime_put_sync(&pdev->dev);
+       pm_runtime_disable(&pdev->dev);
+err:
+       return err;
+}
+
+static int __devexit ti_tscadc_remove(struct platform_device *pdev)
+{
+       struct ti_tscadc_dev    *tscadc = platform_get_drvdata(pdev);
+
+       tscadc_writel(tscadc, REG_SE, 0x00);
+
+       pm_runtime_put_sync(&pdev->dev);
+       pm_runtime_disable(&pdev->dev);
+
+       mfd_remove_devices(tscadc->dev);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int tscadc_suspend(struct device *dev)
+{
+       struct ti_tscadc_dev    *tscadc_dev = dev_get_drvdata(dev);
+
+       tscadc_writel(tscadc_dev, REG_SE, 0x00);
+       pm_runtime_put_sync(dev);
+
+       return 0;
+}
+
+static int tscadc_resume(struct device *dev)
+{
+       struct ti_tscadc_dev    *tscadc_dev = dev_get_drvdata(dev);
+       unsigned int restore, ctrl;
+
+       pm_runtime_get_sync(dev);
+
+       /* context restore */
+       ctrl = CNTRLREG_STEPCONFIGWRT | CNTRLREG_TSCENB |
+                       CNTRLREG_STEPID | CNTRLREG_4WIRE;
+       tscadc_writel(tscadc_dev, REG_CTRL, ctrl);
+       tscadc_idle_config(tscadc_dev);
+       tscadc_writel(tscadc_dev, REG_SE, STPENB_STEPENB);
+       restore = tscadc_readl(tscadc_dev, REG_CTRL);
+       tscadc_writel(tscadc_dev, REG_CTRL,
+                       (restore | CNTRLREG_TSCSSENB));
+
+       return 0;
+}
+
+static const struct dev_pm_ops tscadc_pm_ops = {
+       .suspend = tscadc_suspend,
+       .resume = tscadc_resume,
+};
+#define TSCADC_PM_OPS (&tscadc_pm_ops)
+#else
+#define TSCADC_PM_OPS NULL
+#endif
+
+static struct platform_driver ti_tscadc_driver = {
+       .driver = {
+               .name   = "ti_tscadc",
+               .owner  = THIS_MODULE,
+               .pm     = TSCADC_PM_OPS,
+       },
+       .probe  = ti_tscadc_probe,
+       .remove = __devexit_p(ti_tscadc_remove),
+
+};
+
+module_platform_driver(ti_tscadc_driver);
+
+MODULE_DESCRIPTION("TI touchscreen / ADC MFD controller driver");
+MODULE_AUTHOR("Rachna Patil <rachna@ti.com>");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/mfd/ti_am335x_tscadc.h b/include/linux/mfd/ti_am335x_tscadc.h
new file mode 100644 (file)
index 0000000..b7232b1
--- /dev/null
@@ -0,0 +1,137 @@
+#ifndef __LINUX_TI_AM335X_TSCADC_MFD_H
+#define __LINUX_TI_AM335X_TSCADC_MFD_H
+
+/*
+ * TI Touch Screen / ADC MFD driver
+ *
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/mfd/core.h>
+
+#define REG_RAWIRQSTATUS       0x024
+#define REG_IRQSTATUS          0x028
+#define REG_IRQENABLE          0x02C
+#define REG_IRQCLR             0x030
+#define REG_IRQWAKEUP          0x034
+#define REG_CTRL               0x040
+#define REG_ADCFSM             0x044
+#define REG_CLKDIV             0x04C
+#define REG_SE                 0x054
+#define REG_IDLECONFIG         0x058
+#define REG_CHARGECONFIG       0x05C
+#define REG_CHARGEDELAY                0x060
+#define REG_STEPCONFIG(n)      (0x64 + ((n - 1) * 8))
+#define REG_STEPDELAY(n)       (0x68 + ((n - 1) * 8))
+#define REG_FIFO0CNT           0xE4
+#define REG_FIFO0THR           0xE8
+#define REG_FIFO1CNT           0xF0
+#define REG_FIFO1THR           0xF4
+#define REG_FIFO0              0x100
+#define REG_FIFO1              0x200
+
+/*     Register Bitfields      */
+/* IRQ wakeup enable */
+#define IRQWKUP_ENB            BIT(0)
+
+/* Step Enable */
+#define STEPENB_MASK           (0x1FFFF << 0)
+#define STEPENB(val)           ((val) << 0)
+#define STPENB_STEPENB         STEPENB(0x1FFFF)
+
+/* IRQ enable */
+#define IRQENB_HW_PEN          BIT(0)
+#define IRQENB_FIFO0THRES      BIT(2)
+#define IRQENB_FIFO1THRES      BIT(5)
+#define IRQENB_PENUP           BIT(9)
+
+/* Step Configuration */
+#define STEPCONFIG_MODE_MASK   (3 << 0)
+#define STEPCONFIG_MODE(val)   ((val) << 0)
+#define STEPCONFIG_MODE_HWSYNC STEPCONFIG_MODE(2)
+#define STEPCONFIG_AVG_MASK    (7 << 2)
+#define STEPCONFIG_AVG(val)    ((val) << 2)
+#define STEPCONFIG_AVG_16      STEPCONFIG_AVG(4)
+#define STEPCONFIG_XPP         BIT(5)
+#define STEPCONFIG_XNN         BIT(6)
+#define STEPCONFIG_YPP         BIT(7)
+#define STEPCONFIG_YNN         BIT(8)
+#define STEPCONFIG_XNP         BIT(9)
+#define STEPCONFIG_YPN         BIT(10)
+#define STEPCONFIG_INM_MASK    (0xF << 15)
+#define STEPCONFIG_INM(val)    ((val) << 15)
+#define STEPCONFIG_INM_ADCREFM STEPCONFIG_INM(8)
+#define STEPCONFIG_INP_MASK    (0xF << 19)
+#define STEPCONFIG_INP(val)    ((val) << 19)
+#define STEPCONFIG_INP_AN2     STEPCONFIG_INP(2)
+#define STEPCONFIG_INP_AN3     STEPCONFIG_INP(3)
+#define STEPCONFIG_INP_AN4     STEPCONFIG_INP(4)
+#define STEPCONFIG_INP_ADCREFM STEPCONFIG_INP(8)
+#define STEPCONFIG_FIFO1       BIT(26)
+
+/* Delay register */
+#define STEPDELAY_OPEN_MASK    (0x3FFFF << 0)
+#define STEPDELAY_OPEN(val)    ((val) << 0)
+#define STEPCONFIG_OPENDLY     STEPDELAY_OPEN(0x098)
+#define STEPDELAY_SAMPLE_MASK  (0xFF << 24)
+#define STEPDELAY_SAMPLE(val)  ((val) << 24)
+#define STEPCONFIG_SAMPLEDLY   STEPDELAY_SAMPLE(0)
+
+/* Charge Config */
+#define STEPCHARGE_RFP_MASK    (7 << 12)
+#define STEPCHARGE_RFP(val)    ((val) << 12)
+#define STEPCHARGE_RFP_XPUL    STEPCHARGE_RFP(1)
+#define STEPCHARGE_INM_MASK    (0xF << 15)
+#define STEPCHARGE_INM(val)    ((val) << 15)
+#define STEPCHARGE_INM_AN1     STEPCHARGE_INM(1)
+#define STEPCHARGE_INP_MASK    (0xF << 19)
+#define STEPCHARGE_INP(val)    ((val) << 19)
+#define STEPCHARGE_INP_AN1     STEPCHARGE_INP(1)
+#define STEPCHARGE_RFM_MASK    (3 << 23)
+#define STEPCHARGE_RFM(val)    ((val) << 23)
+#define STEPCHARGE_RFM_XNUR    STEPCHARGE_RFM(1)
+
+/* Charge delay */
+#define CHARGEDLY_OPEN_MASK    (0x3FFFF << 0)
+#define CHARGEDLY_OPEN(val)    ((val) << 0)
+#define CHARGEDLY_OPENDLY      CHARGEDLY_OPEN(1)
+
+/* Control register */
+#define CNTRLREG_TSCSSENB      BIT(0)
+#define CNTRLREG_STEPID                BIT(1)
+#define CNTRLREG_STEPCONFIGWRT BIT(2)
+#define CNTRLREG_POWERDOWN     BIT(4)
+#define CNTRLREG_AFE_CTRL_MASK (3 << 5)
+#define CNTRLREG_AFE_CTRL(val) ((val) << 5)
+#define CNTRLREG_4WIRE         CNTRLREG_AFE_CTRL(1)
+#define CNTRLREG_5WIRE         CNTRLREG_AFE_CTRL(2)
+#define CNTRLREG_8WIRE         CNTRLREG_AFE_CTRL(3)
+#define CNTRLREG_TSCENB                BIT(7)
+
+#define ADC_CLK                        3000000
+#define        MAX_CLK_DIV             7
+
+#define TSCADC_CELLS           0
+
+struct mfd_tscadc_board {
+       struct tsc_data *tsc_init;
+};
+
+struct ti_tscadc_dev {
+       struct device *dev;
+       struct regmap *regmap_tscadc;
+       void __iomem *tscadc_base;
+       int irq;
+       struct mfd_cell cells[TSCADC_CELLS];
+};
+
+#endif