diff mbox series

[21/23] iio: adc: exynos-adc: Add support for ADC V3 controller

Message ID 20220113121143.22280-22-alim.akhtar@samsung.com (mailing list archive)
State Not Applicable
Headers show
Series [01/23] dt-bindings: clock: Document FSD CMU bindings | expand

Commit Message

Alim Akhtar Jan. 13, 2022, 12:11 p.m. UTC
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@tesla.com
Cc: jic23@kernel.org
Cc: linux-iio@vger.kernel.org
Signed-off-by: Tamseel Shams <m.shams@samsung.com>
Signed-off-by: Alim Akhtar <alim.akhtar@samsung.com>
---
 drivers/iio/adc/exynos_adc.c | 74 +++++++++++++++++++++++++++++++++++-
 1 file changed, 72 insertions(+), 2 deletions(-)

Comments

Jonathan Cameron Jan. 16, 2022, 11:19 a.m. UTC | #1
On Thu, 13 Jan 2022 17:41:41 +0530
Alim Akhtar <alim.akhtar@samsung.com> wrote:

> 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@tesla.com
> Cc: jic23@kernel.org
> Cc: linux-iio@vger.kernel.org
> Signed-off-by: Tamseel Shams <m.shams@samsung.com>
> Signed-off-by: Alim Akhtar <alim.akhtar@samsung.com>

Hi Alim,

A few minor suggestions below.  I'm not seeing a binding update though...

I'd also suggest that it would be more appropriate to break this out as
a separate mini series from the main support so that it can be reviewed
and merge separately. It's not ideal when a list just gets patch 21 of
23 with no cover letter etc sent to it.

Jonathan

> ---
>  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

Given we have a mixture of required an unrequired elements in this structure
it might be a good idea to add some documentation.  Kernel-doc for the whole
structure preferred.  Note this isn't necessarily something that needs to be
in this patch given the lack of docs predates this and with the change to make
adc_isr() required that I suggest below things aren't made worse by this patch.

> @@ -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;

Shouldn't need the cast as cast from void * to another pointer
is always valid in C without the explicit cast.

> +	u32 mask = info->data->mask;
> +
> +	info->value = readl(ADC_V3_DAT(info->regs)) & mask;
> +
> +	if (info->data->clear_irq)
> +		info->data->clear_irq(info);

Don't need this currently as v3_isr() is always matched
with clear_isr() being provided.  Having the check implies
otherwise which is probably not a good thing to do until some future
device support (maybe) needs it.

> +
> +	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);

I'd rather see the slightly larger change of providing adc_isr for existing
parts and the conditional part here going away.

Jonathan


>  	if (ret < 0) {
>  		dev_err(&pdev->dev, "failed requesting irq, irq = %d\n",
>  							info->irq);
Alim Akhtar Jan. 17, 2022, 12:20 p.m. UTC | #2
Hi Jonathan

>-----Original Message-----
>From: Jonathan Cameron [mailto:jic23@kernel.org]
>Sent: Sunday, January 16, 2022 4:50 PM
>To: Alim Akhtar <alim.akhtar@samsung.com>
>Cc: linux-arm-kernel@lists.infradead.org; linux-kernel@vger.kernel.org;
>soc@kernel.org; linux-clk@vger.kernel.org; devicetree@vger.kernel.org;
>olof@lixom.net; linus.walleij@linaro.org; catalin.marinas@arm.com;
>robh+dt@kernel.org; krzysztof.kozlowski@canonical.com;
>s.nawrocki@samsung.com; linux-samsung-soc@vger.kernel.org;
>pankaj.dubey@samsung.com; linux-fsd@tesla.com; linux-
>iio@vger.kernel.org; Tamseel Shams <m.shams@samsung.com>
>Subject: Re: [PATCH 21/23] iio: adc: exynos-adc: Add support for ADC V3
>controller
>
>On Thu, 13 Jan 2022 17:41:41 +0530
>Alim Akhtar <alim.akhtar@samsung.com> wrote:
>
>> 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@tesla.com
>> Cc: jic23@kernel.org
>> Cc: linux-iio@vger.kernel.org
>> Signed-off-by: Tamseel Shams <m.shams@samsung.com>
>> Signed-off-by: Alim Akhtar <alim.akhtar@samsung.com>
>
>Hi Alim,
>
>A few minor suggestions below.  I'm not seeing a binding update though...
>
>I'd also suggest that it would be more appropriate to break this out as a
>separate mini series from the main support so that it can be reviewed and
>merge separately. It's not ideal when a list just gets patch 21 of
>23 with no cover letter etc sent to it.
>
Thanks for the detailed review, I agree, will send as a separate patch set
only related with ADC support.
And addressing rest of your comments in this patch.

>Jonathan
>
>> ---
>>  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
>
>Given we have a mixture of required an unrequired elements in this
structure
>it might be a good idea to add some documentation.  Kernel-doc for the
>whole structure preferred.  Note this isn't necessarily something that
needs
>to be in this patch given the lack of docs predates this and with the
change to
>make
>adc_isr() required that I suggest below things aren't made worse by this
>patch.
>
>> @@ -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;
>
>Shouldn't need the cast as cast from void * to another pointer is always
valid
>in C without the explicit cast.
>
>> +	u32 mask = info->data->mask;
>> +
>> +	info->value = readl(ADC_V3_DAT(info->regs)) & mask;
>> +
>> +	if (info->data->clear_irq)
>> +		info->data->clear_irq(info);
>
>Don't need this currently as v3_isr() is always matched with clear_isr()
being
>provided.  Having the check implies otherwise which is probably not a good
>thing to do until some future device support (maybe) needs it.
>
>> +
>> +	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);
>
>I'd rather see the slightly larger change of providing adc_isr for existing
parts
>and the conditional part here going away.
>
>Jonathan
>
>
>>  	if (ret < 0) {
>>  		dev_err(&pdev->dev, "failed requesting irq, irq = %d\n",
>>  							info->irq);
diff mbox series

Patch

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);