Il 14/03/2023 04:03, Bough Chen ha scritto:
-----Original Message-----
From: Luca Ellero <[email protected]>
Sent: 2023年3月13日 21:06
To: [email protected]; [email protected]; [email protected]; dl-uboot-imx
<[email protected]>; [email protected]; Ye Li <[email protected]>;
Peng Fan <[email protected]>; Bough Chen <[email protected]>
Cc: Luca Ellero <[email protected]>
Subject: [PATCH 1/3] 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 <[email protected]>
---
  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 <[email protected]>
+ *
+ * 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);
+

Better to add imx93_adc_power_down() here.

Best Regards
Haibo Chen


Hi Haibo,
thank you for your reply.
Ok, I'll fix this and resend.
Best regards
Luca


Reply via email to