Exynos's ADC-V3 has some difference in registers set, number of programmable channels (16 channel) etc. This patch adds support for ADC-V3 controller version. Cc: linux-fsd@xxxxxxxxx Cc: jic23@xxxxxxxxxx Cc: linux-iio@xxxxxxxxxxxxxxx Signed-off-by: Tamseel Shams <m.shams@xxxxxxxxxxx> Signed-off-by: Alim Akhtar <alim.akhtar@xxxxxxxxxxx> --- drivers/iio/adc/exynos_adc.c | 74 +++++++++++++++++++++++++++++++++++- 1 file changed, 72 insertions(+), 2 deletions(-) diff --git a/drivers/iio/adc/exynos_adc.c b/drivers/iio/adc/exynos_adc.c index 3b3868aa2533..61752e798fd6 100644 --- a/drivers/iio/adc/exynos_adc.c +++ b/drivers/iio/adc/exynos_adc.c @@ -55,6 +55,11 @@ #define ADC_V2_INT_ST(x) ((x) + 0x14) #define ADC_V2_VER(x) ((x) + 0x20) +/* ADC_V3 register definitions */ +#define ADC_V3_DAT(x) ((x) + 0x08) +#define ADC_V3_DAT_SUM(x) ((x) + 0x0C) +#define ADC_V3_DBG_DATA(x) ((x) + 0x1C) + /* Bit definitions for ADC_V1 */ #define ADC_V1_CON_RES (1u << 16) #define ADC_V1_CON_PRSCEN (1u << 14) @@ -92,6 +97,7 @@ /* Bit definitions for ADC_V2 */ #define ADC_V2_CON1_SOFT_RESET (1u << 2) +#define ADC_V2_CON1_SOFT_NON_RESET (1u << 1) #define ADC_V2_CON2_OSEL (1u << 10) #define ADC_V2_CON2_ESEL (1u << 9) @@ -100,6 +106,7 @@ #define ADC_V2_CON2_ACH_SEL(x) (((x) & 0xF) << 0) #define ADC_V2_CON2_ACH_MASK 0xF +#define MAX_ADC_V3_CHANNELS 16 #define MAX_ADC_V2_CHANNELS 10 #define MAX_ADC_V1_CHANNELS 8 #define MAX_EXYNOS3250_ADC_CHANNELS 2 @@ -164,6 +171,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); + irqreturn_t (*adc_isr)(int irq, void *dev_id); }; static void exynos_adc_unprepare_clk(struct exynos_adc *info) @@ -484,6 +492,59 @@ static const struct exynos_adc_data exynos7_adc_data = { .start_conv = exynos_adc_v2_start_conv, }; +static void exynos_adc_v3_init_hw(struct exynos_adc *info) +{ + u32 con2; + + writel(ADC_V2_CON1_SOFT_RESET, ADC_V2_CON1(info->regs)); + + writel(ADC_V2_CON1_SOFT_NON_RESET, ADC_V2_CON1(info->regs)); + + con2 = ADC_V2_CON2_C_TIME(6); + writel(con2, ADC_V2_CON2(info->regs)); + + /* Enable interrupts */ + writel(1, ADC_V2_INT_EN(info->regs)); +} + +static void exynos_adc_v3_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)); + + /* Disable interrupts */ + writel(0, ADC_V2_INT_EN(info->regs)); +} + +static irqreturn_t exynos_adc_v3_isr(int irq, void *dev_id) +{ + struct exynos_adc *info = (struct exynos_adc *)dev_id; + u32 mask = info->data->mask; + + info->value = readl(ADC_V3_DAT(info->regs)) & mask; + + if (info->data->clear_irq) + info->data->clear_irq(info); + + complete(&info->completion); + + return IRQ_HANDLED; +} + +static const struct exynos_adc_data exynos_adc_v3_adc_data = { + .num_channels = MAX_ADC_V3_CHANNELS, + .mask = ADC_DATX_MASK, /* 12 bit ADC resolution */ + + .init_hw = exynos_adc_v3_init_hw, + .exit_hw = exynos_adc_v3_exit_hw, + .clear_irq = exynos_adc_v2_clear_irq, + .start_conv = exynos_adc_v2_start_conv, + .adc_isr = exynos_adc_v3_isr, +}; + static const struct of_device_id exynos_adc_match[] = { { .compatible = "samsung,s3c2410-adc", @@ -518,6 +579,9 @@ static const struct of_device_id exynos_adc_match[] = { }, { .compatible = "samsung,exynos7-adc", .data = &exynos7_adc_data, + }, { + .compatible = "samsung,exynos-adc-v3", + .data = &exynos_adc_v3_adc_data, }, {}, }; @@ -719,6 +783,12 @@ static const struct iio_chan_spec exynos_adc_iio_channels[] = { ADC_CHANNEL(7, "adc7"), ADC_CHANNEL(8, "adc8"), ADC_CHANNEL(9, "adc9"), + ADC_CHANNEL(10, "adc10"), + ADC_CHANNEL(11, "adc11"), + ADC_CHANNEL(12, "adc12"), + ADC_CHANNEL(13, "adc13"), + ADC_CHANNEL(14, "adc14"), + ADC_CHANNEL(15, "adc15"), }; static int exynos_adc_remove_devices(struct device *dev, void *c) @@ -885,8 +955,8 @@ static int exynos_adc_probe(struct platform_device *pdev) mutex_init(&info->lock); - ret = request_irq(info->irq, exynos_adc_isr, - 0, dev_name(&pdev->dev), info); + ret = request_irq(info->irq, info->data->adc_isr ? info->data->adc_isr : + exynos_adc_isr, 0, dev_name(&pdev->dev), info); if (ret < 0) { dev_err(&pdev->dev, "failed requesting irq, irq = %d\n", info->irq); -- 2.17.1