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