[PATCH 2/4] iio: adc: exynos_adc: add Exynos850 support

From: Alexey Klimov

Date: Wed Sep 30 2026 - 23:09:37 EST


The Exynos850 ADC controller located inside the CMGP block differs from
other Exynos ADC controllers, mainly in its register layout.
Specifically, the data register sits at offset 0x08.

Abstract the read_value() operation in driver data exynos_adc_data to
support SoC-dependent data register read callbacks.
Add the exynos850_adc_data struct that describes Exynos850 ADC ops.
It also requires its own hardware initialization and exit routines.
Add them as well.

Signed-off-by: Alexey Klimov <alexey.klimov@xxxxxxxxxx>
---
drivers/iio/adc/exynos_adc.c | 66 ++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 64 insertions(+), 2 deletions(-)

diff --git a/drivers/iio/adc/exynos_adc.c b/drivers/iio/adc/exynos_adc.c
index c1a0061b16ca..3e9b5d28b922 100644
--- a/drivers/iio/adc/exynos_adc.c
+++ b/drivers/iio/adc/exynos_adc.c
@@ -46,6 +46,8 @@
#define ADC_V2_INT_ST(x) ((x) + 0x14)
#define ADC_V2_VER(x) ((x) + 0x20)

+#define ADC_EXYNOS850_DAT(x) ((x) + 0x08)
+
/* Bit definitions for ADC_V1 */
#define ADC_V1_CON_RES (1u << 16)
#define ADC_V1_CON_PRSCEN (1u << 14)
@@ -133,6 +135,7 @@ struct exynos_adc_data {
void (*exit_hw)(struct exynos_adc *info);
void (*clear_irq)(struct exynos_adc *info);
void (*start_conv)(struct exynos_adc *info, unsigned long addr);
+ u32 (*read_value)(struct exynos_adc *info);
};

static void exynos_adc_unprepare_clk(struct exynos_adc *info)
@@ -195,6 +198,11 @@ static int exynos_adc_enable_clk(struct exynos_adc *info)
return 0;
}

+static u32 exynos_adc_v1_read_value(struct exynos_adc *info)
+{
+ return readl(ADC_V1_DATX(info->regs)) & info->data->mask;
+}
+
static void exynos_adc_v1_init_hw(struct exynos_adc *info)
{
u32 con1;
@@ -252,6 +260,7 @@ static const struct exynos_adc_data exynos4212_adc_data = {
.exit_hw = exynos_adc_v1_exit_hw,
.clear_irq = exynos_adc_v1_clear_irq,
.start_conv = exynos_adc_v1_start_conv,
+ .read_value = exynos_adc_v1_read_value,
};

static const struct exynos_adc_data exynos_adc_v1_data = {
@@ -264,6 +273,7 @@ static const struct exynos_adc_data exynos_adc_v1_data = {
.exit_hw = exynos_adc_v1_exit_hw,
.clear_irq = exynos_adc_v1_clear_irq,
.start_conv = exynos_adc_v1_start_conv,
+ .read_value = exynos_adc_v1_read_value,
};

static const struct exynos_adc_data exynos_adc_s5pv210_data = {
@@ -274,6 +284,7 @@ static const struct exynos_adc_data exynos_adc_s5pv210_data = {
.exit_hw = exynos_adc_v1_exit_hw,
.clear_irq = exynos_adc_v1_clear_irq,
.start_conv = exynos_adc_v1_start_conv,
+ .read_value = exynos_adc_v1_read_value,
};

static void exynos_adc_s3c64xx_start_conv(struct exynos_adc *info,
@@ -295,6 +306,7 @@ static struct exynos_adc_data const exynos_adc_s3c64xx_data = {
.exit_hw = exynos_adc_v1_exit_hw,
.clear_irq = exynos_adc_v1_clear_irq,
.start_conv = exynos_adc_s3c64xx_start_conv,
+ .read_value = exynos_adc_v1_read_value,
};

static void exynos_adc_v2_init_hw(struct exynos_adc *info)
@@ -356,6 +368,7 @@ static const struct exynos_adc_data exynos_adc_v2_data = {
.exit_hw = exynos_adc_v2_exit_hw,
.clear_irq = exynos_adc_v2_clear_irq,
.start_conv = exynos_adc_v2_start_conv,
+ .read_value = exynos_adc_v1_read_value,
};

static const struct exynos_adc_data exynos3250_adc_data = {
@@ -369,6 +382,7 @@ static const struct exynos_adc_data exynos3250_adc_data = {
.exit_hw = exynos_adc_v2_exit_hw,
.clear_irq = exynos_adc_v2_clear_irq,
.start_conv = exynos_adc_v2_start_conv,
+ .read_value = exynos_adc_v1_read_value,
};

static void exynos_adc_exynos7_init_hw(struct exynos_adc *info)
@@ -395,6 +409,52 @@ static const struct exynos_adc_data exynos7_adc_data = {
.exit_hw = exynos_adc_v2_exit_hw,
.clear_irq = exynos_adc_v2_clear_irq,
.start_conv = exynos_adc_v2_start_conv,
+ .read_value = exynos_adc_v1_read_value,
+};
+
+static void exynos850_adc_init_hw(struct exynos_adc *info)
+{
+ u32 con1, con2;
+
+ con1 = ADC_V2_CON1_SOFT_RESET;
+ writel(con1, ADC_V2_CON1(info->regs));
+
+ /* Bring out of reset */
+ writel(0, ADC_V2_CON1(info->regs));
+
+ /* 64 times conversion */
+ con2 = ADC_V2_CON2_C_TIME(6);
+ writel(con2, ADC_V2_CON2(info->regs));
+
+ writel(1, ADC_V2_INT_EN(info->regs));
+}
+
+static void exynos850_adc_exit_hw(struct exynos_adc *info)
+{
+ u32 con2;
+
+ con2 = readl(ADC_V2_CON2(info->regs));
+ con2 &= ~ADC_V2_CON2_C_TIME(7);
+ writel(con2, ADC_V2_CON2(info->regs));
+
+ writel(0, ADC_V2_INT_EN(info->regs));
+}
+
+static u32 exynos850_adc_read_value(struct exynos_adc *info)
+{
+ return readl(ADC_EXYNOS850_DAT(info->regs)) & info->data->mask;
+}
+
+static const struct exynos_adc_data exynos850_adc_data = {
+ .num_channels = MAX_ADC_V2_CHANNELS,
+ .mask = ADC_DATX_MASK, /* 12 bit ADC resolution */
+ .needs_adc_phy = false,
+
+ .init_hw = exynos850_adc_init_hw,
+ .exit_hw = exynos850_adc_exit_hw,
+ .clear_irq = exynos_adc_v2_clear_irq,
+ .start_conv = exynos_adc_v2_start_conv,
+ .read_value = exynos850_adc_read_value,
};

static const struct of_device_id exynos_adc_match[] = {
@@ -419,6 +479,9 @@ static const struct of_device_id exynos_adc_match[] = {
}, {
.compatible = "samsung,exynos7-adc",
.data = &exynos7_adc_data,
+ }, {
+ .compatible = "samsung,exynos850-adc",
+ .data = &exynos850_adc_data,
},
{ }
};
@@ -484,10 +547,9 @@ static int exynos_read_raw(struct iio_dev *indio_dev,
static irqreturn_t exynos_adc_isr(int irq, void *dev_id)
{
struct exynos_adc *info = dev_id;
- u32 mask = info->data->mask;

/* Read value */
- info->value = readl(ADC_V1_DATX(info->regs)) & mask;
+ info->value = info->data->read_value(info);

/* clear irq */
if (info->data->clear_irq)

--
2.51.0