[PATCH 1/3] dm: adc: add iMX93 ADC support
Luca Ellero
luca.ellero at brickedbrain.com
Tue Mar 14 11:26:29 CET 2023
Il 14/03/2023 04:03, Bough Chen ha scritto:
>> -----Original Message-----
>> From: Luca Ellero <l.ellero at asem.it>
>> Sent: 2023年3月13日 21:06
>> To: u-boot at lists.denx.de; sbabic at denx.de; festevam at gmail.com; dl-uboot-imx
>> <uboot-imx at nxp.com>; luca.ellero at brickedbrain.com; Ye Li <ye.li at nxp.com>;
>> Peng Fan <peng.fan at nxp.com>; Bough Chen <haibo.chen at nxp.com>
>> Cc: Luca Ellero <l.ellero at asem.it>
>> 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 <l.ellero at 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 at 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);
>> +
>
> 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
More information about the U-Boot
mailing list