Blame view
drivers/mfd/ti_am335x_tscadc.c
9.7 KB
01636eb97
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
/* * 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> |
01636eb97
|
17 18 19 20 21 22 23 |
#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> |
a6543a1cb
|
24 25 |
#include <linux/of.h> #include <linux/of_device.h> |
7ca6740cd
|
26 |
#include <linux/sched.h> |
01636eb97
|
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
#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, }; |
7e170c6e4
|
50 |
void am335x_tsc_se_set_cache(struct ti_tscadc_dev *tsadc, u32 val) |
abeccee40
|
51 |
{ |
317b20999
|
52 53 54 |
unsigned long flags; spin_lock_irqsave(&tsadc->reg_lock, flags); |
6ac734d22
|
55 |
tsadc->reg_se_cache |= val; |
7ca6740cd
|
56 57 58 |
if (tsadc->adc_waiting) wake_up(&tsadc->reg_se_wait); else if (!tsadc->adc_in_use) |
6a71f38dd
|
59 |
tscadc_writel(tsadc, REG_SE, tsadc->reg_se_cache); |
7ca6740cd
|
60 |
|
317b20999
|
61 |
spin_unlock_irqrestore(&tsadc->reg_lock, flags); |
abeccee40
|
62 |
} |
7e170c6e4
|
63 |
EXPORT_SYMBOL_GPL(am335x_tsc_se_set_cache); |
7ca6740cd
|
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
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; prepare_to_wait(&tsadc->reg_se_wait, &wait, TASK_UNINTERRUPTIBLE); spin_unlock_irq(&tsadc->reg_lock); schedule(); spin_lock_irq(&tsadc->reg_lock); finish_wait(&tsadc->reg_se_wait, &wait); reg = tscadc_readl(tsadc, REG_ADCFSM); WARN_ON(reg & SEQ_STATUS); tsadc->adc_waiting = false; } tsadc->adc_in_use = true; } |
7e170c6e4
|
93 94 |
void am335x_tsc_se_set_once(struct ti_tscadc_dev *tsadc, u32 val) { |
7ca6740cd
|
95 |
spin_lock_irq(&tsadc->reg_lock); |
6ac734d22
|
96 |
tsadc->reg_se_cache |= val; |
7ca6740cd
|
97 98 99 100 101 102 103 104 105 |
am335x_tscadc_need_adc(tsadc); tscadc_writel(tsadc, REG_SE, val); spin_unlock_irq(&tsadc->reg_lock); } EXPORT_SYMBOL_GPL(am335x_tsc_se_set_once); void am335x_tsc_se_adc_done(struct ti_tscadc_dev *tsadc) { |
7e170c6e4
|
106 107 108 |
unsigned long flags; spin_lock_irqsave(&tsadc->reg_lock, flags); |
7ca6740cd
|
109 110 |
tsadc->adc_in_use = false; tscadc_writel(tsadc, REG_SE, tsadc->reg_se_cache); |
7e170c6e4
|
111 112 |
spin_unlock_irqrestore(&tsadc->reg_lock, flags); } |
7ca6740cd
|
113 |
EXPORT_SYMBOL_GPL(am335x_tsc_se_adc_done); |
abeccee40
|
114 115 116 |
void am335x_tsc_se_clr(struct ti_tscadc_dev *tsadc, u32 val) { |
317b20999
|
117 118 119 |
unsigned long flags; spin_lock_irqsave(&tsadc->reg_lock, flags); |
abeccee40
|
120 |
tsadc->reg_se_cache &= ~val; |
7ca6740cd
|
121 |
tscadc_writel(tsadc, REG_SE, tsadc->reg_se_cache); |
317b20999
|
122 |
spin_unlock_irqrestore(&tsadc->reg_lock, flags); |
abeccee40
|
123 124 |
} EXPORT_SYMBOL_GPL(am335x_tsc_se_clr); |
01636eb97
|
125 126 127 128 129 130 131 132 133 |
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); } |
612b95cd7
|
134 |
static int ti_tscadc_probe(struct platform_device *pdev) |
01636eb97
|
135 136 137 138 |
{ struct ti_tscadc_dev *tscadc; struct resource *res; struct clk *clk; |
a6543a1cb
|
139 |
struct device_node *node = pdev->dev.of_node; |
2b99bafab
|
140 |
struct mfd_cell *cell; |
18926edeb
|
141 142 143 |
struct property *prop; const __be32 *cur; u32 val; |
01636eb97
|
144 |
int err, ctrl; |
e90f87541
|
145 |
int clock_rate; |
a6543a1cb
|
146 |
int tsc_wires = 0, adc_channels = 0, total_channels; |
18926edeb
|
147 |
int readouts = 0; |
01636eb97
|
148 |
|
9e5775f31
|
149 150 151 |
if (!pdev->dev.of_node) { dev_err(&pdev->dev, "Could not find valid DT data. "); |
01636eb97
|
152 153 |
return -EINVAL; } |
9e5775f31
|
154 155 |
node = of_get_child_by_name(pdev->dev.of_node, "tsc"); of_property_read_u32(node, "ti,wires", &tsc_wires); |
18926edeb
|
156 |
of_property_read_u32(node, "ti,coordiante-readouts", &readouts); |
a6543a1cb
|
157 |
|
9e5775f31
|
158 |
node = of_get_child_by_name(pdev->dev.of_node, "adc"); |
18926edeb
|
159 160 161 162 163 164 165 166 167 |
of_property_for_each_u32(node, "ti,adc-channels", prop, cur, val) { adc_channels++; if (val > 7) { dev_err(&pdev->dev, " PIN numbers are 0..7 (not %d) ", val); return -EINVAL; } } |
5e53a69b4
|
168 |
total_channels = tsc_wires + adc_channels; |
5e53a69b4
|
169 170 171 172 173 |
if (total_channels > 8) { dev_err(&pdev->dev, "Number of i/p channels more than 8 "); return -EINVAL; } |
24d5c82f8
|
174 175 176 177 178 |
if (total_channels == 0) { dev_err(&pdev->dev, "Need atleast one channel. "); return -EINVAL; } |
2b99bafab
|
179 |
|
18926edeb
|
180 181 182 183 184 |
if (readouts * 2 + 2 + adc_channels > 16) { dev_err(&pdev->dev, "Too many step configurations requested "); return -EINVAL; } |
01636eb97
|
185 186 187 188 189 190 191 192 193 |
/* 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. "); return -ENOMEM; } tscadc->dev = &pdev->dev; |
3c39c9c6e
|
194 195 196 197 198 199 200 201 |
err = platform_get_irq(pdev, 0); if (err < 0) { dev_err(&pdev->dev, "no irq ID is specified. "); goto ret; } else tscadc->irq = err; |
01636eb97
|
202 |
|
924ff918a
|
203 204 205 206 |
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); |
01636eb97
|
207 208 209 210 211 212 213 |
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 "); err = PTR_ERR(tscadc->regmap_tscadc); |
3c39c9c6e
|
214 |
goto ret; |
01636eb97
|
215 |
} |
abeccee40
|
216 |
spin_lock_init(&tscadc->reg_lock); |
7ca6740cd
|
217 |
init_waitqueue_head(&tscadc->reg_se_wait); |
01636eb97
|
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 |
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 "); err = PTR_ERR(clk); goto err_disable_clk; } clock_rate = clk_get_rate(clk); clk_put(clk); |
e90f87541
|
238 |
tscadc->clk_div = clock_rate / ADC_CLK; |
efe3126af
|
239 |
|
01636eb97
|
240 |
/* TSCADC_CLKDIV needs to be configured to the value minus 1 */ |
e90f87541
|
241 242 |
tscadc->clk_div--; tscadc_writel(tscadc, REG_CLKDIV, tscadc->clk_div); |
01636eb97
|
243 244 |
/* Set the control register bits */ |
f0933a60d
|
245 |
ctrl = CNTRLREG_STEPCONFIGWRT | CNTRLREG_STEPID; |
01636eb97
|
246 247 248 |
tscadc_writel(tscadc, REG_CTRL, ctrl); /* Set register bits for Idle Config Mode */ |
f0933a60d
|
249 250 251 252 253 254 |
if (tsc_wires > 0) { tscadc->tsc_wires = tsc_wires; if (tsc_wires == 5) ctrl |= CNTRLREG_5WIRE | CNTRLREG_TSCENB; else ctrl |= CNTRLREG_4WIRE | CNTRLREG_TSCENB; |
b5f8b7632
|
255 |
tscadc_idle_config(tscadc); |
f0933a60d
|
256 |
} |
01636eb97
|
257 258 |
/* Enable the TSC module enable bit */ |
01636eb97
|
259 260 |
ctrl |= CNTRLREG_TSCSSENB; tscadc_writel(tscadc, REG_CTRL, ctrl); |
24d5c82f8
|
261 262 263 |
tscadc->used_cells = 0; tscadc->tsc_cell = -1; tscadc->adc_cell = -1; |
2b99bafab
|
264 |
/* TSC Cell */ |
24d5c82f8
|
265 266 267 |
if (tsc_wires > 0) { tscadc->tsc_cell = tscadc->used_cells; cell = &tscadc->cells[tscadc->used_cells++]; |
5f184e63c
|
268 |
cell->name = "TI-am335x-tsc"; |
24d5c82f8
|
269 270 271 272 |
cell->of_compatible = "ti,am3359-tsc"; cell->platform_data = &tscadc; cell->pdata_size = sizeof(tscadc); } |
2b99bafab
|
273 |
|
5e53a69b4
|
274 |
/* ADC Cell */ |
24d5c82f8
|
275 276 277 |
if (adc_channels > 0) { tscadc->adc_cell = tscadc->used_cells; cell = &tscadc->cells[tscadc->used_cells++]; |
9f99928fe
|
278 |
cell->name = "TI-am335x-adc"; |
24d5c82f8
|
279 280 281 282 |
cell->of_compatible = "ti,am3359-adc"; cell->platform_data = &tscadc; cell->pdata_size = sizeof(tscadc); } |
5e53a69b4
|
283 |
|
01636eb97
|
284 |
err = mfd_add_devices(&pdev->dev, pdev->id, tscadc->cells, |
24d5c82f8
|
285 |
tscadc->used_cells, NULL, 0, NULL); |
01636eb97
|
286 287 288 289 290 |
if (err < 0) goto err_disable_clk; device_init_wakeup(&pdev->dev, true); platform_set_drvdata(pdev, tscadc); |
01636eb97
|
291 292 293 294 295 |
return 0; err_disable_clk: pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); |
3c39c9c6e
|
296 |
ret: |
01636eb97
|
297 298 |
return err; } |
612b95cd7
|
299 |
static int ti_tscadc_remove(struct platform_device *pdev) |
01636eb97
|
300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 |
{ 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); |
f0933a60d
|
327 |
u32 ctrl; |
01636eb97
|
328 329 330 331 |
pm_runtime_get_sync(dev); /* context restore */ |
b5f8b7632
|
332 |
ctrl = CNTRLREG_STEPCONFIGWRT | CNTRLREG_STEPID; |
01636eb97
|
333 |
tscadc_writel(tscadc_dev, REG_CTRL, ctrl); |
b5f8b7632
|
334 |
|
f0933a60d
|
335 336 337 338 339 |
if (tscadc_dev->tsc_cell != -1) { if (tscadc_dev->tsc_wires == 5) ctrl |= CNTRLREG_5WIRE | CNTRLREG_TSCENB; else ctrl |= CNTRLREG_4WIRE | CNTRLREG_TSCENB; |
b5f8b7632
|
340 |
tscadc_idle_config(tscadc_dev); |
f0933a60d
|
341 342 343 |
} ctrl |= CNTRLREG_TSCSSENB; tscadc_writel(tscadc_dev, REG_CTRL, ctrl); |
01636eb97
|
344 |
|
e90f87541
|
345 |
tscadc_writel(tscadc_dev, REG_CLKDIV, tscadc_dev->clk_div); |
01636eb97
|
346 347 348 349 350 351 352 353 354 355 356 |
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 |
a6543a1cb
|
357 358 359 360 361 |
static const struct of_device_id ti_tscadc_dt_ids[] = { { .compatible = "ti,am3359-tscadc", }, { } }; MODULE_DEVICE_TABLE(of, ti_tscadc_dt_ids); |
01636eb97
|
362 363 |
static struct platform_driver ti_tscadc_driver = { .driver = { |
a6543a1cb
|
364 |
.name = "ti_am3359-tscadc", |
01636eb97
|
365 |
.pm = TSCADC_PM_OPS, |
131221bc5
|
366 |
.of_match_table = ti_tscadc_dt_ids, |
01636eb97
|
367 368 |
}, .probe = ti_tscadc_probe, |
612b95cd7
|
369 |
.remove = ti_tscadc_remove, |
01636eb97
|
370 371 372 373 374 375 376 377 |
}; module_platform_driver(ti_tscadc_driver); MODULE_DESCRIPTION("TI touchscreen / ADC MFD controller driver"); MODULE_AUTHOR("Rachna Patil <rachna@ti.com>"); MODULE_LICENSE("GPL"); |