diff options
-rw-r--r-- | drivers/mfd/Kconfig | 11 | ||||
-rw-r--r-- | drivers/mfd/Makefile | 1 | ||||
-rw-r--r-- | drivers/mfd/ti_am335x_tscadc.c | 250 | ||||
-rw-r--r-- | include/linux/mfd/ti_am335x_tscadc.h | 137 |
4 files changed, 399 insertions, 0 deletions
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index acab3ef8a310..9bba7f78ff36 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -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 diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index d8ccb630ddb0..442c17e40741 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -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 index 000000000000..14df67bc390f --- /dev/null +++ b/drivers/mfd/ti_am335x_tscadc.c @@ -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 index 000000000000..b7232b157061 --- /dev/null +++ b/include/linux/mfd/ti_am335x_tscadc.h @@ -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 |