162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * LPC32xx built-in touchscreen driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2010 NXP Semiconductors
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/platform_device.h>
962306a36Sopenharmony_ci#include <linux/input.h>
1062306a36Sopenharmony_ci#include <linux/interrupt.h>
1162306a36Sopenharmony_ci#include <linux/module.h>
1262306a36Sopenharmony_ci#include <linux/clk.h>
1362306a36Sopenharmony_ci#include <linux/io.h>
1462306a36Sopenharmony_ci#include <linux/slab.h>
1562306a36Sopenharmony_ci#include <linux/of.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci/*
1862306a36Sopenharmony_ci * Touchscreen controller register offsets
1962306a36Sopenharmony_ci */
2062306a36Sopenharmony_ci#define LPC32XX_TSC_STAT			0x00
2162306a36Sopenharmony_ci#define LPC32XX_TSC_SEL				0x04
2262306a36Sopenharmony_ci#define LPC32XX_TSC_CON				0x08
2362306a36Sopenharmony_ci#define LPC32XX_TSC_FIFO			0x0C
2462306a36Sopenharmony_ci#define LPC32XX_TSC_DTR				0x10
2562306a36Sopenharmony_ci#define LPC32XX_TSC_RTR				0x14
2662306a36Sopenharmony_ci#define LPC32XX_TSC_UTR				0x18
2762306a36Sopenharmony_ci#define LPC32XX_TSC_TTR				0x1C
2862306a36Sopenharmony_ci#define LPC32XX_TSC_DXP				0x20
2962306a36Sopenharmony_ci#define LPC32XX_TSC_MIN_X			0x24
3062306a36Sopenharmony_ci#define LPC32XX_TSC_MAX_X			0x28
3162306a36Sopenharmony_ci#define LPC32XX_TSC_MIN_Y			0x2C
3262306a36Sopenharmony_ci#define LPC32XX_TSC_MAX_Y			0x30
3362306a36Sopenharmony_ci#define LPC32XX_TSC_AUX_UTR			0x34
3462306a36Sopenharmony_ci#define LPC32XX_TSC_AUX_MIN			0x38
3562306a36Sopenharmony_ci#define LPC32XX_TSC_AUX_MAX			0x3C
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#define LPC32XX_TSC_STAT_FIFO_OVRRN		BIT(8)
3862306a36Sopenharmony_ci#define LPC32XX_TSC_STAT_FIFO_EMPTY		BIT(7)
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci#define LPC32XX_TSC_SEL_DEFVAL			0x0284
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci#define LPC32XX_TSC_ADCCON_IRQ_TO_FIFO_4	(0x1 << 11)
4362306a36Sopenharmony_ci#define LPC32XX_TSC_ADCCON_X_SAMPLE_SIZE(s)	((10 - (s)) << 7)
4462306a36Sopenharmony_ci#define LPC32XX_TSC_ADCCON_Y_SAMPLE_SIZE(s)	((10 - (s)) << 4)
4562306a36Sopenharmony_ci#define LPC32XX_TSC_ADCCON_POWER_UP		BIT(2)
4662306a36Sopenharmony_ci#define LPC32XX_TSC_ADCCON_AUTO_EN		BIT(0)
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci#define LPC32XX_TSC_FIFO_TS_P_LEVEL		BIT(31)
4962306a36Sopenharmony_ci#define LPC32XX_TSC_FIFO_NORMALIZE_X_VAL(x)	(((x) & 0x03FF0000) >> 16)
5062306a36Sopenharmony_ci#define LPC32XX_TSC_FIFO_NORMALIZE_Y_VAL(y)	((y) & 0x000003FF)
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci#define LPC32XX_TSC_ADCDAT_VALUE_MASK		0x000003FF
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci#define LPC32XX_TSC_MIN_XY_VAL			0x0
5562306a36Sopenharmony_ci#define LPC32XX_TSC_MAX_XY_VAL			0x3FF
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci#define MOD_NAME "ts-lpc32xx"
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci#define tsc_readl(dev, reg) \
6062306a36Sopenharmony_ci	__raw_readl((dev)->tsc_base + (reg))
6162306a36Sopenharmony_ci#define tsc_writel(dev, reg, val) \
6262306a36Sopenharmony_ci	__raw_writel((val), (dev)->tsc_base + (reg))
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cistruct lpc32xx_tsc {
6562306a36Sopenharmony_ci	struct input_dev *dev;
6662306a36Sopenharmony_ci	void __iomem *tsc_base;
6762306a36Sopenharmony_ci	int irq;
6862306a36Sopenharmony_ci	struct clk *clk;
6962306a36Sopenharmony_ci};
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_cistatic void lpc32xx_fifo_clear(struct lpc32xx_tsc *tsc)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	while (!(tsc_readl(tsc, LPC32XX_TSC_STAT) &
7462306a36Sopenharmony_ci			LPC32XX_TSC_STAT_FIFO_EMPTY))
7562306a36Sopenharmony_ci		tsc_readl(tsc, LPC32XX_TSC_FIFO);
7662306a36Sopenharmony_ci}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_cistatic irqreturn_t lpc32xx_ts_interrupt(int irq, void *dev_id)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	u32 tmp, rv[4], xs[4], ys[4];
8162306a36Sopenharmony_ci	int idx;
8262306a36Sopenharmony_ci	struct lpc32xx_tsc *tsc = dev_id;
8362306a36Sopenharmony_ci	struct input_dev *input = tsc->dev;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	tmp = tsc_readl(tsc, LPC32XX_TSC_STAT);
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	if (tmp & LPC32XX_TSC_STAT_FIFO_OVRRN) {
8862306a36Sopenharmony_ci		/* FIFO overflow - throw away samples */
8962306a36Sopenharmony_ci		lpc32xx_fifo_clear(tsc);
9062306a36Sopenharmony_ci		return IRQ_HANDLED;
9162306a36Sopenharmony_ci	}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	/*
9462306a36Sopenharmony_ci	 * Gather and normalize 4 samples. Pen-up events may have less
9562306a36Sopenharmony_ci	 * than 4 samples, but its ok to pop 4 and let the last sample
9662306a36Sopenharmony_ci	 * pen status check drop the samples.
9762306a36Sopenharmony_ci	 */
9862306a36Sopenharmony_ci	idx = 0;
9962306a36Sopenharmony_ci	while (idx < 4 &&
10062306a36Sopenharmony_ci	       !(tsc_readl(tsc, LPC32XX_TSC_STAT) &
10162306a36Sopenharmony_ci			LPC32XX_TSC_STAT_FIFO_EMPTY)) {
10262306a36Sopenharmony_ci		tmp = tsc_readl(tsc, LPC32XX_TSC_FIFO);
10362306a36Sopenharmony_ci		xs[idx] = LPC32XX_TSC_ADCDAT_VALUE_MASK -
10462306a36Sopenharmony_ci			LPC32XX_TSC_FIFO_NORMALIZE_X_VAL(tmp);
10562306a36Sopenharmony_ci		ys[idx] = LPC32XX_TSC_ADCDAT_VALUE_MASK -
10662306a36Sopenharmony_ci			LPC32XX_TSC_FIFO_NORMALIZE_Y_VAL(tmp);
10762306a36Sopenharmony_ci		rv[idx] = tmp;
10862306a36Sopenharmony_ci		idx++;
10962306a36Sopenharmony_ci	}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	/* Data is only valid if pen is still down in last sample */
11262306a36Sopenharmony_ci	if (!(rv[3] & LPC32XX_TSC_FIFO_TS_P_LEVEL) && idx == 4) {
11362306a36Sopenharmony_ci		/* Use average of 2nd and 3rd sample for position */
11462306a36Sopenharmony_ci		input_report_abs(input, ABS_X, (xs[1] + xs[2]) / 2);
11562306a36Sopenharmony_ci		input_report_abs(input, ABS_Y, (ys[1] + ys[2]) / 2);
11662306a36Sopenharmony_ci		input_report_key(input, BTN_TOUCH, 1);
11762306a36Sopenharmony_ci	} else {
11862306a36Sopenharmony_ci		input_report_key(input, BTN_TOUCH, 0);
11962306a36Sopenharmony_ci	}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	input_sync(input);
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	return IRQ_HANDLED;
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_cistatic void lpc32xx_stop_tsc(struct lpc32xx_tsc *tsc)
12762306a36Sopenharmony_ci{
12862306a36Sopenharmony_ci	/* Disable auto mode */
12962306a36Sopenharmony_ci	tsc_writel(tsc, LPC32XX_TSC_CON,
13062306a36Sopenharmony_ci		   tsc_readl(tsc, LPC32XX_TSC_CON) &
13162306a36Sopenharmony_ci			     ~LPC32XX_TSC_ADCCON_AUTO_EN);
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	clk_disable_unprepare(tsc->clk);
13462306a36Sopenharmony_ci}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_cistatic int lpc32xx_setup_tsc(struct lpc32xx_tsc *tsc)
13762306a36Sopenharmony_ci{
13862306a36Sopenharmony_ci	u32 tmp;
13962306a36Sopenharmony_ci	int err;
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	err = clk_prepare_enable(tsc->clk);
14262306a36Sopenharmony_ci	if (err)
14362306a36Sopenharmony_ci		return err;
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	tmp = tsc_readl(tsc, LPC32XX_TSC_CON) & ~LPC32XX_TSC_ADCCON_POWER_UP;
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	/* Set the TSC FIFO depth to 4 samples @ 10-bits per sample (max) */
14862306a36Sopenharmony_ci	tmp = LPC32XX_TSC_ADCCON_IRQ_TO_FIFO_4 |
14962306a36Sopenharmony_ci	      LPC32XX_TSC_ADCCON_X_SAMPLE_SIZE(10) |
15062306a36Sopenharmony_ci	      LPC32XX_TSC_ADCCON_Y_SAMPLE_SIZE(10);
15162306a36Sopenharmony_ci	tsc_writel(tsc, LPC32XX_TSC_CON, tmp);
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	/* These values are all preset */
15462306a36Sopenharmony_ci	tsc_writel(tsc, LPC32XX_TSC_SEL, LPC32XX_TSC_SEL_DEFVAL);
15562306a36Sopenharmony_ci	tsc_writel(tsc, LPC32XX_TSC_MIN_X, LPC32XX_TSC_MIN_XY_VAL);
15662306a36Sopenharmony_ci	tsc_writel(tsc, LPC32XX_TSC_MAX_X, LPC32XX_TSC_MAX_XY_VAL);
15762306a36Sopenharmony_ci	tsc_writel(tsc, LPC32XX_TSC_MIN_Y, LPC32XX_TSC_MIN_XY_VAL);
15862306a36Sopenharmony_ci	tsc_writel(tsc, LPC32XX_TSC_MAX_Y, LPC32XX_TSC_MAX_XY_VAL);
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	/* Aux support is not used */
16162306a36Sopenharmony_ci	tsc_writel(tsc, LPC32XX_TSC_AUX_UTR, 0);
16262306a36Sopenharmony_ci	tsc_writel(tsc, LPC32XX_TSC_AUX_MIN, 0);
16362306a36Sopenharmony_ci	tsc_writel(tsc, LPC32XX_TSC_AUX_MAX, 0);
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	/*
16662306a36Sopenharmony_ci	 * Set sample rate to about 240Hz per X/Y pair. A single measurement
16762306a36Sopenharmony_ci	 * consists of 4 pairs which gives about a 60Hz sample rate based on
16862306a36Sopenharmony_ci	 * a stable 32768Hz clock source. Values are in clocks.
16962306a36Sopenharmony_ci	 * Rate is (32768 / (RTR + XCONV + RTR + YCONV + DXP + TTR + UTR) / 4
17062306a36Sopenharmony_ci	 */
17162306a36Sopenharmony_ci	tsc_writel(tsc, LPC32XX_TSC_RTR, 0x2);
17262306a36Sopenharmony_ci	tsc_writel(tsc, LPC32XX_TSC_DTR, 0x2);
17362306a36Sopenharmony_ci	tsc_writel(tsc, LPC32XX_TSC_TTR, 0x10);
17462306a36Sopenharmony_ci	tsc_writel(tsc, LPC32XX_TSC_DXP, 0x4);
17562306a36Sopenharmony_ci	tsc_writel(tsc, LPC32XX_TSC_UTR, 88);
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	lpc32xx_fifo_clear(tsc);
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	/* Enable automatic ts event capture */
18062306a36Sopenharmony_ci	tsc_writel(tsc, LPC32XX_TSC_CON, tmp | LPC32XX_TSC_ADCCON_AUTO_EN);
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	return 0;
18362306a36Sopenharmony_ci}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_cistatic int lpc32xx_ts_open(struct input_dev *dev)
18662306a36Sopenharmony_ci{
18762306a36Sopenharmony_ci	struct lpc32xx_tsc *tsc = input_get_drvdata(dev);
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	return lpc32xx_setup_tsc(tsc);
19062306a36Sopenharmony_ci}
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_cistatic void lpc32xx_ts_close(struct input_dev *dev)
19362306a36Sopenharmony_ci{
19462306a36Sopenharmony_ci	struct lpc32xx_tsc *tsc = input_get_drvdata(dev);
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	lpc32xx_stop_tsc(tsc);
19762306a36Sopenharmony_ci}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_cistatic int lpc32xx_ts_probe(struct platform_device *pdev)
20062306a36Sopenharmony_ci{
20162306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
20262306a36Sopenharmony_ci	struct lpc32xx_tsc *tsc;
20362306a36Sopenharmony_ci	struct input_dev *input;
20462306a36Sopenharmony_ci	int irq;
20562306a36Sopenharmony_ci	int error;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	irq = platform_get_irq(pdev, 0);
20862306a36Sopenharmony_ci	if (irq < 0)
20962306a36Sopenharmony_ci		return irq;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	tsc = devm_kzalloc(dev, sizeof(*tsc), GFP_KERNEL);
21262306a36Sopenharmony_ci	if (!tsc)
21362306a36Sopenharmony_ci		return -ENOMEM;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	tsc->irq = irq;
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	tsc->tsc_base = devm_platform_ioremap_resource(pdev, 0);
21862306a36Sopenharmony_ci	if (IS_ERR(tsc->tsc_base))
21962306a36Sopenharmony_ci		return PTR_ERR(tsc->tsc_base);
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	tsc->clk = devm_clk_get(dev, NULL);
22262306a36Sopenharmony_ci	if (IS_ERR(tsc->clk)) {
22362306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed getting clock\n");
22462306a36Sopenharmony_ci		return PTR_ERR(tsc->clk);
22562306a36Sopenharmony_ci	}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	input = devm_input_allocate_device(dev);
22862306a36Sopenharmony_ci	if (!input) {
22962306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed allocating input device\n");
23062306a36Sopenharmony_ci		return -ENOMEM;
23162306a36Sopenharmony_ci	}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	input->name = MOD_NAME;
23462306a36Sopenharmony_ci	input->phys = "lpc32xx/input0";
23562306a36Sopenharmony_ci	input->id.bustype = BUS_HOST;
23662306a36Sopenharmony_ci	input->id.vendor = 0x0001;
23762306a36Sopenharmony_ci	input->id.product = 0x0002;
23862306a36Sopenharmony_ci	input->id.version = 0x0100;
23962306a36Sopenharmony_ci	input->open = lpc32xx_ts_open;
24062306a36Sopenharmony_ci	input->close = lpc32xx_ts_close;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	input_set_capability(input, EV_KEY, BTN_TOUCH);
24362306a36Sopenharmony_ci	input_set_abs_params(input, ABS_X, LPC32XX_TSC_MIN_XY_VAL,
24462306a36Sopenharmony_ci			     LPC32XX_TSC_MAX_XY_VAL, 0, 0);
24562306a36Sopenharmony_ci	input_set_abs_params(input, ABS_Y, LPC32XX_TSC_MIN_XY_VAL,
24662306a36Sopenharmony_ci			     LPC32XX_TSC_MAX_XY_VAL, 0, 0);
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	input_set_drvdata(input, tsc);
24962306a36Sopenharmony_ci	tsc->dev = input;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	error = devm_request_irq(dev, tsc->irq, lpc32xx_ts_interrupt,
25262306a36Sopenharmony_ci				 0, pdev->name, tsc);
25362306a36Sopenharmony_ci	if (error) {
25462306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed requesting interrupt\n");
25562306a36Sopenharmony_ci		return error;
25662306a36Sopenharmony_ci	}
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	error = input_register_device(input);
25962306a36Sopenharmony_ci	if (error) {
26062306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed registering input device\n");
26162306a36Sopenharmony_ci		return error;
26262306a36Sopenharmony_ci	}
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	platform_set_drvdata(pdev, tsc);
26562306a36Sopenharmony_ci	device_init_wakeup(&pdev->dev, true);
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	return 0;
26862306a36Sopenharmony_ci}
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci#ifdef CONFIG_PM
27162306a36Sopenharmony_cistatic int lpc32xx_ts_suspend(struct device *dev)
27262306a36Sopenharmony_ci{
27362306a36Sopenharmony_ci	struct lpc32xx_tsc *tsc = dev_get_drvdata(dev);
27462306a36Sopenharmony_ci	struct input_dev *input = tsc->dev;
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	/*
27762306a36Sopenharmony_ci	 * Suspend and resume can be called when the device hasn't been
27862306a36Sopenharmony_ci	 * enabled. If there are no users that have the device open, then
27962306a36Sopenharmony_ci	 * avoid calling the TSC stop and start functions as the TSC
28062306a36Sopenharmony_ci	 * isn't yet clocked.
28162306a36Sopenharmony_ci	 */
28262306a36Sopenharmony_ci	mutex_lock(&input->mutex);
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	if (input_device_enabled(input)) {
28562306a36Sopenharmony_ci		if (device_may_wakeup(dev))
28662306a36Sopenharmony_ci			enable_irq_wake(tsc->irq);
28762306a36Sopenharmony_ci		else
28862306a36Sopenharmony_ci			lpc32xx_stop_tsc(tsc);
28962306a36Sopenharmony_ci	}
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	mutex_unlock(&input->mutex);
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	return 0;
29462306a36Sopenharmony_ci}
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_cistatic int lpc32xx_ts_resume(struct device *dev)
29762306a36Sopenharmony_ci{
29862306a36Sopenharmony_ci	struct lpc32xx_tsc *tsc = dev_get_drvdata(dev);
29962306a36Sopenharmony_ci	struct input_dev *input = tsc->dev;
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	mutex_lock(&input->mutex);
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	if (input_device_enabled(input)) {
30462306a36Sopenharmony_ci		if (device_may_wakeup(dev))
30562306a36Sopenharmony_ci			disable_irq_wake(tsc->irq);
30662306a36Sopenharmony_ci		else
30762306a36Sopenharmony_ci			lpc32xx_setup_tsc(tsc);
30862306a36Sopenharmony_ci	}
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	mutex_unlock(&input->mutex);
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	return 0;
31362306a36Sopenharmony_ci}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_cistatic const struct dev_pm_ops lpc32xx_ts_pm_ops = {
31662306a36Sopenharmony_ci	.suspend	= lpc32xx_ts_suspend,
31762306a36Sopenharmony_ci	.resume		= lpc32xx_ts_resume,
31862306a36Sopenharmony_ci};
31962306a36Sopenharmony_ci#define LPC32XX_TS_PM_OPS (&lpc32xx_ts_pm_ops)
32062306a36Sopenharmony_ci#else
32162306a36Sopenharmony_ci#define LPC32XX_TS_PM_OPS NULL
32262306a36Sopenharmony_ci#endif
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci#ifdef CONFIG_OF
32562306a36Sopenharmony_cistatic const struct of_device_id lpc32xx_tsc_of_match[] = {
32662306a36Sopenharmony_ci	{ .compatible = "nxp,lpc3220-tsc", },
32762306a36Sopenharmony_ci	{ },
32862306a36Sopenharmony_ci};
32962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, lpc32xx_tsc_of_match);
33062306a36Sopenharmony_ci#endif
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_cistatic struct platform_driver lpc32xx_ts_driver = {
33362306a36Sopenharmony_ci	.probe		= lpc32xx_ts_probe,
33462306a36Sopenharmony_ci	.driver		= {
33562306a36Sopenharmony_ci		.name	= MOD_NAME,
33662306a36Sopenharmony_ci		.pm	= LPC32XX_TS_PM_OPS,
33762306a36Sopenharmony_ci		.of_match_table = of_match_ptr(lpc32xx_tsc_of_match),
33862306a36Sopenharmony_ci	},
33962306a36Sopenharmony_ci};
34062306a36Sopenharmony_cimodule_platform_driver(lpc32xx_ts_driver);
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ciMODULE_AUTHOR("Kevin Wells <kevin.wells@nxp.com");
34362306a36Sopenharmony_ciMODULE_DESCRIPTION("LPC32XX TSC Driver");
34462306a36Sopenharmony_ciMODULE_LICENSE("GPL");
34562306a36Sopenharmony_ciMODULE_ALIAS("platform:lpc32xx_ts");
346