* [PATCH v5 0/2 RESEND] imx93: add ADC support
@ 2023-07-05 12:56 Luca Ellero
2023-07-05 12:56 ` [PATCH 1/2] dm: adc: add iMX93 " Luca Ellero
2023-07-05 12:56 ` [PATCH 2/2] imx93_evk: defconfig: add adc support Luca Ellero
0 siblings, 2 replies; 13+ messages in thread
From: Luca Ellero @ 2023-07-05 12:56 UTC (permalink / raw)
To: u-boot, sbabic, festevam, uboot-imx, luca.ellero, ye.li,
peng.fan, haibo.chen
Cc: Luca Ellero
Add ADC support for NXP iMX93
Changes for v2:
- add "static" to functions
- enable ADC in iMX93 EVK
Changes for v3:
- split in 3 commits
- keep dts file in sync with Linux devicetree
- add comments to commits
Changes for v4:
- add imx93_adc_power_down() in imx93_adc_stop()
Changes for v5:
- simplify code
- remove redundant code
- add clock handling
Luca Ellero (2):
dm: adc: add iMX93 ADC support
imx93_evk: defconfig: add adc support
configs/imx93_11x11_evk_defconfig | 1 +
drivers/adc/Kconfig | 8 +
drivers/adc/Makefile | 1 +
drivers/adc/imx93-adc.c | 290 ++++++++++++++++++++++++++++++
4 files changed, 300 insertions(+)
create mode 100644 drivers/adc/imx93-adc.c
--
2.25.1
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH 1/2] dm: adc: add iMX93 ADC support
2023-07-05 12:56 [PATCH v5 0/2 RESEND] imx93: add ADC support Luca Ellero
@ 2023-07-05 12:56 ` Luca Ellero
2023-07-06 2:09 ` Bough Chen
2023-07-11 19:47 ` sbabic
2023-07-05 12:56 ` [PATCH 2/2] imx93_evk: defconfig: add adc support Luca Ellero
1 sibling, 2 replies; 13+ messages in thread
From: Luca Ellero @ 2023-07-05 12:56 UTC (permalink / raw)
To: u-boot, sbabic, festevam, uboot-imx, luca.ellero, ye.li,
peng.fan, haibo.chen
Cc: Luca Ellero
This commit adds driver for iMX93 ADC.
The driver is implemented using driver model and provides
ADC uclass's methods for ADC single channel operations:
- adc_start_channel()
- adc_channel_data()
- adc_stop()
ADC features:
- channels: 4
- resolution: 12-bit
Signed-off-by: Luca Ellero <l.ellero@asem.it>
---
drivers/adc/Kconfig | 8 ++
drivers/adc/Makefile | 1 +
drivers/adc/imx93-adc.c | 290 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 299 insertions(+)
create mode 100644 drivers/adc/imx93-adc.c
diff --git a/drivers/adc/Kconfig b/drivers/adc/Kconfig
index e719c38bb3..4336732dee 100644
--- a/drivers/adc/Kconfig
+++ b/drivers/adc/Kconfig
@@ -63,3 +63,11 @@ config STM32_ADC
- core driver to deal with common resources
- child driver to deal with individual ADC resources (declare ADC
device and associated channels, start/stop conversions)
+
+config ADC_IMX93
+ bool "Enable NXP IMX93 ADC driver"
+ help
+ This enables basic driver for NXP IMX93 ADC.
+ It provides:
+ - 4 analog input channels
+ - 12-bit resolution
diff --git a/drivers/adc/Makefile b/drivers/adc/Makefile
index c1387f3a34..5336c82097 100644
--- a/drivers/adc/Makefile
+++ b/drivers/adc/Makefile
@@ -10,3 +10,4 @@ obj-$(CONFIG_ADC_SANDBOX) += sandbox.o
obj-$(CONFIG_SARADC_ROCKCHIP) += rockchip-saradc.o
obj-$(CONFIG_SARADC_MESON) += meson-saradc.o
obj-$(CONFIG_STM32_ADC) += stm32-adc.o stm32-adc-core.o
+obj-$(CONFIG_ADC_IMX93) += imx93-adc.o
diff --git a/drivers/adc/imx93-adc.c b/drivers/adc/imx93-adc.c
new file mode 100644
index 0000000000..41d04e0426
--- /dev/null
+++ b/drivers/adc/imx93-adc.c
@@ -0,0 +1,290 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2023 ASEM Srl
+ * Author: Luca Ellero <l.ellero@asem.it>
+ *
+ * Originally based on NXP linux-imx kernel v5.15 drivers/iio/adc/imx93_adc.c
+ */
+
+#include <common.h>
+#include <errno.h>
+#include <dm.h>
+#include <linux/bitfield.h>
+#include <linux/iopoll.h>
+#include <clk.h>
+#include <adc.h>
+
+#define IMX93_ADC_MCR 0x00
+#define IMX93_ADC_MSR 0x04
+#define IMX93_ADC_ISR 0x10
+#define IMX93_ADC_IMR 0x20
+#define IMX93_ADC_CIMR0 0x24
+#define IMX93_ADC_CTR0 0x94
+#define IMX93_ADC_NCMR0 0xA4
+#define IMX93_ADC_PCDR0 0x100
+#define IMX93_ADC_PCDR1 0x104
+#define IMX93_ADC_PCDR2 0x108
+#define IMX93_ADC_PCDR3 0x10c
+#define IMX93_ADC_PCDR4 0x110
+#define IMX93_ADC_PCDR5 0x114
+#define IMX93_ADC_PCDR6 0x118
+#define IMX93_ADC_PCDR7 0x11c
+#define IMX93_ADC_CALSTAT 0x39C
+
+#define IMX93_ADC_MCR_MODE_MASK BIT(29)
+#define IMX93_ADC_MCR_NSTART_MASK BIT(24)
+#define IMX93_ADC_MCR_CALSTART_MASK BIT(14)
+#define IMX93_ADC_MCR_ADCLKSE_MASK BIT(8)
+#define IMX93_ADC_MCR_PWDN_MASK BIT(0)
+
+#define IMX93_ADC_MSR_CALFAIL_MASK BIT(30)
+#define IMX93_ADC_MSR_CALBUSY_MASK BIT(29)
+#define IMX93_ADC_MSR_ADCSTATUS_MASK GENMASK(2, 0)
+
+#define IMX93_ADC_ISR_EOC_MASK BIT(1)
+
+#define IMX93_ADC_IMR_EOC_MASK BIT(1)
+#define IMX93_ADC_IMR_ECH_MASK BIT(0)
+
+#define IMX93_ADC_PCDR_CDATA_MASK GENMASK(11, 0)
+
+#define IDLE 0
+#define POWER_DOWN 1
+#define WAIT_STATE 2
+#define BUSY_IN_CALIBRATION 3
+#define SAMPLE 4
+#define CONVERSION 6
+
+#define IMX93_ADC_MAX_CHANNEL 3
+#define IMX93_ADC_DAT_MASK 0xfff
+#define IMX93_ADC_TIMEOUT 100000
+
+struct imx93_adc_priv {
+ int active_channel;
+ void __iomem *regs;
+ struct clk ipg_clk;
+};
+
+static void imx93_adc_power_down(struct imx93_adc_priv *adc)
+{
+ u32 mcr, msr;
+ int ret;
+
+ mcr = readl(adc->regs + IMX93_ADC_MCR);
+ mcr |= FIELD_PREP(IMX93_ADC_MCR_PWDN_MASK, 1);
+ writel(mcr, adc->regs + IMX93_ADC_MCR);
+
+ ret = readl_poll_timeout(adc->regs + IMX93_ADC_MSR, msr,
+ ((msr & IMX93_ADC_MSR_ADCSTATUS_MASK) == POWER_DOWN), 50);
+ if (ret == -ETIMEDOUT)
+ pr_warn("ADC not in power down mode, current MSR: %x\n", msr);
+}
+
+static void imx93_adc_power_up(struct imx93_adc_priv *adc)
+{
+ u32 mcr;
+
+ /* bring ADC out of power down state, in idle state */
+ mcr = readl(adc->regs + IMX93_ADC_MCR);
+ mcr &= ~FIELD_PREP(IMX93_ADC_MCR_PWDN_MASK, 1);
+ writel(mcr, adc->regs + IMX93_ADC_MCR);
+}
+
+static void imx93_adc_config_ad_clk(struct imx93_adc_priv *adc)
+{
+ u32 mcr;
+
+ /* put adc in power down mode */
+ imx93_adc_power_down(adc);
+
+ /* config the AD_CLK equal to bus clock */
+ mcr = readl(adc->regs + IMX93_ADC_MCR);
+ mcr |= FIELD_PREP(IMX93_ADC_MCR_ADCLKSE_MASK, 1);
+ writel(mcr, adc->regs + IMX93_ADC_MCR);
+
+ /* bring ADC out of power down state, in idle state */
+ imx93_adc_power_up(adc);
+}
+
+static int imx93_adc_calibration(struct imx93_adc_priv *adc)
+{
+ u32 mcr, msr;
+ int ret;
+
+ /* make sure ADC is in power down mode */
+ imx93_adc_power_down(adc);
+
+ /* config SAR controller operating clock */
+ mcr = readl(adc->regs + IMX93_ADC_MCR);
+ mcr &= ~FIELD_PREP(IMX93_ADC_MCR_ADCLKSE_MASK, 1);
+ writel(mcr, adc->regs + IMX93_ADC_MCR);
+
+ /* bring ADC out of power down state */
+ imx93_adc_power_up(adc);
+
+ /*
+ * we use the default TSAMP/NRSMPL/AVGEN in MCR,
+ * can add the setting of these bit if need
+ */
+
+ /* run calibration */
+ mcr = readl(adc->regs + IMX93_ADC_MCR);
+ mcr |= FIELD_PREP(IMX93_ADC_MCR_CALSTART_MASK, 1);
+ writel(mcr, adc->regs + IMX93_ADC_MCR);
+
+ /* wait calibration to be finished */
+ ret = readl_poll_timeout(adc->regs + IMX93_ADC_MSR, msr,
+ !(msr & IMX93_ADC_MSR_CALBUSY_MASK), 2000000);
+ if (ret == -ETIMEDOUT) {
+ pr_warn("ADC calibration timeout\n");
+ return ret;
+ }
+
+ /* check whether calbration is successful or not */
+ msr = readl(adc->regs + IMX93_ADC_MSR);
+ if (msr & IMX93_ADC_MSR_CALFAIL_MASK) {
+ pr_warn("ADC calibration failed!\n");
+ return -EAGAIN;
+ }
+
+ return 0;
+}
+
+static int imx93_adc_channel_data(struct udevice *dev, int channel,
+ unsigned int *data)
+{
+ struct imx93_adc_priv *adc = dev_get_priv(dev);
+ u32 isr, pcda;
+ int ret;
+
+ if (channel != adc->active_channel) {
+ pr_err("Requested channel is not active!\n");
+ return -EINVAL;
+ }
+
+ ret = readl_poll_timeout(adc->regs + IMX93_ADC_ISR, isr,
+ (isr & IMX93_ADC_ISR_EOC_MASK), IMX93_ADC_TIMEOUT);
+
+ /* clear interrupts */
+ writel(isr, adc->regs + IMX93_ADC_ISR);
+
+ if (ret == -ETIMEDOUT) {
+ pr_warn("ADC conversion timeout!\n");
+ return ret;
+ }
+
+ pcda = readl(adc->regs + IMX93_ADC_PCDR0 + channel * 4);
+
+ *data = FIELD_GET(IMX93_ADC_PCDR_CDATA_MASK, pcda);
+
+ return 0;
+}
+
+static int imx93_adc_start_channel(struct udevice *dev, int channel)
+{
+ struct imx93_adc_priv *adc = dev_get_priv(dev);
+ u32 imr, mcr;
+
+ /* config channel mask register */
+ writel(1 << channel, adc->regs + IMX93_ADC_NCMR0);
+
+ /* config interrupt mask */
+ imr = FIELD_PREP(IMX93_ADC_IMR_EOC_MASK, 1);
+ writel(imr, adc->regs + IMX93_ADC_IMR);
+ writel(1 << channel, adc->regs + IMX93_ADC_CIMR0);
+
+ /* config one-shot mode */
+ mcr = readl(adc->regs + IMX93_ADC_MCR);
+ mcr &= ~FIELD_PREP(IMX93_ADC_MCR_MODE_MASK, 1);
+ writel(mcr, adc->regs + IMX93_ADC_MCR);
+
+ /* start normal conversion */
+ mcr = readl(adc->regs + IMX93_ADC_MCR);
+ mcr |= FIELD_PREP(IMX93_ADC_MCR_NSTART_MASK, 1);
+ writel(mcr, adc->regs + IMX93_ADC_MCR);
+
+ adc->active_channel = channel;
+
+ return 0;
+}
+
+static int imx93_adc_stop(struct udevice *dev)
+{
+ struct imx93_adc_priv *adc = dev_get_priv(dev);
+
+ imx93_adc_power_down(adc);
+
+ adc->active_channel = -1;
+
+ return 0;
+}
+
+static int imx93_adc_probe(struct udevice *dev)
+{
+ struct imx93_adc_priv *adc = dev_get_priv(dev);
+ unsigned int ret;
+
+ ret = imx93_adc_calibration(adc);
+ if (ret < 0)
+ return ret;
+
+ imx93_adc_config_ad_clk(adc);
+
+ adc->active_channel = -1;
+
+ return 0;
+}
+
+static int imx93_adc_of_to_plat(struct udevice *dev)
+{
+ struct adc_uclass_plat *uc_pdata = dev_get_uclass_plat(dev);
+ struct imx93_adc_priv *adc = dev_get_priv(dev);
+ unsigned int ret;
+
+ adc->regs = dev_read_addr_ptr(dev);
+ if (adc->regs == (struct imx93_adc *)FDT_ADDR_T_NONE) {
+ pr_err("Dev: %s - can't get address!", dev->name);
+ return -ENODATA;
+ }
+
+ ret = clk_get_by_name(dev, "ipg", &adc->ipg_clk);
+ if (ret < 0) {
+ pr_err("Can't get ADC ipg clk: %d\n", ret);
+ return ret;
+ }
+ ret = clk_enable(&adc->ipg_clk);
+ if(ret) {
+ pr_err("Can't enable ADC ipg clk: %d\n", ret);
+ return ret;
+ }
+
+ uc_pdata->data_mask = IMX93_ADC_DAT_MASK;
+ uc_pdata->data_format = ADC_DATA_FORMAT_BIN;
+ uc_pdata->data_timeout_us = IMX93_ADC_TIMEOUT;
+
+ /* Mask available channel bits: [0:3] */
+ uc_pdata->channel_mask = (2 << IMX93_ADC_MAX_CHANNEL) - 1;
+
+ return 0;
+}
+
+static const struct adc_ops imx93_adc_ops = {
+ .start_channel = imx93_adc_start_channel,
+ .channel_data = imx93_adc_channel_data,
+ .stop = imx93_adc_stop,
+};
+
+static const struct udevice_id imx93_adc_ids[] = {
+ { .compatible = "nxp,imx93-adc" },
+ { }
+};
+
+U_BOOT_DRIVER(imx93_adc) = {
+ .name = "imx93-adc",
+ .id = UCLASS_ADC,
+ .of_match = imx93_adc_ids,
+ .ops = &imx93_adc_ops,
+ .probe = imx93_adc_probe,
+ .of_to_plat = imx93_adc_of_to_plat,
+ .priv_auto = sizeof(struct imx93_adc_priv),
+};
--
2.25.1
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH 2/2] imx93_evk: defconfig: add adc support
2023-07-05 12:56 [PATCH v5 0/2 RESEND] imx93: add ADC support Luca Ellero
2023-07-05 12:56 ` [PATCH 1/2] dm: adc: add iMX93 " Luca Ellero
@ 2023-07-05 12:56 ` Luca Ellero
2023-07-11 19:44 ` sbabic
1 sibling, 1 reply; 13+ messages in thread
From: Luca Ellero @ 2023-07-05 12:56 UTC (permalink / raw)
To: u-boot, sbabic, festevam, uboot-imx, luca.ellero, ye.li,
peng.fan, haibo.chen
Cc: Luca Ellero
iMX93 ADC features:
- 4 channels
- 12 bit resolution
Signed-off-by: Luca Ellero <l.ellero@asem.it>
---
configs/imx93_11x11_evk_defconfig | 1 +
1 file changed, 1 insertion(+)
diff --git a/configs/imx93_11x11_evk_defconfig b/configs/imx93_11x11_evk_defconfig
index 89edebc4c6..30ef460c80 100644
--- a/configs/imx93_11x11_evk_defconfig
+++ b/configs/imx93_11x11_evk_defconfig
@@ -81,6 +81,7 @@ CONFIG_NET_RANDOM_ETHADDR=y
CONFIG_SPL_DM=y
CONFIG_REGMAP=y
CONFIG_SYSCON=y
+CONFIG_ADC_IMX93=y
CONFIG_CPU=y
CONFIG_CPU_IMX=y
CONFIG_IMX_RGPIO2P=y
--
2.25.1
^ permalink raw reply related [flat|nested] 13+ messages in thread
* RE: [PATCH 1/2] dm: adc: add iMX93 ADC support
2023-07-05 12:56 ` [PATCH 1/2] dm: adc: add iMX93 " Luca Ellero
@ 2023-07-06 2:09 ` Bough Chen
2023-07-11 19:47 ` sbabic
1 sibling, 0 replies; 13+ messages in thread
From: Bough Chen @ 2023-07-06 2:09 UTC (permalink / raw)
To: Luca Ellero, u-boot, sbabic, festevam, dl-uboot-imx, luca.ellero,
Ye Li, Peng Fan
> -----Original Message-----
> From: Luca Ellero <l.ellero@asem.it>
> Sent: 2023年7月5日 20:56
> To: u-boot@lists.denx.de; sbabic@denx.de; festevam@gmail.com; dl-uboot-imx
> <uboot-imx@nxp.com>; luca.ellero@brickedbrain.com; Ye Li <ye.li@nxp.com>;
> Peng Fan <peng.fan@nxp.com>; Bough Chen <haibo.chen@nxp.com>
> Cc: Luca Ellero <l.ellero@asem.it>
> Subject: [PATCH 1/2] dm: adc: add iMX93 ADC support
>
> This commit adds driver for iMX93 ADC.
>
> The driver is implemented using driver model and provides ADC uclass's methods
> for ADC single channel operations:
> - adc_start_channel()
> - adc_channel_data()
> - adc_stop()
>
> ADC features:
> - channels: 4
> - resolution: 12-bit
>
> Signed-off-by: Luca Ellero <l.ellero@asem.it>
Reviewed-by: Haibo Chen <haibo.chen@nxp.com>
Best Regards
Haibo Chen
> ---
> drivers/adc/Kconfig | 8 ++
> drivers/adc/Makefile | 1 +
> drivers/adc/imx93-adc.c | 290
> ++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 299 insertions(+)
> create mode 100644 drivers/adc/imx93-adc.c
>
> diff --git a/drivers/adc/Kconfig b/drivers/adc/Kconfig index
> e719c38bb3..4336732dee 100644
> --- a/drivers/adc/Kconfig
> +++ b/drivers/adc/Kconfig
> @@ -63,3 +63,11 @@ config STM32_ADC
> - core driver to deal with common resources
> - child driver to deal with individual ADC resources (declare ADC
> device and associated channels, start/stop conversions)
> +
> +config ADC_IMX93
> + bool "Enable NXP IMX93 ADC driver"
> + help
> + This enables basic driver for NXP IMX93 ADC.
> + It provides:
> + - 4 analog input channels
> + - 12-bit resolution
> diff --git a/drivers/adc/Makefile b/drivers/adc/Makefile index
> c1387f3a34..5336c82097 100644
> --- a/drivers/adc/Makefile
> +++ b/drivers/adc/Makefile
> @@ -10,3 +10,4 @@ obj-$(CONFIG_ADC_SANDBOX) += sandbox.o
> obj-$(CONFIG_SARADC_ROCKCHIP) += rockchip-saradc.o
> obj-$(CONFIG_SARADC_MESON) += meson-saradc.o
> obj-$(CONFIG_STM32_ADC) += stm32-adc.o stm32-adc-core.o
> +obj-$(CONFIG_ADC_IMX93) += imx93-adc.o
> diff --git a/drivers/adc/imx93-adc.c b/drivers/adc/imx93-adc.c new file mode
> 100644 index 0000000000..41d04e0426
> --- /dev/null
> +++ b/drivers/adc/imx93-adc.c
> @@ -0,0 +1,290 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Copyright (C) 2023 ASEM Srl
> + * Author: Luca Ellero <l.ellero@asem.it>
> + *
> + * Originally based on NXP linux-imx kernel v5.15
> +drivers/iio/adc/imx93_adc.c */
> +
> +#include <common.h>
> +#include <errno.h>
> +#include <dm.h>
> +#include <linux/bitfield.h>
> +#include <linux/iopoll.h>
> +#include <clk.h>
> +#include <adc.h>
> +
> +#define IMX93_ADC_MCR 0x00
> +#define IMX93_ADC_MSR 0x04
> +#define IMX93_ADC_ISR 0x10
> +#define IMX93_ADC_IMR 0x20
> +#define IMX93_ADC_CIMR0 0x24
> +#define IMX93_ADC_CTR0 0x94
> +#define IMX93_ADC_NCMR0 0xA4
> +#define IMX93_ADC_PCDR0 0x100
> +#define IMX93_ADC_PCDR1 0x104
> +#define IMX93_ADC_PCDR2 0x108
> +#define IMX93_ADC_PCDR3 0x10c
> +#define IMX93_ADC_PCDR4 0x110
> +#define IMX93_ADC_PCDR5 0x114
> +#define IMX93_ADC_PCDR6 0x118
> +#define IMX93_ADC_PCDR7 0x11c
> +#define IMX93_ADC_CALSTAT 0x39C
> +
> +#define IMX93_ADC_MCR_MODE_MASK BIT(29)
> +#define IMX93_ADC_MCR_NSTART_MASK BIT(24)
> +#define IMX93_ADC_MCR_CALSTART_MASK BIT(14)
> +#define IMX93_ADC_MCR_ADCLKSE_MASK BIT(8)
> +#define IMX93_ADC_MCR_PWDN_MASK BIT(0)
> +
> +#define IMX93_ADC_MSR_CALFAIL_MASK BIT(30)
> +#define IMX93_ADC_MSR_CALBUSY_MASK BIT(29)
> +#define IMX93_ADC_MSR_ADCSTATUS_MASK GENMASK(2, 0)
> +
> +#define IMX93_ADC_ISR_EOC_MASK BIT(1)
> +
> +#define IMX93_ADC_IMR_EOC_MASK BIT(1)
> +#define IMX93_ADC_IMR_ECH_MASK BIT(0)
> +
> +#define IMX93_ADC_PCDR_CDATA_MASK GENMASK(11, 0)
> +
> +#define IDLE 0
> +#define POWER_DOWN 1
> +#define WAIT_STATE 2
> +#define BUSY_IN_CALIBRATION 3
> +#define SAMPLE 4
> +#define CONVERSION 6
> +
> +#define IMX93_ADC_MAX_CHANNEL 3
> +#define IMX93_ADC_DAT_MASK 0xfff
> +#define IMX93_ADC_TIMEOUT 100000
> +
> +struct imx93_adc_priv {
> + int active_channel;
> + void __iomem *regs;
> + struct clk ipg_clk;
> +};
> +
> +static void imx93_adc_power_down(struct imx93_adc_priv *adc) {
> + u32 mcr, msr;
> + int ret;
> +
> + mcr = readl(adc->regs + IMX93_ADC_MCR);
> + mcr |= FIELD_PREP(IMX93_ADC_MCR_PWDN_MASK, 1);
> + writel(mcr, adc->regs + IMX93_ADC_MCR);
> +
> + ret = readl_poll_timeout(adc->regs + IMX93_ADC_MSR, msr,
> + ((msr & IMX93_ADC_MSR_ADCSTATUS_MASK) == POWER_DOWN),
> 50);
> + if (ret == -ETIMEDOUT)
> + pr_warn("ADC not in power down mode, current MSR: %x\n", msr); }
> +
> +static void imx93_adc_power_up(struct imx93_adc_priv *adc) {
> + u32 mcr;
> +
> + /* bring ADC out of power down state, in idle state */
> + mcr = readl(adc->regs + IMX93_ADC_MCR);
> + mcr &= ~FIELD_PREP(IMX93_ADC_MCR_PWDN_MASK, 1);
> + writel(mcr, adc->regs + IMX93_ADC_MCR); }
> +
> +static void imx93_adc_config_ad_clk(struct imx93_adc_priv *adc) {
> + u32 mcr;
> +
> + /* put adc in power down mode */
> + imx93_adc_power_down(adc);
> +
> + /* config the AD_CLK equal to bus clock */
> + mcr = readl(adc->regs + IMX93_ADC_MCR);
> + mcr |= FIELD_PREP(IMX93_ADC_MCR_ADCLKSE_MASK, 1);
> + writel(mcr, adc->regs + IMX93_ADC_MCR);
> +
> + /* bring ADC out of power down state, in idle state */
> + imx93_adc_power_up(adc);
> +}
> +
> +static int imx93_adc_calibration(struct imx93_adc_priv *adc) {
> + u32 mcr, msr;
> + int ret;
> +
> + /* make sure ADC is in power down mode */
> + imx93_adc_power_down(adc);
> +
> + /* config SAR controller operating clock */
> + mcr = readl(adc->regs + IMX93_ADC_MCR);
> + mcr &= ~FIELD_PREP(IMX93_ADC_MCR_ADCLKSE_MASK, 1);
> + writel(mcr, adc->regs + IMX93_ADC_MCR);
> +
> + /* bring ADC out of power down state */
> + imx93_adc_power_up(adc);
> +
> + /*
> + * we use the default TSAMP/NRSMPL/AVGEN in MCR,
> + * can add the setting of these bit if need
> + */
> +
> + /* run calibration */
> + mcr = readl(adc->regs + IMX93_ADC_MCR);
> + mcr |= FIELD_PREP(IMX93_ADC_MCR_CALSTART_MASK, 1);
> + writel(mcr, adc->regs + IMX93_ADC_MCR);
> +
> + /* wait calibration to be finished */
> + ret = readl_poll_timeout(adc->regs + IMX93_ADC_MSR, msr,
> + !(msr & IMX93_ADC_MSR_CALBUSY_MASK), 2000000);
> + if (ret == -ETIMEDOUT) {
> + pr_warn("ADC calibration timeout\n");
> + return ret;
> + }
> +
> + /* check whether calbration is successful or not */
> + msr = readl(adc->regs + IMX93_ADC_MSR);
> + if (msr & IMX93_ADC_MSR_CALFAIL_MASK) {
> + pr_warn("ADC calibration failed!\n");
> + return -EAGAIN;
> + }
> +
> + return 0;
> +}
> +
> +static int imx93_adc_channel_data(struct udevice *dev, int channel,
> + unsigned int *data)
> +{
> + struct imx93_adc_priv *adc = dev_get_priv(dev);
> + u32 isr, pcda;
> + int ret;
> +
> + if (channel != adc->active_channel) {
> + pr_err("Requested channel is not active!\n");
> + return -EINVAL;
> + }
> +
> + ret = readl_poll_timeout(adc->regs + IMX93_ADC_ISR, isr,
> + (isr & IMX93_ADC_ISR_EOC_MASK), IMX93_ADC_TIMEOUT);
> +
> + /* clear interrupts */
> + writel(isr, adc->regs + IMX93_ADC_ISR);
> +
> + if (ret == -ETIMEDOUT) {
> + pr_warn("ADC conversion timeout!\n");
> + return ret;
> + }
> +
> + pcda = readl(adc->regs + IMX93_ADC_PCDR0 + channel * 4);
> +
> + *data = FIELD_GET(IMX93_ADC_PCDR_CDATA_MASK, pcda);
> +
> + return 0;
> +}
> +
> +static int imx93_adc_start_channel(struct udevice *dev, int channel) {
> + struct imx93_adc_priv *adc = dev_get_priv(dev);
> + u32 imr, mcr;
> +
> + /* config channel mask register */
> + writel(1 << channel, adc->regs + IMX93_ADC_NCMR0);
> +
> + /* config interrupt mask */
> + imr = FIELD_PREP(IMX93_ADC_IMR_EOC_MASK, 1);
> + writel(imr, adc->regs + IMX93_ADC_IMR);
> + writel(1 << channel, adc->regs + IMX93_ADC_CIMR0);
> +
> + /* config one-shot mode */
> + mcr = readl(adc->regs + IMX93_ADC_MCR);
> + mcr &= ~FIELD_PREP(IMX93_ADC_MCR_MODE_MASK, 1);
> + writel(mcr, adc->regs + IMX93_ADC_MCR);
> +
> + /* start normal conversion */
> + mcr = readl(adc->regs + IMX93_ADC_MCR);
> + mcr |= FIELD_PREP(IMX93_ADC_MCR_NSTART_MASK, 1);
> + writel(mcr, adc->regs + IMX93_ADC_MCR);
> +
> + adc->active_channel = channel;
> +
> + return 0;
> +}
> +
> +static int imx93_adc_stop(struct udevice *dev) {
> + struct imx93_adc_priv *adc = dev_get_priv(dev);
> +
> + imx93_adc_power_down(adc);
> +
> + adc->active_channel = -1;
> +
> + return 0;
> +}
> +
> +static int imx93_adc_probe(struct udevice *dev) {
> + struct imx93_adc_priv *adc = dev_get_priv(dev);
> + unsigned int ret;
> +
> + ret = imx93_adc_calibration(adc);
> + if (ret < 0)
> + return ret;
> +
> + imx93_adc_config_ad_clk(adc);
> +
> + adc->active_channel = -1;
> +
> + return 0;
> +}
> +
> +static int imx93_adc_of_to_plat(struct udevice *dev) {
> + struct adc_uclass_plat *uc_pdata = dev_get_uclass_plat(dev);
> + struct imx93_adc_priv *adc = dev_get_priv(dev);
> + unsigned int ret;
> +
> + adc->regs = dev_read_addr_ptr(dev);
> + if (adc->regs == (struct imx93_adc *)FDT_ADDR_T_NONE) {
> + pr_err("Dev: %s - can't get address!", dev->name);
> + return -ENODATA;
> + }
> +
> + ret = clk_get_by_name(dev, "ipg", &adc->ipg_clk);
> + if (ret < 0) {
> + pr_err("Can't get ADC ipg clk: %d\n", ret);
> + return ret;
> + }
> + ret = clk_enable(&adc->ipg_clk);
> + if(ret) {
> + pr_err("Can't enable ADC ipg clk: %d\n", ret);
> + return ret;
> + }
> +
> + uc_pdata->data_mask = IMX93_ADC_DAT_MASK;
> + uc_pdata->data_format = ADC_DATA_FORMAT_BIN;
> + uc_pdata->data_timeout_us = IMX93_ADC_TIMEOUT;
> +
> + /* Mask available channel bits: [0:3] */
> + uc_pdata->channel_mask = (2 << IMX93_ADC_MAX_CHANNEL) - 1;
> +
> + return 0;
> +}
> +
> +static const struct adc_ops imx93_adc_ops = {
> + .start_channel = imx93_adc_start_channel,
> + .channel_data = imx93_adc_channel_data,
> + .stop = imx93_adc_stop,
> +};
> +
> +static const struct udevice_id imx93_adc_ids[] = {
> + { .compatible = "nxp,imx93-adc" },
> + { }
> +};
> +
> +U_BOOT_DRIVER(imx93_adc) = {
> + .name = "imx93-adc",
> + .id = UCLASS_ADC,
> + .of_match = imx93_adc_ids,
> + .ops = &imx93_adc_ops,
> + .probe = imx93_adc_probe,
> + .of_to_plat = imx93_adc_of_to_plat,
> + .priv_auto = sizeof(struct imx93_adc_priv),
> +};
> --
> 2.25.1
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH 2/2] imx93_evk: defconfig: add adc support
2023-07-05 12:56 ` [PATCH 2/2] imx93_evk: defconfig: add adc support Luca Ellero
@ 2023-07-11 19:44 ` sbabic
0 siblings, 0 replies; 13+ messages in thread
From: sbabic @ 2023-07-11 19:44 UTC (permalink / raw)
To: Luca Ellero, u-boot
> iMX93 ADC features:
> - 4 channels
> - 12 bit resolution
> Signed-off-by: Luca Ellero <l.ellero@asem.it>
Applied to u-boot-imx, master, thanks !
Best regards,
Stefano Babic
--
=====================================================================
DENX Software Engineering GmbH, Managing Director: Erika Unter
HRB 165235 Munich, Office: Kirchenstr.5, 82194 Groebenzell, Germany
Phone: +49-8142-66989-53 Fax: +49-8142-66989-80 Email: sbabic@denx.de
=====================================================================
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH 1/2] dm: adc: add iMX93 ADC support
2023-07-05 12:56 ` [PATCH 1/2] dm: adc: add iMX93 " Luca Ellero
2023-07-06 2:09 ` Bough Chen
@ 2023-07-11 19:47 ` sbabic
1 sibling, 0 replies; 13+ messages in thread
From: sbabic @ 2023-07-11 19:47 UTC (permalink / raw)
To: Luca Ellero, u-boot
> This commit adds driver for iMX93 ADC.
> The driver is implemented using driver model and provides
> ADC uclass's methods for ADC single channel operations:
> - adc_start_channel()
> - adc_channel_data()
> - adc_stop()
> ADC features:
> - channels: 4
> - resolution: 12-bit
> Signed-off-by: Luca Ellero <l.ellero@asem.it>
> Reviewed-by: Haibo Chen <haibo.chen@nxp.com>
Applied to u-boot-imx, master, thanks !
Best regards,
Stefano Babic
--
=====================================================================
DENX Software Engineering GmbH, Managing Director: Erika Unter
HRB 165235 Munich, Office: Kirchenstr.5, 82194 Groebenzell, Germany
Phone: +49-8142-66989-53 Fax: +49-8142-66989-80 Email: sbabic@denx.de
=====================================================================
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH 1/2] dm: adc: add iMX93 ADC support
2023-07-03 13:02 [RESEND PATCH v5 0/2] imx93: add ADC support Luca Ellero
@ 2023-07-03 13:02 ` Luca Ellero
0 siblings, 0 replies; 13+ messages in thread
From: Luca Ellero @ 2023-07-03 13:02 UTC (permalink / raw)
To: u-boot, sbabic, festevam, uboot-imx, luca.ellero, ye.li,
peng.fan, haibo.chen
Cc: Luca Ellero
This commit adds driver for iMX93 ADC.
The driver is implemented using driver model and provides
ADC uclass's methods for ADC single channel operations:
- adc_start_channel()
- adc_channel_data()
- adc_stop()
ADC features:
- channels: 4
- resolution: 12-bit
Signed-off-by: Luca Ellero <l.ellero@asem.it>
---
drivers/adc/Kconfig | 8 ++
drivers/adc/Makefile | 1 +
drivers/adc/imx93-adc.c | 290 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 299 insertions(+)
create mode 100644 drivers/adc/imx93-adc.c
diff --git a/drivers/adc/Kconfig b/drivers/adc/Kconfig
index e719c38bb3..4336732dee 100644
--- a/drivers/adc/Kconfig
+++ b/drivers/adc/Kconfig
@@ -63,3 +63,11 @@ config STM32_ADC
- core driver to deal with common resources
- child driver to deal with individual ADC resources (declare ADC
device and associated channels, start/stop conversions)
+
+config ADC_IMX93
+ bool "Enable NXP IMX93 ADC driver"
+ help
+ This enables basic driver for NXP IMX93 ADC.
+ It provides:
+ - 4 analog input channels
+ - 12-bit resolution
diff --git a/drivers/adc/Makefile b/drivers/adc/Makefile
index c1387f3a34..5336c82097 100644
--- a/drivers/adc/Makefile
+++ b/drivers/adc/Makefile
@@ -10,3 +10,4 @@ obj-$(CONFIG_ADC_SANDBOX) += sandbox.o
obj-$(CONFIG_SARADC_ROCKCHIP) += rockchip-saradc.o
obj-$(CONFIG_SARADC_MESON) += meson-saradc.o
obj-$(CONFIG_STM32_ADC) += stm32-adc.o stm32-adc-core.o
+obj-$(CONFIG_ADC_IMX93) += imx93-adc.o
diff --git a/drivers/adc/imx93-adc.c b/drivers/adc/imx93-adc.c
new file mode 100644
index 0000000000..41d04e0426
--- /dev/null
+++ b/drivers/adc/imx93-adc.c
@@ -0,0 +1,290 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2023 ASEM Srl
+ * Author: Luca Ellero <l.ellero@asem.it>
+ *
+ * Originally based on NXP linux-imx kernel v5.15 drivers/iio/adc/imx93_adc.c
+ */
+
+#include <common.h>
+#include <errno.h>
+#include <dm.h>
+#include <linux/bitfield.h>
+#include <linux/iopoll.h>
+#include <clk.h>
+#include <adc.h>
+
+#define IMX93_ADC_MCR 0x00
+#define IMX93_ADC_MSR 0x04
+#define IMX93_ADC_ISR 0x10
+#define IMX93_ADC_IMR 0x20
+#define IMX93_ADC_CIMR0 0x24
+#define IMX93_ADC_CTR0 0x94
+#define IMX93_ADC_NCMR0 0xA4
+#define IMX93_ADC_PCDR0 0x100
+#define IMX93_ADC_PCDR1 0x104
+#define IMX93_ADC_PCDR2 0x108
+#define IMX93_ADC_PCDR3 0x10c
+#define IMX93_ADC_PCDR4 0x110
+#define IMX93_ADC_PCDR5 0x114
+#define IMX93_ADC_PCDR6 0x118
+#define IMX93_ADC_PCDR7 0x11c
+#define IMX93_ADC_CALSTAT 0x39C
+
+#define IMX93_ADC_MCR_MODE_MASK BIT(29)
+#define IMX93_ADC_MCR_NSTART_MASK BIT(24)
+#define IMX93_ADC_MCR_CALSTART_MASK BIT(14)
+#define IMX93_ADC_MCR_ADCLKSE_MASK BIT(8)
+#define IMX93_ADC_MCR_PWDN_MASK BIT(0)
+
+#define IMX93_ADC_MSR_CALFAIL_MASK BIT(30)
+#define IMX93_ADC_MSR_CALBUSY_MASK BIT(29)
+#define IMX93_ADC_MSR_ADCSTATUS_MASK GENMASK(2, 0)
+
+#define IMX93_ADC_ISR_EOC_MASK BIT(1)
+
+#define IMX93_ADC_IMR_EOC_MASK BIT(1)
+#define IMX93_ADC_IMR_ECH_MASK BIT(0)
+
+#define IMX93_ADC_PCDR_CDATA_MASK GENMASK(11, 0)
+
+#define IDLE 0
+#define POWER_DOWN 1
+#define WAIT_STATE 2
+#define BUSY_IN_CALIBRATION 3
+#define SAMPLE 4
+#define CONVERSION 6
+
+#define IMX93_ADC_MAX_CHANNEL 3
+#define IMX93_ADC_DAT_MASK 0xfff
+#define IMX93_ADC_TIMEOUT 100000
+
+struct imx93_adc_priv {
+ int active_channel;
+ void __iomem *regs;
+ struct clk ipg_clk;
+};
+
+static void imx93_adc_power_down(struct imx93_adc_priv *adc)
+{
+ u32 mcr, msr;
+ int ret;
+
+ mcr = readl(adc->regs + IMX93_ADC_MCR);
+ mcr |= FIELD_PREP(IMX93_ADC_MCR_PWDN_MASK, 1);
+ writel(mcr, adc->regs + IMX93_ADC_MCR);
+
+ ret = readl_poll_timeout(adc->regs + IMX93_ADC_MSR, msr,
+ ((msr & IMX93_ADC_MSR_ADCSTATUS_MASK) == POWER_DOWN), 50);
+ if (ret == -ETIMEDOUT)
+ pr_warn("ADC not in power down mode, current MSR: %x\n", msr);
+}
+
+static void imx93_adc_power_up(struct imx93_adc_priv *adc)
+{
+ u32 mcr;
+
+ /* bring ADC out of power down state, in idle state */
+ mcr = readl(adc->regs + IMX93_ADC_MCR);
+ mcr &= ~FIELD_PREP(IMX93_ADC_MCR_PWDN_MASK, 1);
+ writel(mcr, adc->regs + IMX93_ADC_MCR);
+}
+
+static void imx93_adc_config_ad_clk(struct imx93_adc_priv *adc)
+{
+ u32 mcr;
+
+ /* put adc in power down mode */
+ imx93_adc_power_down(adc);
+
+ /* config the AD_CLK equal to bus clock */
+ mcr = readl(adc->regs + IMX93_ADC_MCR);
+ mcr |= FIELD_PREP(IMX93_ADC_MCR_ADCLKSE_MASK, 1);
+ writel(mcr, adc->regs + IMX93_ADC_MCR);
+
+ /* bring ADC out of power down state, in idle state */
+ imx93_adc_power_up(adc);
+}
+
+static int imx93_adc_calibration(struct imx93_adc_priv *adc)
+{
+ u32 mcr, msr;
+ int ret;
+
+ /* make sure ADC is in power down mode */
+ imx93_adc_power_down(adc);
+
+ /* config SAR controller operating clock */
+ mcr = readl(adc->regs + IMX93_ADC_MCR);
+ mcr &= ~FIELD_PREP(IMX93_ADC_MCR_ADCLKSE_MASK, 1);
+ writel(mcr, adc->regs + IMX93_ADC_MCR);
+
+ /* bring ADC out of power down state */
+ imx93_adc_power_up(adc);
+
+ /*
+ * we use the default TSAMP/NRSMPL/AVGEN in MCR,
+ * can add the setting of these bit if need
+ */
+
+ /* run calibration */
+ mcr = readl(adc->regs + IMX93_ADC_MCR);
+ mcr |= FIELD_PREP(IMX93_ADC_MCR_CALSTART_MASK, 1);
+ writel(mcr, adc->regs + IMX93_ADC_MCR);
+
+ /* wait calibration to be finished */
+ ret = readl_poll_timeout(adc->regs + IMX93_ADC_MSR, msr,
+ !(msr & IMX93_ADC_MSR_CALBUSY_MASK), 2000000);
+ if (ret == -ETIMEDOUT) {
+ pr_warn("ADC calibration timeout\n");
+ return ret;
+ }
+
+ /* check whether calbration is successful or not */
+ msr = readl(adc->regs + IMX93_ADC_MSR);
+ if (msr & IMX93_ADC_MSR_CALFAIL_MASK) {
+ pr_warn("ADC calibration failed!\n");
+ return -EAGAIN;
+ }
+
+ return 0;
+}
+
+static int imx93_adc_channel_data(struct udevice *dev, int channel,
+ unsigned int *data)
+{
+ struct imx93_adc_priv *adc = dev_get_priv(dev);
+ u32 isr, pcda;
+ int ret;
+
+ if (channel != adc->active_channel) {
+ pr_err("Requested channel is not active!\n");
+ return -EINVAL;
+ }
+
+ ret = readl_poll_timeout(adc->regs + IMX93_ADC_ISR, isr,
+ (isr & IMX93_ADC_ISR_EOC_MASK), IMX93_ADC_TIMEOUT);
+
+ /* clear interrupts */
+ writel(isr, adc->regs + IMX93_ADC_ISR);
+
+ if (ret == -ETIMEDOUT) {
+ pr_warn("ADC conversion timeout!\n");
+ return ret;
+ }
+
+ pcda = readl(adc->regs + IMX93_ADC_PCDR0 + channel * 4);
+
+ *data = FIELD_GET(IMX93_ADC_PCDR_CDATA_MASK, pcda);
+
+ return 0;
+}
+
+static int imx93_adc_start_channel(struct udevice *dev, int channel)
+{
+ struct imx93_adc_priv *adc = dev_get_priv(dev);
+ u32 imr, mcr;
+
+ /* config channel mask register */
+ writel(1 << channel, adc->regs + IMX93_ADC_NCMR0);
+
+ /* config interrupt mask */
+ imr = FIELD_PREP(IMX93_ADC_IMR_EOC_MASK, 1);
+ writel(imr, adc->regs + IMX93_ADC_IMR);
+ writel(1 << channel, adc->regs + IMX93_ADC_CIMR0);
+
+ /* config one-shot mode */
+ mcr = readl(adc->regs + IMX93_ADC_MCR);
+ mcr &= ~FIELD_PREP(IMX93_ADC_MCR_MODE_MASK, 1);
+ writel(mcr, adc->regs + IMX93_ADC_MCR);
+
+ /* start normal conversion */
+ mcr = readl(adc->regs + IMX93_ADC_MCR);
+ mcr |= FIELD_PREP(IMX93_ADC_MCR_NSTART_MASK, 1);
+ writel(mcr, adc->regs + IMX93_ADC_MCR);
+
+ adc->active_channel = channel;
+
+ return 0;
+}
+
+static int imx93_adc_stop(struct udevice *dev)
+{
+ struct imx93_adc_priv *adc = dev_get_priv(dev);
+
+ imx93_adc_power_down(adc);
+
+ adc->active_channel = -1;
+
+ return 0;
+}
+
+static int imx93_adc_probe(struct udevice *dev)
+{
+ struct imx93_adc_priv *adc = dev_get_priv(dev);
+ unsigned int ret;
+
+ ret = imx93_adc_calibration(adc);
+ if (ret < 0)
+ return ret;
+
+ imx93_adc_config_ad_clk(adc);
+
+ adc->active_channel = -1;
+
+ return 0;
+}
+
+static int imx93_adc_of_to_plat(struct udevice *dev)
+{
+ struct adc_uclass_plat *uc_pdata = dev_get_uclass_plat(dev);
+ struct imx93_adc_priv *adc = dev_get_priv(dev);
+ unsigned int ret;
+
+ adc->regs = dev_read_addr_ptr(dev);
+ if (adc->regs == (struct imx93_adc *)FDT_ADDR_T_NONE) {
+ pr_err("Dev: %s - can't get address!", dev->name);
+ return -ENODATA;
+ }
+
+ ret = clk_get_by_name(dev, "ipg", &adc->ipg_clk);
+ if (ret < 0) {
+ pr_err("Can't get ADC ipg clk: %d\n", ret);
+ return ret;
+ }
+ ret = clk_enable(&adc->ipg_clk);
+ if(ret) {
+ pr_err("Can't enable ADC ipg clk: %d\n", ret);
+ return ret;
+ }
+
+ uc_pdata->data_mask = IMX93_ADC_DAT_MASK;
+ uc_pdata->data_format = ADC_DATA_FORMAT_BIN;
+ uc_pdata->data_timeout_us = IMX93_ADC_TIMEOUT;
+
+ /* Mask available channel bits: [0:3] */
+ uc_pdata->channel_mask = (2 << IMX93_ADC_MAX_CHANNEL) - 1;
+
+ return 0;
+}
+
+static const struct adc_ops imx93_adc_ops = {
+ .start_channel = imx93_adc_start_channel,
+ .channel_data = imx93_adc_channel_data,
+ .stop = imx93_adc_stop,
+};
+
+static const struct udevice_id imx93_adc_ids[] = {
+ { .compatible = "nxp,imx93-adc" },
+ { }
+};
+
+U_BOOT_DRIVER(imx93_adc) = {
+ .name = "imx93-adc",
+ .id = UCLASS_ADC,
+ .of_match = imx93_adc_ids,
+ .ops = &imx93_adc_ops,
+ .probe = imx93_adc_probe,
+ .of_to_plat = imx93_adc_of_to_plat,
+ .priv_auto = sizeof(struct imx93_adc_priv),
+};
--
2.25.1
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH 1/2] dm: adc: add iMX93 ADC support
2023-06-21 14:00 [PATCH v5 0/2 RESEND] imx93: add " Luca Ellero
@ 2023-06-21 14:00 ` Luca Ellero
0 siblings, 0 replies; 13+ messages in thread
From: Luca Ellero @ 2023-06-21 14:00 UTC (permalink / raw)
To: u-boot, sbabic, festevam, uboot-imx, luca.ellero, ye.li,
peng.fan, haibo.chen
Cc: Luca Ellero
This commit adds driver for iMX93 ADC.
The driver is implemented using driver model and provides
ADC uclass's methods for ADC single channel operations:
- adc_start_channel()
- adc_channel_data()
- adc_stop()
ADC features:
- channels: 4
- resolution: 12-bit
Signed-off-by: Luca Ellero <l.ellero@asem.it>
---
drivers/adc/Kconfig | 8 ++
drivers/adc/Makefile | 1 +
drivers/adc/imx93-adc.c | 290 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 299 insertions(+)
create mode 100644 drivers/adc/imx93-adc.c
diff --git a/drivers/adc/Kconfig b/drivers/adc/Kconfig
index e719c38bb3..4336732dee 100644
--- a/drivers/adc/Kconfig
+++ b/drivers/adc/Kconfig
@@ -63,3 +63,11 @@ config STM32_ADC
- core driver to deal with common resources
- child driver to deal with individual ADC resources (declare ADC
device and associated channels, start/stop conversions)
+
+config ADC_IMX93
+ bool "Enable NXP IMX93 ADC driver"
+ help
+ This enables basic driver for NXP IMX93 ADC.
+ It provides:
+ - 4 analog input channels
+ - 12-bit resolution
diff --git a/drivers/adc/Makefile b/drivers/adc/Makefile
index c1387f3a34..5336c82097 100644
--- a/drivers/adc/Makefile
+++ b/drivers/adc/Makefile
@@ -10,3 +10,4 @@ obj-$(CONFIG_ADC_SANDBOX) += sandbox.o
obj-$(CONFIG_SARADC_ROCKCHIP) += rockchip-saradc.o
obj-$(CONFIG_SARADC_MESON) += meson-saradc.o
obj-$(CONFIG_STM32_ADC) += stm32-adc.o stm32-adc-core.o
+obj-$(CONFIG_ADC_IMX93) += imx93-adc.o
diff --git a/drivers/adc/imx93-adc.c b/drivers/adc/imx93-adc.c
new file mode 100644
index 0000000000..41d04e0426
--- /dev/null
+++ b/drivers/adc/imx93-adc.c
@@ -0,0 +1,290 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2023 ASEM Srl
+ * Author: Luca Ellero <l.ellero@asem.it>
+ *
+ * Originally based on NXP linux-imx kernel v5.15 drivers/iio/adc/imx93_adc.c
+ */
+
+#include <common.h>
+#include <errno.h>
+#include <dm.h>
+#include <linux/bitfield.h>
+#include <linux/iopoll.h>
+#include <clk.h>
+#include <adc.h>
+
+#define IMX93_ADC_MCR 0x00
+#define IMX93_ADC_MSR 0x04
+#define IMX93_ADC_ISR 0x10
+#define IMX93_ADC_IMR 0x20
+#define IMX93_ADC_CIMR0 0x24
+#define IMX93_ADC_CTR0 0x94
+#define IMX93_ADC_NCMR0 0xA4
+#define IMX93_ADC_PCDR0 0x100
+#define IMX93_ADC_PCDR1 0x104
+#define IMX93_ADC_PCDR2 0x108
+#define IMX93_ADC_PCDR3 0x10c
+#define IMX93_ADC_PCDR4 0x110
+#define IMX93_ADC_PCDR5 0x114
+#define IMX93_ADC_PCDR6 0x118
+#define IMX93_ADC_PCDR7 0x11c
+#define IMX93_ADC_CALSTAT 0x39C
+
+#define IMX93_ADC_MCR_MODE_MASK BIT(29)
+#define IMX93_ADC_MCR_NSTART_MASK BIT(24)
+#define IMX93_ADC_MCR_CALSTART_MASK BIT(14)
+#define IMX93_ADC_MCR_ADCLKSE_MASK BIT(8)
+#define IMX93_ADC_MCR_PWDN_MASK BIT(0)
+
+#define IMX93_ADC_MSR_CALFAIL_MASK BIT(30)
+#define IMX93_ADC_MSR_CALBUSY_MASK BIT(29)
+#define IMX93_ADC_MSR_ADCSTATUS_MASK GENMASK(2, 0)
+
+#define IMX93_ADC_ISR_EOC_MASK BIT(1)
+
+#define IMX93_ADC_IMR_EOC_MASK BIT(1)
+#define IMX93_ADC_IMR_ECH_MASK BIT(0)
+
+#define IMX93_ADC_PCDR_CDATA_MASK GENMASK(11, 0)
+
+#define IDLE 0
+#define POWER_DOWN 1
+#define WAIT_STATE 2
+#define BUSY_IN_CALIBRATION 3
+#define SAMPLE 4
+#define CONVERSION 6
+
+#define IMX93_ADC_MAX_CHANNEL 3
+#define IMX93_ADC_DAT_MASK 0xfff
+#define IMX93_ADC_TIMEOUT 100000
+
+struct imx93_adc_priv {
+ int active_channel;
+ void __iomem *regs;
+ struct clk ipg_clk;
+};
+
+static void imx93_adc_power_down(struct imx93_adc_priv *adc)
+{
+ u32 mcr, msr;
+ int ret;
+
+ mcr = readl(adc->regs + IMX93_ADC_MCR);
+ mcr |= FIELD_PREP(IMX93_ADC_MCR_PWDN_MASK, 1);
+ writel(mcr, adc->regs + IMX93_ADC_MCR);
+
+ ret = readl_poll_timeout(adc->regs + IMX93_ADC_MSR, msr,
+ ((msr & IMX93_ADC_MSR_ADCSTATUS_MASK) == POWER_DOWN), 50);
+ if (ret == -ETIMEDOUT)
+ pr_warn("ADC not in power down mode, current MSR: %x\n", msr);
+}
+
+static void imx93_adc_power_up(struct imx93_adc_priv *adc)
+{
+ u32 mcr;
+
+ /* bring ADC out of power down state, in idle state */
+ mcr = readl(adc->regs + IMX93_ADC_MCR);
+ mcr &= ~FIELD_PREP(IMX93_ADC_MCR_PWDN_MASK, 1);
+ writel(mcr, adc->regs + IMX93_ADC_MCR);
+}
+
+static void imx93_adc_config_ad_clk(struct imx93_adc_priv *adc)
+{
+ u32 mcr;
+
+ /* put adc in power down mode */
+ imx93_adc_power_down(adc);
+
+ /* config the AD_CLK equal to bus clock */
+ mcr = readl(adc->regs + IMX93_ADC_MCR);
+ mcr |= FIELD_PREP(IMX93_ADC_MCR_ADCLKSE_MASK, 1);
+ writel(mcr, adc->regs + IMX93_ADC_MCR);
+
+ /* bring ADC out of power down state, in idle state */
+ imx93_adc_power_up(adc);
+}
+
+static int imx93_adc_calibration(struct imx93_adc_priv *adc)
+{
+ u32 mcr, msr;
+ int ret;
+
+ /* make sure ADC is in power down mode */
+ imx93_adc_power_down(adc);
+
+ /* config SAR controller operating clock */
+ mcr = readl(adc->regs + IMX93_ADC_MCR);
+ mcr &= ~FIELD_PREP(IMX93_ADC_MCR_ADCLKSE_MASK, 1);
+ writel(mcr, adc->regs + IMX93_ADC_MCR);
+
+ /* bring ADC out of power down state */
+ imx93_adc_power_up(adc);
+
+ /*
+ * we use the default TSAMP/NRSMPL/AVGEN in MCR,
+ * can add the setting of these bit if need
+ */
+
+ /* run calibration */
+ mcr = readl(adc->regs + IMX93_ADC_MCR);
+ mcr |= FIELD_PREP(IMX93_ADC_MCR_CALSTART_MASK, 1);
+ writel(mcr, adc->regs + IMX93_ADC_MCR);
+
+ /* wait calibration to be finished */
+ ret = readl_poll_timeout(adc->regs + IMX93_ADC_MSR, msr,
+ !(msr & IMX93_ADC_MSR_CALBUSY_MASK), 2000000);
+ if (ret == -ETIMEDOUT) {
+ pr_warn("ADC calibration timeout\n");
+ return ret;
+ }
+
+ /* check whether calbration is successful or not */
+ msr = readl(adc->regs + IMX93_ADC_MSR);
+ if (msr & IMX93_ADC_MSR_CALFAIL_MASK) {
+ pr_warn("ADC calibration failed!\n");
+ return -EAGAIN;
+ }
+
+ return 0;
+}
+
+static int imx93_adc_channel_data(struct udevice *dev, int channel,
+ unsigned int *data)
+{
+ struct imx93_adc_priv *adc = dev_get_priv(dev);
+ u32 isr, pcda;
+ int ret;
+
+ if (channel != adc->active_channel) {
+ pr_err("Requested channel is not active!\n");
+ return -EINVAL;
+ }
+
+ ret = readl_poll_timeout(adc->regs + IMX93_ADC_ISR, isr,
+ (isr & IMX93_ADC_ISR_EOC_MASK), IMX93_ADC_TIMEOUT);
+
+ /* clear interrupts */
+ writel(isr, adc->regs + IMX93_ADC_ISR);
+
+ if (ret == -ETIMEDOUT) {
+ pr_warn("ADC conversion timeout!\n");
+ return ret;
+ }
+
+ pcda = readl(adc->regs + IMX93_ADC_PCDR0 + channel * 4);
+
+ *data = FIELD_GET(IMX93_ADC_PCDR_CDATA_MASK, pcda);
+
+ return 0;
+}
+
+static int imx93_adc_start_channel(struct udevice *dev, int channel)
+{
+ struct imx93_adc_priv *adc = dev_get_priv(dev);
+ u32 imr, mcr;
+
+ /* config channel mask register */
+ writel(1 << channel, adc->regs + IMX93_ADC_NCMR0);
+
+ /* config interrupt mask */
+ imr = FIELD_PREP(IMX93_ADC_IMR_EOC_MASK, 1);
+ writel(imr, adc->regs + IMX93_ADC_IMR);
+ writel(1 << channel, adc->regs + IMX93_ADC_CIMR0);
+
+ /* config one-shot mode */
+ mcr = readl(adc->regs + IMX93_ADC_MCR);
+ mcr &= ~FIELD_PREP(IMX93_ADC_MCR_MODE_MASK, 1);
+ writel(mcr, adc->regs + IMX93_ADC_MCR);
+
+ /* start normal conversion */
+ mcr = readl(adc->regs + IMX93_ADC_MCR);
+ mcr |= FIELD_PREP(IMX93_ADC_MCR_NSTART_MASK, 1);
+ writel(mcr, adc->regs + IMX93_ADC_MCR);
+
+ adc->active_channel = channel;
+
+ return 0;
+}
+
+static int imx93_adc_stop(struct udevice *dev)
+{
+ struct imx93_adc_priv *adc = dev_get_priv(dev);
+
+ imx93_adc_power_down(adc);
+
+ adc->active_channel = -1;
+
+ return 0;
+}
+
+static int imx93_adc_probe(struct udevice *dev)
+{
+ struct imx93_adc_priv *adc = dev_get_priv(dev);
+ unsigned int ret;
+
+ ret = imx93_adc_calibration(adc);
+ if (ret < 0)
+ return ret;
+
+ imx93_adc_config_ad_clk(adc);
+
+ adc->active_channel = -1;
+
+ return 0;
+}
+
+static int imx93_adc_of_to_plat(struct udevice *dev)
+{
+ struct adc_uclass_plat *uc_pdata = dev_get_uclass_plat(dev);
+ struct imx93_adc_priv *adc = dev_get_priv(dev);
+ unsigned int ret;
+
+ adc->regs = dev_read_addr_ptr(dev);
+ if (adc->regs == (struct imx93_adc *)FDT_ADDR_T_NONE) {
+ pr_err("Dev: %s - can't get address!", dev->name);
+ return -ENODATA;
+ }
+
+ ret = clk_get_by_name(dev, "ipg", &adc->ipg_clk);
+ if (ret < 0) {
+ pr_err("Can't get ADC ipg clk: %d\n", ret);
+ return ret;
+ }
+ ret = clk_enable(&adc->ipg_clk);
+ if(ret) {
+ pr_err("Can't enable ADC ipg clk: %d\n", ret);
+ return ret;
+ }
+
+ uc_pdata->data_mask = IMX93_ADC_DAT_MASK;
+ uc_pdata->data_format = ADC_DATA_FORMAT_BIN;
+ uc_pdata->data_timeout_us = IMX93_ADC_TIMEOUT;
+
+ /* Mask available channel bits: [0:3] */
+ uc_pdata->channel_mask = (2 << IMX93_ADC_MAX_CHANNEL) - 1;
+
+ return 0;
+}
+
+static const struct adc_ops imx93_adc_ops = {
+ .start_channel = imx93_adc_start_channel,
+ .channel_data = imx93_adc_channel_data,
+ .stop = imx93_adc_stop,
+};
+
+static const struct udevice_id imx93_adc_ids[] = {
+ { .compatible = "nxp,imx93-adc" },
+ { }
+};
+
+U_BOOT_DRIVER(imx93_adc) = {
+ .name = "imx93-adc",
+ .id = UCLASS_ADC,
+ .of_match = imx93_adc_ids,
+ .ops = &imx93_adc_ops,
+ .probe = imx93_adc_probe,
+ .of_to_plat = imx93_adc_of_to_plat,
+ .priv_auto = sizeof(struct imx93_adc_priv),
+};
--
2.25.1
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH 1/2] dm: adc: add iMX93 ADC support
2023-06-19 12:01 [PATCH v5 0/2 RESEND] imx93: add " Luca Ellero
@ 2023-06-19 12:01 ` Luca Ellero
0 siblings, 0 replies; 13+ messages in thread
From: Luca Ellero @ 2023-06-19 12:01 UTC (permalink / raw)
To: u-boot, sbabic, festevam, uboot-imx, luca.ellero, ye.li,
peng.fan, haibo.chen
Cc: Luca Ellero
This commit adds driver for iMX93 ADC.
The driver is implemented using driver model and provides
ADC uclass's methods for ADC single channel operations:
- adc_start_channel()
- adc_channel_data()
- adc_stop()
ADC features:
- channels: 4
- resolution: 12-bit
Signed-off-by: Luca Ellero <l.ellero@asem.it>
---
drivers/adc/Kconfig | 8 ++
drivers/adc/Makefile | 1 +
drivers/adc/imx93-adc.c | 290 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 299 insertions(+)
create mode 100644 drivers/adc/imx93-adc.c
diff --git a/drivers/adc/Kconfig b/drivers/adc/Kconfig
index e719c38bb3..4336732dee 100644
--- a/drivers/adc/Kconfig
+++ b/drivers/adc/Kconfig
@@ -63,3 +63,11 @@ config STM32_ADC
- core driver to deal with common resources
- child driver to deal with individual ADC resources (declare ADC
device and associated channels, start/stop conversions)
+
+config ADC_IMX93
+ bool "Enable NXP IMX93 ADC driver"
+ help
+ This enables basic driver for NXP IMX93 ADC.
+ It provides:
+ - 4 analog input channels
+ - 12-bit resolution
diff --git a/drivers/adc/Makefile b/drivers/adc/Makefile
index c1387f3a34..5336c82097 100644
--- a/drivers/adc/Makefile
+++ b/drivers/adc/Makefile
@@ -10,3 +10,4 @@ obj-$(CONFIG_ADC_SANDBOX) += sandbox.o
obj-$(CONFIG_SARADC_ROCKCHIP) += rockchip-saradc.o
obj-$(CONFIG_SARADC_MESON) += meson-saradc.o
obj-$(CONFIG_STM32_ADC) += stm32-adc.o stm32-adc-core.o
+obj-$(CONFIG_ADC_IMX93) += imx93-adc.o
diff --git a/drivers/adc/imx93-adc.c b/drivers/adc/imx93-adc.c
new file mode 100644
index 0000000000..41d04e0426
--- /dev/null
+++ b/drivers/adc/imx93-adc.c
@@ -0,0 +1,290 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2023 ASEM Srl
+ * Author: Luca Ellero <l.ellero@asem.it>
+ *
+ * Originally based on NXP linux-imx kernel v5.15 drivers/iio/adc/imx93_adc.c
+ */
+
+#include <common.h>
+#include <errno.h>
+#include <dm.h>
+#include <linux/bitfield.h>
+#include <linux/iopoll.h>
+#include <clk.h>
+#include <adc.h>
+
+#define IMX93_ADC_MCR 0x00
+#define IMX93_ADC_MSR 0x04
+#define IMX93_ADC_ISR 0x10
+#define IMX93_ADC_IMR 0x20
+#define IMX93_ADC_CIMR0 0x24
+#define IMX93_ADC_CTR0 0x94
+#define IMX93_ADC_NCMR0 0xA4
+#define IMX93_ADC_PCDR0 0x100
+#define IMX93_ADC_PCDR1 0x104
+#define IMX93_ADC_PCDR2 0x108
+#define IMX93_ADC_PCDR3 0x10c
+#define IMX93_ADC_PCDR4 0x110
+#define IMX93_ADC_PCDR5 0x114
+#define IMX93_ADC_PCDR6 0x118
+#define IMX93_ADC_PCDR7 0x11c
+#define IMX93_ADC_CALSTAT 0x39C
+
+#define IMX93_ADC_MCR_MODE_MASK BIT(29)
+#define IMX93_ADC_MCR_NSTART_MASK BIT(24)
+#define IMX93_ADC_MCR_CALSTART_MASK BIT(14)
+#define IMX93_ADC_MCR_ADCLKSE_MASK BIT(8)
+#define IMX93_ADC_MCR_PWDN_MASK BIT(0)
+
+#define IMX93_ADC_MSR_CALFAIL_MASK BIT(30)
+#define IMX93_ADC_MSR_CALBUSY_MASK BIT(29)
+#define IMX93_ADC_MSR_ADCSTATUS_MASK GENMASK(2, 0)
+
+#define IMX93_ADC_ISR_EOC_MASK BIT(1)
+
+#define IMX93_ADC_IMR_EOC_MASK BIT(1)
+#define IMX93_ADC_IMR_ECH_MASK BIT(0)
+
+#define IMX93_ADC_PCDR_CDATA_MASK GENMASK(11, 0)
+
+#define IDLE 0
+#define POWER_DOWN 1
+#define WAIT_STATE 2
+#define BUSY_IN_CALIBRATION 3
+#define SAMPLE 4
+#define CONVERSION 6
+
+#define IMX93_ADC_MAX_CHANNEL 3
+#define IMX93_ADC_DAT_MASK 0xfff
+#define IMX93_ADC_TIMEOUT 100000
+
+struct imx93_adc_priv {
+ int active_channel;
+ void __iomem *regs;
+ struct clk ipg_clk;
+};
+
+static void imx93_adc_power_down(struct imx93_adc_priv *adc)
+{
+ u32 mcr, msr;
+ int ret;
+
+ mcr = readl(adc->regs + IMX93_ADC_MCR);
+ mcr |= FIELD_PREP(IMX93_ADC_MCR_PWDN_MASK, 1);
+ writel(mcr, adc->regs + IMX93_ADC_MCR);
+
+ ret = readl_poll_timeout(adc->regs + IMX93_ADC_MSR, msr,
+ ((msr & IMX93_ADC_MSR_ADCSTATUS_MASK) == POWER_DOWN), 50);
+ if (ret == -ETIMEDOUT)
+ pr_warn("ADC not in power down mode, current MSR: %x\n", msr);
+}
+
+static void imx93_adc_power_up(struct imx93_adc_priv *adc)
+{
+ u32 mcr;
+
+ /* bring ADC out of power down state, in idle state */
+ mcr = readl(adc->regs + IMX93_ADC_MCR);
+ mcr &= ~FIELD_PREP(IMX93_ADC_MCR_PWDN_MASK, 1);
+ writel(mcr, adc->regs + IMX93_ADC_MCR);
+}
+
+static void imx93_adc_config_ad_clk(struct imx93_adc_priv *adc)
+{
+ u32 mcr;
+
+ /* put adc in power down mode */
+ imx93_adc_power_down(adc);
+
+ /* config the AD_CLK equal to bus clock */
+ mcr = readl(adc->regs + IMX93_ADC_MCR);
+ mcr |= FIELD_PREP(IMX93_ADC_MCR_ADCLKSE_MASK, 1);
+ writel(mcr, adc->regs + IMX93_ADC_MCR);
+
+ /* bring ADC out of power down state, in idle state */
+ imx93_adc_power_up(adc);
+}
+
+static int imx93_adc_calibration(struct imx93_adc_priv *adc)
+{
+ u32 mcr, msr;
+ int ret;
+
+ /* make sure ADC is in power down mode */
+ imx93_adc_power_down(adc);
+
+ /* config SAR controller operating clock */
+ mcr = readl(adc->regs + IMX93_ADC_MCR);
+ mcr &= ~FIELD_PREP(IMX93_ADC_MCR_ADCLKSE_MASK, 1);
+ writel(mcr, adc->regs + IMX93_ADC_MCR);
+
+ /* bring ADC out of power down state */
+ imx93_adc_power_up(adc);
+
+ /*
+ * we use the default TSAMP/NRSMPL/AVGEN in MCR,
+ * can add the setting of these bit if need
+ */
+
+ /* run calibration */
+ mcr = readl(adc->regs + IMX93_ADC_MCR);
+ mcr |= FIELD_PREP(IMX93_ADC_MCR_CALSTART_MASK, 1);
+ writel(mcr, adc->regs + IMX93_ADC_MCR);
+
+ /* wait calibration to be finished */
+ ret = readl_poll_timeout(adc->regs + IMX93_ADC_MSR, msr,
+ !(msr & IMX93_ADC_MSR_CALBUSY_MASK), 2000000);
+ if (ret == -ETIMEDOUT) {
+ pr_warn("ADC calibration timeout\n");
+ return ret;
+ }
+
+ /* check whether calbration is successful or not */
+ msr = readl(adc->regs + IMX93_ADC_MSR);
+ if (msr & IMX93_ADC_MSR_CALFAIL_MASK) {
+ pr_warn("ADC calibration failed!\n");
+ return -EAGAIN;
+ }
+
+ return 0;
+}
+
+static int imx93_adc_channel_data(struct udevice *dev, int channel,
+ unsigned int *data)
+{
+ struct imx93_adc_priv *adc = dev_get_priv(dev);
+ u32 isr, pcda;
+ int ret;
+
+ if (channel != adc->active_channel) {
+ pr_err("Requested channel is not active!\n");
+ return -EINVAL;
+ }
+
+ ret = readl_poll_timeout(adc->regs + IMX93_ADC_ISR, isr,
+ (isr & IMX93_ADC_ISR_EOC_MASK), IMX93_ADC_TIMEOUT);
+
+ /* clear interrupts */
+ writel(isr, adc->regs + IMX93_ADC_ISR);
+
+ if (ret == -ETIMEDOUT) {
+ pr_warn("ADC conversion timeout!\n");
+ return ret;
+ }
+
+ pcda = readl(adc->regs + IMX93_ADC_PCDR0 + channel * 4);
+
+ *data = FIELD_GET(IMX93_ADC_PCDR_CDATA_MASK, pcda);
+
+ return 0;
+}
+
+static int imx93_adc_start_channel(struct udevice *dev, int channel)
+{
+ struct imx93_adc_priv *adc = dev_get_priv(dev);
+ u32 imr, mcr;
+
+ /* config channel mask register */
+ writel(1 << channel, adc->regs + IMX93_ADC_NCMR0);
+
+ /* config interrupt mask */
+ imr = FIELD_PREP(IMX93_ADC_IMR_EOC_MASK, 1);
+ writel(imr, adc->regs + IMX93_ADC_IMR);
+ writel(1 << channel, adc->regs + IMX93_ADC_CIMR0);
+
+ /* config one-shot mode */
+ mcr = readl(adc->regs + IMX93_ADC_MCR);
+ mcr &= ~FIELD_PREP(IMX93_ADC_MCR_MODE_MASK, 1);
+ writel(mcr, adc->regs + IMX93_ADC_MCR);
+
+ /* start normal conversion */
+ mcr = readl(adc->regs + IMX93_ADC_MCR);
+ mcr |= FIELD_PREP(IMX93_ADC_MCR_NSTART_MASK, 1);
+ writel(mcr, adc->regs + IMX93_ADC_MCR);
+
+ adc->active_channel = channel;
+
+ return 0;
+}
+
+static int imx93_adc_stop(struct udevice *dev)
+{
+ struct imx93_adc_priv *adc = dev_get_priv(dev);
+
+ imx93_adc_power_down(adc);
+
+ adc->active_channel = -1;
+
+ return 0;
+}
+
+static int imx93_adc_probe(struct udevice *dev)
+{
+ struct imx93_adc_priv *adc = dev_get_priv(dev);
+ unsigned int ret;
+
+ ret = imx93_adc_calibration(adc);
+ if (ret < 0)
+ return ret;
+
+ imx93_adc_config_ad_clk(adc);
+
+ adc->active_channel = -1;
+
+ return 0;
+}
+
+static int imx93_adc_of_to_plat(struct udevice *dev)
+{
+ struct adc_uclass_plat *uc_pdata = dev_get_uclass_plat(dev);
+ struct imx93_adc_priv *adc = dev_get_priv(dev);
+ unsigned int ret;
+
+ adc->regs = dev_read_addr_ptr(dev);
+ if (adc->regs == (struct imx93_adc *)FDT_ADDR_T_NONE) {
+ pr_err("Dev: %s - can't get address!", dev->name);
+ return -ENODATA;
+ }
+
+ ret = clk_get_by_name(dev, "ipg", &adc->ipg_clk);
+ if (ret < 0) {
+ pr_err("Can't get ADC ipg clk: %d\n", ret);
+ return ret;
+ }
+ ret = clk_enable(&adc->ipg_clk);
+ if(ret) {
+ pr_err("Can't enable ADC ipg clk: %d\n", ret);
+ return ret;
+ }
+
+ uc_pdata->data_mask = IMX93_ADC_DAT_MASK;
+ uc_pdata->data_format = ADC_DATA_FORMAT_BIN;
+ uc_pdata->data_timeout_us = IMX93_ADC_TIMEOUT;
+
+ /* Mask available channel bits: [0:3] */
+ uc_pdata->channel_mask = (2 << IMX93_ADC_MAX_CHANNEL) - 1;
+
+ return 0;
+}
+
+static const struct adc_ops imx93_adc_ops = {
+ .start_channel = imx93_adc_start_channel,
+ .channel_data = imx93_adc_channel_data,
+ .stop = imx93_adc_stop,
+};
+
+static const struct udevice_id imx93_adc_ids[] = {
+ { .compatible = "nxp,imx93-adc" },
+ { }
+};
+
+U_BOOT_DRIVER(imx93_adc) = {
+ .name = "imx93-adc",
+ .id = UCLASS_ADC,
+ .of_match = imx93_adc_ids,
+ .ops = &imx93_adc_ops,
+ .probe = imx93_adc_probe,
+ .of_to_plat = imx93_adc_of_to_plat,
+ .priv_auto = sizeof(struct imx93_adc_priv),
+};
--
2.25.1
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH 1/2] dm: adc: add iMX93 ADC support
2023-06-13 14:32 [PATCH v5 0/2 RESEND] imx93: add " Luca Ellero
@ 2023-06-13 14:32 ` Luca Ellero
0 siblings, 0 replies; 13+ messages in thread
From: Luca Ellero @ 2023-06-13 14:32 UTC (permalink / raw)
To: u-boot, sbabic, festevam, uboot-imx, luca.ellero, ye.li,
peng.fan, haibo.chen
Cc: Luca Ellero
This commit adds driver for iMX93 ADC.
The driver is implemented using driver model and provides
ADC uclass's methods for ADC single channel operations:
- adc_start_channel()
- adc_channel_data()
- adc_stop()
ADC features:
- channels: 4
- resolution: 12-bit
Signed-off-by: Luca Ellero <l.ellero@asem.it>
---
drivers/adc/Kconfig | 8 ++
drivers/adc/Makefile | 1 +
drivers/adc/imx93-adc.c | 290 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 299 insertions(+)
create mode 100644 drivers/adc/imx93-adc.c
diff --git a/drivers/adc/Kconfig b/drivers/adc/Kconfig
index e719c38bb3..4336732dee 100644
--- a/drivers/adc/Kconfig
+++ b/drivers/adc/Kconfig
@@ -63,3 +63,11 @@ config STM32_ADC
- core driver to deal with common resources
- child driver to deal with individual ADC resources (declare ADC
device and associated channels, start/stop conversions)
+
+config ADC_IMX93
+ bool "Enable NXP IMX93 ADC driver"
+ help
+ This enables basic driver for NXP IMX93 ADC.
+ It provides:
+ - 4 analog input channels
+ - 12-bit resolution
diff --git a/drivers/adc/Makefile b/drivers/adc/Makefile
index c1387f3a34..5336c82097 100644
--- a/drivers/adc/Makefile
+++ b/drivers/adc/Makefile
@@ -10,3 +10,4 @@ obj-$(CONFIG_ADC_SANDBOX) += sandbox.o
obj-$(CONFIG_SARADC_ROCKCHIP) += rockchip-saradc.o
obj-$(CONFIG_SARADC_MESON) += meson-saradc.o
obj-$(CONFIG_STM32_ADC) += stm32-adc.o stm32-adc-core.o
+obj-$(CONFIG_ADC_IMX93) += imx93-adc.o
diff --git a/drivers/adc/imx93-adc.c b/drivers/adc/imx93-adc.c
new file mode 100644
index 0000000000..41d04e0426
--- /dev/null
+++ b/drivers/adc/imx93-adc.c
@@ -0,0 +1,290 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2023 ASEM Srl
+ * Author: Luca Ellero <l.ellero@asem.it>
+ *
+ * Originally based on NXP linux-imx kernel v5.15 drivers/iio/adc/imx93_adc.c
+ */
+
+#include <common.h>
+#include <errno.h>
+#include <dm.h>
+#include <linux/bitfield.h>
+#include <linux/iopoll.h>
+#include <clk.h>
+#include <adc.h>
+
+#define IMX93_ADC_MCR 0x00
+#define IMX93_ADC_MSR 0x04
+#define IMX93_ADC_ISR 0x10
+#define IMX93_ADC_IMR 0x20
+#define IMX93_ADC_CIMR0 0x24
+#define IMX93_ADC_CTR0 0x94
+#define IMX93_ADC_NCMR0 0xA4
+#define IMX93_ADC_PCDR0 0x100
+#define IMX93_ADC_PCDR1 0x104
+#define IMX93_ADC_PCDR2 0x108
+#define IMX93_ADC_PCDR3 0x10c
+#define IMX93_ADC_PCDR4 0x110
+#define IMX93_ADC_PCDR5 0x114
+#define IMX93_ADC_PCDR6 0x118
+#define IMX93_ADC_PCDR7 0x11c
+#define IMX93_ADC_CALSTAT 0x39C
+
+#define IMX93_ADC_MCR_MODE_MASK BIT(29)
+#define IMX93_ADC_MCR_NSTART_MASK BIT(24)
+#define IMX93_ADC_MCR_CALSTART_MASK BIT(14)
+#define IMX93_ADC_MCR_ADCLKSE_MASK BIT(8)
+#define IMX93_ADC_MCR_PWDN_MASK BIT(0)
+
+#define IMX93_ADC_MSR_CALFAIL_MASK BIT(30)
+#define IMX93_ADC_MSR_CALBUSY_MASK BIT(29)
+#define IMX93_ADC_MSR_ADCSTATUS_MASK GENMASK(2, 0)
+
+#define IMX93_ADC_ISR_EOC_MASK BIT(1)
+
+#define IMX93_ADC_IMR_EOC_MASK BIT(1)
+#define IMX93_ADC_IMR_ECH_MASK BIT(0)
+
+#define IMX93_ADC_PCDR_CDATA_MASK GENMASK(11, 0)
+
+#define IDLE 0
+#define POWER_DOWN 1
+#define WAIT_STATE 2
+#define BUSY_IN_CALIBRATION 3
+#define SAMPLE 4
+#define CONVERSION 6
+
+#define IMX93_ADC_MAX_CHANNEL 3
+#define IMX93_ADC_DAT_MASK 0xfff
+#define IMX93_ADC_TIMEOUT 100000
+
+struct imx93_adc_priv {
+ int active_channel;
+ void __iomem *regs;
+ struct clk ipg_clk;
+};
+
+static void imx93_adc_power_down(struct imx93_adc_priv *adc)
+{
+ u32 mcr, msr;
+ int ret;
+
+ mcr = readl(adc->regs + IMX93_ADC_MCR);
+ mcr |= FIELD_PREP(IMX93_ADC_MCR_PWDN_MASK, 1);
+ writel(mcr, adc->regs + IMX93_ADC_MCR);
+
+ ret = readl_poll_timeout(adc->regs + IMX93_ADC_MSR, msr,
+ ((msr & IMX93_ADC_MSR_ADCSTATUS_MASK) == POWER_DOWN), 50);
+ if (ret == -ETIMEDOUT)
+ pr_warn("ADC not in power down mode, current MSR: %x\n", msr);
+}
+
+static void imx93_adc_power_up(struct imx93_adc_priv *adc)
+{
+ u32 mcr;
+
+ /* bring ADC out of power down state, in idle state */
+ mcr = readl(adc->regs + IMX93_ADC_MCR);
+ mcr &= ~FIELD_PREP(IMX93_ADC_MCR_PWDN_MASK, 1);
+ writel(mcr, adc->regs + IMX93_ADC_MCR);
+}
+
+static void imx93_adc_config_ad_clk(struct imx93_adc_priv *adc)
+{
+ u32 mcr;
+
+ /* put adc in power down mode */
+ imx93_adc_power_down(adc);
+
+ /* config the AD_CLK equal to bus clock */
+ mcr = readl(adc->regs + IMX93_ADC_MCR);
+ mcr |= FIELD_PREP(IMX93_ADC_MCR_ADCLKSE_MASK, 1);
+ writel(mcr, adc->regs + IMX93_ADC_MCR);
+
+ /* bring ADC out of power down state, in idle state */
+ imx93_adc_power_up(adc);
+}
+
+static int imx93_adc_calibration(struct imx93_adc_priv *adc)
+{
+ u32 mcr, msr;
+ int ret;
+
+ /* make sure ADC is in power down mode */
+ imx93_adc_power_down(adc);
+
+ /* config SAR controller operating clock */
+ mcr = readl(adc->regs + IMX93_ADC_MCR);
+ mcr &= ~FIELD_PREP(IMX93_ADC_MCR_ADCLKSE_MASK, 1);
+ writel(mcr, adc->regs + IMX93_ADC_MCR);
+
+ /* bring ADC out of power down state */
+ imx93_adc_power_up(adc);
+
+ /*
+ * we use the default TSAMP/NRSMPL/AVGEN in MCR,
+ * can add the setting of these bit if need
+ */
+
+ /* run calibration */
+ mcr = readl(adc->regs + IMX93_ADC_MCR);
+ mcr |= FIELD_PREP(IMX93_ADC_MCR_CALSTART_MASK, 1);
+ writel(mcr, adc->regs + IMX93_ADC_MCR);
+
+ /* wait calibration to be finished */
+ ret = readl_poll_timeout(adc->regs + IMX93_ADC_MSR, msr,
+ !(msr & IMX93_ADC_MSR_CALBUSY_MASK), 2000000);
+ if (ret == -ETIMEDOUT) {
+ pr_warn("ADC calibration timeout\n");
+ return ret;
+ }
+
+ /* check whether calbration is successful or not */
+ msr = readl(adc->regs + IMX93_ADC_MSR);
+ if (msr & IMX93_ADC_MSR_CALFAIL_MASK) {
+ pr_warn("ADC calibration failed!\n");
+ return -EAGAIN;
+ }
+
+ return 0;
+}
+
+static int imx93_adc_channel_data(struct udevice *dev, int channel,
+ unsigned int *data)
+{
+ struct imx93_adc_priv *adc = dev_get_priv(dev);
+ u32 isr, pcda;
+ int ret;
+
+ if (channel != adc->active_channel) {
+ pr_err("Requested channel is not active!\n");
+ return -EINVAL;
+ }
+
+ ret = readl_poll_timeout(adc->regs + IMX93_ADC_ISR, isr,
+ (isr & IMX93_ADC_ISR_EOC_MASK), IMX93_ADC_TIMEOUT);
+
+ /* clear interrupts */
+ writel(isr, adc->regs + IMX93_ADC_ISR);
+
+ if (ret == -ETIMEDOUT) {
+ pr_warn("ADC conversion timeout!\n");
+ return ret;
+ }
+
+ pcda = readl(adc->regs + IMX93_ADC_PCDR0 + channel * 4);
+
+ *data = FIELD_GET(IMX93_ADC_PCDR_CDATA_MASK, pcda);
+
+ return 0;
+}
+
+static int imx93_adc_start_channel(struct udevice *dev, int channel)
+{
+ struct imx93_adc_priv *adc = dev_get_priv(dev);
+ u32 imr, mcr;
+
+ /* config channel mask register */
+ writel(1 << channel, adc->regs + IMX93_ADC_NCMR0);
+
+ /* config interrupt mask */
+ imr = FIELD_PREP(IMX93_ADC_IMR_EOC_MASK, 1);
+ writel(imr, adc->regs + IMX93_ADC_IMR);
+ writel(1 << channel, adc->regs + IMX93_ADC_CIMR0);
+
+ /* config one-shot mode */
+ mcr = readl(adc->regs + IMX93_ADC_MCR);
+ mcr &= ~FIELD_PREP(IMX93_ADC_MCR_MODE_MASK, 1);
+ writel(mcr, adc->regs + IMX93_ADC_MCR);
+
+ /* start normal conversion */
+ mcr = readl(adc->regs + IMX93_ADC_MCR);
+ mcr |= FIELD_PREP(IMX93_ADC_MCR_NSTART_MASK, 1);
+ writel(mcr, adc->regs + IMX93_ADC_MCR);
+
+ adc->active_channel = channel;
+
+ return 0;
+}
+
+static int imx93_adc_stop(struct udevice *dev)
+{
+ struct imx93_adc_priv *adc = dev_get_priv(dev);
+
+ imx93_adc_power_down(adc);
+
+ adc->active_channel = -1;
+
+ return 0;
+}
+
+static int imx93_adc_probe(struct udevice *dev)
+{
+ struct imx93_adc_priv *adc = dev_get_priv(dev);
+ unsigned int ret;
+
+ ret = imx93_adc_calibration(adc);
+ if (ret < 0)
+ return ret;
+
+ imx93_adc_config_ad_clk(adc);
+
+ adc->active_channel = -1;
+
+ return 0;
+}
+
+static int imx93_adc_of_to_plat(struct udevice *dev)
+{
+ struct adc_uclass_plat *uc_pdata = dev_get_uclass_plat(dev);
+ struct imx93_adc_priv *adc = dev_get_priv(dev);
+ unsigned int ret;
+
+ adc->regs = dev_read_addr_ptr(dev);
+ if (adc->regs == (struct imx93_adc *)FDT_ADDR_T_NONE) {
+ pr_err("Dev: %s - can't get address!", dev->name);
+ return -ENODATA;
+ }
+
+ ret = clk_get_by_name(dev, "ipg", &adc->ipg_clk);
+ if (ret < 0) {
+ pr_err("Can't get ADC ipg clk: %d\n", ret);
+ return ret;
+ }
+ ret = clk_enable(&adc->ipg_clk);
+ if(ret) {
+ pr_err("Can't enable ADC ipg clk: %d\n", ret);
+ return ret;
+ }
+
+ uc_pdata->data_mask = IMX93_ADC_DAT_MASK;
+ uc_pdata->data_format = ADC_DATA_FORMAT_BIN;
+ uc_pdata->data_timeout_us = IMX93_ADC_TIMEOUT;
+
+ /* Mask available channel bits: [0:3] */
+ uc_pdata->channel_mask = (2 << IMX93_ADC_MAX_CHANNEL) - 1;
+
+ return 0;
+}
+
+static const struct adc_ops imx93_adc_ops = {
+ .start_channel = imx93_adc_start_channel,
+ .channel_data = imx93_adc_channel_data,
+ .stop = imx93_adc_stop,
+};
+
+static const struct udevice_id imx93_adc_ids[] = {
+ { .compatible = "nxp,imx93-adc" },
+ { }
+};
+
+U_BOOT_DRIVER(imx93_adc) = {
+ .name = "imx93-adc",
+ .id = UCLASS_ADC,
+ .of_match = imx93_adc_ids,
+ .ops = &imx93_adc_ops,
+ .probe = imx93_adc_probe,
+ .of_to_plat = imx93_adc_of_to_plat,
+ .priv_auto = sizeof(struct imx93_adc_priv),
+};
--
2.25.1
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH 1/2] dm: adc: add iMX93 ADC support
2023-06-07 9:58 [PATCH v5 0/2 RESEND] imx93: add " Luca Ellero
@ 2023-06-07 9:58 ` Luca Ellero
0 siblings, 0 replies; 13+ messages in thread
From: Luca Ellero @ 2023-06-07 9:58 UTC (permalink / raw)
To: u-boot, sbabic, festevam, uboot-imx, luca.ellero, ye.li,
peng.fan, haibo.chen
Cc: Luca Ellero
This commit adds driver for iMX93 ADC.
The driver is implemented using driver model and provides
ADC uclass's methods for ADC single channel operations:
- adc_start_channel()
- adc_channel_data()
- adc_stop()
ADC features:
- channels: 4
- resolution: 12-bit
Signed-off-by: Luca Ellero <l.ellero@asem.it>
---
drivers/adc/Kconfig | 8 ++
drivers/adc/Makefile | 1 +
drivers/adc/imx93-adc.c | 290 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 299 insertions(+)
create mode 100644 drivers/adc/imx93-adc.c
diff --git a/drivers/adc/Kconfig b/drivers/adc/Kconfig
index e719c38bb3..4336732dee 100644
--- a/drivers/adc/Kconfig
+++ b/drivers/adc/Kconfig
@@ -63,3 +63,11 @@ config STM32_ADC
- core driver to deal with common resources
- child driver to deal with individual ADC resources (declare ADC
device and associated channels, start/stop conversions)
+
+config ADC_IMX93
+ bool "Enable NXP IMX93 ADC driver"
+ help
+ This enables basic driver for NXP IMX93 ADC.
+ It provides:
+ - 4 analog input channels
+ - 12-bit resolution
diff --git a/drivers/adc/Makefile b/drivers/adc/Makefile
index c1387f3a34..5336c82097 100644
--- a/drivers/adc/Makefile
+++ b/drivers/adc/Makefile
@@ -10,3 +10,4 @@ obj-$(CONFIG_ADC_SANDBOX) += sandbox.o
obj-$(CONFIG_SARADC_ROCKCHIP) += rockchip-saradc.o
obj-$(CONFIG_SARADC_MESON) += meson-saradc.o
obj-$(CONFIG_STM32_ADC) += stm32-adc.o stm32-adc-core.o
+obj-$(CONFIG_ADC_IMX93) += imx93-adc.o
diff --git a/drivers/adc/imx93-adc.c b/drivers/adc/imx93-adc.c
new file mode 100644
index 0000000000..41d04e0426
--- /dev/null
+++ b/drivers/adc/imx93-adc.c
@@ -0,0 +1,290 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2023 ASEM Srl
+ * Author: Luca Ellero <l.ellero@asem.it>
+ *
+ * Originally based on NXP linux-imx kernel v5.15 drivers/iio/adc/imx93_adc.c
+ */
+
+#include <common.h>
+#include <errno.h>
+#include <dm.h>
+#include <linux/bitfield.h>
+#include <linux/iopoll.h>
+#include <clk.h>
+#include <adc.h>
+
+#define IMX93_ADC_MCR 0x00
+#define IMX93_ADC_MSR 0x04
+#define IMX93_ADC_ISR 0x10
+#define IMX93_ADC_IMR 0x20
+#define IMX93_ADC_CIMR0 0x24
+#define IMX93_ADC_CTR0 0x94
+#define IMX93_ADC_NCMR0 0xA4
+#define IMX93_ADC_PCDR0 0x100
+#define IMX93_ADC_PCDR1 0x104
+#define IMX93_ADC_PCDR2 0x108
+#define IMX93_ADC_PCDR3 0x10c
+#define IMX93_ADC_PCDR4 0x110
+#define IMX93_ADC_PCDR5 0x114
+#define IMX93_ADC_PCDR6 0x118
+#define IMX93_ADC_PCDR7 0x11c
+#define IMX93_ADC_CALSTAT 0x39C
+
+#define IMX93_ADC_MCR_MODE_MASK BIT(29)
+#define IMX93_ADC_MCR_NSTART_MASK BIT(24)
+#define IMX93_ADC_MCR_CALSTART_MASK BIT(14)
+#define IMX93_ADC_MCR_ADCLKSE_MASK BIT(8)
+#define IMX93_ADC_MCR_PWDN_MASK BIT(0)
+
+#define IMX93_ADC_MSR_CALFAIL_MASK BIT(30)
+#define IMX93_ADC_MSR_CALBUSY_MASK BIT(29)
+#define IMX93_ADC_MSR_ADCSTATUS_MASK GENMASK(2, 0)
+
+#define IMX93_ADC_ISR_EOC_MASK BIT(1)
+
+#define IMX93_ADC_IMR_EOC_MASK BIT(1)
+#define IMX93_ADC_IMR_ECH_MASK BIT(0)
+
+#define IMX93_ADC_PCDR_CDATA_MASK GENMASK(11, 0)
+
+#define IDLE 0
+#define POWER_DOWN 1
+#define WAIT_STATE 2
+#define BUSY_IN_CALIBRATION 3
+#define SAMPLE 4
+#define CONVERSION 6
+
+#define IMX93_ADC_MAX_CHANNEL 3
+#define IMX93_ADC_DAT_MASK 0xfff
+#define IMX93_ADC_TIMEOUT 100000
+
+struct imx93_adc_priv {
+ int active_channel;
+ void __iomem *regs;
+ struct clk ipg_clk;
+};
+
+static void imx93_adc_power_down(struct imx93_adc_priv *adc)
+{
+ u32 mcr, msr;
+ int ret;
+
+ mcr = readl(adc->regs + IMX93_ADC_MCR);
+ mcr |= FIELD_PREP(IMX93_ADC_MCR_PWDN_MASK, 1);
+ writel(mcr, adc->regs + IMX93_ADC_MCR);
+
+ ret = readl_poll_timeout(adc->regs + IMX93_ADC_MSR, msr,
+ ((msr & IMX93_ADC_MSR_ADCSTATUS_MASK) == POWER_DOWN), 50);
+ if (ret == -ETIMEDOUT)
+ pr_warn("ADC not in power down mode, current MSR: %x\n", msr);
+}
+
+static void imx93_adc_power_up(struct imx93_adc_priv *adc)
+{
+ u32 mcr;
+
+ /* bring ADC out of power down state, in idle state */
+ mcr = readl(adc->regs + IMX93_ADC_MCR);
+ mcr &= ~FIELD_PREP(IMX93_ADC_MCR_PWDN_MASK, 1);
+ writel(mcr, adc->regs + IMX93_ADC_MCR);
+}
+
+static void imx93_adc_config_ad_clk(struct imx93_adc_priv *adc)
+{
+ u32 mcr;
+
+ /* put adc in power down mode */
+ imx93_adc_power_down(adc);
+
+ /* config the AD_CLK equal to bus clock */
+ mcr = readl(adc->regs + IMX93_ADC_MCR);
+ mcr |= FIELD_PREP(IMX93_ADC_MCR_ADCLKSE_MASK, 1);
+ writel(mcr, adc->regs + IMX93_ADC_MCR);
+
+ /* bring ADC out of power down state, in idle state */
+ imx93_adc_power_up(adc);
+}
+
+static int imx93_adc_calibration(struct imx93_adc_priv *adc)
+{
+ u32 mcr, msr;
+ int ret;
+
+ /* make sure ADC is in power down mode */
+ imx93_adc_power_down(adc);
+
+ /* config SAR controller operating clock */
+ mcr = readl(adc->regs + IMX93_ADC_MCR);
+ mcr &= ~FIELD_PREP(IMX93_ADC_MCR_ADCLKSE_MASK, 1);
+ writel(mcr, adc->regs + IMX93_ADC_MCR);
+
+ /* bring ADC out of power down state */
+ imx93_adc_power_up(adc);
+
+ /*
+ * we use the default TSAMP/NRSMPL/AVGEN in MCR,
+ * can add the setting of these bit if need
+ */
+
+ /* run calibration */
+ mcr = readl(adc->regs + IMX93_ADC_MCR);
+ mcr |= FIELD_PREP(IMX93_ADC_MCR_CALSTART_MASK, 1);
+ writel(mcr, adc->regs + IMX93_ADC_MCR);
+
+ /* wait calibration to be finished */
+ ret = readl_poll_timeout(adc->regs + IMX93_ADC_MSR, msr,
+ !(msr & IMX93_ADC_MSR_CALBUSY_MASK), 2000000);
+ if (ret == -ETIMEDOUT) {
+ pr_warn("ADC calibration timeout\n");
+ return ret;
+ }
+
+ /* check whether calbration is successful or not */
+ msr = readl(adc->regs + IMX93_ADC_MSR);
+ if (msr & IMX93_ADC_MSR_CALFAIL_MASK) {
+ pr_warn("ADC calibration failed!\n");
+ return -EAGAIN;
+ }
+
+ return 0;
+}
+
+static int imx93_adc_channel_data(struct udevice *dev, int channel,
+ unsigned int *data)
+{
+ struct imx93_adc_priv *adc = dev_get_priv(dev);
+ u32 isr, pcda;
+ int ret;
+
+ if (channel != adc->active_channel) {
+ pr_err("Requested channel is not active!\n");
+ return -EINVAL;
+ }
+
+ ret = readl_poll_timeout(adc->regs + IMX93_ADC_ISR, isr,
+ (isr & IMX93_ADC_ISR_EOC_MASK), IMX93_ADC_TIMEOUT);
+
+ /* clear interrupts */
+ writel(isr, adc->regs + IMX93_ADC_ISR);
+
+ if (ret == -ETIMEDOUT) {
+ pr_warn("ADC conversion timeout!\n");
+ return ret;
+ }
+
+ pcda = readl(adc->regs + IMX93_ADC_PCDR0 + channel * 4);
+
+ *data = FIELD_GET(IMX93_ADC_PCDR_CDATA_MASK, pcda);
+
+ return 0;
+}
+
+static int imx93_adc_start_channel(struct udevice *dev, int channel)
+{
+ struct imx93_adc_priv *adc = dev_get_priv(dev);
+ u32 imr, mcr;
+
+ /* config channel mask register */
+ writel(1 << channel, adc->regs + IMX93_ADC_NCMR0);
+
+ /* config interrupt mask */
+ imr = FIELD_PREP(IMX93_ADC_IMR_EOC_MASK, 1);
+ writel(imr, adc->regs + IMX93_ADC_IMR);
+ writel(1 << channel, adc->regs + IMX93_ADC_CIMR0);
+
+ /* config one-shot mode */
+ mcr = readl(adc->regs + IMX93_ADC_MCR);
+ mcr &= ~FIELD_PREP(IMX93_ADC_MCR_MODE_MASK, 1);
+ writel(mcr, adc->regs + IMX93_ADC_MCR);
+
+ /* start normal conversion */
+ mcr = readl(adc->regs + IMX93_ADC_MCR);
+ mcr |= FIELD_PREP(IMX93_ADC_MCR_NSTART_MASK, 1);
+ writel(mcr, adc->regs + IMX93_ADC_MCR);
+
+ adc->active_channel = channel;
+
+ return 0;
+}
+
+static int imx93_adc_stop(struct udevice *dev)
+{
+ struct imx93_adc_priv *adc = dev_get_priv(dev);
+
+ imx93_adc_power_down(adc);
+
+ adc->active_channel = -1;
+
+ return 0;
+}
+
+static int imx93_adc_probe(struct udevice *dev)
+{
+ struct imx93_adc_priv *adc = dev_get_priv(dev);
+ unsigned int ret;
+
+ ret = imx93_adc_calibration(adc);
+ if (ret < 0)
+ return ret;
+
+ imx93_adc_config_ad_clk(adc);
+
+ adc->active_channel = -1;
+
+ return 0;
+}
+
+static int imx93_adc_of_to_plat(struct udevice *dev)
+{
+ struct adc_uclass_plat *uc_pdata = dev_get_uclass_plat(dev);
+ struct imx93_adc_priv *adc = dev_get_priv(dev);
+ unsigned int ret;
+
+ adc->regs = dev_read_addr_ptr(dev);
+ if (adc->regs == (struct imx93_adc *)FDT_ADDR_T_NONE) {
+ pr_err("Dev: %s - can't get address!", dev->name);
+ return -ENODATA;
+ }
+
+ ret = clk_get_by_name(dev, "ipg", &adc->ipg_clk);
+ if (ret < 0) {
+ pr_err("Can't get ADC ipg clk: %d\n", ret);
+ return ret;
+ }
+ ret = clk_enable(&adc->ipg_clk);
+ if(ret) {
+ pr_err("Can't enable ADC ipg clk: %d\n", ret);
+ return ret;
+ }
+
+ uc_pdata->data_mask = IMX93_ADC_DAT_MASK;
+ uc_pdata->data_format = ADC_DATA_FORMAT_BIN;
+ uc_pdata->data_timeout_us = IMX93_ADC_TIMEOUT;
+
+ /* Mask available channel bits: [0:3] */
+ uc_pdata->channel_mask = (2 << IMX93_ADC_MAX_CHANNEL) - 1;
+
+ return 0;
+}
+
+static const struct adc_ops imx93_adc_ops = {
+ .start_channel = imx93_adc_start_channel,
+ .channel_data = imx93_adc_channel_data,
+ .stop = imx93_adc_stop,
+};
+
+static const struct udevice_id imx93_adc_ids[] = {
+ { .compatible = "nxp,imx93-adc" },
+ { }
+};
+
+U_BOOT_DRIVER(imx93_adc) = {
+ .name = "imx93-adc",
+ .id = UCLASS_ADC,
+ .of_match = imx93_adc_ids,
+ .ops = &imx93_adc_ops,
+ .probe = imx93_adc_probe,
+ .of_to_plat = imx93_adc_of_to_plat,
+ .priv_auto = sizeof(struct imx93_adc_priv),
+};
--
2.25.1
^ permalink raw reply related [flat|nested] 13+ messages in thread
* Re: [PATCH 1/2] dm: adc: add iMX93 ADC support
2023-02-23 12:51 ` [PATCH 1/2] dm: adc: add iMX93 " Luca Ellero
@ 2023-02-23 14:02 ` Fabio Estevam
0 siblings, 0 replies; 13+ messages in thread
From: Fabio Estevam @ 2023-02-23 14:02 UTC (permalink / raw)
To: Luca Ellero; +Cc: u-boot, sbabic, uboot-imx, luca.ellero, ye.li, peng.fan
On Thu, Feb 23, 2023 at 9:52 AM Luca Ellero <l.ellero@asem.it> wrote:
>
> This commit adds driver for iMX93 ADC.
>
> The driver is implemented using driver model and provides
> ADC uclass's methods for ADC single channel operations:
> - adc_start_channel()
> - adc_channel_data()
> - adc_stop()
>
> ADC features:
> - channels: 4
> - resolution: 12-bit
>
> Signed-off-by: Luca Ellero <l.ellero@asem.it>
Reviewed-by: Fabio Estevam <festevam@denx.de>
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH 1/2] dm: adc: add iMX93 ADC support
2023-02-23 12:51 [PATCH v2 0/2] imx93: add " Luca Ellero
@ 2023-02-23 12:51 ` Luca Ellero
2023-02-23 14:02 ` Fabio Estevam
0 siblings, 1 reply; 13+ messages in thread
From: Luca Ellero @ 2023-02-23 12:51 UTC (permalink / raw)
To: u-boot, sbabic, festevam, uboot-imx, luca.ellero, ye.li, peng.fan
Cc: Luca Ellero
This commit adds driver for iMX93 ADC.
The driver is implemented using driver model and provides
ADC uclass's methods for ADC single channel operations:
- adc_start_channel()
- adc_channel_data()
- adc_stop()
ADC features:
- channels: 4
- resolution: 12-bit
Signed-off-by: Luca Ellero <l.ellero@asem.it>
---
drivers/adc/Kconfig | 8 ++
drivers/adc/Makefile | 1 +
drivers/adc/imx93-adc.c | 284 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 293 insertions(+)
create mode 100644 drivers/adc/imx93-adc.c
diff --git a/drivers/adc/Kconfig b/drivers/adc/Kconfig
index e719c38bb3..4336732dee 100644
--- a/drivers/adc/Kconfig
+++ b/drivers/adc/Kconfig
@@ -63,3 +63,11 @@ config STM32_ADC
- core driver to deal with common resources
- child driver to deal with individual ADC resources (declare ADC
device and associated channels, start/stop conversions)
+
+config ADC_IMX93
+ bool "Enable NXP IMX93 ADC driver"
+ help
+ This enables basic driver for NXP IMX93 ADC.
+ It provides:
+ - 4 analog input channels
+ - 12-bit resolution
diff --git a/drivers/adc/Makefile b/drivers/adc/Makefile
index c1387f3a34..5336c82097 100644
--- a/drivers/adc/Makefile
+++ b/drivers/adc/Makefile
@@ -10,3 +10,4 @@ obj-$(CONFIG_ADC_SANDBOX) += sandbox.o
obj-$(CONFIG_SARADC_ROCKCHIP) += rockchip-saradc.o
obj-$(CONFIG_SARADC_MESON) += meson-saradc.o
obj-$(CONFIG_STM32_ADC) += stm32-adc.o stm32-adc-core.o
+obj-$(CONFIG_ADC_IMX93) += imx93-adc.o
diff --git a/drivers/adc/imx93-adc.c b/drivers/adc/imx93-adc.c
new file mode 100644
index 0000000000..c5f0268eca
--- /dev/null
+++ b/drivers/adc/imx93-adc.c
@@ -0,0 +1,284 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2023 ASEM Srl
+ * Author: Luca Ellero <l.ellero@asem.it>
+ *
+ * Originally based on NXP linux-imx kernel v5.15 drivers/iio/adc/imx93_adc.c
+ */
+
+#include <common.h>
+#include <errno.h>
+#include <dm.h>
+#include <linux/bitfield.h>
+#include <linux/iopoll.h>
+#include <adc.h>
+
+#define IMX93_ADC_MCR 0x00
+#define IMX93_ADC_MSR 0x04
+#define IMX93_ADC_ISR 0x10
+#define IMX93_ADC_IMR 0x20
+#define IMX93_ADC_CIMR0 0x24
+#define IMX93_ADC_CTR0 0x94
+#define IMX93_ADC_NCMR0 0xA4
+#define IMX93_ADC_PCDR0 0x100
+#define IMX93_ADC_PCDR1 0x104
+#define IMX93_ADC_PCDR2 0x108
+#define IMX93_ADC_PCDR3 0x10c
+#define IMX93_ADC_PCDR4 0x110
+#define IMX93_ADC_PCDR5 0x114
+#define IMX93_ADC_PCDR6 0x118
+#define IMX93_ADC_PCDR7 0x11c
+#define IMX93_ADC_CALSTAT 0x39C
+
+#define IMX93_ADC_MCR_MODE_MASK BIT(29)
+#define IMX93_ADC_MCR_NSTART_MASK BIT(24)
+#define IMX93_ADC_MCR_CALSTART_MASK BIT(14)
+#define IMX93_ADC_MCR_ADCLKSE_MASK BIT(8)
+#define IMX93_ADC_MCR_PWDN_MASK BIT(0)
+
+#define IMX93_ADC_MSR_CALFAIL_MASK BIT(30)
+#define IMX93_ADC_MSR_CALBUSY_MASK BIT(29)
+#define IMX93_ADC_MSR_ADCSTATUS_MASK GENMASK(2, 0)
+
+#define IMX93_ADC_ISR_EOC_MASK BIT(1)
+
+#define IMX93_ADC_IMR_EOC_MASK BIT(1)
+#define IMX93_ADC_IMR_ECH_MASK BIT(0)
+
+#define IMX93_ADC_PCDR_CDATA_MASK GENMASK(11, 0)
+
+#define IDLE 0
+#define POWER_DOWN 1
+#define WAIT_STATE 2
+#define BUSY_IN_CALIBRATION 3
+#define SAMPLE 4
+#define CONVERSION 6
+
+#define IMX93_ADC_MAX_CHANNEL 3
+#define IMX93_ADC_DAT_MASK 0xfff
+#define IMX93_ADC_TIMEOUT 100000
+
+struct imx93_adc_priv {
+ int active_channel;
+ void __iomem *regs;
+};
+
+static int imx93_adc_power_down(struct imx93_adc_priv *adc)
+{
+ u32 mcr, msr;
+ int ret;
+
+ mcr = readl(adc->regs + IMX93_ADC_MCR);
+ mcr |= FIELD_PREP(IMX93_ADC_MCR_PWDN_MASK, 1);
+ writel(mcr, adc->regs + IMX93_ADC_MCR);
+
+ ret = readl_poll_timeout(adc->regs + IMX93_ADC_MSR, msr,
+ ((msr & IMX93_ADC_MSR_ADCSTATUS_MASK) == POWER_DOWN), 50);
+ if (ret == -ETIMEDOUT)
+ pr_warn("ADC not in power down mode, current MSR: %x\n", msr);
+
+ return ret;
+}
+
+static int imx93_adc_config_ad_clk(struct imx93_adc_priv *adc)
+{
+ u32 mcr;
+ int ret;
+
+ /* put adc in power down mode */
+ ret = imx93_adc_power_down(adc);
+ if (ret < 0)
+ return ret;
+
+ /* config the AD_CLK equal to bus clock */
+ mcr = readl(adc->regs + IMX93_ADC_MCR);
+ mcr |= FIELD_PREP(IMX93_ADC_MCR_ADCLKSE_MASK, 1);
+ writel(mcr, adc->regs + IMX93_ADC_MCR);
+
+ /* bring ADC out of power down state, in idle state */
+ mcr = readl(adc->regs + IMX93_ADC_MCR);
+ mcr &= ~FIELD_PREP(IMX93_ADC_MCR_PWDN_MASK, 1);
+ writel(mcr, adc->regs + IMX93_ADC_MCR);
+
+ return ret;
+}
+
+static int imx93_adc_calibration(struct imx93_adc_priv *adc)
+{
+ u32 mcr, msr, adc_status;
+ int ret;
+
+ /* make sure ADC is in power down mode */
+ msr = readl(adc->regs + IMX93_ADC_MSR);
+
+ adc_status = FIELD_GET(IMX93_ADC_MSR_ADCSTATUS_MASK, msr);
+ if (adc_status != POWER_DOWN) {
+ ret = imx93_adc_power_down(adc);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* config SAR controller operating clock */
+ mcr = readl(adc->regs + IMX93_ADC_MCR);
+ mcr &= ~FIELD_PREP(IMX93_ADC_MCR_ADCLKSE_MASK, 1);
+ writel(mcr, adc->regs + IMX93_ADC_MCR);
+
+ /* bring ADC out of power down state */
+ mcr = readl(adc->regs + IMX93_ADC_MCR);
+ mcr &= ~FIELD_PREP(IMX93_ADC_MCR_PWDN_MASK, 1);
+ writel(mcr, adc->regs + IMX93_ADC_MCR);
+
+ /*
+ * we use the default TSAMP/NRSMPL/AVGEN in MCR,
+ * can add the setting of these bit if need
+ */
+
+ /* run calibration */
+ mcr = readl(adc->regs + IMX93_ADC_MCR);
+ mcr |= FIELD_PREP(IMX93_ADC_MCR_CALSTART_MASK, 1);
+ writel(mcr, adc->regs + IMX93_ADC_MCR);
+
+ /* wait calibration to be finished */
+ ret = readl_poll_timeout(adc->regs + IMX93_ADC_MSR, msr,
+ !(msr & IMX93_ADC_MSR_CALBUSY_MASK), 2000000);
+ if (ret == -ETIMEDOUT) {
+ pr_warn("ADC calibration timeout\n");
+ return ret;
+ }
+
+ /* check whether calbration is successful or not */
+ msr = readl(adc->regs + IMX93_ADC_MSR);
+ if (msr & IMX93_ADC_MSR_CALFAIL_MASK) {
+ pr_warn("ADC calibration failed!\n");
+ return -EAGAIN;
+ }
+
+ return 0;
+}
+
+static int imx93_adc_channel_data(struct udevice *dev, int channel,
+ unsigned int *data)
+{
+ struct imx93_adc_priv *adc = dev_get_priv(dev);
+ u32 isr, pcda;
+ int ret;
+
+ if (channel != adc->active_channel) {
+ pr_err("Requested channel is not active!\n");
+ return -EINVAL;
+ }
+
+ ret = readl_poll_timeout(adc->regs + IMX93_ADC_ISR, isr,
+ (isr & IMX93_ADC_ISR_EOC_MASK), IMX93_ADC_TIMEOUT);
+
+ /* clear interrupts */
+ writel(isr, adc->regs + IMX93_ADC_ISR);
+
+ if (ret == -ETIMEDOUT) {
+ pr_warn("ADC conversion timeout!\n");
+ return ret;
+ }
+
+ pcda = readl(adc->regs + IMX93_ADC_PCDR0 + channel * 4);
+
+ *data = FIELD_GET(IMX93_ADC_PCDR_CDATA_MASK, pcda);
+
+ return 0;
+}
+
+static int imx93_adc_start_channel(struct udevice *dev, int channel)
+{
+ struct imx93_adc_priv *adc = dev_get_priv(dev);
+ u32 imr, mcr;
+
+ /* config channel mask register */
+ writel(1 << channel, adc->regs + IMX93_ADC_NCMR0);
+
+ /* config interrupt mask */
+ imr = FIELD_PREP(IMX93_ADC_IMR_EOC_MASK, 1);
+ writel(imr, adc->regs + IMX93_ADC_IMR);
+ writel(1 << channel, adc->regs + IMX93_ADC_CIMR0);
+
+ /* config one-shot mode */
+ mcr = readl(adc->regs + IMX93_ADC_MCR);
+ mcr &= ~FIELD_PREP(IMX93_ADC_MCR_MODE_MASK, 1);
+ writel(mcr, adc->regs + IMX93_ADC_MCR);
+
+ /* start normal conversion */
+ mcr = readl(adc->regs + IMX93_ADC_MCR);
+ mcr |= FIELD_PREP(IMX93_ADC_MCR_NSTART_MASK, 1);
+ writel(mcr, adc->regs + IMX93_ADC_MCR);
+
+ adc->active_channel = channel;
+
+ return 0;
+}
+
+static int imx93_adc_stop(struct udevice *dev)
+{
+ struct imx93_adc_priv *adc = dev_get_priv(dev);
+
+ adc->active_channel = -1;
+
+ return 0;
+}
+
+static int imx93_adc_probe(struct udevice *dev)
+{
+ struct imx93_adc_priv *adc = dev_get_priv(dev);
+ unsigned int ret;
+
+ ret = imx93_adc_calibration(adc);
+ if (ret < 0)
+ return ret;
+
+ ret = imx93_adc_config_ad_clk(adc);
+ if (ret < 0)
+ return ret;
+
+ adc->active_channel = -1;
+
+ return 0;
+}
+
+static int imx93_adc_of_to_plat(struct udevice *dev)
+{
+ struct adc_uclass_plat *uc_pdata = dev_get_uclass_plat(dev);
+ struct imx93_adc_priv *adc = dev_get_priv(dev);
+
+ adc->regs = dev_read_addr_ptr(dev);
+ if (adc->regs == (struct imx93_adc *)FDT_ADDR_T_NONE) {
+ pr_err("Dev: %s - can't get address!", dev->name);
+ return -ENODATA;
+ }
+
+ uc_pdata->data_mask = IMX93_ADC_DAT_MASK;
+ uc_pdata->data_format = ADC_DATA_FORMAT_BIN;
+ uc_pdata->data_timeout_us = IMX93_ADC_TIMEOUT;
+
+ /* Mask available channel bits: [0:3] */
+ uc_pdata->channel_mask = (2 << IMX93_ADC_MAX_CHANNEL) - 1;
+
+ return 0;
+}
+
+static const struct adc_ops imx93_adc_ops = {
+ .start_channel = imx93_adc_start_channel,
+ .channel_data = imx93_adc_channel_data,
+ .stop = imx93_adc_stop,
+};
+
+static const struct udevice_id imx93_adc_ids[] = {
+ { .compatible = "nxp,imx93-adc" },
+ { }
+};
+
+U_BOOT_DRIVER(imx93_adc) = {
+ .name = "imx93-adc",
+ .id = UCLASS_ADC,
+ .of_match = imx93_adc_ids,
+ .ops = &imx93_adc_ops,
+ .probe = imx93_adc_probe,
+ .of_to_plat = imx93_adc_of_to_plat,
+ .priv_auto = sizeof(struct imx93_adc_priv),
+};
--
2.25.1
^ permalink raw reply related [flat|nested] 13+ messages in thread
end of thread, other threads:[~2023-07-11 19:57 UTC | newest]
Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-07-05 12:56 [PATCH v5 0/2 RESEND] imx93: add ADC support Luca Ellero
2023-07-05 12:56 ` [PATCH 1/2] dm: adc: add iMX93 " Luca Ellero
2023-07-06 2:09 ` Bough Chen
2023-07-11 19:47 ` sbabic
2023-07-05 12:56 ` [PATCH 2/2] imx93_evk: defconfig: add adc support Luca Ellero
2023-07-11 19:44 ` sbabic
-- strict thread matches above, loose matches on Subject: below --
2023-07-03 13:02 [RESEND PATCH v5 0/2] imx93: add ADC support Luca Ellero
2023-07-03 13:02 ` [PATCH 1/2] dm: adc: add iMX93 " Luca Ellero
2023-06-21 14:00 [PATCH v5 0/2 RESEND] imx93: add " Luca Ellero
2023-06-21 14:00 ` [PATCH 1/2] dm: adc: add iMX93 " Luca Ellero
2023-06-19 12:01 [PATCH v5 0/2 RESEND] imx93: add " Luca Ellero
2023-06-19 12:01 ` [PATCH 1/2] dm: adc: add iMX93 " Luca Ellero
2023-06-13 14:32 [PATCH v5 0/2 RESEND] imx93: add " Luca Ellero
2023-06-13 14:32 ` [PATCH 1/2] dm: adc: add iMX93 " Luca Ellero
2023-06-07 9:58 [PATCH v5 0/2 RESEND] imx93: add " Luca Ellero
2023-06-07 9:58 ` [PATCH 1/2] dm: adc: add iMX93 " Luca Ellero
2023-02-23 12:51 [PATCH v2 0/2] imx93: add " Luca Ellero
2023-02-23 12:51 ` [PATCH 1/2] dm: adc: add iMX93 " Luca Ellero
2023-02-23 14:02 ` Fabio Estevam
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.