xref: /linux/drivers/input/touchscreen/imx6ul_tsc.c (revision cdd38c5f1ce4398ec58fec95904b75824daab7b5)
15ae37699SFabio Estevam // SPDX-License-Identifier: GPL-2.0
25ae37699SFabio Estevam //
35ae37699SFabio Estevam // Freescale i.MX6UL touchscreen controller driver
45ae37699SFabio Estevam //
55ae37699SFabio Estevam // Copyright (C) 2015 Freescale Semiconductor, Inc.
69a436d52SHaibo Chen 
79a436d52SHaibo Chen #include <linux/errno.h>
89a436d52SHaibo Chen #include <linux/kernel.h>
99a436d52SHaibo Chen #include <linux/module.h>
109a436d52SHaibo Chen #include <linux/gpio/consumer.h>
119a436d52SHaibo Chen #include <linux/input.h>
129a436d52SHaibo Chen #include <linux/slab.h>
139a436d52SHaibo Chen #include <linux/completion.h>
149a436d52SHaibo Chen #include <linux/delay.h>
159a436d52SHaibo Chen #include <linux/of.h>
169a436d52SHaibo Chen #include <linux/interrupt.h>
179a436d52SHaibo Chen #include <linux/platform_device.h>
189a436d52SHaibo Chen #include <linux/clk.h>
199a436d52SHaibo Chen #include <linux/io.h>
20f43d3ec3SGuy Shapiro #include <linux/log2.h>
219a436d52SHaibo Chen 
229a436d52SHaibo Chen /* ADC configuration registers field define */
239a436d52SHaibo Chen #define ADC_AIEN		(0x1 << 7)
249a436d52SHaibo Chen #define ADC_CONV_DISABLE	0x1F
25031bfed2SGuy Shapiro #define ADC_AVGE		(0x1 << 5)
269a436d52SHaibo Chen #define ADC_CAL			(0x1 << 7)
279a436d52SHaibo Chen #define ADC_CALF		0x2
289a436d52SHaibo Chen #define ADC_12BIT_MODE		(0x2 << 2)
2970f5a294SHaibo Chen #define ADC_CONV_MODE_MASK	(0x3 << 2)
309a436d52SHaibo Chen #define ADC_IPG_CLK		0x00
3170f5a294SHaibo Chen #define ADC_INPUT_CLK_MASK	0x3
329a436d52SHaibo Chen #define ADC_CLK_DIV_8		(0x03 << 5)
3370f5a294SHaibo Chen #define ADC_CLK_DIV_MASK	(0x3 << 5)
349a436d52SHaibo Chen #define ADC_SHORT_SAMPLE_MODE	(0x0 << 4)
3570f5a294SHaibo Chen #define ADC_SAMPLE_MODE_MASK	(0x1 << 4)
369a436d52SHaibo Chen #define ADC_HARDWARE_TRIGGER	(0x1 << 13)
37031bfed2SGuy Shapiro #define ADC_AVGS_SHIFT		14
3870f5a294SHaibo Chen #define ADC_AVGS_MASK		(0x3 << 14)
399a436d52SHaibo Chen #define SELECT_CHANNEL_4	0x04
409a436d52SHaibo Chen #define SELECT_CHANNEL_1	0x01
419a436d52SHaibo Chen #define DISABLE_CONVERSION_INT	(0x0 << 7)
429a436d52SHaibo Chen 
439a436d52SHaibo Chen /* ADC registers */
449a436d52SHaibo Chen #define REG_ADC_HC0		0x00
459a436d52SHaibo Chen #define REG_ADC_HC1		0x04
469a436d52SHaibo Chen #define REG_ADC_HC2		0x08
479a436d52SHaibo Chen #define REG_ADC_HC3		0x0C
489a436d52SHaibo Chen #define REG_ADC_HC4		0x10
499a436d52SHaibo Chen #define REG_ADC_HS		0x14
509a436d52SHaibo Chen #define REG_ADC_R0		0x18
519a436d52SHaibo Chen #define REG_ADC_CFG		0x2C
529a436d52SHaibo Chen #define REG_ADC_GC		0x30
539a436d52SHaibo Chen #define REG_ADC_GS		0x34
549a436d52SHaibo Chen 
559a436d52SHaibo Chen #define ADC_TIMEOUT		msecs_to_jiffies(100)
569a436d52SHaibo Chen 
579a436d52SHaibo Chen /* TSC registers */
589a436d52SHaibo Chen #define REG_TSC_BASIC_SETING	0x00
599a436d52SHaibo Chen #define REG_TSC_PRE_CHARGE_TIME	0x10
609a436d52SHaibo Chen #define REG_TSC_FLOW_CONTROL	0x20
619a436d52SHaibo Chen #define REG_TSC_MEASURE_VALUE	0x30
629a436d52SHaibo Chen #define REG_TSC_INT_EN		0x40
639a436d52SHaibo Chen #define REG_TSC_INT_SIG_EN	0x50
649a436d52SHaibo Chen #define REG_TSC_INT_STATUS	0x60
659a436d52SHaibo Chen #define REG_TSC_DEBUG_MODE	0x70
669a436d52SHaibo Chen #define REG_TSC_DEBUG_MODE2	0x80
679a436d52SHaibo Chen 
689a436d52SHaibo Chen /* TSC configuration registers field define */
699a436d52SHaibo Chen #define DETECT_4_WIRE_MODE	(0x0 << 4)
709a436d52SHaibo Chen #define AUTO_MEASURE		0x1
719a436d52SHaibo Chen #define MEASURE_SIGNAL		0x1
729a436d52SHaibo Chen #define DETECT_SIGNAL		(0x1 << 4)
739a436d52SHaibo Chen #define VALID_SIGNAL		(0x1 << 8)
749a436d52SHaibo Chen #define MEASURE_INT_EN		0x1
759a436d52SHaibo Chen #define MEASURE_SIG_EN		0x1
769a436d52SHaibo Chen #define VALID_SIG_EN		(0x1 << 8)
779a436d52SHaibo Chen #define DE_GLITCH_2		(0x2 << 29)
789a436d52SHaibo Chen #define START_SENSE		(0x1 << 12)
799a436d52SHaibo Chen #define TSC_DISABLE		(0x1 << 16)
809a436d52SHaibo Chen #define DETECT_MODE		0x2
819a436d52SHaibo Chen 
829a436d52SHaibo Chen struct imx6ul_tsc {
839a436d52SHaibo Chen 	struct device *dev;
849a436d52SHaibo Chen 	struct input_dev *input;
859a436d52SHaibo Chen 	void __iomem *tsc_regs;
869a436d52SHaibo Chen 	void __iomem *adc_regs;
879a436d52SHaibo Chen 	struct clk *tsc_clk;
889a436d52SHaibo Chen 	struct clk *adc_clk;
899a436d52SHaibo Chen 	struct gpio_desc *xnur_gpio;
909a436d52SHaibo Chen 
91accbcea3SGuy Shapiro 	u32 measure_delay_time;
92accbcea3SGuy Shapiro 	u32 pre_charge_time;
93f43d3ec3SGuy Shapiro 	bool average_enable;
94f43d3ec3SGuy Shapiro 	u32 average_select;
959a436d52SHaibo Chen 
969a436d52SHaibo Chen 	struct completion completion;
979a436d52SHaibo Chen };
989a436d52SHaibo Chen 
999a436d52SHaibo Chen /*
1009a436d52SHaibo Chen  * TSC module need ADC to get the measure value. So
1019a436d52SHaibo Chen  * before config TSC, we should initialize ADC module.
1029a436d52SHaibo Chen  */
1036cc527b0SFabio Estevam static int imx6ul_adc_init(struct imx6ul_tsc *tsc)
1049a436d52SHaibo Chen {
105accbcea3SGuy Shapiro 	u32 adc_hc = 0;
106accbcea3SGuy Shapiro 	u32 adc_gc;
107accbcea3SGuy Shapiro 	u32 adc_gs;
108accbcea3SGuy Shapiro 	u32 adc_cfg;
109accbcea3SGuy Shapiro 	unsigned long timeout;
1109a436d52SHaibo Chen 
1119a436d52SHaibo Chen 	reinit_completion(&tsc->completion);
1129a436d52SHaibo Chen 
1139a436d52SHaibo Chen 	adc_cfg = readl(tsc->adc_regs + REG_ADC_CFG);
11470f5a294SHaibo Chen 	adc_cfg &= ~(ADC_CONV_MODE_MASK | ADC_INPUT_CLK_MASK);
1159a436d52SHaibo Chen 	adc_cfg |= ADC_12BIT_MODE | ADC_IPG_CLK;
11670f5a294SHaibo Chen 	adc_cfg &= ~(ADC_CLK_DIV_MASK | ADC_SAMPLE_MODE_MASK);
1179a436d52SHaibo Chen 	adc_cfg |= ADC_CLK_DIV_8 | ADC_SHORT_SAMPLE_MODE;
118f43d3ec3SGuy Shapiro 	if (tsc->average_enable) {
11970f5a294SHaibo Chen 		adc_cfg &= ~ADC_AVGS_MASK;
120f43d3ec3SGuy Shapiro 		adc_cfg |= (tsc->average_select) << ADC_AVGS_SHIFT;
12170f5a294SHaibo Chen 	}
1229a436d52SHaibo Chen 	adc_cfg &= ~ADC_HARDWARE_TRIGGER;
1239a436d52SHaibo Chen 	writel(adc_cfg, tsc->adc_regs + REG_ADC_CFG);
1249a436d52SHaibo Chen 
1259a436d52SHaibo Chen 	/* enable calibration interrupt */
1269a436d52SHaibo Chen 	adc_hc |= ADC_AIEN;
1279a436d52SHaibo Chen 	adc_hc |= ADC_CONV_DISABLE;
1289a436d52SHaibo Chen 	writel(adc_hc, tsc->adc_regs + REG_ADC_HC0);
1299a436d52SHaibo Chen 
1309a436d52SHaibo Chen 	/* start ADC calibration */
1319a436d52SHaibo Chen 	adc_gc = readl(tsc->adc_regs + REG_ADC_GC);
1329a436d52SHaibo Chen 	adc_gc |= ADC_CAL;
133f43d3ec3SGuy Shapiro 	if (tsc->average_enable)
134031bfed2SGuy Shapiro 		adc_gc |= ADC_AVGE;
1359a436d52SHaibo Chen 	writel(adc_gc, tsc->adc_regs + REG_ADC_GC);
1369a436d52SHaibo Chen 
1379a436d52SHaibo Chen 	timeout = wait_for_completion_timeout
1389a436d52SHaibo Chen 			(&tsc->completion, ADC_TIMEOUT);
1396cc527b0SFabio Estevam 	if (timeout == 0) {
1409a436d52SHaibo Chen 		dev_err(tsc->dev, "Timeout for adc calibration\n");
1416cc527b0SFabio Estevam 		return -ETIMEDOUT;
1426cc527b0SFabio Estevam 	}
1439a436d52SHaibo Chen 
1449a436d52SHaibo Chen 	adc_gs = readl(tsc->adc_regs + REG_ADC_GS);
1456cc527b0SFabio Estevam 	if (adc_gs & ADC_CALF) {
1469a436d52SHaibo Chen 		dev_err(tsc->dev, "ADC calibration failed\n");
1476cc527b0SFabio Estevam 		return -EINVAL;
1486cc527b0SFabio Estevam 	}
1499a436d52SHaibo Chen 
1509a436d52SHaibo Chen 	/* TSC need the ADC work in hardware trigger */
1519a436d52SHaibo Chen 	adc_cfg = readl(tsc->adc_regs + REG_ADC_CFG);
1529a436d52SHaibo Chen 	adc_cfg |= ADC_HARDWARE_TRIGGER;
1539a436d52SHaibo Chen 	writel(adc_cfg, tsc->adc_regs + REG_ADC_CFG);
1546cc527b0SFabio Estevam 
1556cc527b0SFabio Estevam 	return 0;
1569a436d52SHaibo Chen }
1579a436d52SHaibo Chen 
1589a436d52SHaibo Chen /*
1599a436d52SHaibo Chen  * This is a TSC workaround. Currently TSC misconnect two
1609a436d52SHaibo Chen  * ADC channels, this function remap channel configure for
1619a436d52SHaibo Chen  * hardware trigger.
1629a436d52SHaibo Chen  */
1639a436d52SHaibo Chen static void imx6ul_tsc_channel_config(struct imx6ul_tsc *tsc)
1649a436d52SHaibo Chen {
165accbcea3SGuy Shapiro 	u32 adc_hc0, adc_hc1, adc_hc2, adc_hc3, adc_hc4;
1669a436d52SHaibo Chen 
1679a436d52SHaibo Chen 	adc_hc0 = DISABLE_CONVERSION_INT;
1689a436d52SHaibo Chen 	writel(adc_hc0, tsc->adc_regs + REG_ADC_HC0);
1699a436d52SHaibo Chen 
1709a436d52SHaibo Chen 	adc_hc1 = DISABLE_CONVERSION_INT | SELECT_CHANNEL_4;
1719a436d52SHaibo Chen 	writel(adc_hc1, tsc->adc_regs + REG_ADC_HC1);
1729a436d52SHaibo Chen 
1739a436d52SHaibo Chen 	adc_hc2 = DISABLE_CONVERSION_INT;
1749a436d52SHaibo Chen 	writel(adc_hc2, tsc->adc_regs + REG_ADC_HC2);
1759a436d52SHaibo Chen 
1769a436d52SHaibo Chen 	adc_hc3 = DISABLE_CONVERSION_INT | SELECT_CHANNEL_1;
1779a436d52SHaibo Chen 	writel(adc_hc3, tsc->adc_regs + REG_ADC_HC3);
1789a436d52SHaibo Chen 
1799a436d52SHaibo Chen 	adc_hc4 = DISABLE_CONVERSION_INT;
1809a436d52SHaibo Chen 	writel(adc_hc4, tsc->adc_regs + REG_ADC_HC4);
1819a436d52SHaibo Chen }
1829a436d52SHaibo Chen 
1839a436d52SHaibo Chen /*
1849a436d52SHaibo Chen  * TSC setting, confige the pre-charge time and measure delay time.
1859a436d52SHaibo Chen  * different touch screen may need different pre-charge time and
1869a436d52SHaibo Chen  * measure delay time.
1879a436d52SHaibo Chen  */
1889a436d52SHaibo Chen static void imx6ul_tsc_set(struct imx6ul_tsc *tsc)
1899a436d52SHaibo Chen {
190accbcea3SGuy Shapiro 	u32 basic_setting = 0;
191accbcea3SGuy Shapiro 	u32 start;
1929a436d52SHaibo Chen 
1939a436d52SHaibo Chen 	basic_setting |= tsc->measure_delay_time << 8;
1949a436d52SHaibo Chen 	basic_setting |= DETECT_4_WIRE_MODE | AUTO_MEASURE;
1959a436d52SHaibo Chen 	writel(basic_setting, tsc->tsc_regs + REG_TSC_BASIC_SETING);
1969a436d52SHaibo Chen 
1979a436d52SHaibo Chen 	writel(DE_GLITCH_2, tsc->tsc_regs + REG_TSC_DEBUG_MODE2);
1989a436d52SHaibo Chen 
1999a436d52SHaibo Chen 	writel(tsc->pre_charge_time, tsc->tsc_regs + REG_TSC_PRE_CHARGE_TIME);
2009a436d52SHaibo Chen 	writel(MEASURE_INT_EN, tsc->tsc_regs + REG_TSC_INT_EN);
2019a436d52SHaibo Chen 	writel(MEASURE_SIG_EN | VALID_SIG_EN,
2029a436d52SHaibo Chen 		tsc->tsc_regs + REG_TSC_INT_SIG_EN);
2039a436d52SHaibo Chen 
2049a436d52SHaibo Chen 	/* start sense detection */
2059a436d52SHaibo Chen 	start = readl(tsc->tsc_regs + REG_TSC_FLOW_CONTROL);
2069a436d52SHaibo Chen 	start |= START_SENSE;
2079a436d52SHaibo Chen 	start &= ~TSC_DISABLE;
2089a436d52SHaibo Chen 	writel(start, tsc->tsc_regs + REG_TSC_FLOW_CONTROL);
2099a436d52SHaibo Chen }
2109a436d52SHaibo Chen 
2116cc527b0SFabio Estevam static int imx6ul_tsc_init(struct imx6ul_tsc *tsc)
2129a436d52SHaibo Chen {
2136cc527b0SFabio Estevam 	int err;
2146cc527b0SFabio Estevam 
2156cc527b0SFabio Estevam 	err = imx6ul_adc_init(tsc);
2166cc527b0SFabio Estevam 	if (err)
2176cc527b0SFabio Estevam 		return err;
2189a436d52SHaibo Chen 	imx6ul_tsc_channel_config(tsc);
2199a436d52SHaibo Chen 	imx6ul_tsc_set(tsc);
2206cc527b0SFabio Estevam 
2216cc527b0SFabio Estevam 	return 0;
2229a436d52SHaibo Chen }
2239a436d52SHaibo Chen 
2249a436d52SHaibo Chen static void imx6ul_tsc_disable(struct imx6ul_tsc *tsc)
2259a436d52SHaibo Chen {
226accbcea3SGuy Shapiro 	u32 tsc_flow;
227accbcea3SGuy Shapiro 	u32 adc_cfg;
2289a436d52SHaibo Chen 
2299a436d52SHaibo Chen 	/* TSC controller enters to idle status */
2309a436d52SHaibo Chen 	tsc_flow = readl(tsc->tsc_regs + REG_TSC_FLOW_CONTROL);
2319a436d52SHaibo Chen 	tsc_flow |= TSC_DISABLE;
2329a436d52SHaibo Chen 	writel(tsc_flow, tsc->tsc_regs + REG_TSC_FLOW_CONTROL);
2339a436d52SHaibo Chen 
2349a436d52SHaibo Chen 	/* ADC controller enters to stop mode */
2359a436d52SHaibo Chen 	adc_cfg = readl(tsc->adc_regs + REG_ADC_HC0);
2369a436d52SHaibo Chen 	adc_cfg |= ADC_CONV_DISABLE;
2379a436d52SHaibo Chen 	writel(adc_cfg, tsc->adc_regs + REG_ADC_HC0);
2389a436d52SHaibo Chen }
2399a436d52SHaibo Chen 
2409a436d52SHaibo Chen /* Delay some time (max 2ms), wait the pre-charge done. */
2419a436d52SHaibo Chen static bool tsc_wait_detect_mode(struct imx6ul_tsc *tsc)
2429a436d52SHaibo Chen {
2439a436d52SHaibo Chen 	unsigned long timeout = jiffies + msecs_to_jiffies(2);
244accbcea3SGuy Shapiro 	u32 state_machine;
245accbcea3SGuy Shapiro 	u32 debug_mode2;
2469a436d52SHaibo Chen 
2479a436d52SHaibo Chen 	do {
2489a436d52SHaibo Chen 		if (time_after(jiffies, timeout))
2499a436d52SHaibo Chen 			return false;
2509a436d52SHaibo Chen 
2519a436d52SHaibo Chen 		usleep_range(200, 400);
2529a436d52SHaibo Chen 		debug_mode2 = readl(tsc->tsc_regs + REG_TSC_DEBUG_MODE2);
2539a436d52SHaibo Chen 		state_machine = (debug_mode2 >> 20) & 0x7;
2549a436d52SHaibo Chen 	} while (state_machine != DETECT_MODE);
2559a436d52SHaibo Chen 
2569a436d52SHaibo Chen 	usleep_range(200, 400);
2579a436d52SHaibo Chen 	return true;
2589a436d52SHaibo Chen }
2599a436d52SHaibo Chen 
2609a436d52SHaibo Chen static irqreturn_t tsc_irq_fn(int irq, void *dev_id)
2619a436d52SHaibo Chen {
2629a436d52SHaibo Chen 	struct imx6ul_tsc *tsc = dev_id;
263accbcea3SGuy Shapiro 	u32 status;
264accbcea3SGuy Shapiro 	u32 value;
265accbcea3SGuy Shapiro 	u32 x, y;
266accbcea3SGuy Shapiro 	u32 start;
2679a436d52SHaibo Chen 
2689a436d52SHaibo Chen 	status = readl(tsc->tsc_regs + REG_TSC_INT_STATUS);
2699a436d52SHaibo Chen 
2709a436d52SHaibo Chen 	/* write 1 to clear the bit measure-signal */
2719a436d52SHaibo Chen 	writel(MEASURE_SIGNAL | DETECT_SIGNAL,
2729a436d52SHaibo Chen 		tsc->tsc_regs + REG_TSC_INT_STATUS);
2739a436d52SHaibo Chen 
2749a436d52SHaibo Chen 	/* It's a HW self-clean bit. Set this bit and start sense detection */
2759a436d52SHaibo Chen 	start = readl(tsc->tsc_regs + REG_TSC_FLOW_CONTROL);
2769a436d52SHaibo Chen 	start |= START_SENSE;
2779a436d52SHaibo Chen 	writel(start, tsc->tsc_regs + REG_TSC_FLOW_CONTROL);
2789a436d52SHaibo Chen 
2799a436d52SHaibo Chen 	if (status & MEASURE_SIGNAL) {
2809a436d52SHaibo Chen 		value = readl(tsc->tsc_regs + REG_TSC_MEASURE_VALUE);
2819a436d52SHaibo Chen 		x = (value >> 16) & 0x0fff;
2829a436d52SHaibo Chen 		y = value & 0x0fff;
2839a436d52SHaibo Chen 
2849a436d52SHaibo Chen 		/*
2859a436d52SHaibo Chen 		 * In detect mode, we can get the xnur gpio value,
2869a436d52SHaibo Chen 		 * otherwise assume contact is stiull active.
2879a436d52SHaibo Chen 		 */
2889a436d52SHaibo Chen 		if (!tsc_wait_detect_mode(tsc) ||
2899a436d52SHaibo Chen 		    gpiod_get_value_cansleep(tsc->xnur_gpio)) {
2909a436d52SHaibo Chen 			input_report_key(tsc->input, BTN_TOUCH, 1);
2919a436d52SHaibo Chen 			input_report_abs(tsc->input, ABS_X, x);
2929a436d52SHaibo Chen 			input_report_abs(tsc->input, ABS_Y, y);
2939a436d52SHaibo Chen 		} else {
2949a436d52SHaibo Chen 			input_report_key(tsc->input, BTN_TOUCH, 0);
2959a436d52SHaibo Chen 		}
2969a436d52SHaibo Chen 
2979a436d52SHaibo Chen 		input_sync(tsc->input);
2989a436d52SHaibo Chen 	}
2999a436d52SHaibo Chen 
3009a436d52SHaibo Chen 	return IRQ_HANDLED;
3019a436d52SHaibo Chen }
3029a436d52SHaibo Chen 
3039a436d52SHaibo Chen static irqreturn_t adc_irq_fn(int irq, void *dev_id)
3049a436d52SHaibo Chen {
3059a436d52SHaibo Chen 	struct imx6ul_tsc *tsc = dev_id;
306accbcea3SGuy Shapiro 	u32 coco;
3079a436d52SHaibo Chen 
3089a436d52SHaibo Chen 	coco = readl(tsc->adc_regs + REG_ADC_HS);
3099a436d52SHaibo Chen 	if (coco & 0x01) {
310cd536aa5SLee Jones 		readl(tsc->adc_regs + REG_ADC_R0);
3119a436d52SHaibo Chen 		complete(&tsc->completion);
3129a436d52SHaibo Chen 	}
3139a436d52SHaibo Chen 
3149a436d52SHaibo Chen 	return IRQ_HANDLED;
3159a436d52SHaibo Chen }
3169a436d52SHaibo Chen 
317925145f9SDmitry Torokhov static int imx6ul_tsc_start(struct imx6ul_tsc *tsc)
3189a436d52SHaibo Chen {
3199a436d52SHaibo Chen 	int err;
3209a436d52SHaibo Chen 
3219a436d52SHaibo Chen 	err = clk_prepare_enable(tsc->adc_clk);
3229a436d52SHaibo Chen 	if (err) {
3239a436d52SHaibo Chen 		dev_err(tsc->dev,
3249a436d52SHaibo Chen 			"Could not prepare or enable the adc clock: %d\n",
3259a436d52SHaibo Chen 			err);
3269a436d52SHaibo Chen 		return err;
3279a436d52SHaibo Chen 	}
3289a436d52SHaibo Chen 
3299a436d52SHaibo Chen 	err = clk_prepare_enable(tsc->tsc_clk);
3309a436d52SHaibo Chen 	if (err) {
3319a436d52SHaibo Chen 		dev_err(tsc->dev,
3329a436d52SHaibo Chen 			"Could not prepare or enable the tsc clock: %d\n",
3339a436d52SHaibo Chen 			err);
334c2868417SFabio Estevam 		goto disable_adc_clk;
3359a436d52SHaibo Chen 	}
3369a436d52SHaibo Chen 
337c2868417SFabio Estevam 	err = imx6ul_tsc_init(tsc);
338c2868417SFabio Estevam 	if (err)
339c2868417SFabio Estevam 		goto disable_tsc_clk;
340c2868417SFabio Estevam 
341c2868417SFabio Estevam 	return 0;
342c2868417SFabio Estevam 
343c2868417SFabio Estevam disable_tsc_clk:
344c2868417SFabio Estevam 	clk_disable_unprepare(tsc->tsc_clk);
345c2868417SFabio Estevam disable_adc_clk:
346c2868417SFabio Estevam 	clk_disable_unprepare(tsc->adc_clk);
347c2868417SFabio Estevam 	return err;
3489a436d52SHaibo Chen }
3499a436d52SHaibo Chen 
350925145f9SDmitry Torokhov static void imx6ul_tsc_stop(struct imx6ul_tsc *tsc)
3519a436d52SHaibo Chen {
3529a436d52SHaibo Chen 	imx6ul_tsc_disable(tsc);
3539a436d52SHaibo Chen 
3549a436d52SHaibo Chen 	clk_disable_unprepare(tsc->tsc_clk);
3559a436d52SHaibo Chen 	clk_disable_unprepare(tsc->adc_clk);
3569a436d52SHaibo Chen }
3579a436d52SHaibo Chen 
358925145f9SDmitry Torokhov 
359925145f9SDmitry Torokhov static int imx6ul_tsc_open(struct input_dev *input_dev)
360925145f9SDmitry Torokhov {
361925145f9SDmitry Torokhov 	struct imx6ul_tsc *tsc = input_get_drvdata(input_dev);
362925145f9SDmitry Torokhov 
363925145f9SDmitry Torokhov 	return imx6ul_tsc_start(tsc);
364925145f9SDmitry Torokhov }
365925145f9SDmitry Torokhov 
366925145f9SDmitry Torokhov static void imx6ul_tsc_close(struct input_dev *input_dev)
367925145f9SDmitry Torokhov {
368925145f9SDmitry Torokhov 	struct imx6ul_tsc *tsc = input_get_drvdata(input_dev);
369925145f9SDmitry Torokhov 
370925145f9SDmitry Torokhov 	imx6ul_tsc_stop(tsc);
371925145f9SDmitry Torokhov }
372925145f9SDmitry Torokhov 
3739a436d52SHaibo Chen static int imx6ul_tsc_probe(struct platform_device *pdev)
3749a436d52SHaibo Chen {
3759a436d52SHaibo Chen 	struct device_node *np = pdev->dev.of_node;
3769a436d52SHaibo Chen 	struct imx6ul_tsc *tsc;
3779a436d52SHaibo Chen 	struct input_dev *input_dev;
3789a436d52SHaibo Chen 	int err;
3799a436d52SHaibo Chen 	int tsc_irq;
3809a436d52SHaibo Chen 	int adc_irq;
381f43d3ec3SGuy Shapiro 	u32 average_samples;
3829a436d52SHaibo Chen 
3835eab3cf3SFabio Estevam 	tsc = devm_kzalloc(&pdev->dev, sizeof(*tsc), GFP_KERNEL);
3849a436d52SHaibo Chen 	if (!tsc)
3859a436d52SHaibo Chen 		return -ENOMEM;
3869a436d52SHaibo Chen 
3879a436d52SHaibo Chen 	input_dev = devm_input_allocate_device(&pdev->dev);
3889a436d52SHaibo Chen 	if (!input_dev)
3899a436d52SHaibo Chen 		return -ENOMEM;
3909a436d52SHaibo Chen 
391002801fcSFabio Estevam 	input_dev->name = "iMX6UL Touchscreen Controller";
3929a436d52SHaibo Chen 	input_dev->id.bustype = BUS_HOST;
3939a436d52SHaibo Chen 
3949a436d52SHaibo Chen 	input_dev->open = imx6ul_tsc_open;
3959a436d52SHaibo Chen 	input_dev->close = imx6ul_tsc_close;
3969a436d52SHaibo Chen 
3979a436d52SHaibo Chen 	input_set_capability(input_dev, EV_KEY, BTN_TOUCH);
3989a436d52SHaibo Chen 	input_set_abs_params(input_dev, ABS_X, 0, 0xFFF, 0, 0);
3999a436d52SHaibo Chen 	input_set_abs_params(input_dev, ABS_Y, 0, 0xFFF, 0, 0);
4009a436d52SHaibo Chen 
4019a436d52SHaibo Chen 	input_set_drvdata(input_dev, tsc);
4029a436d52SHaibo Chen 
4039a436d52SHaibo Chen 	tsc->dev = &pdev->dev;
4049a436d52SHaibo Chen 	tsc->input = input_dev;
4059a436d52SHaibo Chen 	init_completion(&tsc->completion);
4069a436d52SHaibo Chen 
4079a436d52SHaibo Chen 	tsc->xnur_gpio = devm_gpiod_get(&pdev->dev, "xnur", GPIOD_IN);
4089a436d52SHaibo Chen 	if (IS_ERR(tsc->xnur_gpio)) {
4099a436d52SHaibo Chen 		err = PTR_ERR(tsc->xnur_gpio);
4109a436d52SHaibo Chen 		dev_err(&pdev->dev,
4119a436d52SHaibo Chen 			"failed to request GPIO tsc_X- (xnur): %d\n", err);
4129a436d52SHaibo Chen 		return err;
4139a436d52SHaibo Chen 	}
4149a436d52SHaibo Chen 
415f1222f5eSAnson Huang 	tsc->tsc_regs = devm_platform_ioremap_resource(pdev, 0);
4169a436d52SHaibo Chen 	if (IS_ERR(tsc->tsc_regs)) {
4179a436d52SHaibo Chen 		err = PTR_ERR(tsc->tsc_regs);
4189a436d52SHaibo Chen 		dev_err(&pdev->dev, "failed to remap tsc memory: %d\n", err);
4199a436d52SHaibo Chen 		return err;
4209a436d52SHaibo Chen 	}
4219a436d52SHaibo Chen 
422f1222f5eSAnson Huang 	tsc->adc_regs = devm_platform_ioremap_resource(pdev, 1);
4239a436d52SHaibo Chen 	if (IS_ERR(tsc->adc_regs)) {
4249a436d52SHaibo Chen 		err = PTR_ERR(tsc->adc_regs);
4259a436d52SHaibo Chen 		dev_err(&pdev->dev, "failed to remap adc memory: %d\n", err);
4269a436d52SHaibo Chen 		return err;
4279a436d52SHaibo Chen 	}
4289a436d52SHaibo Chen 
4299a436d52SHaibo Chen 	tsc->tsc_clk = devm_clk_get(&pdev->dev, "tsc");
4309a436d52SHaibo Chen 	if (IS_ERR(tsc->tsc_clk)) {
4319a436d52SHaibo Chen 		err = PTR_ERR(tsc->tsc_clk);
4329a436d52SHaibo Chen 		dev_err(&pdev->dev, "failed getting tsc clock: %d\n", err);
4339a436d52SHaibo Chen 		return err;
4349a436d52SHaibo Chen 	}
4359a436d52SHaibo Chen 
4369a436d52SHaibo Chen 	tsc->adc_clk = devm_clk_get(&pdev->dev, "adc");
4379a436d52SHaibo Chen 	if (IS_ERR(tsc->adc_clk)) {
4389a436d52SHaibo Chen 		err = PTR_ERR(tsc->adc_clk);
4399a436d52SHaibo Chen 		dev_err(&pdev->dev, "failed getting adc clock: %d\n", err);
4409a436d52SHaibo Chen 		return err;
4419a436d52SHaibo Chen 	}
4429a436d52SHaibo Chen 
4439a436d52SHaibo Chen 	tsc_irq = platform_get_irq(pdev, 0);
4440bec8b7eSStephen Boyd 	if (tsc_irq < 0)
4459a436d52SHaibo Chen 		return tsc_irq;
4469a436d52SHaibo Chen 
4479a436d52SHaibo Chen 	adc_irq = platform_get_irq(pdev, 1);
4480bec8b7eSStephen Boyd 	if (adc_irq < 0)
4499a436d52SHaibo Chen 		return adc_irq;
4509a436d52SHaibo Chen 
4519a436d52SHaibo Chen 	err = devm_request_threaded_irq(tsc->dev, tsc_irq,
4529a436d52SHaibo Chen 					NULL, tsc_irq_fn, IRQF_ONESHOT,
4539a436d52SHaibo Chen 					dev_name(&pdev->dev), tsc);
4549a436d52SHaibo Chen 	if (err) {
4559a436d52SHaibo Chen 		dev_err(&pdev->dev,
4569a436d52SHaibo Chen 			"failed requesting tsc irq %d: %d\n",
4579a436d52SHaibo Chen 			tsc_irq, err);
4589a436d52SHaibo Chen 		return err;
4599a436d52SHaibo Chen 	}
4609a436d52SHaibo Chen 
4619a436d52SHaibo Chen 	err = devm_request_irq(tsc->dev, adc_irq, adc_irq_fn, 0,
4629a436d52SHaibo Chen 				dev_name(&pdev->dev), tsc);
4639a436d52SHaibo Chen 	if (err) {
4649a436d52SHaibo Chen 		dev_err(&pdev->dev,
4659a436d52SHaibo Chen 			"failed requesting adc irq %d: %d\n",
4669a436d52SHaibo Chen 			adc_irq, err);
4679a436d52SHaibo Chen 		return err;
4689a436d52SHaibo Chen 	}
4699a436d52SHaibo Chen 
4709a436d52SHaibo Chen 	err = of_property_read_u32(np, "measure-delay-time",
4719a436d52SHaibo Chen 				   &tsc->measure_delay_time);
4729a436d52SHaibo Chen 	if (err)
4739a436d52SHaibo Chen 		tsc->measure_delay_time = 0xffff;
4749a436d52SHaibo Chen 
4759a436d52SHaibo Chen 	err = of_property_read_u32(np, "pre-charge-time",
4769a436d52SHaibo Chen 				   &tsc->pre_charge_time);
4779a436d52SHaibo Chen 	if (err)
4789a436d52SHaibo Chen 		tsc->pre_charge_time = 0xfff;
4799a436d52SHaibo Chen 
480f43d3ec3SGuy Shapiro 	err = of_property_read_u32(np, "touchscreen-average-samples",
481f43d3ec3SGuy Shapiro 				   &average_samples);
482031bfed2SGuy Shapiro 	if (err)
483f43d3ec3SGuy Shapiro 		average_samples = 1;
484031bfed2SGuy Shapiro 
485f43d3ec3SGuy Shapiro 	switch (average_samples) {
486f43d3ec3SGuy Shapiro 	case 1:
487f43d3ec3SGuy Shapiro 		tsc->average_enable = false;
488f43d3ec3SGuy Shapiro 		tsc->average_select = 0; /* value unused; initialize anyway */
489f43d3ec3SGuy Shapiro 		break;
490f43d3ec3SGuy Shapiro 	case 4:
491f43d3ec3SGuy Shapiro 	case 8:
492f43d3ec3SGuy Shapiro 	case 16:
493f43d3ec3SGuy Shapiro 	case 32:
494f43d3ec3SGuy Shapiro 		tsc->average_enable = true;
495f43d3ec3SGuy Shapiro 		tsc->average_select = ilog2(average_samples) - 2;
496f43d3ec3SGuy Shapiro 		break;
497f43d3ec3SGuy Shapiro 	default:
498f43d3ec3SGuy Shapiro 		dev_err(&pdev->dev,
499f43d3ec3SGuy Shapiro 			"touchscreen-average-samples (%u) must be 1, 4, 8, 16 or 32\n",
500f43d3ec3SGuy Shapiro 			average_samples);
501031bfed2SGuy Shapiro 		return -EINVAL;
502031bfed2SGuy Shapiro 	}
503031bfed2SGuy Shapiro 
5049a436d52SHaibo Chen 	err = input_register_device(tsc->input);
5059a436d52SHaibo Chen 	if (err) {
5069a436d52SHaibo Chen 		dev_err(&pdev->dev,
5079a436d52SHaibo Chen 			"failed to register input device: %d\n", err);
5089a436d52SHaibo Chen 		return err;
5099a436d52SHaibo Chen 	}
5109a436d52SHaibo Chen 
5119a436d52SHaibo Chen 	platform_set_drvdata(pdev, tsc);
5129a436d52SHaibo Chen 	return 0;
5139a436d52SHaibo Chen }
5149a436d52SHaibo Chen 
5159a436d52SHaibo Chen static int __maybe_unused imx6ul_tsc_suspend(struct device *dev)
5169a436d52SHaibo Chen {
5179a436d52SHaibo Chen 	struct platform_device *pdev = to_platform_device(dev);
5189a436d52SHaibo Chen 	struct imx6ul_tsc *tsc = platform_get_drvdata(pdev);
5199a436d52SHaibo Chen 	struct input_dev *input_dev = tsc->input;
5209a436d52SHaibo Chen 
5219a436d52SHaibo Chen 	mutex_lock(&input_dev->mutex);
5229a436d52SHaibo Chen 
523*d69f0a43SAndrzej Pietrasiewicz 	if (input_device_enabled(input_dev))
524925145f9SDmitry Torokhov 		imx6ul_tsc_stop(tsc);
5259a436d52SHaibo Chen 
5269a436d52SHaibo Chen 	mutex_unlock(&input_dev->mutex);
5279a436d52SHaibo Chen 
5289a436d52SHaibo Chen 	return 0;
5299a436d52SHaibo Chen }
5309a436d52SHaibo Chen 
5319a436d52SHaibo Chen static int __maybe_unused imx6ul_tsc_resume(struct device *dev)
5329a436d52SHaibo Chen {
5339a436d52SHaibo Chen 	struct platform_device *pdev = to_platform_device(dev);
5349a436d52SHaibo Chen 	struct imx6ul_tsc *tsc = platform_get_drvdata(pdev);
5359a436d52SHaibo Chen 	struct input_dev *input_dev = tsc->input;
5369a436d52SHaibo Chen 	int retval = 0;
5379a436d52SHaibo Chen 
5389a436d52SHaibo Chen 	mutex_lock(&input_dev->mutex);
5399a436d52SHaibo Chen 
540*d69f0a43SAndrzej Pietrasiewicz 	if (input_device_enabled(input_dev))
541925145f9SDmitry Torokhov 		retval = imx6ul_tsc_start(tsc);
54230df23c5SDan Carpenter 
5439a436d52SHaibo Chen 	mutex_unlock(&input_dev->mutex);
544925145f9SDmitry Torokhov 
5459a436d52SHaibo Chen 	return retval;
5469a436d52SHaibo Chen }
5479a436d52SHaibo Chen 
5489a436d52SHaibo Chen static SIMPLE_DEV_PM_OPS(imx6ul_tsc_pm_ops,
5499a436d52SHaibo Chen 			 imx6ul_tsc_suspend, imx6ul_tsc_resume);
5509a436d52SHaibo Chen 
5519a436d52SHaibo Chen static const struct of_device_id imx6ul_tsc_match[] = {
5529a436d52SHaibo Chen 	{ .compatible = "fsl,imx6ul-tsc", },
5539a436d52SHaibo Chen 	{ /* sentinel */ }
5549a436d52SHaibo Chen };
5559a436d52SHaibo Chen MODULE_DEVICE_TABLE(of, imx6ul_tsc_match);
5569a436d52SHaibo Chen 
5579a436d52SHaibo Chen static struct platform_driver imx6ul_tsc_driver = {
5589a436d52SHaibo Chen 	.driver		= {
5599a436d52SHaibo Chen 		.name	= "imx6ul-tsc",
5609a436d52SHaibo Chen 		.of_match_table	= imx6ul_tsc_match,
5619a436d52SHaibo Chen 		.pm	= &imx6ul_tsc_pm_ops,
5629a436d52SHaibo Chen 	},
5639a436d52SHaibo Chen 	.probe		= imx6ul_tsc_probe,
5649a436d52SHaibo Chen };
5659a436d52SHaibo Chen module_platform_driver(imx6ul_tsc_driver);
5669a436d52SHaibo Chen 
5679a436d52SHaibo Chen MODULE_AUTHOR("Haibo Chen <haibo.chen@freescale.com>");
5689a436d52SHaibo Chen MODULE_DESCRIPTION("Freescale i.MX6UL Touchscreen controller driver");
5699a436d52SHaibo Chen MODULE_LICENSE("GPL v2");
570