diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/mfd/Kconfig | 11 | ||||
-rw-r--r-- | drivers/mfd/Makefile | 1 | ||||
-rw-r--r-- | drivers/mfd/ti_am335x_tscadc.c | 250 |
3 files changed, 262 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 | |||
94 | To compile this driver as a module, choose M here: the | 94 | To compile this driver as a module, choose M here: the |
95 | module will be called ti-ssp. | 95 | module will be called ti-ssp. |
96 | 96 | ||
97 | config MFD_TI_AM335X_TSCADC | ||
98 | tristate "TI ADC / Touch Screen chip support" | ||
99 | select MFD_CORE | ||
100 | select REGMAP | ||
101 | select REGMAP_MMIO | ||
102 | help | ||
103 | If you say yes here you get support for Texas Instruments series | ||
104 | of Touch Screen /ADC chips. | ||
105 | To compile this driver as a module, choose M here: the | ||
106 | module will be called ti_am335x_tscadc. | ||
107 | |||
97 | config HTC_EGPIO | 108 | config HTC_EGPIO |
98 | bool "HTC EGPIO support" | 109 | bool "HTC EGPIO support" |
99 | depends on GENERIC_HARDIRQS && GPIOLIB && ARM | 110 | 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 | |||
16 | obj-$(CONFIG_MFD_DAVINCI_VOICECODEC) += davinci_voicecodec.o | 16 | obj-$(CONFIG_MFD_DAVINCI_VOICECODEC) += davinci_voicecodec.o |
17 | obj-$(CONFIG_MFD_DM355EVM_MSP) += dm355evm_msp.o | 17 | obj-$(CONFIG_MFD_DM355EVM_MSP) += dm355evm_msp.o |
18 | obj-$(CONFIG_MFD_TI_SSP) += ti-ssp.o | 18 | obj-$(CONFIG_MFD_TI_SSP) += ti-ssp.o |
19 | obj-$(CONFIG_MFD_TI_AM335X_TSCADC) += ti_am335x_tscadc.o | ||
19 | 20 | ||
20 | obj-$(CONFIG_MFD_STA2X11) += sta2x11-mfd.o | 21 | obj-$(CONFIG_MFD_STA2X11) += sta2x11-mfd.o |
21 | obj-$(CONFIG_MFD_STMPE) += stmpe.o | 22 | 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 @@ | |||
1 | /* | ||
2 | * TI Touch Screen / ADC MFD driver | ||
3 | * | ||
4 | * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License as | ||
8 | * published by the Free Software Foundation version 2. | ||
9 | * | ||
10 | * This program is distributed "as is" WITHOUT ANY WARRANTY of any | ||
11 | * kind, whether express or implied; without even the implied warranty | ||
12 | * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | */ | ||
15 | |||
16 | #include <linux/module.h> | ||
17 | #include <linux/init.h> | ||
18 | #include <linux/slab.h> | ||
19 | #include <linux/err.h> | ||
20 | #include <linux/io.h> | ||
21 | #include <linux/clk.h> | ||
22 | #include <linux/regmap.h> | ||
23 | #include <linux/mfd/core.h> | ||
24 | #include <linux/pm_runtime.h> | ||
25 | |||
26 | #include <linux/mfd/ti_am335x_tscadc.h> | ||
27 | |||
28 | static unsigned int tscadc_readl(struct ti_tscadc_dev *tsadc, unsigned int reg) | ||
29 | { | ||
30 | unsigned int val; | ||
31 | |||
32 | regmap_read(tsadc->regmap_tscadc, reg, &val); | ||
33 | return val; | ||
34 | } | ||
35 | |||
36 | static void tscadc_writel(struct ti_tscadc_dev *tsadc, unsigned int reg, | ||
37 | unsigned int val) | ||
38 | { | ||
39 | regmap_write(tsadc->regmap_tscadc, reg, val); | ||
40 | } | ||
41 | |||
42 | static const struct regmap_config tscadc_regmap_config = { | ||
43 | .name = "ti_tscadc", | ||
44 | .reg_bits = 32, | ||
45 | .reg_stride = 4, | ||
46 | .val_bits = 32, | ||
47 | }; | ||
48 | |||
49 | static void tscadc_idle_config(struct ti_tscadc_dev *config) | ||
50 | { | ||
51 | unsigned int idleconfig; | ||
52 | |||
53 | idleconfig = STEPCONFIG_YNN | STEPCONFIG_INM_ADCREFM | | ||
54 | STEPCONFIG_INP_ADCREFM | STEPCONFIG_YPN; | ||
55 | |||
56 | tscadc_writel(config, REG_IDLECONFIG, idleconfig); | ||
57 | } | ||
58 | |||
59 | static int __devinit ti_tscadc_probe(struct platform_device *pdev) | ||
60 | { | ||
61 | struct ti_tscadc_dev *tscadc; | ||
62 | struct resource *res; | ||
63 | struct clk *clk; | ||
64 | struct mfd_tscadc_board *pdata = pdev->dev.platform_data; | ||
65 | int irq; | ||
66 | int err, ctrl; | ||
67 | int clk_value, clock_rate; | ||
68 | |||
69 | if (!pdata) { | ||
70 | dev_err(&pdev->dev, "Could not find platform data\n"); | ||
71 | return -EINVAL; | ||
72 | } | ||
73 | |||
74 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
75 | if (!res) { | ||
76 | dev_err(&pdev->dev, "no memory resource defined.\n"); | ||
77 | return -EINVAL; | ||
78 | } | ||
79 | |||
80 | irq = platform_get_irq(pdev, 0); | ||
81 | if (irq < 0) { | ||
82 | dev_err(&pdev->dev, "no irq ID is specified.\n"); | ||
83 | return -EINVAL; | ||
84 | } | ||
85 | |||
86 | /* Allocate memory for device */ | ||
87 | tscadc = devm_kzalloc(&pdev->dev, | ||
88 | sizeof(struct ti_tscadc_dev), GFP_KERNEL); | ||
89 | if (!tscadc) { | ||
90 | dev_err(&pdev->dev, "failed to allocate memory.\n"); | ||
91 | return -ENOMEM; | ||
92 | } | ||
93 | tscadc->dev = &pdev->dev; | ||
94 | tscadc->irq = irq; | ||
95 | |||
96 | res = devm_request_mem_region(&pdev->dev, | ||
97 | res->start, resource_size(res), pdev->name); | ||
98 | if (!res) { | ||
99 | dev_err(&pdev->dev, "failed to reserve registers.\n"); | ||
100 | err = -EBUSY; | ||
101 | goto err; | ||
102 | } | ||
103 | |||
104 | tscadc->tscadc_base = devm_ioremap(&pdev->dev, | ||
105 | res->start, resource_size(res)); | ||
106 | if (!tscadc->tscadc_base) { | ||
107 | dev_err(&pdev->dev, "failed to map registers.\n"); | ||
108 | err = -ENOMEM; | ||
109 | goto err; | ||
110 | } | ||
111 | |||
112 | tscadc->regmap_tscadc = devm_regmap_init_mmio(&pdev->dev, | ||
113 | tscadc->tscadc_base, &tscadc_regmap_config); | ||
114 | if (IS_ERR(tscadc->regmap_tscadc)) { | ||
115 | dev_err(&pdev->dev, "regmap init failed\n"); | ||
116 | err = PTR_ERR(tscadc->regmap_tscadc); | ||
117 | goto err; | ||
118 | } | ||
119 | |||
120 | pm_runtime_enable(&pdev->dev); | ||
121 | pm_runtime_get_sync(&pdev->dev); | ||
122 | |||
123 | /* | ||
124 | * The TSC_ADC_Subsystem has 2 clock domains | ||
125 | * OCP_CLK and ADC_CLK. | ||
126 | * The ADC clock is expected to run at target of 3MHz, | ||
127 | * and expected to capture 12-bit data at a rate of 200 KSPS. | ||
128 | * The TSC_ADC_SS controller design assumes the OCP clock is | ||
129 | * at least 6x faster than the ADC clock. | ||
130 | */ | ||
131 | clk = clk_get(&pdev->dev, "adc_tsc_fck"); | ||
132 | if (IS_ERR(clk)) { | ||
133 | dev_err(&pdev->dev, "failed to get TSC fck\n"); | ||
134 | err = PTR_ERR(clk); | ||
135 | goto err_disable_clk; | ||
136 | } | ||
137 | clock_rate = clk_get_rate(clk); | ||
138 | clk_put(clk); | ||
139 | clk_value = clock_rate / ADC_CLK; | ||
140 | if (clk_value < MAX_CLK_DIV) { | ||
141 | dev_err(&pdev->dev, "clock input less than min clock requirement\n"); | ||
142 | err = -EINVAL; | ||
143 | goto err_disable_clk; | ||
144 | } | ||
145 | /* TSCADC_CLKDIV needs to be configured to the value minus 1 */ | ||
146 | clk_value = clk_value - 1; | ||
147 | tscadc_writel(tscadc, REG_CLKDIV, clk_value); | ||
148 | |||
149 | /* Set the control register bits */ | ||
150 | ctrl = CNTRLREG_STEPCONFIGWRT | | ||
151 | CNTRLREG_TSCENB | | ||
152 | CNTRLREG_STEPID | | ||
153 | CNTRLREG_4WIRE; | ||
154 | tscadc_writel(tscadc, REG_CTRL, ctrl); | ||
155 | |||
156 | /* Set register bits for Idle Config Mode */ | ||
157 | tscadc_idle_config(tscadc); | ||
158 | |||
159 | /* Enable the TSC module enable bit */ | ||
160 | ctrl = tscadc_readl(tscadc, REG_CTRL); | ||
161 | ctrl |= CNTRLREG_TSCSSENB; | ||
162 | tscadc_writel(tscadc, REG_CTRL, ctrl); | ||
163 | |||
164 | err = mfd_add_devices(&pdev->dev, pdev->id, tscadc->cells, | ||
165 | TSCADC_CELLS, NULL, 0, NULL); | ||
166 | if (err < 0) | ||
167 | goto err_disable_clk; | ||
168 | |||
169 | device_init_wakeup(&pdev->dev, true); | ||
170 | platform_set_drvdata(pdev, tscadc); | ||
171 | |||
172 | return 0; | ||
173 | |||
174 | err_disable_clk: | ||
175 | pm_runtime_put_sync(&pdev->dev); | ||
176 | pm_runtime_disable(&pdev->dev); | ||
177 | err: | ||
178 | return err; | ||
179 | } | ||
180 | |||
181 | static int __devexit ti_tscadc_remove(struct platform_device *pdev) | ||
182 | { | ||
183 | struct ti_tscadc_dev *tscadc = platform_get_drvdata(pdev); | ||
184 | |||
185 | tscadc_writel(tscadc, REG_SE, 0x00); | ||
186 | |||
187 | pm_runtime_put_sync(&pdev->dev); | ||
188 | pm_runtime_disable(&pdev->dev); | ||
189 | |||
190 | mfd_remove_devices(tscadc->dev); | ||
191 | |||
192 | return 0; | ||
193 | } | ||
194 | |||
195 | #ifdef CONFIG_PM | ||
196 | static int tscadc_suspend(struct device *dev) | ||
197 | { | ||
198 | struct ti_tscadc_dev *tscadc_dev = dev_get_drvdata(dev); | ||
199 | |||
200 | tscadc_writel(tscadc_dev, REG_SE, 0x00); | ||
201 | pm_runtime_put_sync(dev); | ||
202 | |||
203 | return 0; | ||
204 | } | ||
205 | |||
206 | static int tscadc_resume(struct device *dev) | ||
207 | { | ||
208 | struct ti_tscadc_dev *tscadc_dev = dev_get_drvdata(dev); | ||
209 | unsigned int restore, ctrl; | ||
210 | |||
211 | pm_runtime_get_sync(dev); | ||
212 | |||
213 | /* context restore */ | ||
214 | ctrl = CNTRLREG_STEPCONFIGWRT | CNTRLREG_TSCENB | | ||
215 | CNTRLREG_STEPID | CNTRLREG_4WIRE; | ||
216 | tscadc_writel(tscadc_dev, REG_CTRL, ctrl); | ||
217 | tscadc_idle_config(tscadc_dev); | ||
218 | tscadc_writel(tscadc_dev, REG_SE, STPENB_STEPENB); | ||
219 | restore = tscadc_readl(tscadc_dev, REG_CTRL); | ||
220 | tscadc_writel(tscadc_dev, REG_CTRL, | ||
221 | (restore | CNTRLREG_TSCSSENB)); | ||
222 | |||
223 | return 0; | ||
224 | } | ||
225 | |||
226 | static const struct dev_pm_ops tscadc_pm_ops = { | ||
227 | .suspend = tscadc_suspend, | ||
228 | .resume = tscadc_resume, | ||
229 | }; | ||
230 | #define TSCADC_PM_OPS (&tscadc_pm_ops) | ||
231 | #else | ||
232 | #define TSCADC_PM_OPS NULL | ||
233 | #endif | ||
234 | |||
235 | static struct platform_driver ti_tscadc_driver = { | ||
236 | .driver = { | ||
237 | .name = "ti_tscadc", | ||
238 | .owner = THIS_MODULE, | ||
239 | .pm = TSCADC_PM_OPS, | ||
240 | }, | ||
241 | .probe = ti_tscadc_probe, | ||
242 | .remove = __devexit_p(ti_tscadc_remove), | ||
243 | |||
244 | }; | ||
245 | |||
246 | module_platform_driver(ti_tscadc_driver); | ||
247 | |||
248 | MODULE_DESCRIPTION("TI touchscreen / ADC MFD controller driver"); | ||
249 | MODULE_AUTHOR("Rachna Patil <rachna@ti.com>"); | ||
250 | MODULE_LICENSE("GPL"); | ||