aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mfd/ti_am335x_tscadc.c
diff options
context:
space:
mode:
authorPatil, Rachna <rachna@ti.com>2012-10-16 03:25:43 -0400
committerSamuel Ortiz <sameo@linux.intel.com>2012-11-05 17:50:27 -0500
commit01636eb970a029897b06fb96026941429212ddd9 (patch)
tree5abde58ab7ded90feb9b85ecee82d66b5daf6a23 /drivers/mfd/ti_am335x_tscadc.c
parent55c04de5176ea3eac6fdc469a6a063c5cb91ed7c (diff)
mfd: ti_tscadc: Add support for TI's TSC/ADC MFDevice
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>
Diffstat (limited to 'drivers/mfd/ti_am335x_tscadc.c')
-rw-r--r--drivers/mfd/ti_am335x_tscadc.c250
1 files changed, 250 insertions, 0 deletions
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
28static 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
36static 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
42static 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
49static 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
59static 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
174err_disable_clk:
175 pm_runtime_put_sync(&pdev->dev);
176 pm_runtime_disable(&pdev->dev);
177err:
178 return err;
179}
180
181static 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
196static 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
206static 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
226static 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
235static 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
246module_platform_driver(ti_tscadc_driver);
247
248MODULE_DESCRIPTION("TI touchscreen / ADC MFD controller driver");
249MODULE_AUTHOR("Rachna Patil <rachna@ti.com>");
250MODULE_LICENSE("GPL");