162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  exynos_adc.c - Support for ADC in EXYNOS SoCs
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  8 ~ 10 channel, 10/12-bit ADC
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci *  Copyright (C) 2013 Naveen Krishna Chatradhi <ch.naveen@samsung.com>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/compiler.h>
1162306a36Sopenharmony_ci#include <linux/module.h>
1262306a36Sopenharmony_ci#include <linux/platform_device.h>
1362306a36Sopenharmony_ci#include <linux/interrupt.h>
1462306a36Sopenharmony_ci#include <linux/delay.h>
1562306a36Sopenharmony_ci#include <linux/errno.h>
1662306a36Sopenharmony_ci#include <linux/kernel.h>
1762306a36Sopenharmony_ci#include <linux/slab.h>
1862306a36Sopenharmony_ci#include <linux/io.h>
1962306a36Sopenharmony_ci#include <linux/clk.h>
2062306a36Sopenharmony_ci#include <linux/completion.h>
2162306a36Sopenharmony_ci#include <linux/of.h>
2262306a36Sopenharmony_ci#include <linux/of_irq.h>
2362306a36Sopenharmony_ci#include <linux/regulator/consumer.h>
2462306a36Sopenharmony_ci#include <linux/of_platform.h>
2562306a36Sopenharmony_ci#include <linux/err.h>
2662306a36Sopenharmony_ci#include <linux/input.h>
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#include <linux/iio/iio.h>
2962306a36Sopenharmony_ci#include <linux/iio/machine.h>
3062306a36Sopenharmony_ci#include <linux/iio/driver.h>
3162306a36Sopenharmony_ci#include <linux/mfd/syscon.h>
3262306a36Sopenharmony_ci#include <linux/regmap.h>
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci#include <linux/platform_data/touchscreen-s3c2410.h>
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci/* S3C/EXYNOS4412/5250 ADC_V1 registers definitions */
3762306a36Sopenharmony_ci#define ADC_V1_CON(x)		((x) + 0x00)
3862306a36Sopenharmony_ci#define ADC_V1_TSC(x)		((x) + 0x04)
3962306a36Sopenharmony_ci#define ADC_V1_DLY(x)		((x) + 0x08)
4062306a36Sopenharmony_ci#define ADC_V1_DATX(x)		((x) + 0x0C)
4162306a36Sopenharmony_ci#define ADC_V1_DATY(x)		((x) + 0x10)
4262306a36Sopenharmony_ci#define ADC_V1_UPDN(x)		((x) + 0x14)
4362306a36Sopenharmony_ci#define ADC_V1_INTCLR(x)	((x) + 0x18)
4462306a36Sopenharmony_ci#define ADC_V1_MUX(x)		((x) + 0x1c)
4562306a36Sopenharmony_ci#define ADC_V1_CLRINTPNDNUP(x)	((x) + 0x20)
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci/* S3C2410 ADC registers definitions */
4862306a36Sopenharmony_ci#define ADC_S3C2410_MUX(x)	((x) + 0x18)
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci/* Future ADC_V2 registers definitions */
5162306a36Sopenharmony_ci#define ADC_V2_CON1(x)		((x) + 0x00)
5262306a36Sopenharmony_ci#define ADC_V2_CON2(x)		((x) + 0x04)
5362306a36Sopenharmony_ci#define ADC_V2_STAT(x)		((x) + 0x08)
5462306a36Sopenharmony_ci#define ADC_V2_INT_EN(x)	((x) + 0x10)
5562306a36Sopenharmony_ci#define ADC_V2_INT_ST(x)	((x) + 0x14)
5662306a36Sopenharmony_ci#define ADC_V2_VER(x)		((x) + 0x20)
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci/* Bit definitions for ADC_V1 */
5962306a36Sopenharmony_ci#define ADC_V1_CON_RES		(1u << 16)
6062306a36Sopenharmony_ci#define ADC_V1_CON_PRSCEN	(1u << 14)
6162306a36Sopenharmony_ci#define ADC_V1_CON_PRSCLV(x)	(((x) & 0xFF) << 6)
6262306a36Sopenharmony_ci#define ADC_V1_CON_STANDBY	(1u << 2)
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci/* Bit definitions for S3C2410 ADC */
6562306a36Sopenharmony_ci#define ADC_S3C2410_CON_SELMUX(x) (((x) & 7) << 3)
6662306a36Sopenharmony_ci#define ADC_S3C2410_DATX_MASK	0x3FF
6762306a36Sopenharmony_ci#define ADC_S3C2416_CON_RES_SEL	(1u << 3)
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci/* touch screen always uses channel 0 */
7062306a36Sopenharmony_ci#define ADC_S3C2410_MUX_TS	0
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci/* ADCTSC Register Bits */
7362306a36Sopenharmony_ci#define ADC_S3C2443_TSC_UD_SEN		(1u << 8)
7462306a36Sopenharmony_ci#define ADC_S3C2410_TSC_YM_SEN		(1u << 7)
7562306a36Sopenharmony_ci#define ADC_S3C2410_TSC_YP_SEN		(1u << 6)
7662306a36Sopenharmony_ci#define ADC_S3C2410_TSC_XM_SEN		(1u << 5)
7762306a36Sopenharmony_ci#define ADC_S3C2410_TSC_XP_SEN		(1u << 4)
7862306a36Sopenharmony_ci#define ADC_S3C2410_TSC_PULL_UP_DISABLE	(1u << 3)
7962306a36Sopenharmony_ci#define ADC_S3C2410_TSC_AUTO_PST	(1u << 2)
8062306a36Sopenharmony_ci#define ADC_S3C2410_TSC_XY_PST(x)	(((x) & 0x3) << 0)
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci#define ADC_TSC_WAIT4INT (ADC_S3C2410_TSC_YM_SEN | \
8362306a36Sopenharmony_ci			 ADC_S3C2410_TSC_YP_SEN | \
8462306a36Sopenharmony_ci			 ADC_S3C2410_TSC_XP_SEN | \
8562306a36Sopenharmony_ci			 ADC_S3C2410_TSC_XY_PST(3))
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci#define ADC_TSC_AUTOPST	(ADC_S3C2410_TSC_YM_SEN | \
8862306a36Sopenharmony_ci			 ADC_S3C2410_TSC_YP_SEN | \
8962306a36Sopenharmony_ci			 ADC_S3C2410_TSC_XP_SEN | \
9062306a36Sopenharmony_ci			 ADC_S3C2410_TSC_AUTO_PST | \
9162306a36Sopenharmony_ci			 ADC_S3C2410_TSC_XY_PST(0))
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci/* Bit definitions for ADC_V2 */
9462306a36Sopenharmony_ci#define ADC_V2_CON1_SOFT_RESET	(1u << 2)
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci#define ADC_V2_CON2_OSEL	(1u << 10)
9762306a36Sopenharmony_ci#define ADC_V2_CON2_ESEL	(1u << 9)
9862306a36Sopenharmony_ci#define ADC_V2_CON2_HIGHF	(1u << 8)
9962306a36Sopenharmony_ci#define ADC_V2_CON2_C_TIME(x)	(((x) & 7) << 4)
10062306a36Sopenharmony_ci#define ADC_V2_CON2_ACH_SEL(x)	(((x) & 0xF) << 0)
10162306a36Sopenharmony_ci#define ADC_V2_CON2_ACH_MASK	0xF
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci#define MAX_ADC_V2_CHANNELS		10
10462306a36Sopenharmony_ci#define MAX_ADC_V1_CHANNELS		8
10562306a36Sopenharmony_ci#define MAX_EXYNOS3250_ADC_CHANNELS	2
10662306a36Sopenharmony_ci#define MAX_EXYNOS4212_ADC_CHANNELS	4
10762306a36Sopenharmony_ci#define MAX_S5PV210_ADC_CHANNELS	10
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci/* Bit definitions common for ADC_V1 and ADC_V2 */
11062306a36Sopenharmony_ci#define ADC_CON_EN_START	(1u << 0)
11162306a36Sopenharmony_ci#define ADC_CON_EN_START_MASK	(0x3 << 0)
11262306a36Sopenharmony_ci#define ADC_DATX_PRESSED	(1u << 15)
11362306a36Sopenharmony_ci#define ADC_DATX_MASK		0xFFF
11462306a36Sopenharmony_ci#define ADC_DATY_MASK		0xFFF
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci#define EXYNOS_ADC_TIMEOUT	(msecs_to_jiffies(100))
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci#define EXYNOS_ADCV1_PHY_OFFSET	0x0718
11962306a36Sopenharmony_ci#define EXYNOS_ADCV2_PHY_OFFSET	0x0720
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_cistruct exynos_adc {
12262306a36Sopenharmony_ci	struct exynos_adc_data	*data;
12362306a36Sopenharmony_ci	struct device		*dev;
12462306a36Sopenharmony_ci	struct input_dev	*input;
12562306a36Sopenharmony_ci	void __iomem		*regs;
12662306a36Sopenharmony_ci	struct regmap		*pmu_map;
12762306a36Sopenharmony_ci	struct clk		*clk;
12862306a36Sopenharmony_ci	struct clk		*sclk;
12962306a36Sopenharmony_ci	unsigned int		irq;
13062306a36Sopenharmony_ci	unsigned int		tsirq;
13162306a36Sopenharmony_ci	unsigned int		delay;
13262306a36Sopenharmony_ci	struct regulator	*vdd;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	struct completion	completion;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	u32			value;
13762306a36Sopenharmony_ci	unsigned int            version;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	bool			ts_enabled;
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	bool			read_ts;
14262306a36Sopenharmony_ci	u32			ts_x;
14362306a36Sopenharmony_ci	u32			ts_y;
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	/*
14662306a36Sopenharmony_ci	 * Lock to protect from potential concurrent access to the
14762306a36Sopenharmony_ci	 * completion callback during a manual conversion. For this driver
14862306a36Sopenharmony_ci	 * a wait-callback is used to wait for the conversion result,
14962306a36Sopenharmony_ci	 * so in the meantime no other read request (or conversion start)
15062306a36Sopenharmony_ci	 * must be performed, otherwise it would interfere with the
15162306a36Sopenharmony_ci	 * current conversion result.
15262306a36Sopenharmony_ci	 */
15362306a36Sopenharmony_ci	struct mutex		lock;
15462306a36Sopenharmony_ci};
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_cistruct exynos_adc_data {
15762306a36Sopenharmony_ci	int num_channels;
15862306a36Sopenharmony_ci	bool needs_sclk;
15962306a36Sopenharmony_ci	bool needs_adc_phy;
16062306a36Sopenharmony_ci	int phy_offset;
16162306a36Sopenharmony_ci	u32 mask;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	void (*init_hw)(struct exynos_adc *info);
16462306a36Sopenharmony_ci	void (*exit_hw)(struct exynos_adc *info);
16562306a36Sopenharmony_ci	void (*clear_irq)(struct exynos_adc *info);
16662306a36Sopenharmony_ci	void (*start_conv)(struct exynos_adc *info, unsigned long addr);
16762306a36Sopenharmony_ci};
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_cistatic void exynos_adc_unprepare_clk(struct exynos_adc *info)
17062306a36Sopenharmony_ci{
17162306a36Sopenharmony_ci	if (info->data->needs_sclk)
17262306a36Sopenharmony_ci		clk_unprepare(info->sclk);
17362306a36Sopenharmony_ci	clk_unprepare(info->clk);
17462306a36Sopenharmony_ci}
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_cistatic int exynos_adc_prepare_clk(struct exynos_adc *info)
17762306a36Sopenharmony_ci{
17862306a36Sopenharmony_ci	int ret;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	ret = clk_prepare(info->clk);
18162306a36Sopenharmony_ci	if (ret) {
18262306a36Sopenharmony_ci		dev_err(info->dev, "failed preparing adc clock: %d\n", ret);
18362306a36Sopenharmony_ci		return ret;
18462306a36Sopenharmony_ci	}
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	if (info->data->needs_sclk) {
18762306a36Sopenharmony_ci		ret = clk_prepare(info->sclk);
18862306a36Sopenharmony_ci		if (ret) {
18962306a36Sopenharmony_ci			clk_unprepare(info->clk);
19062306a36Sopenharmony_ci			dev_err(info->dev,
19162306a36Sopenharmony_ci				"failed preparing sclk_adc clock: %d\n", ret);
19262306a36Sopenharmony_ci			return ret;
19362306a36Sopenharmony_ci		}
19462306a36Sopenharmony_ci	}
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	return 0;
19762306a36Sopenharmony_ci}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_cistatic void exynos_adc_disable_clk(struct exynos_adc *info)
20062306a36Sopenharmony_ci{
20162306a36Sopenharmony_ci	if (info->data->needs_sclk)
20262306a36Sopenharmony_ci		clk_disable(info->sclk);
20362306a36Sopenharmony_ci	clk_disable(info->clk);
20462306a36Sopenharmony_ci}
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_cistatic int exynos_adc_enable_clk(struct exynos_adc *info)
20762306a36Sopenharmony_ci{
20862306a36Sopenharmony_ci	int ret;
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	ret = clk_enable(info->clk);
21162306a36Sopenharmony_ci	if (ret) {
21262306a36Sopenharmony_ci		dev_err(info->dev, "failed enabling adc clock: %d\n", ret);
21362306a36Sopenharmony_ci		return ret;
21462306a36Sopenharmony_ci	}
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	if (info->data->needs_sclk) {
21762306a36Sopenharmony_ci		ret = clk_enable(info->sclk);
21862306a36Sopenharmony_ci		if (ret) {
21962306a36Sopenharmony_ci			clk_disable(info->clk);
22062306a36Sopenharmony_ci			dev_err(info->dev,
22162306a36Sopenharmony_ci				"failed enabling sclk_adc clock: %d\n", ret);
22262306a36Sopenharmony_ci			return ret;
22362306a36Sopenharmony_ci		}
22462306a36Sopenharmony_ci	}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	return 0;
22762306a36Sopenharmony_ci}
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_cistatic void exynos_adc_v1_init_hw(struct exynos_adc *info)
23062306a36Sopenharmony_ci{
23162306a36Sopenharmony_ci	u32 con1;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	if (info->data->needs_adc_phy)
23462306a36Sopenharmony_ci		regmap_write(info->pmu_map, info->data->phy_offset, 1);
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	/* set default prescaler values and Enable prescaler */
23762306a36Sopenharmony_ci	con1 =  ADC_V1_CON_PRSCLV(49) | ADC_V1_CON_PRSCEN;
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	/* Enable 12-bit ADC resolution */
24062306a36Sopenharmony_ci	con1 |= ADC_V1_CON_RES;
24162306a36Sopenharmony_ci	writel(con1, ADC_V1_CON(info->regs));
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	/* set touchscreen delay */
24462306a36Sopenharmony_ci	writel(info->delay, ADC_V1_DLY(info->regs));
24562306a36Sopenharmony_ci}
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_cistatic void exynos_adc_v1_exit_hw(struct exynos_adc *info)
24862306a36Sopenharmony_ci{
24962306a36Sopenharmony_ci	u32 con;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	if (info->data->needs_adc_phy)
25262306a36Sopenharmony_ci		regmap_write(info->pmu_map, info->data->phy_offset, 0);
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	con = readl(ADC_V1_CON(info->regs));
25562306a36Sopenharmony_ci	con |= ADC_V1_CON_STANDBY;
25662306a36Sopenharmony_ci	writel(con, ADC_V1_CON(info->regs));
25762306a36Sopenharmony_ci}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_cistatic void exynos_adc_v1_clear_irq(struct exynos_adc *info)
26062306a36Sopenharmony_ci{
26162306a36Sopenharmony_ci	writel(1, ADC_V1_INTCLR(info->regs));
26262306a36Sopenharmony_ci}
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_cistatic void exynos_adc_v1_start_conv(struct exynos_adc *info,
26562306a36Sopenharmony_ci				     unsigned long addr)
26662306a36Sopenharmony_ci{
26762306a36Sopenharmony_ci	u32 con1;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	writel(addr, ADC_V1_MUX(info->regs));
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	con1 = readl(ADC_V1_CON(info->regs));
27262306a36Sopenharmony_ci	writel(con1 | ADC_CON_EN_START, ADC_V1_CON(info->regs));
27362306a36Sopenharmony_ci}
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci/* Exynos4212 and 4412 is like ADCv1 but with four channels only */
27662306a36Sopenharmony_cistatic const struct exynos_adc_data exynos4212_adc_data = {
27762306a36Sopenharmony_ci	.num_channels	= MAX_EXYNOS4212_ADC_CHANNELS,
27862306a36Sopenharmony_ci	.mask		= ADC_DATX_MASK,	/* 12 bit ADC resolution */
27962306a36Sopenharmony_ci	.needs_adc_phy	= true,
28062306a36Sopenharmony_ci	.phy_offset	= EXYNOS_ADCV1_PHY_OFFSET,
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	.init_hw	= exynos_adc_v1_init_hw,
28362306a36Sopenharmony_ci	.exit_hw	= exynos_adc_v1_exit_hw,
28462306a36Sopenharmony_ci	.clear_irq	= exynos_adc_v1_clear_irq,
28562306a36Sopenharmony_ci	.start_conv	= exynos_adc_v1_start_conv,
28662306a36Sopenharmony_ci};
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_cistatic const struct exynos_adc_data exynos_adc_v1_data = {
28962306a36Sopenharmony_ci	.num_channels	= MAX_ADC_V1_CHANNELS,
29062306a36Sopenharmony_ci	.mask		= ADC_DATX_MASK,	/* 12 bit ADC resolution */
29162306a36Sopenharmony_ci	.needs_adc_phy	= true,
29262306a36Sopenharmony_ci	.phy_offset	= EXYNOS_ADCV1_PHY_OFFSET,
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	.init_hw	= exynos_adc_v1_init_hw,
29562306a36Sopenharmony_ci	.exit_hw	= exynos_adc_v1_exit_hw,
29662306a36Sopenharmony_ci	.clear_irq	= exynos_adc_v1_clear_irq,
29762306a36Sopenharmony_ci	.start_conv	= exynos_adc_v1_start_conv,
29862306a36Sopenharmony_ci};
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_cistatic const struct exynos_adc_data exynos_adc_s5pv210_data = {
30162306a36Sopenharmony_ci	.num_channels	= MAX_S5PV210_ADC_CHANNELS,
30262306a36Sopenharmony_ci	.mask		= ADC_DATX_MASK,	/* 12 bit ADC resolution */
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	.init_hw	= exynos_adc_v1_init_hw,
30562306a36Sopenharmony_ci	.exit_hw	= exynos_adc_v1_exit_hw,
30662306a36Sopenharmony_ci	.clear_irq	= exynos_adc_v1_clear_irq,
30762306a36Sopenharmony_ci	.start_conv	= exynos_adc_v1_start_conv,
30862306a36Sopenharmony_ci};
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_cistatic void exynos_adc_s3c2416_start_conv(struct exynos_adc *info,
31162306a36Sopenharmony_ci					  unsigned long addr)
31262306a36Sopenharmony_ci{
31362306a36Sopenharmony_ci	u32 con1;
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	/* Enable 12 bit ADC resolution */
31662306a36Sopenharmony_ci	con1 = readl(ADC_V1_CON(info->regs));
31762306a36Sopenharmony_ci	con1 |= ADC_S3C2416_CON_RES_SEL;
31862306a36Sopenharmony_ci	writel(con1, ADC_V1_CON(info->regs));
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	/* Select channel for S3C2416 */
32162306a36Sopenharmony_ci	writel(addr, ADC_S3C2410_MUX(info->regs));
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	con1 = readl(ADC_V1_CON(info->regs));
32462306a36Sopenharmony_ci	writel(con1 | ADC_CON_EN_START, ADC_V1_CON(info->regs));
32562306a36Sopenharmony_ci}
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_cistatic struct exynos_adc_data const exynos_adc_s3c2416_data = {
32862306a36Sopenharmony_ci	.num_channels	= MAX_ADC_V1_CHANNELS,
32962306a36Sopenharmony_ci	.mask		= ADC_DATX_MASK,	/* 12 bit ADC resolution */
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	.init_hw	= exynos_adc_v1_init_hw,
33262306a36Sopenharmony_ci	.exit_hw	= exynos_adc_v1_exit_hw,
33362306a36Sopenharmony_ci	.start_conv	= exynos_adc_s3c2416_start_conv,
33462306a36Sopenharmony_ci};
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_cistatic void exynos_adc_s3c2443_start_conv(struct exynos_adc *info,
33762306a36Sopenharmony_ci					  unsigned long addr)
33862306a36Sopenharmony_ci{
33962306a36Sopenharmony_ci	u32 con1;
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	/* Select channel for S3C2433 */
34262306a36Sopenharmony_ci	writel(addr, ADC_S3C2410_MUX(info->regs));
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	con1 = readl(ADC_V1_CON(info->regs));
34562306a36Sopenharmony_ci	writel(con1 | ADC_CON_EN_START, ADC_V1_CON(info->regs));
34662306a36Sopenharmony_ci}
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_cistatic struct exynos_adc_data const exynos_adc_s3c2443_data = {
34962306a36Sopenharmony_ci	.num_channels	= MAX_ADC_V1_CHANNELS,
35062306a36Sopenharmony_ci	.mask		= ADC_S3C2410_DATX_MASK, /* 10 bit ADC resolution */
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	.init_hw	= exynos_adc_v1_init_hw,
35362306a36Sopenharmony_ci	.exit_hw	= exynos_adc_v1_exit_hw,
35462306a36Sopenharmony_ci	.start_conv	= exynos_adc_s3c2443_start_conv,
35562306a36Sopenharmony_ci};
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_cistatic void exynos_adc_s3c64xx_start_conv(struct exynos_adc *info,
35862306a36Sopenharmony_ci					  unsigned long addr)
35962306a36Sopenharmony_ci{
36062306a36Sopenharmony_ci	u32 con1;
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	con1 = readl(ADC_V1_CON(info->regs));
36362306a36Sopenharmony_ci	con1 &= ~ADC_S3C2410_CON_SELMUX(0x7);
36462306a36Sopenharmony_ci	con1 |= ADC_S3C2410_CON_SELMUX(addr);
36562306a36Sopenharmony_ci	writel(con1 | ADC_CON_EN_START, ADC_V1_CON(info->regs));
36662306a36Sopenharmony_ci}
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_cistatic struct exynos_adc_data const exynos_adc_s3c24xx_data = {
36962306a36Sopenharmony_ci	.num_channels	= MAX_ADC_V1_CHANNELS,
37062306a36Sopenharmony_ci	.mask		= ADC_S3C2410_DATX_MASK, /* 10 bit ADC resolution */
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	.init_hw	= exynos_adc_v1_init_hw,
37362306a36Sopenharmony_ci	.exit_hw	= exynos_adc_v1_exit_hw,
37462306a36Sopenharmony_ci	.start_conv	= exynos_adc_s3c64xx_start_conv,
37562306a36Sopenharmony_ci};
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_cistatic struct exynos_adc_data const exynos_adc_s3c64xx_data = {
37862306a36Sopenharmony_ci	.num_channels	= MAX_ADC_V1_CHANNELS,
37962306a36Sopenharmony_ci	.mask		= ADC_DATX_MASK,	/* 12 bit ADC resolution */
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	.init_hw	= exynos_adc_v1_init_hw,
38262306a36Sopenharmony_ci	.exit_hw	= exynos_adc_v1_exit_hw,
38362306a36Sopenharmony_ci	.clear_irq	= exynos_adc_v1_clear_irq,
38462306a36Sopenharmony_ci	.start_conv	= exynos_adc_s3c64xx_start_conv,
38562306a36Sopenharmony_ci};
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_cistatic void exynos_adc_v2_init_hw(struct exynos_adc *info)
38862306a36Sopenharmony_ci{
38962306a36Sopenharmony_ci	u32 con1, con2;
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	if (info->data->needs_adc_phy)
39262306a36Sopenharmony_ci		regmap_write(info->pmu_map, info->data->phy_offset, 1);
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	con1 = ADC_V2_CON1_SOFT_RESET;
39562306a36Sopenharmony_ci	writel(con1, ADC_V2_CON1(info->regs));
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	con2 = ADC_V2_CON2_OSEL | ADC_V2_CON2_ESEL |
39862306a36Sopenharmony_ci		ADC_V2_CON2_HIGHF | ADC_V2_CON2_C_TIME(0);
39962306a36Sopenharmony_ci	writel(con2, ADC_V2_CON2(info->regs));
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	/* Enable interrupts */
40262306a36Sopenharmony_ci	writel(1, ADC_V2_INT_EN(info->regs));
40362306a36Sopenharmony_ci}
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_cistatic void exynos_adc_v2_exit_hw(struct exynos_adc *info)
40662306a36Sopenharmony_ci{
40762306a36Sopenharmony_ci	u32 con;
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	if (info->data->needs_adc_phy)
41062306a36Sopenharmony_ci		regmap_write(info->pmu_map, info->data->phy_offset, 0);
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	con = readl(ADC_V2_CON1(info->regs));
41362306a36Sopenharmony_ci	con &= ~ADC_CON_EN_START;
41462306a36Sopenharmony_ci	writel(con, ADC_V2_CON1(info->regs));
41562306a36Sopenharmony_ci}
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_cistatic void exynos_adc_v2_clear_irq(struct exynos_adc *info)
41862306a36Sopenharmony_ci{
41962306a36Sopenharmony_ci	writel(1, ADC_V2_INT_ST(info->regs));
42062306a36Sopenharmony_ci}
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_cistatic void exynos_adc_v2_start_conv(struct exynos_adc *info,
42362306a36Sopenharmony_ci				     unsigned long addr)
42462306a36Sopenharmony_ci{
42562306a36Sopenharmony_ci	u32 con1, con2;
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	con2 = readl(ADC_V2_CON2(info->regs));
42862306a36Sopenharmony_ci	con2 &= ~ADC_V2_CON2_ACH_MASK;
42962306a36Sopenharmony_ci	con2 |= ADC_V2_CON2_ACH_SEL(addr);
43062306a36Sopenharmony_ci	writel(con2, ADC_V2_CON2(info->regs));
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	con1 = readl(ADC_V2_CON1(info->regs));
43362306a36Sopenharmony_ci	writel(con1 | ADC_CON_EN_START, ADC_V2_CON1(info->regs));
43462306a36Sopenharmony_ci}
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_cistatic const struct exynos_adc_data exynos_adc_v2_data = {
43762306a36Sopenharmony_ci	.num_channels	= MAX_ADC_V2_CHANNELS,
43862306a36Sopenharmony_ci	.mask		= ADC_DATX_MASK, /* 12 bit ADC resolution */
43962306a36Sopenharmony_ci	.needs_adc_phy	= true,
44062306a36Sopenharmony_ci	.phy_offset	= EXYNOS_ADCV2_PHY_OFFSET,
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	.init_hw	= exynos_adc_v2_init_hw,
44362306a36Sopenharmony_ci	.exit_hw	= exynos_adc_v2_exit_hw,
44462306a36Sopenharmony_ci	.clear_irq	= exynos_adc_v2_clear_irq,
44562306a36Sopenharmony_ci	.start_conv	= exynos_adc_v2_start_conv,
44662306a36Sopenharmony_ci};
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_cistatic const struct exynos_adc_data exynos3250_adc_data = {
44962306a36Sopenharmony_ci	.num_channels	= MAX_EXYNOS3250_ADC_CHANNELS,
45062306a36Sopenharmony_ci	.mask		= ADC_DATX_MASK, /* 12 bit ADC resolution */
45162306a36Sopenharmony_ci	.needs_sclk	= true,
45262306a36Sopenharmony_ci	.needs_adc_phy	= true,
45362306a36Sopenharmony_ci	.phy_offset	= EXYNOS_ADCV1_PHY_OFFSET,
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	.init_hw	= exynos_adc_v2_init_hw,
45662306a36Sopenharmony_ci	.exit_hw	= exynos_adc_v2_exit_hw,
45762306a36Sopenharmony_ci	.clear_irq	= exynos_adc_v2_clear_irq,
45862306a36Sopenharmony_ci	.start_conv	= exynos_adc_v2_start_conv,
45962306a36Sopenharmony_ci};
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_cistatic void exynos_adc_exynos7_init_hw(struct exynos_adc *info)
46262306a36Sopenharmony_ci{
46362306a36Sopenharmony_ci	u32 con1, con2;
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	con1 = ADC_V2_CON1_SOFT_RESET;
46662306a36Sopenharmony_ci	writel(con1, ADC_V2_CON1(info->regs));
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	con2 = readl(ADC_V2_CON2(info->regs));
46962306a36Sopenharmony_ci	con2 &= ~ADC_V2_CON2_C_TIME(7);
47062306a36Sopenharmony_ci	con2 |= ADC_V2_CON2_C_TIME(0);
47162306a36Sopenharmony_ci	writel(con2, ADC_V2_CON2(info->regs));
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	/* Enable interrupts */
47462306a36Sopenharmony_ci	writel(1, ADC_V2_INT_EN(info->regs));
47562306a36Sopenharmony_ci}
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_cistatic const struct exynos_adc_data exynos7_adc_data = {
47862306a36Sopenharmony_ci	.num_channels	= MAX_ADC_V1_CHANNELS,
47962306a36Sopenharmony_ci	.mask		= ADC_DATX_MASK, /* 12 bit ADC resolution */
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	.init_hw	= exynos_adc_exynos7_init_hw,
48262306a36Sopenharmony_ci	.exit_hw	= exynos_adc_v2_exit_hw,
48362306a36Sopenharmony_ci	.clear_irq	= exynos_adc_v2_clear_irq,
48462306a36Sopenharmony_ci	.start_conv	= exynos_adc_v2_start_conv,
48562306a36Sopenharmony_ci};
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_cistatic const struct of_device_id exynos_adc_match[] = {
48862306a36Sopenharmony_ci	{
48962306a36Sopenharmony_ci		.compatible = "samsung,s3c2410-adc",
49062306a36Sopenharmony_ci		.data = &exynos_adc_s3c24xx_data,
49162306a36Sopenharmony_ci	}, {
49262306a36Sopenharmony_ci		.compatible = "samsung,s3c2416-adc",
49362306a36Sopenharmony_ci		.data = &exynos_adc_s3c2416_data,
49462306a36Sopenharmony_ci	}, {
49562306a36Sopenharmony_ci		.compatible = "samsung,s3c2440-adc",
49662306a36Sopenharmony_ci		.data = &exynos_adc_s3c24xx_data,
49762306a36Sopenharmony_ci	}, {
49862306a36Sopenharmony_ci		.compatible = "samsung,s3c2443-adc",
49962306a36Sopenharmony_ci		.data = &exynos_adc_s3c2443_data,
50062306a36Sopenharmony_ci	}, {
50162306a36Sopenharmony_ci		.compatible = "samsung,s3c6410-adc",
50262306a36Sopenharmony_ci		.data = &exynos_adc_s3c64xx_data,
50362306a36Sopenharmony_ci	}, {
50462306a36Sopenharmony_ci		.compatible = "samsung,s5pv210-adc",
50562306a36Sopenharmony_ci		.data = &exynos_adc_s5pv210_data,
50662306a36Sopenharmony_ci	}, {
50762306a36Sopenharmony_ci		.compatible = "samsung,exynos4212-adc",
50862306a36Sopenharmony_ci		.data = &exynos4212_adc_data,
50962306a36Sopenharmony_ci	}, {
51062306a36Sopenharmony_ci		.compatible = "samsung,exynos-adc-v1",
51162306a36Sopenharmony_ci		.data = &exynos_adc_v1_data,
51262306a36Sopenharmony_ci	}, {
51362306a36Sopenharmony_ci		.compatible = "samsung,exynos-adc-v2",
51462306a36Sopenharmony_ci		.data = &exynos_adc_v2_data,
51562306a36Sopenharmony_ci	}, {
51662306a36Sopenharmony_ci		.compatible = "samsung,exynos3250-adc",
51762306a36Sopenharmony_ci		.data = &exynos3250_adc_data,
51862306a36Sopenharmony_ci	}, {
51962306a36Sopenharmony_ci		.compatible = "samsung,exynos7-adc",
52062306a36Sopenharmony_ci		.data = &exynos7_adc_data,
52162306a36Sopenharmony_ci	},
52262306a36Sopenharmony_ci	{},
52362306a36Sopenharmony_ci};
52462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, exynos_adc_match);
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_cistatic struct exynos_adc_data *exynos_adc_get_data(struct platform_device *pdev)
52762306a36Sopenharmony_ci{
52862306a36Sopenharmony_ci	const struct of_device_id *match;
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	match = of_match_node(exynos_adc_match, pdev->dev.of_node);
53162306a36Sopenharmony_ci	return (struct exynos_adc_data *)match->data;
53262306a36Sopenharmony_ci}
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_cistatic int exynos_read_raw(struct iio_dev *indio_dev,
53562306a36Sopenharmony_ci				struct iio_chan_spec const *chan,
53662306a36Sopenharmony_ci				int *val,
53762306a36Sopenharmony_ci				int *val2,
53862306a36Sopenharmony_ci				long mask)
53962306a36Sopenharmony_ci{
54062306a36Sopenharmony_ci	struct exynos_adc *info = iio_priv(indio_dev);
54162306a36Sopenharmony_ci	unsigned long timeout;
54262306a36Sopenharmony_ci	int ret;
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	if (mask == IIO_CHAN_INFO_SCALE) {
54562306a36Sopenharmony_ci		ret = regulator_get_voltage(info->vdd);
54662306a36Sopenharmony_ci		if (ret < 0)
54762306a36Sopenharmony_ci			return ret;
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci		/* Regulator voltage is in uV, but need mV */
55062306a36Sopenharmony_ci		*val = ret / 1000;
55162306a36Sopenharmony_ci		*val2 = info->data->mask;
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci		return IIO_VAL_FRACTIONAL;
55462306a36Sopenharmony_ci	} else if (mask != IIO_CHAN_INFO_RAW) {
55562306a36Sopenharmony_ci		return -EINVAL;
55662306a36Sopenharmony_ci	}
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	mutex_lock(&info->lock);
55962306a36Sopenharmony_ci	reinit_completion(&info->completion);
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	/* Select the channel to be used and Trigger conversion */
56262306a36Sopenharmony_ci	if (info->data->start_conv)
56362306a36Sopenharmony_ci		info->data->start_conv(info, chan->address);
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	timeout = wait_for_completion_timeout(&info->completion,
56662306a36Sopenharmony_ci					      EXYNOS_ADC_TIMEOUT);
56762306a36Sopenharmony_ci	if (timeout == 0) {
56862306a36Sopenharmony_ci		dev_warn(&indio_dev->dev, "Conversion timed out! Resetting\n");
56962306a36Sopenharmony_ci		if (info->data->init_hw)
57062306a36Sopenharmony_ci			info->data->init_hw(info);
57162306a36Sopenharmony_ci		ret = -ETIMEDOUT;
57262306a36Sopenharmony_ci	} else {
57362306a36Sopenharmony_ci		*val = info->value;
57462306a36Sopenharmony_ci		*val2 = 0;
57562306a36Sopenharmony_ci		ret = IIO_VAL_INT;
57662306a36Sopenharmony_ci	}
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci	mutex_unlock(&info->lock);
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	return ret;
58162306a36Sopenharmony_ci}
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_cistatic int exynos_read_s3c64xx_ts(struct iio_dev *indio_dev, int *x, int *y)
58462306a36Sopenharmony_ci{
58562306a36Sopenharmony_ci	struct exynos_adc *info = iio_priv(indio_dev);
58662306a36Sopenharmony_ci	unsigned long timeout;
58762306a36Sopenharmony_ci	int ret;
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	mutex_lock(&info->lock);
59062306a36Sopenharmony_ci	info->read_ts = true;
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci	reinit_completion(&info->completion);
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	writel(ADC_S3C2410_TSC_PULL_UP_DISABLE | ADC_TSC_AUTOPST,
59562306a36Sopenharmony_ci	       ADC_V1_TSC(info->regs));
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	/* Select the ts channel to be used and Trigger conversion */
59862306a36Sopenharmony_ci	info->data->start_conv(info, ADC_S3C2410_MUX_TS);
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	timeout = wait_for_completion_timeout(&info->completion,
60162306a36Sopenharmony_ci					      EXYNOS_ADC_TIMEOUT);
60262306a36Sopenharmony_ci	if (timeout == 0) {
60362306a36Sopenharmony_ci		dev_warn(&indio_dev->dev, "Conversion timed out! Resetting\n");
60462306a36Sopenharmony_ci		if (info->data->init_hw)
60562306a36Sopenharmony_ci			info->data->init_hw(info);
60662306a36Sopenharmony_ci		ret = -ETIMEDOUT;
60762306a36Sopenharmony_ci	} else {
60862306a36Sopenharmony_ci		*x = info->ts_x;
60962306a36Sopenharmony_ci		*y = info->ts_y;
61062306a36Sopenharmony_ci		ret = 0;
61162306a36Sopenharmony_ci	}
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	info->read_ts = false;
61462306a36Sopenharmony_ci	mutex_unlock(&info->lock);
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	return ret;
61762306a36Sopenharmony_ci}
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_cistatic irqreturn_t exynos_adc_isr(int irq, void *dev_id)
62062306a36Sopenharmony_ci{
62162306a36Sopenharmony_ci	struct exynos_adc *info = dev_id;
62262306a36Sopenharmony_ci	u32 mask = info->data->mask;
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	/* Read value */
62562306a36Sopenharmony_ci	if (info->read_ts) {
62662306a36Sopenharmony_ci		info->ts_x = readl(ADC_V1_DATX(info->regs));
62762306a36Sopenharmony_ci		info->ts_y = readl(ADC_V1_DATY(info->regs));
62862306a36Sopenharmony_ci		writel(ADC_TSC_WAIT4INT | ADC_S3C2443_TSC_UD_SEN, ADC_V1_TSC(info->regs));
62962306a36Sopenharmony_ci	} else {
63062306a36Sopenharmony_ci		info->value = readl(ADC_V1_DATX(info->regs)) & mask;
63162306a36Sopenharmony_ci	}
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	/* clear irq */
63462306a36Sopenharmony_ci	if (info->data->clear_irq)
63562306a36Sopenharmony_ci		info->data->clear_irq(info);
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci	complete(&info->completion);
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	return IRQ_HANDLED;
64062306a36Sopenharmony_ci}
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci/*
64362306a36Sopenharmony_ci * Here we (ab)use a threaded interrupt handler to stay running
64462306a36Sopenharmony_ci * for as long as the touchscreen remains pressed, we report
64562306a36Sopenharmony_ci * a new event with the latest data and then sleep until the
64662306a36Sopenharmony_ci * next timer tick. This mirrors the behavior of the old
64762306a36Sopenharmony_ci * driver, with much less code.
64862306a36Sopenharmony_ci */
64962306a36Sopenharmony_cistatic irqreturn_t exynos_ts_isr(int irq, void *dev_id)
65062306a36Sopenharmony_ci{
65162306a36Sopenharmony_ci	struct exynos_adc *info = dev_id;
65262306a36Sopenharmony_ci	struct iio_dev *dev = dev_get_drvdata(info->dev);
65362306a36Sopenharmony_ci	u32 x, y;
65462306a36Sopenharmony_ci	bool pressed;
65562306a36Sopenharmony_ci	int ret;
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	while (READ_ONCE(info->ts_enabled)) {
65862306a36Sopenharmony_ci		ret = exynos_read_s3c64xx_ts(dev, &x, &y);
65962306a36Sopenharmony_ci		if (ret == -ETIMEDOUT)
66062306a36Sopenharmony_ci			break;
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci		pressed = x & y & ADC_DATX_PRESSED;
66362306a36Sopenharmony_ci		if (!pressed) {
66462306a36Sopenharmony_ci			input_report_key(info->input, BTN_TOUCH, 0);
66562306a36Sopenharmony_ci			input_sync(info->input);
66662306a36Sopenharmony_ci			break;
66762306a36Sopenharmony_ci		}
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci		input_report_abs(info->input, ABS_X, x & ADC_DATX_MASK);
67062306a36Sopenharmony_ci		input_report_abs(info->input, ABS_Y, y & ADC_DATY_MASK);
67162306a36Sopenharmony_ci		input_report_key(info->input, BTN_TOUCH, 1);
67262306a36Sopenharmony_ci		input_sync(info->input);
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci		usleep_range(1000, 1100);
67562306a36Sopenharmony_ci	}
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci	writel(0, ADC_V1_CLRINTPNDNUP(info->regs));
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	return IRQ_HANDLED;
68062306a36Sopenharmony_ci}
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_cistatic int exynos_adc_reg_access(struct iio_dev *indio_dev,
68362306a36Sopenharmony_ci			      unsigned reg, unsigned writeval,
68462306a36Sopenharmony_ci			      unsigned *readval)
68562306a36Sopenharmony_ci{
68662306a36Sopenharmony_ci	struct exynos_adc *info = iio_priv(indio_dev);
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci	if (readval == NULL)
68962306a36Sopenharmony_ci		return -EINVAL;
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci	*readval = readl(info->regs + reg);
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci	return 0;
69462306a36Sopenharmony_ci}
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_cistatic const struct iio_info exynos_adc_iio_info = {
69762306a36Sopenharmony_ci	.read_raw = &exynos_read_raw,
69862306a36Sopenharmony_ci	.debugfs_reg_access = &exynos_adc_reg_access,
69962306a36Sopenharmony_ci};
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci#define ADC_CHANNEL(_index, _id) {			\
70262306a36Sopenharmony_ci	.type = IIO_VOLTAGE,				\
70362306a36Sopenharmony_ci	.indexed = 1,					\
70462306a36Sopenharmony_ci	.channel = _index,				\
70562306a36Sopenharmony_ci	.address = _index,				\
70662306a36Sopenharmony_ci	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),	\
70762306a36Sopenharmony_ci	.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE),	\
70862306a36Sopenharmony_ci	.datasheet_name = _id,				\
70962306a36Sopenharmony_ci}
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_cistatic const struct iio_chan_spec exynos_adc_iio_channels[] = {
71262306a36Sopenharmony_ci	ADC_CHANNEL(0, "adc0"),
71362306a36Sopenharmony_ci	ADC_CHANNEL(1, "adc1"),
71462306a36Sopenharmony_ci	ADC_CHANNEL(2, "adc2"),
71562306a36Sopenharmony_ci	ADC_CHANNEL(3, "adc3"),
71662306a36Sopenharmony_ci	ADC_CHANNEL(4, "adc4"),
71762306a36Sopenharmony_ci	ADC_CHANNEL(5, "adc5"),
71862306a36Sopenharmony_ci	ADC_CHANNEL(6, "adc6"),
71962306a36Sopenharmony_ci	ADC_CHANNEL(7, "adc7"),
72062306a36Sopenharmony_ci	ADC_CHANNEL(8, "adc8"),
72162306a36Sopenharmony_ci	ADC_CHANNEL(9, "adc9"),
72262306a36Sopenharmony_ci};
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_cistatic int exynos_adc_remove_devices(struct device *dev, void *c)
72562306a36Sopenharmony_ci{
72662306a36Sopenharmony_ci	struct platform_device *pdev = to_platform_device(dev);
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	platform_device_unregister(pdev);
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	return 0;
73162306a36Sopenharmony_ci}
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_cistatic int exynos_adc_ts_open(struct input_dev *dev)
73462306a36Sopenharmony_ci{
73562306a36Sopenharmony_ci	struct exynos_adc *info = input_get_drvdata(dev);
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci	WRITE_ONCE(info->ts_enabled, true);
73862306a36Sopenharmony_ci	enable_irq(info->tsirq);
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci	return 0;
74162306a36Sopenharmony_ci}
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_cistatic void exynos_adc_ts_close(struct input_dev *dev)
74462306a36Sopenharmony_ci{
74562306a36Sopenharmony_ci	struct exynos_adc *info = input_get_drvdata(dev);
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci	WRITE_ONCE(info->ts_enabled, false);
74862306a36Sopenharmony_ci	disable_irq(info->tsirq);
74962306a36Sopenharmony_ci}
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_cistatic int exynos_adc_ts_init(struct exynos_adc *info)
75262306a36Sopenharmony_ci{
75362306a36Sopenharmony_ci	int ret;
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci	if (info->tsirq <= 0)
75662306a36Sopenharmony_ci		return -ENODEV;
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci	info->input = input_allocate_device();
75962306a36Sopenharmony_ci	if (!info->input)
76062306a36Sopenharmony_ci		return -ENOMEM;
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci	info->input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
76362306a36Sopenharmony_ci	info->input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci	input_set_abs_params(info->input, ABS_X, 0, 0x3FF, 0, 0);
76662306a36Sopenharmony_ci	input_set_abs_params(info->input, ABS_Y, 0, 0x3FF, 0, 0);
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci	info->input->name = "S3C24xx TouchScreen";
76962306a36Sopenharmony_ci	info->input->id.bustype = BUS_HOST;
77062306a36Sopenharmony_ci	info->input->open = exynos_adc_ts_open;
77162306a36Sopenharmony_ci	info->input->close = exynos_adc_ts_close;
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci	input_set_drvdata(info->input, info);
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci	ret = input_register_device(info->input);
77662306a36Sopenharmony_ci	if (ret) {
77762306a36Sopenharmony_ci		input_free_device(info->input);
77862306a36Sopenharmony_ci		return ret;
77962306a36Sopenharmony_ci	}
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci	ret = request_threaded_irq(info->tsirq, NULL, exynos_ts_isr,
78262306a36Sopenharmony_ci				   IRQF_ONESHOT | IRQF_NO_AUTOEN,
78362306a36Sopenharmony_ci				   "touchscreen", info);
78462306a36Sopenharmony_ci	if (ret)
78562306a36Sopenharmony_ci		input_unregister_device(info->input);
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci	return ret;
78862306a36Sopenharmony_ci}
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_cistatic int exynos_adc_probe(struct platform_device *pdev)
79162306a36Sopenharmony_ci{
79262306a36Sopenharmony_ci	struct exynos_adc *info = NULL;
79362306a36Sopenharmony_ci	struct device_node *np = pdev->dev.of_node;
79462306a36Sopenharmony_ci	struct s3c2410_ts_mach_info *pdata = dev_get_platdata(&pdev->dev);
79562306a36Sopenharmony_ci	struct iio_dev *indio_dev = NULL;
79662306a36Sopenharmony_ci	bool has_ts = false;
79762306a36Sopenharmony_ci	int ret;
79862306a36Sopenharmony_ci	int irq;
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_ci	indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(struct exynos_adc));
80162306a36Sopenharmony_ci	if (!indio_dev) {
80262306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed allocating iio device\n");
80362306a36Sopenharmony_ci		return -ENOMEM;
80462306a36Sopenharmony_ci	}
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci	info = iio_priv(indio_dev);
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci	info->data = exynos_adc_get_data(pdev);
80962306a36Sopenharmony_ci	if (!info->data) {
81062306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed getting exynos_adc_data\n");
81162306a36Sopenharmony_ci		return -EINVAL;
81262306a36Sopenharmony_ci	}
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci	info->regs = devm_platform_ioremap_resource(pdev, 0);
81562306a36Sopenharmony_ci	if (IS_ERR(info->regs))
81662306a36Sopenharmony_ci		return PTR_ERR(info->regs);
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci	if (info->data->needs_adc_phy) {
82062306a36Sopenharmony_ci		info->pmu_map = syscon_regmap_lookup_by_phandle(
82162306a36Sopenharmony_ci					pdev->dev.of_node,
82262306a36Sopenharmony_ci					"samsung,syscon-phandle");
82362306a36Sopenharmony_ci		if (IS_ERR(info->pmu_map)) {
82462306a36Sopenharmony_ci			dev_err(&pdev->dev, "syscon regmap lookup failed.\n");
82562306a36Sopenharmony_ci			return PTR_ERR(info->pmu_map);
82662306a36Sopenharmony_ci		}
82762306a36Sopenharmony_ci	}
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci	/* leave out any TS related code if unreachable */
83062306a36Sopenharmony_ci	if (IS_REACHABLE(CONFIG_INPUT)) {
83162306a36Sopenharmony_ci		has_ts = of_property_read_bool(pdev->dev.of_node,
83262306a36Sopenharmony_ci					       "has-touchscreen") || pdata;
83362306a36Sopenharmony_ci	}
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci	irq = platform_get_irq(pdev, 0);
83662306a36Sopenharmony_ci	if (irq < 0)
83762306a36Sopenharmony_ci		return irq;
83862306a36Sopenharmony_ci	info->irq = irq;
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci	if (has_ts) {
84162306a36Sopenharmony_ci		irq = platform_get_irq(pdev, 1);
84262306a36Sopenharmony_ci		if (irq == -EPROBE_DEFER)
84362306a36Sopenharmony_ci			return irq;
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_ci		info->tsirq = irq;
84662306a36Sopenharmony_ci	} else {
84762306a36Sopenharmony_ci		info->tsirq = -1;
84862306a36Sopenharmony_ci	}
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci	info->dev = &pdev->dev;
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci	init_completion(&info->completion);
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci	info->clk = devm_clk_get(&pdev->dev, "adc");
85562306a36Sopenharmony_ci	if (IS_ERR(info->clk)) {
85662306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed getting clock, err = %ld\n",
85762306a36Sopenharmony_ci							PTR_ERR(info->clk));
85862306a36Sopenharmony_ci		return PTR_ERR(info->clk);
85962306a36Sopenharmony_ci	}
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci	if (info->data->needs_sclk) {
86262306a36Sopenharmony_ci		info->sclk = devm_clk_get(&pdev->dev, "sclk");
86362306a36Sopenharmony_ci		if (IS_ERR(info->sclk)) {
86462306a36Sopenharmony_ci			dev_err(&pdev->dev,
86562306a36Sopenharmony_ci				"failed getting sclk clock, err = %ld\n",
86662306a36Sopenharmony_ci				PTR_ERR(info->sclk));
86762306a36Sopenharmony_ci			return PTR_ERR(info->sclk);
86862306a36Sopenharmony_ci		}
86962306a36Sopenharmony_ci	}
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci	info->vdd = devm_regulator_get(&pdev->dev, "vdd");
87262306a36Sopenharmony_ci	if (IS_ERR(info->vdd))
87362306a36Sopenharmony_ci		return dev_err_probe(&pdev->dev, PTR_ERR(info->vdd),
87462306a36Sopenharmony_ci				     "failed getting regulator");
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_ci	ret = regulator_enable(info->vdd);
87762306a36Sopenharmony_ci	if (ret)
87862306a36Sopenharmony_ci		return ret;
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_ci	ret = exynos_adc_prepare_clk(info);
88162306a36Sopenharmony_ci	if (ret)
88262306a36Sopenharmony_ci		goto err_disable_reg;
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ci	ret = exynos_adc_enable_clk(info);
88562306a36Sopenharmony_ci	if (ret)
88662306a36Sopenharmony_ci		goto err_unprepare_clk;
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci	platform_set_drvdata(pdev, indio_dev);
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ci	indio_dev->name = dev_name(&pdev->dev);
89162306a36Sopenharmony_ci	indio_dev->info = &exynos_adc_iio_info;
89262306a36Sopenharmony_ci	indio_dev->modes = INDIO_DIRECT_MODE;
89362306a36Sopenharmony_ci	indio_dev->channels = exynos_adc_iio_channels;
89462306a36Sopenharmony_ci	indio_dev->num_channels = info->data->num_channels;
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ci	mutex_init(&info->lock);
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci	ret = request_irq(info->irq, exynos_adc_isr,
89962306a36Sopenharmony_ci					0, dev_name(&pdev->dev), info);
90062306a36Sopenharmony_ci	if (ret < 0) {
90162306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed requesting irq, irq = %d\n",
90262306a36Sopenharmony_ci							info->irq);
90362306a36Sopenharmony_ci		goto err_disable_clk;
90462306a36Sopenharmony_ci	}
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_ci	ret = iio_device_register(indio_dev);
90762306a36Sopenharmony_ci	if (ret)
90862306a36Sopenharmony_ci		goto err_irq;
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci	if (info->data->init_hw)
91162306a36Sopenharmony_ci		info->data->init_hw(info);
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_ci	if (pdata)
91462306a36Sopenharmony_ci		info->delay = pdata->delay;
91562306a36Sopenharmony_ci	else
91662306a36Sopenharmony_ci		info->delay = 10000;
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci	if (has_ts)
91962306a36Sopenharmony_ci		ret = exynos_adc_ts_init(info);
92062306a36Sopenharmony_ci	if (ret)
92162306a36Sopenharmony_ci		goto err_iio;
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci	ret = of_platform_populate(np, exynos_adc_match, NULL, &indio_dev->dev);
92462306a36Sopenharmony_ci	if (ret < 0) {
92562306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed adding child nodes\n");
92662306a36Sopenharmony_ci		goto err_of_populate;
92762306a36Sopenharmony_ci	}
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_ci	return 0;
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_cierr_of_populate:
93262306a36Sopenharmony_ci	device_for_each_child(&indio_dev->dev, NULL,
93362306a36Sopenharmony_ci				exynos_adc_remove_devices);
93462306a36Sopenharmony_ci	if (has_ts) {
93562306a36Sopenharmony_ci		input_unregister_device(info->input);
93662306a36Sopenharmony_ci		free_irq(info->tsirq, info);
93762306a36Sopenharmony_ci	}
93862306a36Sopenharmony_cierr_iio:
93962306a36Sopenharmony_ci	iio_device_unregister(indio_dev);
94062306a36Sopenharmony_cierr_irq:
94162306a36Sopenharmony_ci	free_irq(info->irq, info);
94262306a36Sopenharmony_cierr_disable_clk:
94362306a36Sopenharmony_ci	if (info->data->exit_hw)
94462306a36Sopenharmony_ci		info->data->exit_hw(info);
94562306a36Sopenharmony_ci	exynos_adc_disable_clk(info);
94662306a36Sopenharmony_cierr_unprepare_clk:
94762306a36Sopenharmony_ci	exynos_adc_unprepare_clk(info);
94862306a36Sopenharmony_cierr_disable_reg:
94962306a36Sopenharmony_ci	regulator_disable(info->vdd);
95062306a36Sopenharmony_ci	return ret;
95162306a36Sopenharmony_ci}
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_cistatic int exynos_adc_remove(struct platform_device *pdev)
95462306a36Sopenharmony_ci{
95562306a36Sopenharmony_ci	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
95662306a36Sopenharmony_ci	struct exynos_adc *info = iio_priv(indio_dev);
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci	if (IS_REACHABLE(CONFIG_INPUT) && info->input) {
95962306a36Sopenharmony_ci		free_irq(info->tsirq, info);
96062306a36Sopenharmony_ci		input_unregister_device(info->input);
96162306a36Sopenharmony_ci	}
96262306a36Sopenharmony_ci	device_for_each_child(&indio_dev->dev, NULL,
96362306a36Sopenharmony_ci				exynos_adc_remove_devices);
96462306a36Sopenharmony_ci	iio_device_unregister(indio_dev);
96562306a36Sopenharmony_ci	free_irq(info->irq, info);
96662306a36Sopenharmony_ci	if (info->data->exit_hw)
96762306a36Sopenharmony_ci		info->data->exit_hw(info);
96862306a36Sopenharmony_ci	exynos_adc_disable_clk(info);
96962306a36Sopenharmony_ci	exynos_adc_unprepare_clk(info);
97062306a36Sopenharmony_ci	regulator_disable(info->vdd);
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_ci	return 0;
97362306a36Sopenharmony_ci}
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_cistatic int exynos_adc_suspend(struct device *dev)
97662306a36Sopenharmony_ci{
97762306a36Sopenharmony_ci	struct iio_dev *indio_dev = dev_get_drvdata(dev);
97862306a36Sopenharmony_ci	struct exynos_adc *info = iio_priv(indio_dev);
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci	if (info->data->exit_hw)
98162306a36Sopenharmony_ci		info->data->exit_hw(info);
98262306a36Sopenharmony_ci	exynos_adc_disable_clk(info);
98362306a36Sopenharmony_ci	regulator_disable(info->vdd);
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci	return 0;
98662306a36Sopenharmony_ci}
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_cistatic int exynos_adc_resume(struct device *dev)
98962306a36Sopenharmony_ci{
99062306a36Sopenharmony_ci	struct iio_dev *indio_dev = dev_get_drvdata(dev);
99162306a36Sopenharmony_ci	struct exynos_adc *info = iio_priv(indio_dev);
99262306a36Sopenharmony_ci	int ret;
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_ci	ret = regulator_enable(info->vdd);
99562306a36Sopenharmony_ci	if (ret)
99662306a36Sopenharmony_ci		return ret;
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ci	ret = exynos_adc_enable_clk(info);
99962306a36Sopenharmony_ci	if (ret)
100062306a36Sopenharmony_ci		return ret;
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_ci	if (info->data->init_hw)
100362306a36Sopenharmony_ci		info->data->init_hw(info);
100462306a36Sopenharmony_ci
100562306a36Sopenharmony_ci	return 0;
100662306a36Sopenharmony_ci}
100762306a36Sopenharmony_ci
100862306a36Sopenharmony_cistatic DEFINE_SIMPLE_DEV_PM_OPS(exynos_adc_pm_ops, exynos_adc_suspend,
100962306a36Sopenharmony_ci				exynos_adc_resume);
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_cistatic struct platform_driver exynos_adc_driver = {
101262306a36Sopenharmony_ci	.probe		= exynos_adc_probe,
101362306a36Sopenharmony_ci	.remove		= exynos_adc_remove,
101462306a36Sopenharmony_ci	.driver		= {
101562306a36Sopenharmony_ci		.name	= "exynos-adc",
101662306a36Sopenharmony_ci		.of_match_table = exynos_adc_match,
101762306a36Sopenharmony_ci		.pm	= pm_sleep_ptr(&exynos_adc_pm_ops),
101862306a36Sopenharmony_ci	},
101962306a36Sopenharmony_ci};
102062306a36Sopenharmony_ci
102162306a36Sopenharmony_cimodule_platform_driver(exynos_adc_driver);
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ciMODULE_AUTHOR("Naveen Krishna Chatradhi <ch.naveen@samsung.com>");
102462306a36Sopenharmony_ciMODULE_DESCRIPTION("Samsung EXYNOS5 ADC driver");
102562306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
1026