Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  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/slab.h>
 18#include <linux/err.h>
 19#include <linux/io.h>
 20#include <linux/clk.h>
 21#include <linux/regmap.h>
 22#include <linux/mfd/core.h>
 23#include <linux/pm_runtime.h>
 24#include <linux/of.h>
 25#include <linux/of_device.h>
 26#include <linux/sched.h>
 27
 28#include <linux/mfd/ti_am335x_tscadc.h>
 29
 30static const struct regmap_config tscadc_regmap_config = {
 31	.name = "ti_tscadc",
 32	.reg_bits = 32,
 33	.reg_stride = 4,
 34	.val_bits = 32,
 35};
 36
 37void am335x_tsc_se_set_cache(struct ti_tscadc_dev *tscadc, u32 val)
 38{
 39	unsigned long flags;
 40
 41	spin_lock_irqsave(&tscadc->reg_lock, flags);
 42	tscadc->reg_se_cache |= val;
 43	if (tscadc->adc_waiting)
 44		wake_up(&tscadc->reg_se_wait);
 45	else if (!tscadc->adc_in_use)
 46		regmap_write(tscadc->regmap, REG_SE, tscadc->reg_se_cache);
 47
 48	spin_unlock_irqrestore(&tscadc->reg_lock, flags);
 49}
 50EXPORT_SYMBOL_GPL(am335x_tsc_se_set_cache);
 51
 52static void am335x_tscadc_need_adc(struct ti_tscadc_dev *tscadc)
 53{
 54	DEFINE_WAIT(wait);
 55	u32 reg;
 56
 57	regmap_read(tscadc->regmap, REG_ADCFSM, &reg);
 58	if (reg & SEQ_STATUS) {
 59		tscadc->adc_waiting = true;
 60		prepare_to_wait(&tscadc->reg_se_wait, &wait,
 61				TASK_UNINTERRUPTIBLE);
 62		spin_unlock_irq(&tscadc->reg_lock);
 63
 64		schedule();
 65
 66		spin_lock_irq(&tscadc->reg_lock);
 67		finish_wait(&tscadc->reg_se_wait, &wait);
 68
 69		/*
 70		 * Sequencer should either be idle or
 71		 * busy applying the charge step.
 72		 */
 73		regmap_read(tscadc->regmap, REG_ADCFSM, &reg);
 74		WARN_ON((reg & SEQ_STATUS) && !(reg & CHARGE_STEP));
 75		tscadc->adc_waiting = false;
 76	}
 77	tscadc->adc_in_use = true;
 78}
 79
 80void am335x_tsc_se_set_once(struct ti_tscadc_dev *tscadc, u32 val)
 81{
 82	spin_lock_irq(&tscadc->reg_lock);
 83	am335x_tscadc_need_adc(tscadc);
 84
 85	regmap_write(tscadc->regmap, REG_SE, val);
 86	spin_unlock_irq(&tscadc->reg_lock);
 87}
 88EXPORT_SYMBOL_GPL(am335x_tsc_se_set_once);
 89
 90void am335x_tsc_se_adc_done(struct ti_tscadc_dev *tscadc)
 91{
 92	unsigned long flags;
 93
 94	spin_lock_irqsave(&tscadc->reg_lock, flags);
 95	tscadc->adc_in_use = false;
 96	regmap_write(tscadc->regmap, REG_SE, tscadc->reg_se_cache);
 97	spin_unlock_irqrestore(&tscadc->reg_lock, flags);
 98}
 99EXPORT_SYMBOL_GPL(am335x_tsc_se_adc_done);
100
101void am335x_tsc_se_clr(struct ti_tscadc_dev *tscadc, u32 val)
102{
103	unsigned long flags;
104
105	spin_lock_irqsave(&tscadc->reg_lock, flags);
106	tscadc->reg_se_cache &= ~val;
107	regmap_write(tscadc->regmap, REG_SE, tscadc->reg_se_cache);
108	spin_unlock_irqrestore(&tscadc->reg_lock, flags);
109}
110EXPORT_SYMBOL_GPL(am335x_tsc_se_clr);
111
112static void tscadc_idle_config(struct ti_tscadc_dev *tscadc)
113{
114	unsigned int idleconfig;
115
116	idleconfig = STEPCONFIG_YNN | STEPCONFIG_INM_ADCREFM |
117			STEPCONFIG_INP_ADCREFM | STEPCONFIG_YPN;
118
119	regmap_write(tscadc->regmap, REG_IDLECONFIG, idleconfig);
120}
121
122static	int ti_tscadc_probe(struct platform_device *pdev)
123{
124	struct ti_tscadc_dev	*tscadc;
125	struct resource		*res;
126	struct clk		*clk;
127	struct device_node	*node = pdev->dev.of_node;
128	struct mfd_cell		*cell;
129	struct property         *prop;
130	const __be32            *cur;
131	u32			val;
132	int			err, ctrl;
133	int			clock_rate;
134	int			tsc_wires = 0, adc_channels = 0, total_channels;
135	int			readouts = 0;
136
137	if (!pdev->dev.of_node) {
138		dev_err(&pdev->dev, "Could not find valid DT data.\n");
139		return -EINVAL;
140	}
141
142	node = of_get_child_by_name(pdev->dev.of_node, "tsc");
143	of_property_read_u32(node, "ti,wires", &tsc_wires);
144	of_property_read_u32(node, "ti,coordiante-readouts", &readouts);
145
146	node = of_get_child_by_name(pdev->dev.of_node, "adc");
147	of_property_for_each_u32(node, "ti,adc-channels", prop, cur, val) {
148		adc_channels++;
149		if (val > 7) {
150			dev_err(&pdev->dev, " PIN numbers are 0..7 (not %d)\n",
151					val);
152			return -EINVAL;
153		}
154	}
155	total_channels = tsc_wires + adc_channels;
156	if (total_channels > 8) {
157		dev_err(&pdev->dev, "Number of i/p channels more than 8\n");
158		return -EINVAL;
159	}
160	if (total_channels == 0) {
161		dev_err(&pdev->dev, "Need atleast one channel.\n");
162		return -EINVAL;
163	}
164
165	if (readouts * 2 + 2 + adc_channels > 16) {
166		dev_err(&pdev->dev, "Too many step configurations requested\n");
167		return -EINVAL;
168	}
169
170	/* Allocate memory for device */
171	tscadc = devm_kzalloc(&pdev->dev, sizeof(*tscadc), GFP_KERNEL);
172	if (!tscadc) {
173		dev_err(&pdev->dev, "failed to allocate memory.\n");
174		return -ENOMEM;
175	}
176	tscadc->dev = &pdev->dev;
177
178	err = platform_get_irq(pdev, 0);
179	if (err < 0) {
180		dev_err(&pdev->dev, "no irq ID is specified.\n");
181		goto ret;
182	} else
183		tscadc->irq = err;
184
185	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
186	tscadc->tscadc_phys_base = res->start;
187	tscadc->tscadc_base = devm_ioremap_resource(&pdev->dev, res);
188	if (IS_ERR(tscadc->tscadc_base))
189		return PTR_ERR(tscadc->tscadc_base);
190
191	tscadc->regmap = devm_regmap_init_mmio(&pdev->dev,
192			tscadc->tscadc_base, &tscadc_regmap_config);
193	if (IS_ERR(tscadc->regmap)) {
194		dev_err(&pdev->dev, "regmap init failed\n");
195		err = PTR_ERR(tscadc->regmap);
196		goto ret;
197	}
198
199	spin_lock_init(&tscadc->reg_lock);
200	init_waitqueue_head(&tscadc->reg_se_wait);
201
202	pm_runtime_enable(&pdev->dev);
203	pm_runtime_get_sync(&pdev->dev);
204
205	/*
206	 * The TSC_ADC_Subsystem has 2 clock domains
207	 * OCP_CLK and ADC_CLK.
208	 * The ADC clock is expected to run at target of 3MHz,
209	 * and expected to capture 12-bit data at a rate of 200 KSPS.
210	 * The TSC_ADC_SS controller design assumes the OCP clock is
211	 * at least 6x faster than the ADC clock.
212	 */
213	clk = clk_get(&pdev->dev, "adc_tsc_fck");
214	if (IS_ERR(clk)) {
215		dev_err(&pdev->dev, "failed to get TSC fck\n");
216		err = PTR_ERR(clk);
217		goto err_disable_clk;
218	}
219	clock_rate = clk_get_rate(clk);
220	clk_put(clk);
221	tscadc->clk_div = clock_rate / ADC_CLK;
222
223	/* TSCADC_CLKDIV needs to be configured to the value minus 1 */
224	tscadc->clk_div--;
225	regmap_write(tscadc->regmap, REG_CLKDIV, tscadc->clk_div);
226
227	/* Set the control register bits */
228	ctrl = CNTRLREG_STEPCONFIGWRT |	CNTRLREG_STEPID;
229	regmap_write(tscadc->regmap, REG_CTRL, ctrl);
230
231	/* Set register bits for Idle Config Mode */
232	if (tsc_wires > 0) {
233		tscadc->tsc_wires = tsc_wires;
234		if (tsc_wires == 5)
235			ctrl |= CNTRLREG_5WIRE | CNTRLREG_TSCENB;
236		else
237			ctrl |= CNTRLREG_4WIRE | CNTRLREG_TSCENB;
238		tscadc_idle_config(tscadc);
239	}
240
241	/* Enable the TSC module enable bit */
242	ctrl |= CNTRLREG_TSCSSENB;
243	regmap_write(tscadc->regmap, REG_CTRL, ctrl);
244
245	tscadc->used_cells = 0;
246	tscadc->tsc_cell = -1;
247	tscadc->adc_cell = -1;
248
249	/* TSC Cell */
250	if (tsc_wires > 0) {
251		tscadc->tsc_cell = tscadc->used_cells;
252		cell = &tscadc->cells[tscadc->used_cells++];
253		cell->name = "TI-am335x-tsc";
254		cell->of_compatible = "ti,am3359-tsc";
255		cell->platform_data = &tscadc;
256		cell->pdata_size = sizeof(tscadc);
257	}
258
259	/* ADC Cell */
260	if (adc_channels > 0) {
261		tscadc->adc_cell = tscadc->used_cells;
262		cell = &tscadc->cells[tscadc->used_cells++];
263		cell->name = "TI-am335x-adc";
264		cell->of_compatible = "ti,am3359-adc";
265		cell->platform_data = &tscadc;
266		cell->pdata_size = sizeof(tscadc);
267	}
268
269	err = mfd_add_devices(&pdev->dev, pdev->id, tscadc->cells,
270			tscadc->used_cells, NULL, 0, NULL);
271	if (err < 0)
272		goto err_disable_clk;
273
274	device_init_wakeup(&pdev->dev, true);
275	platform_set_drvdata(pdev, tscadc);
276	return 0;
277
278err_disable_clk:
279	pm_runtime_put_sync(&pdev->dev);
280	pm_runtime_disable(&pdev->dev);
281ret:
282	return err;
283}
284
285static int ti_tscadc_remove(struct platform_device *pdev)
286{
287	struct ti_tscadc_dev	*tscadc = platform_get_drvdata(pdev);
288
289	regmap_write(tscadc->regmap, REG_SE, 0x00);
290
291	pm_runtime_put_sync(&pdev->dev);
292	pm_runtime_disable(&pdev->dev);
293
294	mfd_remove_devices(tscadc->dev);
295
296	return 0;
297}
298
299static int __maybe_unused tscadc_suspend(struct device *dev)
300{
301	struct ti_tscadc_dev	*tscadc = dev_get_drvdata(dev);
302
303	regmap_write(tscadc->regmap, REG_SE, 0x00);
304	pm_runtime_put_sync(dev);
305
306	return 0;
307}
308
309static int __maybe_unused tscadc_resume(struct device *dev)
310{
311	struct ti_tscadc_dev	*tscadc = dev_get_drvdata(dev);
312	u32 ctrl;
313
314	pm_runtime_get_sync(dev);
315
316	/* context restore */
317	ctrl = CNTRLREG_STEPCONFIGWRT |	CNTRLREG_STEPID;
318	regmap_write(tscadc->regmap, REG_CTRL, ctrl);
319
320	if (tscadc->tsc_cell != -1) {
321		if (tscadc->tsc_wires == 5)
322			ctrl |= CNTRLREG_5WIRE | CNTRLREG_TSCENB;
323		else
324			ctrl |= CNTRLREG_4WIRE | CNTRLREG_TSCENB;
325		tscadc_idle_config(tscadc);
326	}
327	ctrl |= CNTRLREG_TSCSSENB;
328	regmap_write(tscadc->regmap, REG_CTRL, ctrl);
329
330	regmap_write(tscadc->regmap, REG_CLKDIV, tscadc->clk_div);
331
332	return 0;
333}
334
335static SIMPLE_DEV_PM_OPS(tscadc_pm_ops, tscadc_suspend, tscadc_resume);
336
337static const struct of_device_id ti_tscadc_dt_ids[] = {
338	{ .compatible = "ti,am3359-tscadc", },
339	{ }
340};
341MODULE_DEVICE_TABLE(of, ti_tscadc_dt_ids);
342
343static struct platform_driver ti_tscadc_driver = {
344	.driver = {
345		.name   = "ti_am3359-tscadc",
346		.pm	= &tscadc_pm_ops,
347		.of_match_table = ti_tscadc_dt_ids,
348	},
349	.probe	= ti_tscadc_probe,
350	.remove	= ti_tscadc_remove,
351
352};
353
354module_platform_driver(ti_tscadc_driver);
355
356MODULE_DESCRIPTION("TI touchscreen / ADC MFD controller driver");
357MODULE_AUTHOR("Rachna Patil <rachna@ti.com>");
358MODULE_LICENSE("GPL");