162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci//
362306a36Sopenharmony_ci// Freescale i.MX6UL touchscreen controller driver
462306a36Sopenharmony_ci//
562306a36Sopenharmony_ci// Copyright (C) 2015 Freescale Semiconductor, Inc.
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/errno.h>
862306a36Sopenharmony_ci#include <linux/kernel.h>
962306a36Sopenharmony_ci#include <linux/module.h>
1062306a36Sopenharmony_ci#include <linux/gpio/consumer.h>
1162306a36Sopenharmony_ci#include <linux/input.h>
1262306a36Sopenharmony_ci#include <linux/slab.h>
1362306a36Sopenharmony_ci#include <linux/completion.h>
1462306a36Sopenharmony_ci#include <linux/delay.h>
1562306a36Sopenharmony_ci#include <linux/of.h>
1662306a36Sopenharmony_ci#include <linux/interrupt.h>
1762306a36Sopenharmony_ci#include <linux/platform_device.h>
1862306a36Sopenharmony_ci#include <linux/clk.h>
1962306a36Sopenharmony_ci#include <linux/io.h>
2062306a36Sopenharmony_ci#include <linux/log2.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci/* ADC configuration registers field define */
2362306a36Sopenharmony_ci#define ADC_AIEN		(0x1 << 7)
2462306a36Sopenharmony_ci#define ADC_CONV_DISABLE	0x1F
2562306a36Sopenharmony_ci#define ADC_AVGE		(0x1 << 5)
2662306a36Sopenharmony_ci#define ADC_CAL			(0x1 << 7)
2762306a36Sopenharmony_ci#define ADC_CALF		0x2
2862306a36Sopenharmony_ci#define ADC_12BIT_MODE		(0x2 << 2)
2962306a36Sopenharmony_ci#define ADC_CONV_MODE_MASK	(0x3 << 2)
3062306a36Sopenharmony_ci#define ADC_IPG_CLK		0x00
3162306a36Sopenharmony_ci#define ADC_INPUT_CLK_MASK	0x3
3262306a36Sopenharmony_ci#define ADC_CLK_DIV_8		(0x03 << 5)
3362306a36Sopenharmony_ci#define ADC_CLK_DIV_MASK	(0x3 << 5)
3462306a36Sopenharmony_ci#define ADC_SHORT_SAMPLE_MODE	(0x0 << 4)
3562306a36Sopenharmony_ci#define ADC_SAMPLE_MODE_MASK	(0x1 << 4)
3662306a36Sopenharmony_ci#define ADC_HARDWARE_TRIGGER	(0x1 << 13)
3762306a36Sopenharmony_ci#define ADC_AVGS_SHIFT		14
3862306a36Sopenharmony_ci#define ADC_AVGS_MASK		(0x3 << 14)
3962306a36Sopenharmony_ci#define SELECT_CHANNEL_4	0x04
4062306a36Sopenharmony_ci#define SELECT_CHANNEL_1	0x01
4162306a36Sopenharmony_ci#define DISABLE_CONVERSION_INT	(0x0 << 7)
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci/* ADC registers */
4462306a36Sopenharmony_ci#define REG_ADC_HC0		0x00
4562306a36Sopenharmony_ci#define REG_ADC_HC1		0x04
4662306a36Sopenharmony_ci#define REG_ADC_HC2		0x08
4762306a36Sopenharmony_ci#define REG_ADC_HC3		0x0C
4862306a36Sopenharmony_ci#define REG_ADC_HC4		0x10
4962306a36Sopenharmony_ci#define REG_ADC_HS		0x14
5062306a36Sopenharmony_ci#define REG_ADC_R0		0x18
5162306a36Sopenharmony_ci#define REG_ADC_CFG		0x2C
5262306a36Sopenharmony_ci#define REG_ADC_GC		0x30
5362306a36Sopenharmony_ci#define REG_ADC_GS		0x34
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci#define ADC_TIMEOUT		msecs_to_jiffies(100)
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci/* TSC registers */
5862306a36Sopenharmony_ci#define REG_TSC_BASIC_SETING	0x00
5962306a36Sopenharmony_ci#define REG_TSC_PRE_CHARGE_TIME	0x10
6062306a36Sopenharmony_ci#define REG_TSC_FLOW_CONTROL	0x20
6162306a36Sopenharmony_ci#define REG_TSC_MEASURE_VALUE	0x30
6262306a36Sopenharmony_ci#define REG_TSC_INT_EN		0x40
6362306a36Sopenharmony_ci#define REG_TSC_INT_SIG_EN	0x50
6462306a36Sopenharmony_ci#define REG_TSC_INT_STATUS	0x60
6562306a36Sopenharmony_ci#define REG_TSC_DEBUG_MODE	0x70
6662306a36Sopenharmony_ci#define REG_TSC_DEBUG_MODE2	0x80
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci/* TSC configuration registers field define */
6962306a36Sopenharmony_ci#define DETECT_4_WIRE_MODE	(0x0 << 4)
7062306a36Sopenharmony_ci#define AUTO_MEASURE		0x1
7162306a36Sopenharmony_ci#define MEASURE_SIGNAL		0x1
7262306a36Sopenharmony_ci#define DETECT_SIGNAL		(0x1 << 4)
7362306a36Sopenharmony_ci#define VALID_SIGNAL		(0x1 << 8)
7462306a36Sopenharmony_ci#define MEASURE_INT_EN		0x1
7562306a36Sopenharmony_ci#define MEASURE_SIG_EN		0x1
7662306a36Sopenharmony_ci#define VALID_SIG_EN		(0x1 << 8)
7762306a36Sopenharmony_ci#define DE_GLITCH_2		(0x2 << 29)
7862306a36Sopenharmony_ci#define START_SENSE		(0x1 << 12)
7962306a36Sopenharmony_ci#define TSC_DISABLE		(0x1 << 16)
8062306a36Sopenharmony_ci#define DETECT_MODE		0x2
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_cistruct imx6ul_tsc {
8362306a36Sopenharmony_ci	struct device *dev;
8462306a36Sopenharmony_ci	struct input_dev *input;
8562306a36Sopenharmony_ci	void __iomem *tsc_regs;
8662306a36Sopenharmony_ci	void __iomem *adc_regs;
8762306a36Sopenharmony_ci	struct clk *tsc_clk;
8862306a36Sopenharmony_ci	struct clk *adc_clk;
8962306a36Sopenharmony_ci	struct gpio_desc *xnur_gpio;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	u32 measure_delay_time;
9262306a36Sopenharmony_ci	u32 pre_charge_time;
9362306a36Sopenharmony_ci	bool average_enable;
9462306a36Sopenharmony_ci	u32 average_select;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	struct completion completion;
9762306a36Sopenharmony_ci};
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci/*
10062306a36Sopenharmony_ci * TSC module need ADC to get the measure value. So
10162306a36Sopenharmony_ci * before config TSC, we should initialize ADC module.
10262306a36Sopenharmony_ci */
10362306a36Sopenharmony_cistatic int imx6ul_adc_init(struct imx6ul_tsc *tsc)
10462306a36Sopenharmony_ci{
10562306a36Sopenharmony_ci	u32 adc_hc = 0;
10662306a36Sopenharmony_ci	u32 adc_gc;
10762306a36Sopenharmony_ci	u32 adc_gs;
10862306a36Sopenharmony_ci	u32 adc_cfg;
10962306a36Sopenharmony_ci	unsigned long timeout;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	reinit_completion(&tsc->completion);
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	adc_cfg = readl(tsc->adc_regs + REG_ADC_CFG);
11462306a36Sopenharmony_ci	adc_cfg &= ~(ADC_CONV_MODE_MASK | ADC_INPUT_CLK_MASK);
11562306a36Sopenharmony_ci	adc_cfg |= ADC_12BIT_MODE | ADC_IPG_CLK;
11662306a36Sopenharmony_ci	adc_cfg &= ~(ADC_CLK_DIV_MASK | ADC_SAMPLE_MODE_MASK);
11762306a36Sopenharmony_ci	adc_cfg |= ADC_CLK_DIV_8 | ADC_SHORT_SAMPLE_MODE;
11862306a36Sopenharmony_ci	if (tsc->average_enable) {
11962306a36Sopenharmony_ci		adc_cfg &= ~ADC_AVGS_MASK;
12062306a36Sopenharmony_ci		adc_cfg |= (tsc->average_select) << ADC_AVGS_SHIFT;
12162306a36Sopenharmony_ci	}
12262306a36Sopenharmony_ci	adc_cfg &= ~ADC_HARDWARE_TRIGGER;
12362306a36Sopenharmony_ci	writel(adc_cfg, tsc->adc_regs + REG_ADC_CFG);
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	/* enable calibration interrupt */
12662306a36Sopenharmony_ci	adc_hc |= ADC_AIEN;
12762306a36Sopenharmony_ci	adc_hc |= ADC_CONV_DISABLE;
12862306a36Sopenharmony_ci	writel(adc_hc, tsc->adc_regs + REG_ADC_HC0);
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	/* start ADC calibration */
13162306a36Sopenharmony_ci	adc_gc = readl(tsc->adc_regs + REG_ADC_GC);
13262306a36Sopenharmony_ci	adc_gc |= ADC_CAL;
13362306a36Sopenharmony_ci	if (tsc->average_enable)
13462306a36Sopenharmony_ci		adc_gc |= ADC_AVGE;
13562306a36Sopenharmony_ci	writel(adc_gc, tsc->adc_regs + REG_ADC_GC);
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	timeout = wait_for_completion_timeout
13862306a36Sopenharmony_ci			(&tsc->completion, ADC_TIMEOUT);
13962306a36Sopenharmony_ci	if (timeout == 0) {
14062306a36Sopenharmony_ci		dev_err(tsc->dev, "Timeout for adc calibration\n");
14162306a36Sopenharmony_ci		return -ETIMEDOUT;
14262306a36Sopenharmony_ci	}
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	adc_gs = readl(tsc->adc_regs + REG_ADC_GS);
14562306a36Sopenharmony_ci	if (adc_gs & ADC_CALF) {
14662306a36Sopenharmony_ci		dev_err(tsc->dev, "ADC calibration failed\n");
14762306a36Sopenharmony_ci		return -EINVAL;
14862306a36Sopenharmony_ci	}
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	/* TSC need the ADC work in hardware trigger */
15162306a36Sopenharmony_ci	adc_cfg = readl(tsc->adc_regs + REG_ADC_CFG);
15262306a36Sopenharmony_ci	adc_cfg |= ADC_HARDWARE_TRIGGER;
15362306a36Sopenharmony_ci	writel(adc_cfg, tsc->adc_regs + REG_ADC_CFG);
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	return 0;
15662306a36Sopenharmony_ci}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci/*
15962306a36Sopenharmony_ci * This is a TSC workaround. Currently TSC misconnect two
16062306a36Sopenharmony_ci * ADC channels, this function remap channel configure for
16162306a36Sopenharmony_ci * hardware trigger.
16262306a36Sopenharmony_ci */
16362306a36Sopenharmony_cistatic void imx6ul_tsc_channel_config(struct imx6ul_tsc *tsc)
16462306a36Sopenharmony_ci{
16562306a36Sopenharmony_ci	u32 adc_hc0, adc_hc1, adc_hc2, adc_hc3, adc_hc4;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	adc_hc0 = DISABLE_CONVERSION_INT;
16862306a36Sopenharmony_ci	writel(adc_hc0, tsc->adc_regs + REG_ADC_HC0);
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	adc_hc1 = DISABLE_CONVERSION_INT | SELECT_CHANNEL_4;
17162306a36Sopenharmony_ci	writel(adc_hc1, tsc->adc_regs + REG_ADC_HC1);
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	adc_hc2 = DISABLE_CONVERSION_INT;
17462306a36Sopenharmony_ci	writel(adc_hc2, tsc->adc_regs + REG_ADC_HC2);
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	adc_hc3 = DISABLE_CONVERSION_INT | SELECT_CHANNEL_1;
17762306a36Sopenharmony_ci	writel(adc_hc3, tsc->adc_regs + REG_ADC_HC3);
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	adc_hc4 = DISABLE_CONVERSION_INT;
18062306a36Sopenharmony_ci	writel(adc_hc4, tsc->adc_regs + REG_ADC_HC4);
18162306a36Sopenharmony_ci}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci/*
18462306a36Sopenharmony_ci * TSC setting, confige the pre-charge time and measure delay time.
18562306a36Sopenharmony_ci * different touch screen may need different pre-charge time and
18662306a36Sopenharmony_ci * measure delay time.
18762306a36Sopenharmony_ci */
18862306a36Sopenharmony_cistatic void imx6ul_tsc_set(struct imx6ul_tsc *tsc)
18962306a36Sopenharmony_ci{
19062306a36Sopenharmony_ci	u32 basic_setting = 0;
19162306a36Sopenharmony_ci	u32 start;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	basic_setting |= tsc->measure_delay_time << 8;
19462306a36Sopenharmony_ci	basic_setting |= DETECT_4_WIRE_MODE | AUTO_MEASURE;
19562306a36Sopenharmony_ci	writel(basic_setting, tsc->tsc_regs + REG_TSC_BASIC_SETING);
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	writel(DE_GLITCH_2, tsc->tsc_regs + REG_TSC_DEBUG_MODE2);
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	writel(tsc->pre_charge_time, tsc->tsc_regs + REG_TSC_PRE_CHARGE_TIME);
20062306a36Sopenharmony_ci	writel(MEASURE_INT_EN, tsc->tsc_regs + REG_TSC_INT_EN);
20162306a36Sopenharmony_ci	writel(MEASURE_SIG_EN | VALID_SIG_EN,
20262306a36Sopenharmony_ci		tsc->tsc_regs + REG_TSC_INT_SIG_EN);
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	/* start sense detection */
20562306a36Sopenharmony_ci	start = readl(tsc->tsc_regs + REG_TSC_FLOW_CONTROL);
20662306a36Sopenharmony_ci	start |= START_SENSE;
20762306a36Sopenharmony_ci	start &= ~TSC_DISABLE;
20862306a36Sopenharmony_ci	writel(start, tsc->tsc_regs + REG_TSC_FLOW_CONTROL);
20962306a36Sopenharmony_ci}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_cistatic int imx6ul_tsc_init(struct imx6ul_tsc *tsc)
21262306a36Sopenharmony_ci{
21362306a36Sopenharmony_ci	int err;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	err = imx6ul_adc_init(tsc);
21662306a36Sopenharmony_ci	if (err)
21762306a36Sopenharmony_ci		return err;
21862306a36Sopenharmony_ci	imx6ul_tsc_channel_config(tsc);
21962306a36Sopenharmony_ci	imx6ul_tsc_set(tsc);
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	return 0;
22262306a36Sopenharmony_ci}
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_cistatic void imx6ul_tsc_disable(struct imx6ul_tsc *tsc)
22562306a36Sopenharmony_ci{
22662306a36Sopenharmony_ci	u32 tsc_flow;
22762306a36Sopenharmony_ci	u32 adc_cfg;
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	/* TSC controller enters to idle status */
23062306a36Sopenharmony_ci	tsc_flow = readl(tsc->tsc_regs + REG_TSC_FLOW_CONTROL);
23162306a36Sopenharmony_ci	tsc_flow |= TSC_DISABLE;
23262306a36Sopenharmony_ci	writel(tsc_flow, tsc->tsc_regs + REG_TSC_FLOW_CONTROL);
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	/* ADC controller enters to stop mode */
23562306a36Sopenharmony_ci	adc_cfg = readl(tsc->adc_regs + REG_ADC_HC0);
23662306a36Sopenharmony_ci	adc_cfg |= ADC_CONV_DISABLE;
23762306a36Sopenharmony_ci	writel(adc_cfg, tsc->adc_regs + REG_ADC_HC0);
23862306a36Sopenharmony_ci}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci/* Delay some time (max 2ms), wait the pre-charge done. */
24162306a36Sopenharmony_cistatic bool tsc_wait_detect_mode(struct imx6ul_tsc *tsc)
24262306a36Sopenharmony_ci{
24362306a36Sopenharmony_ci	unsigned long timeout = jiffies + msecs_to_jiffies(2);
24462306a36Sopenharmony_ci	u32 state_machine;
24562306a36Sopenharmony_ci	u32 debug_mode2;
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	do {
24862306a36Sopenharmony_ci		if (time_after(jiffies, timeout))
24962306a36Sopenharmony_ci			return false;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci		usleep_range(200, 400);
25262306a36Sopenharmony_ci		debug_mode2 = readl(tsc->tsc_regs + REG_TSC_DEBUG_MODE2);
25362306a36Sopenharmony_ci		state_machine = (debug_mode2 >> 20) & 0x7;
25462306a36Sopenharmony_ci	} while (state_machine != DETECT_MODE);
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	usleep_range(200, 400);
25762306a36Sopenharmony_ci	return true;
25862306a36Sopenharmony_ci}
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_cistatic irqreturn_t tsc_irq_fn(int irq, void *dev_id)
26162306a36Sopenharmony_ci{
26262306a36Sopenharmony_ci	struct imx6ul_tsc *tsc = dev_id;
26362306a36Sopenharmony_ci	u32 status;
26462306a36Sopenharmony_ci	u32 value;
26562306a36Sopenharmony_ci	u32 x, y;
26662306a36Sopenharmony_ci	u32 start;
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	status = readl(tsc->tsc_regs + REG_TSC_INT_STATUS);
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	/* write 1 to clear the bit measure-signal */
27162306a36Sopenharmony_ci	writel(MEASURE_SIGNAL | DETECT_SIGNAL,
27262306a36Sopenharmony_ci		tsc->tsc_regs + REG_TSC_INT_STATUS);
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	/* It's a HW self-clean bit. Set this bit and start sense detection */
27562306a36Sopenharmony_ci	start = readl(tsc->tsc_regs + REG_TSC_FLOW_CONTROL);
27662306a36Sopenharmony_ci	start |= START_SENSE;
27762306a36Sopenharmony_ci	writel(start, tsc->tsc_regs + REG_TSC_FLOW_CONTROL);
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	if (status & MEASURE_SIGNAL) {
28062306a36Sopenharmony_ci		value = readl(tsc->tsc_regs + REG_TSC_MEASURE_VALUE);
28162306a36Sopenharmony_ci		x = (value >> 16) & 0x0fff;
28262306a36Sopenharmony_ci		y = value & 0x0fff;
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci		/*
28562306a36Sopenharmony_ci		 * In detect mode, we can get the xnur gpio value,
28662306a36Sopenharmony_ci		 * otherwise assume contact is stiull active.
28762306a36Sopenharmony_ci		 */
28862306a36Sopenharmony_ci		if (!tsc_wait_detect_mode(tsc) ||
28962306a36Sopenharmony_ci		    gpiod_get_value_cansleep(tsc->xnur_gpio)) {
29062306a36Sopenharmony_ci			input_report_key(tsc->input, BTN_TOUCH, 1);
29162306a36Sopenharmony_ci			input_report_abs(tsc->input, ABS_X, x);
29262306a36Sopenharmony_ci			input_report_abs(tsc->input, ABS_Y, y);
29362306a36Sopenharmony_ci		} else {
29462306a36Sopenharmony_ci			input_report_key(tsc->input, BTN_TOUCH, 0);
29562306a36Sopenharmony_ci		}
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci		input_sync(tsc->input);
29862306a36Sopenharmony_ci	}
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	return IRQ_HANDLED;
30162306a36Sopenharmony_ci}
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_cistatic irqreturn_t adc_irq_fn(int irq, void *dev_id)
30462306a36Sopenharmony_ci{
30562306a36Sopenharmony_ci	struct imx6ul_tsc *tsc = dev_id;
30662306a36Sopenharmony_ci	u32 coco;
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	coco = readl(tsc->adc_regs + REG_ADC_HS);
30962306a36Sopenharmony_ci	if (coco & 0x01) {
31062306a36Sopenharmony_ci		readl(tsc->adc_regs + REG_ADC_R0);
31162306a36Sopenharmony_ci		complete(&tsc->completion);
31262306a36Sopenharmony_ci	}
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	return IRQ_HANDLED;
31562306a36Sopenharmony_ci}
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_cistatic int imx6ul_tsc_start(struct imx6ul_tsc *tsc)
31862306a36Sopenharmony_ci{
31962306a36Sopenharmony_ci	int err;
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	err = clk_prepare_enable(tsc->adc_clk);
32262306a36Sopenharmony_ci	if (err) {
32362306a36Sopenharmony_ci		dev_err(tsc->dev,
32462306a36Sopenharmony_ci			"Could not prepare or enable the adc clock: %d\n",
32562306a36Sopenharmony_ci			err);
32662306a36Sopenharmony_ci		return err;
32762306a36Sopenharmony_ci	}
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	err = clk_prepare_enable(tsc->tsc_clk);
33062306a36Sopenharmony_ci	if (err) {
33162306a36Sopenharmony_ci		dev_err(tsc->dev,
33262306a36Sopenharmony_ci			"Could not prepare or enable the tsc clock: %d\n",
33362306a36Sopenharmony_ci			err);
33462306a36Sopenharmony_ci		goto disable_adc_clk;
33562306a36Sopenharmony_ci	}
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	err = imx6ul_tsc_init(tsc);
33862306a36Sopenharmony_ci	if (err)
33962306a36Sopenharmony_ci		goto disable_tsc_clk;
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	return 0;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_cidisable_tsc_clk:
34462306a36Sopenharmony_ci	clk_disable_unprepare(tsc->tsc_clk);
34562306a36Sopenharmony_cidisable_adc_clk:
34662306a36Sopenharmony_ci	clk_disable_unprepare(tsc->adc_clk);
34762306a36Sopenharmony_ci	return err;
34862306a36Sopenharmony_ci}
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_cistatic void imx6ul_tsc_stop(struct imx6ul_tsc *tsc)
35162306a36Sopenharmony_ci{
35262306a36Sopenharmony_ci	imx6ul_tsc_disable(tsc);
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	clk_disable_unprepare(tsc->tsc_clk);
35562306a36Sopenharmony_ci	clk_disable_unprepare(tsc->adc_clk);
35662306a36Sopenharmony_ci}
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_cistatic int imx6ul_tsc_open(struct input_dev *input_dev)
36062306a36Sopenharmony_ci{
36162306a36Sopenharmony_ci	struct imx6ul_tsc *tsc = input_get_drvdata(input_dev);
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	return imx6ul_tsc_start(tsc);
36462306a36Sopenharmony_ci}
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_cistatic void imx6ul_tsc_close(struct input_dev *input_dev)
36762306a36Sopenharmony_ci{
36862306a36Sopenharmony_ci	struct imx6ul_tsc *tsc = input_get_drvdata(input_dev);
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	imx6ul_tsc_stop(tsc);
37162306a36Sopenharmony_ci}
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_cistatic int imx6ul_tsc_probe(struct platform_device *pdev)
37462306a36Sopenharmony_ci{
37562306a36Sopenharmony_ci	struct device_node *np = pdev->dev.of_node;
37662306a36Sopenharmony_ci	struct imx6ul_tsc *tsc;
37762306a36Sopenharmony_ci	struct input_dev *input_dev;
37862306a36Sopenharmony_ci	int err;
37962306a36Sopenharmony_ci	int tsc_irq;
38062306a36Sopenharmony_ci	int adc_irq;
38162306a36Sopenharmony_ci	u32 average_samples;
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	tsc = devm_kzalloc(&pdev->dev, sizeof(*tsc), GFP_KERNEL);
38462306a36Sopenharmony_ci	if (!tsc)
38562306a36Sopenharmony_ci		return -ENOMEM;
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	input_dev = devm_input_allocate_device(&pdev->dev);
38862306a36Sopenharmony_ci	if (!input_dev)
38962306a36Sopenharmony_ci		return -ENOMEM;
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	input_dev->name = "iMX6UL Touchscreen Controller";
39262306a36Sopenharmony_ci	input_dev->id.bustype = BUS_HOST;
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	input_dev->open = imx6ul_tsc_open;
39562306a36Sopenharmony_ci	input_dev->close = imx6ul_tsc_close;
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	input_set_capability(input_dev, EV_KEY, BTN_TOUCH);
39862306a36Sopenharmony_ci	input_set_abs_params(input_dev, ABS_X, 0, 0xFFF, 0, 0);
39962306a36Sopenharmony_ci	input_set_abs_params(input_dev, ABS_Y, 0, 0xFFF, 0, 0);
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	input_set_drvdata(input_dev, tsc);
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	tsc->dev = &pdev->dev;
40462306a36Sopenharmony_ci	tsc->input = input_dev;
40562306a36Sopenharmony_ci	init_completion(&tsc->completion);
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	tsc->xnur_gpio = devm_gpiod_get(&pdev->dev, "xnur", GPIOD_IN);
40862306a36Sopenharmony_ci	if (IS_ERR(tsc->xnur_gpio)) {
40962306a36Sopenharmony_ci		err = PTR_ERR(tsc->xnur_gpio);
41062306a36Sopenharmony_ci		dev_err(&pdev->dev,
41162306a36Sopenharmony_ci			"failed to request GPIO tsc_X- (xnur): %d\n", err);
41262306a36Sopenharmony_ci		return err;
41362306a36Sopenharmony_ci	}
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	tsc->tsc_regs = devm_platform_ioremap_resource(pdev, 0);
41662306a36Sopenharmony_ci	if (IS_ERR(tsc->tsc_regs)) {
41762306a36Sopenharmony_ci		err = PTR_ERR(tsc->tsc_regs);
41862306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to remap tsc memory: %d\n", err);
41962306a36Sopenharmony_ci		return err;
42062306a36Sopenharmony_ci	}
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	tsc->adc_regs = devm_platform_ioremap_resource(pdev, 1);
42362306a36Sopenharmony_ci	if (IS_ERR(tsc->adc_regs)) {
42462306a36Sopenharmony_ci		err = PTR_ERR(tsc->adc_regs);
42562306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to remap adc memory: %d\n", err);
42662306a36Sopenharmony_ci		return err;
42762306a36Sopenharmony_ci	}
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	tsc->tsc_clk = devm_clk_get(&pdev->dev, "tsc");
43062306a36Sopenharmony_ci	if (IS_ERR(tsc->tsc_clk)) {
43162306a36Sopenharmony_ci		err = PTR_ERR(tsc->tsc_clk);
43262306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed getting tsc clock: %d\n", err);
43362306a36Sopenharmony_ci		return err;
43462306a36Sopenharmony_ci	}
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	tsc->adc_clk = devm_clk_get(&pdev->dev, "adc");
43762306a36Sopenharmony_ci	if (IS_ERR(tsc->adc_clk)) {
43862306a36Sopenharmony_ci		err = PTR_ERR(tsc->adc_clk);
43962306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed getting adc clock: %d\n", err);
44062306a36Sopenharmony_ci		return err;
44162306a36Sopenharmony_ci	}
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	tsc_irq = platform_get_irq(pdev, 0);
44462306a36Sopenharmony_ci	if (tsc_irq < 0)
44562306a36Sopenharmony_ci		return tsc_irq;
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	adc_irq = platform_get_irq(pdev, 1);
44862306a36Sopenharmony_ci	if (adc_irq < 0)
44962306a36Sopenharmony_ci		return adc_irq;
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	err = devm_request_threaded_irq(tsc->dev, tsc_irq,
45262306a36Sopenharmony_ci					NULL, tsc_irq_fn, IRQF_ONESHOT,
45362306a36Sopenharmony_ci					dev_name(&pdev->dev), tsc);
45462306a36Sopenharmony_ci	if (err) {
45562306a36Sopenharmony_ci		dev_err(&pdev->dev,
45662306a36Sopenharmony_ci			"failed requesting tsc irq %d: %d\n",
45762306a36Sopenharmony_ci			tsc_irq, err);
45862306a36Sopenharmony_ci		return err;
45962306a36Sopenharmony_ci	}
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	err = devm_request_irq(tsc->dev, adc_irq, adc_irq_fn, 0,
46262306a36Sopenharmony_ci				dev_name(&pdev->dev), tsc);
46362306a36Sopenharmony_ci	if (err) {
46462306a36Sopenharmony_ci		dev_err(&pdev->dev,
46562306a36Sopenharmony_ci			"failed requesting adc irq %d: %d\n",
46662306a36Sopenharmony_ci			adc_irq, err);
46762306a36Sopenharmony_ci		return err;
46862306a36Sopenharmony_ci	}
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	err = of_property_read_u32(np, "measure-delay-time",
47162306a36Sopenharmony_ci				   &tsc->measure_delay_time);
47262306a36Sopenharmony_ci	if (err)
47362306a36Sopenharmony_ci		tsc->measure_delay_time = 0xffff;
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	err = of_property_read_u32(np, "pre-charge-time",
47662306a36Sopenharmony_ci				   &tsc->pre_charge_time);
47762306a36Sopenharmony_ci	if (err)
47862306a36Sopenharmony_ci		tsc->pre_charge_time = 0xfff;
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	err = of_property_read_u32(np, "touchscreen-average-samples",
48162306a36Sopenharmony_ci				   &average_samples);
48262306a36Sopenharmony_ci	if (err)
48362306a36Sopenharmony_ci		average_samples = 1;
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	switch (average_samples) {
48662306a36Sopenharmony_ci	case 1:
48762306a36Sopenharmony_ci		tsc->average_enable = false;
48862306a36Sopenharmony_ci		tsc->average_select = 0; /* value unused; initialize anyway */
48962306a36Sopenharmony_ci		break;
49062306a36Sopenharmony_ci	case 4:
49162306a36Sopenharmony_ci	case 8:
49262306a36Sopenharmony_ci	case 16:
49362306a36Sopenharmony_ci	case 32:
49462306a36Sopenharmony_ci		tsc->average_enable = true;
49562306a36Sopenharmony_ci		tsc->average_select = ilog2(average_samples) - 2;
49662306a36Sopenharmony_ci		break;
49762306a36Sopenharmony_ci	default:
49862306a36Sopenharmony_ci		dev_err(&pdev->dev,
49962306a36Sopenharmony_ci			"touchscreen-average-samples (%u) must be 1, 4, 8, 16 or 32\n",
50062306a36Sopenharmony_ci			average_samples);
50162306a36Sopenharmony_ci		return -EINVAL;
50262306a36Sopenharmony_ci	}
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	err = input_register_device(tsc->input);
50562306a36Sopenharmony_ci	if (err) {
50662306a36Sopenharmony_ci		dev_err(&pdev->dev,
50762306a36Sopenharmony_ci			"failed to register input device: %d\n", err);
50862306a36Sopenharmony_ci		return err;
50962306a36Sopenharmony_ci	}
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	platform_set_drvdata(pdev, tsc);
51262306a36Sopenharmony_ci	return 0;
51362306a36Sopenharmony_ci}
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_cistatic int imx6ul_tsc_suspend(struct device *dev)
51662306a36Sopenharmony_ci{
51762306a36Sopenharmony_ci	struct platform_device *pdev = to_platform_device(dev);
51862306a36Sopenharmony_ci	struct imx6ul_tsc *tsc = platform_get_drvdata(pdev);
51962306a36Sopenharmony_ci	struct input_dev *input_dev = tsc->input;
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	mutex_lock(&input_dev->mutex);
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	if (input_device_enabled(input_dev))
52462306a36Sopenharmony_ci		imx6ul_tsc_stop(tsc);
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	mutex_unlock(&input_dev->mutex);
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	return 0;
52962306a36Sopenharmony_ci}
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_cistatic int imx6ul_tsc_resume(struct device *dev)
53262306a36Sopenharmony_ci{
53362306a36Sopenharmony_ci	struct platform_device *pdev = to_platform_device(dev);
53462306a36Sopenharmony_ci	struct imx6ul_tsc *tsc = platform_get_drvdata(pdev);
53562306a36Sopenharmony_ci	struct input_dev *input_dev = tsc->input;
53662306a36Sopenharmony_ci	int retval = 0;
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	mutex_lock(&input_dev->mutex);
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	if (input_device_enabled(input_dev))
54162306a36Sopenharmony_ci		retval = imx6ul_tsc_start(tsc);
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	mutex_unlock(&input_dev->mutex);
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	return retval;
54662306a36Sopenharmony_ci}
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_cistatic DEFINE_SIMPLE_DEV_PM_OPS(imx6ul_tsc_pm_ops,
54962306a36Sopenharmony_ci				imx6ul_tsc_suspend, imx6ul_tsc_resume);
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_cistatic const struct of_device_id imx6ul_tsc_match[] = {
55262306a36Sopenharmony_ci	{ .compatible = "fsl,imx6ul-tsc", },
55362306a36Sopenharmony_ci	{ /* sentinel */ }
55462306a36Sopenharmony_ci};
55562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, imx6ul_tsc_match);
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_cistatic struct platform_driver imx6ul_tsc_driver = {
55862306a36Sopenharmony_ci	.driver		= {
55962306a36Sopenharmony_ci		.name	= "imx6ul-tsc",
56062306a36Sopenharmony_ci		.of_match_table	= imx6ul_tsc_match,
56162306a36Sopenharmony_ci		.pm	= pm_sleep_ptr(&imx6ul_tsc_pm_ops),
56262306a36Sopenharmony_ci	},
56362306a36Sopenharmony_ci	.probe		= imx6ul_tsc_probe,
56462306a36Sopenharmony_ci};
56562306a36Sopenharmony_cimodule_platform_driver(imx6ul_tsc_driver);
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ciMODULE_AUTHOR("Haibo Chen <haibo.chen@freescale.com>");
56862306a36Sopenharmony_ciMODULE_DESCRIPTION("Freescale i.MX6UL Touchscreen controller driver");
56962306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
570