mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-10 15:14:43 +00:00
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>
This commit is contained in:
parent
cb5fe9e336
commit
1ac30d1301
3 changed files with 299 additions and 0 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
290
drivers/adc/imx93-adc.c
Normal file
290
drivers/adc/imx93-adc.c
Normal file
|
@ -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),
|
||||
};
|
Loading…
Reference in a new issue