162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * NXP i.MX8QXP ADC driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Based on the work of Haibo Chen <haibo.chen@nxp.com> 662306a36Sopenharmony_ci * The initial developer of the original code is Haibo Chen. 762306a36Sopenharmony_ci * Portions created by Haibo Chen are Copyright (C) 2018 NXP. 862306a36Sopenharmony_ci * All Rights Reserved. 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Copyright (C) 2018 NXP 1162306a36Sopenharmony_ci * Copyright (C) 2021 Cai Huoqing 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci#include <linux/bitfield.h> 1462306a36Sopenharmony_ci#include <linux/bits.h> 1562306a36Sopenharmony_ci#include <linux/clk.h> 1662306a36Sopenharmony_ci#include <linux/completion.h> 1762306a36Sopenharmony_ci#include <linux/delay.h> 1862306a36Sopenharmony_ci#include <linux/err.h> 1962306a36Sopenharmony_ci#include <linux/interrupt.h> 2062306a36Sopenharmony_ci#include <linux/io.h> 2162306a36Sopenharmony_ci#include <linux/kernel.h> 2262306a36Sopenharmony_ci#include <linux/mod_devicetable.h> 2362306a36Sopenharmony_ci#include <linux/module.h> 2462306a36Sopenharmony_ci#include <linux/platform_device.h> 2562306a36Sopenharmony_ci#include <linux/pm_runtime.h> 2662306a36Sopenharmony_ci#include <linux/regulator/consumer.h> 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#include <linux/iio/iio.h> 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#define ADC_DRIVER_NAME "imx8qxp-adc" 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci/* Register map definition */ 3362306a36Sopenharmony_ci#define IMX8QXP_ADR_ADC_CTRL 0x10 3462306a36Sopenharmony_ci#define IMX8QXP_ADR_ADC_STAT 0x14 3562306a36Sopenharmony_ci#define IMX8QXP_ADR_ADC_IE 0x18 3662306a36Sopenharmony_ci#define IMX8QXP_ADR_ADC_DE 0x1c 3762306a36Sopenharmony_ci#define IMX8QXP_ADR_ADC_CFG 0x20 3862306a36Sopenharmony_ci#define IMX8QXP_ADR_ADC_FCTRL 0x30 3962306a36Sopenharmony_ci#define IMX8QXP_ADR_ADC_SWTRIG 0x34 4062306a36Sopenharmony_ci#define IMX8QXP_ADR_ADC_TCTRL(tid) (0xc0 + (tid) * 4) 4162306a36Sopenharmony_ci#define IMX8QXP_ADR_ADC_CMDL(cid) (0x100 + (cid) * 8) 4262306a36Sopenharmony_ci#define IMX8QXP_ADR_ADC_CMDH(cid) (0x104 + (cid) * 8) 4362306a36Sopenharmony_ci#define IMX8QXP_ADR_ADC_RESFIFO 0x300 4462306a36Sopenharmony_ci#define IMX8QXP_ADR_ADC_TST 0xffc 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci/* ADC bit shift */ 4762306a36Sopenharmony_ci#define IMX8QXP_ADC_IE_FWMIE_MASK GENMASK(1, 0) 4862306a36Sopenharmony_ci#define IMX8QXP_ADC_CTRL_FIFO_RESET_MASK BIT(8) 4962306a36Sopenharmony_ci#define IMX8QXP_ADC_CTRL_SOFTWARE_RESET_MASK BIT(1) 5062306a36Sopenharmony_ci#define IMX8QXP_ADC_CTRL_ADC_EN_MASK BIT(0) 5162306a36Sopenharmony_ci#define IMX8QXP_ADC_TCTRL_TCMD_MASK GENMASK(31, 24) 5262306a36Sopenharmony_ci#define IMX8QXP_ADC_TCTRL_TDLY_MASK GENMASK(23, 16) 5362306a36Sopenharmony_ci#define IMX8QXP_ADC_TCTRL_TPRI_MASK GENMASK(15, 8) 5462306a36Sopenharmony_ci#define IMX8QXP_ADC_TCTRL_HTEN_MASK GENMASK(7, 0) 5562306a36Sopenharmony_ci#define IMX8QXP_ADC_CMDL_CSCALE_MASK GENMASK(13, 8) 5662306a36Sopenharmony_ci#define IMX8QXP_ADC_CMDL_MODE_MASK BIT(7) 5762306a36Sopenharmony_ci#define IMX8QXP_ADC_CMDL_DIFF_MASK BIT(6) 5862306a36Sopenharmony_ci#define IMX8QXP_ADC_CMDL_ABSEL_MASK BIT(5) 5962306a36Sopenharmony_ci#define IMX8QXP_ADC_CMDL_ADCH_MASK GENMASK(2, 0) 6062306a36Sopenharmony_ci#define IMX8QXP_ADC_CMDH_NEXT_MASK GENMASK(31, 24) 6162306a36Sopenharmony_ci#define IMX8QXP_ADC_CMDH_LOOP_MASK GENMASK(23, 16) 6262306a36Sopenharmony_ci#define IMX8QXP_ADC_CMDH_AVGS_MASK GENMASK(15, 12) 6362306a36Sopenharmony_ci#define IMX8QXP_ADC_CMDH_STS_MASK BIT(8) 6462306a36Sopenharmony_ci#define IMX8QXP_ADC_CMDH_LWI_MASK GENMASK(7, 7) 6562306a36Sopenharmony_ci#define IMX8QXP_ADC_CMDH_CMPEN_MASK GENMASK(0, 0) 6662306a36Sopenharmony_ci#define IMX8QXP_ADC_CFG_PWREN_MASK BIT(28) 6762306a36Sopenharmony_ci#define IMX8QXP_ADC_CFG_PUDLY_MASK GENMASK(23, 16) 6862306a36Sopenharmony_ci#define IMX8QXP_ADC_CFG_REFSEL_MASK GENMASK(7, 6) 6962306a36Sopenharmony_ci#define IMX8QXP_ADC_CFG_PWRSEL_MASK GENMASK(5, 4) 7062306a36Sopenharmony_ci#define IMX8QXP_ADC_CFG_TPRICTRL_MASK GENMASK(3, 0) 7162306a36Sopenharmony_ci#define IMX8QXP_ADC_FCTRL_FWMARK_MASK GENMASK(20, 16) 7262306a36Sopenharmony_ci#define IMX8QXP_ADC_FCTRL_FCOUNT_MASK GENMASK(4, 0) 7362306a36Sopenharmony_ci#define IMX8QXP_ADC_RESFIFO_VAL_MASK GENMASK(18, 3) 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci/* ADC PARAMETER*/ 7662306a36Sopenharmony_ci#define IMX8QXP_ADC_CMDL_CHANNEL_SCALE_FULL GENMASK(5, 0) 7762306a36Sopenharmony_ci#define IMX8QXP_ADC_CMDL_SEL_A_A_B_CHANNEL 0 7862306a36Sopenharmony_ci#define IMX8QXP_ADC_CMDL_STANDARD_RESOLUTION 0 7962306a36Sopenharmony_ci#define IMX8QXP_ADC_CMDL_MODE_SINGLE 0 8062306a36Sopenharmony_ci#define IMX8QXP_ADC_CMDH_LWI_INCREMENT_DIS 0 8162306a36Sopenharmony_ci#define IMX8QXP_ADC_CMDH_CMPEN_DIS 0 8262306a36Sopenharmony_ci#define IMX8QXP_ADC_PAUSE_EN BIT(31) 8362306a36Sopenharmony_ci#define IMX8QXP_ADC_TCTRL_TPRI_PRIORITY_HIGH 0 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci#define IMX8QXP_ADC_TCTRL_HTEN_HW_TIRG_DIS 0 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci#define IMX8QXP_ADC_TIMEOUT msecs_to_jiffies(100) 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci#define IMX8QXP_ADC_MAX_FIFO_SIZE 16 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistruct imx8qxp_adc { 9262306a36Sopenharmony_ci struct device *dev; 9362306a36Sopenharmony_ci void __iomem *regs; 9462306a36Sopenharmony_ci struct clk *clk; 9562306a36Sopenharmony_ci struct clk *ipg_clk; 9662306a36Sopenharmony_ci struct regulator *vref; 9762306a36Sopenharmony_ci /* Serialise ADC channel reads */ 9862306a36Sopenharmony_ci struct mutex lock; 9962306a36Sopenharmony_ci struct completion completion; 10062306a36Sopenharmony_ci u32 fifo[IMX8QXP_ADC_MAX_FIFO_SIZE]; 10162306a36Sopenharmony_ci}; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci#define IMX8QXP_ADC_CHAN(_idx) { \ 10462306a36Sopenharmony_ci .type = IIO_VOLTAGE, \ 10562306a36Sopenharmony_ci .indexed = 1, \ 10662306a36Sopenharmony_ci .channel = (_idx), \ 10762306a36Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ 10862306a36Sopenharmony_ci .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ 10962306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_SAMP_FREQ), \ 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cistatic const struct iio_chan_spec imx8qxp_adc_iio_channels[] = { 11362306a36Sopenharmony_ci IMX8QXP_ADC_CHAN(0), 11462306a36Sopenharmony_ci IMX8QXP_ADC_CHAN(1), 11562306a36Sopenharmony_ci IMX8QXP_ADC_CHAN(2), 11662306a36Sopenharmony_ci IMX8QXP_ADC_CHAN(3), 11762306a36Sopenharmony_ci IMX8QXP_ADC_CHAN(4), 11862306a36Sopenharmony_ci IMX8QXP_ADC_CHAN(5), 11962306a36Sopenharmony_ci IMX8QXP_ADC_CHAN(6), 12062306a36Sopenharmony_ci IMX8QXP_ADC_CHAN(7), 12162306a36Sopenharmony_ci}; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistatic void imx8qxp_adc_reset(struct imx8qxp_adc *adc) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci u32 ctrl; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci /*software reset, need to clear the set bit*/ 12862306a36Sopenharmony_ci ctrl = readl(adc->regs + IMX8QXP_ADR_ADC_CTRL); 12962306a36Sopenharmony_ci ctrl |= FIELD_PREP(IMX8QXP_ADC_CTRL_SOFTWARE_RESET_MASK, 1); 13062306a36Sopenharmony_ci writel(ctrl, adc->regs + IMX8QXP_ADR_ADC_CTRL); 13162306a36Sopenharmony_ci udelay(10); 13262306a36Sopenharmony_ci ctrl &= ~FIELD_PREP(IMX8QXP_ADC_CTRL_SOFTWARE_RESET_MASK, 1); 13362306a36Sopenharmony_ci writel(ctrl, adc->regs + IMX8QXP_ADR_ADC_CTRL); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci /* reset the fifo */ 13662306a36Sopenharmony_ci ctrl |= FIELD_PREP(IMX8QXP_ADC_CTRL_FIFO_RESET_MASK, 1); 13762306a36Sopenharmony_ci writel(ctrl, adc->regs + IMX8QXP_ADR_ADC_CTRL); 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistatic void imx8qxp_adc_reg_config(struct imx8qxp_adc *adc, int channel) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci u32 adc_cfg, adc_tctrl, adc_cmdl, adc_cmdh; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci /* ADC configuration */ 14562306a36Sopenharmony_ci adc_cfg = FIELD_PREP(IMX8QXP_ADC_CFG_PWREN_MASK, 1) | 14662306a36Sopenharmony_ci FIELD_PREP(IMX8QXP_ADC_CFG_PUDLY_MASK, 0x80)| 14762306a36Sopenharmony_ci FIELD_PREP(IMX8QXP_ADC_CFG_REFSEL_MASK, 0) | 14862306a36Sopenharmony_ci FIELD_PREP(IMX8QXP_ADC_CFG_PWRSEL_MASK, 3) | 14962306a36Sopenharmony_ci FIELD_PREP(IMX8QXP_ADC_CFG_TPRICTRL_MASK, 0); 15062306a36Sopenharmony_ci writel(adc_cfg, adc->regs + IMX8QXP_ADR_ADC_CFG); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci /* config the trigger control */ 15362306a36Sopenharmony_ci adc_tctrl = FIELD_PREP(IMX8QXP_ADC_TCTRL_TCMD_MASK, 1) | 15462306a36Sopenharmony_ci FIELD_PREP(IMX8QXP_ADC_TCTRL_TDLY_MASK, 0) | 15562306a36Sopenharmony_ci FIELD_PREP(IMX8QXP_ADC_TCTRL_TPRI_MASK, IMX8QXP_ADC_TCTRL_TPRI_PRIORITY_HIGH) | 15662306a36Sopenharmony_ci FIELD_PREP(IMX8QXP_ADC_TCTRL_HTEN_MASK, IMX8QXP_ADC_TCTRL_HTEN_HW_TIRG_DIS); 15762306a36Sopenharmony_ci writel(adc_tctrl, adc->regs + IMX8QXP_ADR_ADC_TCTRL(0)); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci /* config the cmd */ 16062306a36Sopenharmony_ci adc_cmdl = FIELD_PREP(IMX8QXP_ADC_CMDL_CSCALE_MASK, IMX8QXP_ADC_CMDL_CHANNEL_SCALE_FULL) | 16162306a36Sopenharmony_ci FIELD_PREP(IMX8QXP_ADC_CMDL_MODE_MASK, IMX8QXP_ADC_CMDL_STANDARD_RESOLUTION) | 16262306a36Sopenharmony_ci FIELD_PREP(IMX8QXP_ADC_CMDL_DIFF_MASK, IMX8QXP_ADC_CMDL_MODE_SINGLE) | 16362306a36Sopenharmony_ci FIELD_PREP(IMX8QXP_ADC_CMDL_ABSEL_MASK, IMX8QXP_ADC_CMDL_SEL_A_A_B_CHANNEL) | 16462306a36Sopenharmony_ci FIELD_PREP(IMX8QXP_ADC_CMDL_ADCH_MASK, channel); 16562306a36Sopenharmony_ci writel(adc_cmdl, adc->regs + IMX8QXP_ADR_ADC_CMDL(0)); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci adc_cmdh = FIELD_PREP(IMX8QXP_ADC_CMDH_NEXT_MASK, 0) | 16862306a36Sopenharmony_ci FIELD_PREP(IMX8QXP_ADC_CMDH_LOOP_MASK, 0) | 16962306a36Sopenharmony_ci FIELD_PREP(IMX8QXP_ADC_CMDH_AVGS_MASK, 7) | 17062306a36Sopenharmony_ci FIELD_PREP(IMX8QXP_ADC_CMDH_STS_MASK, 0) | 17162306a36Sopenharmony_ci FIELD_PREP(IMX8QXP_ADC_CMDH_LWI_MASK, IMX8QXP_ADC_CMDH_LWI_INCREMENT_DIS) | 17262306a36Sopenharmony_ci FIELD_PREP(IMX8QXP_ADC_CMDH_CMPEN_MASK, IMX8QXP_ADC_CMDH_CMPEN_DIS); 17362306a36Sopenharmony_ci writel(adc_cmdh, adc->regs + IMX8QXP_ADR_ADC_CMDH(0)); 17462306a36Sopenharmony_ci} 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cistatic void imx8qxp_adc_fifo_config(struct imx8qxp_adc *adc) 17762306a36Sopenharmony_ci{ 17862306a36Sopenharmony_ci u32 fifo_ctrl, interrupt_en; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci fifo_ctrl = readl(adc->regs + IMX8QXP_ADR_ADC_FCTRL); 18162306a36Sopenharmony_ci fifo_ctrl &= ~IMX8QXP_ADC_FCTRL_FWMARK_MASK; 18262306a36Sopenharmony_ci /* set the watermark level to 1 */ 18362306a36Sopenharmony_ci fifo_ctrl |= FIELD_PREP(IMX8QXP_ADC_FCTRL_FWMARK_MASK, 0); 18462306a36Sopenharmony_ci writel(fifo_ctrl, adc->regs + IMX8QXP_ADR_ADC_FCTRL); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci /* FIFO Watermark Interrupt Enable */ 18762306a36Sopenharmony_ci interrupt_en = readl(adc->regs + IMX8QXP_ADR_ADC_IE); 18862306a36Sopenharmony_ci interrupt_en |= FIELD_PREP(IMX8QXP_ADC_IE_FWMIE_MASK, 1); 18962306a36Sopenharmony_ci writel(interrupt_en, adc->regs + IMX8QXP_ADR_ADC_IE); 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cistatic void imx8qxp_adc_disable(struct imx8qxp_adc *adc) 19362306a36Sopenharmony_ci{ 19462306a36Sopenharmony_ci u32 ctrl; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci ctrl = readl(adc->regs + IMX8QXP_ADR_ADC_CTRL); 19762306a36Sopenharmony_ci ctrl &= ~FIELD_PREP(IMX8QXP_ADC_CTRL_ADC_EN_MASK, 1); 19862306a36Sopenharmony_ci writel(ctrl, adc->regs + IMX8QXP_ADR_ADC_CTRL); 19962306a36Sopenharmony_ci} 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_cistatic int imx8qxp_adc_read_raw(struct iio_dev *indio_dev, 20262306a36Sopenharmony_ci struct iio_chan_spec const *chan, 20362306a36Sopenharmony_ci int *val, int *val2, long mask) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci struct imx8qxp_adc *adc = iio_priv(indio_dev); 20662306a36Sopenharmony_ci struct device *dev = adc->dev; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci u32 ctrl; 20962306a36Sopenharmony_ci long ret; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci switch (mask) { 21262306a36Sopenharmony_ci case IIO_CHAN_INFO_RAW: 21362306a36Sopenharmony_ci pm_runtime_get_sync(dev); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci mutex_lock(&adc->lock); 21662306a36Sopenharmony_ci reinit_completion(&adc->completion); 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci imx8qxp_adc_reg_config(adc, chan->channel); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci imx8qxp_adc_fifo_config(adc); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci /* adc enable */ 22362306a36Sopenharmony_ci ctrl = readl(adc->regs + IMX8QXP_ADR_ADC_CTRL); 22462306a36Sopenharmony_ci ctrl |= FIELD_PREP(IMX8QXP_ADC_CTRL_ADC_EN_MASK, 1); 22562306a36Sopenharmony_ci writel(ctrl, adc->regs + IMX8QXP_ADR_ADC_CTRL); 22662306a36Sopenharmony_ci /* adc start */ 22762306a36Sopenharmony_ci writel(1, adc->regs + IMX8QXP_ADR_ADC_SWTRIG); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci ret = wait_for_completion_interruptible_timeout(&adc->completion, 23062306a36Sopenharmony_ci IMX8QXP_ADC_TIMEOUT); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci pm_runtime_mark_last_busy(dev); 23362306a36Sopenharmony_ci pm_runtime_put_sync_autosuspend(dev); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci if (ret == 0) { 23662306a36Sopenharmony_ci mutex_unlock(&adc->lock); 23762306a36Sopenharmony_ci return -ETIMEDOUT; 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci if (ret < 0) { 24062306a36Sopenharmony_ci mutex_unlock(&adc->lock); 24162306a36Sopenharmony_ci return ret; 24262306a36Sopenharmony_ci } 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci *val = adc->fifo[0]; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci mutex_unlock(&adc->lock); 24762306a36Sopenharmony_ci return IIO_VAL_INT; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci case IIO_CHAN_INFO_SCALE: 25062306a36Sopenharmony_ci ret = regulator_get_voltage(adc->vref); 25162306a36Sopenharmony_ci if (ret < 0) 25262306a36Sopenharmony_ci return ret; 25362306a36Sopenharmony_ci *val = ret / 1000; 25462306a36Sopenharmony_ci *val2 = 12; 25562306a36Sopenharmony_ci return IIO_VAL_FRACTIONAL_LOG2; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci case IIO_CHAN_INFO_SAMP_FREQ: 25862306a36Sopenharmony_ci *val = clk_get_rate(adc->clk) / 3; 25962306a36Sopenharmony_ci return IIO_VAL_INT; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci default: 26262306a36Sopenharmony_ci return -EINVAL; 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci} 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_cistatic irqreturn_t imx8qxp_adc_isr(int irq, void *dev_id) 26762306a36Sopenharmony_ci{ 26862306a36Sopenharmony_ci struct imx8qxp_adc *adc = dev_id; 26962306a36Sopenharmony_ci u32 fifo_count; 27062306a36Sopenharmony_ci int i; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci fifo_count = FIELD_GET(IMX8QXP_ADC_FCTRL_FCOUNT_MASK, 27362306a36Sopenharmony_ci readl(adc->regs + IMX8QXP_ADR_ADC_FCTRL)); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci for (i = 0; i < fifo_count; i++) 27662306a36Sopenharmony_ci adc->fifo[i] = FIELD_GET(IMX8QXP_ADC_RESFIFO_VAL_MASK, 27762306a36Sopenharmony_ci readl_relaxed(adc->regs + IMX8QXP_ADR_ADC_RESFIFO)); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci if (fifo_count) 28062306a36Sopenharmony_ci complete(&adc->completion); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci return IRQ_HANDLED; 28362306a36Sopenharmony_ci} 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_cistatic int imx8qxp_adc_reg_access(struct iio_dev *indio_dev, unsigned int reg, 28662306a36Sopenharmony_ci unsigned int writeval, unsigned int *readval) 28762306a36Sopenharmony_ci{ 28862306a36Sopenharmony_ci struct imx8qxp_adc *adc = iio_priv(indio_dev); 28962306a36Sopenharmony_ci struct device *dev = adc->dev; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci if (!readval || reg % 4 || reg > IMX8QXP_ADR_ADC_TST) 29262306a36Sopenharmony_ci return -EINVAL; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci pm_runtime_get_sync(dev); 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci *readval = readl(adc->regs + reg); 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci pm_runtime_mark_last_busy(dev); 29962306a36Sopenharmony_ci pm_runtime_put_sync_autosuspend(dev); 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci return 0; 30262306a36Sopenharmony_ci} 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_cistatic const struct iio_info imx8qxp_adc_iio_info = { 30562306a36Sopenharmony_ci .read_raw = &imx8qxp_adc_read_raw, 30662306a36Sopenharmony_ci .debugfs_reg_access = &imx8qxp_adc_reg_access, 30762306a36Sopenharmony_ci}; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_cistatic int imx8qxp_adc_probe(struct platform_device *pdev) 31062306a36Sopenharmony_ci{ 31162306a36Sopenharmony_ci struct imx8qxp_adc *adc; 31262306a36Sopenharmony_ci struct iio_dev *indio_dev; 31362306a36Sopenharmony_ci struct device *dev = &pdev->dev; 31462306a36Sopenharmony_ci int irq; 31562306a36Sopenharmony_ci int ret; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci indio_dev = devm_iio_device_alloc(dev, sizeof(*adc)); 31862306a36Sopenharmony_ci if (!indio_dev) { 31962306a36Sopenharmony_ci dev_err(dev, "Failed allocating iio device\n"); 32062306a36Sopenharmony_ci return -ENOMEM; 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci adc = iio_priv(indio_dev); 32462306a36Sopenharmony_ci adc->dev = dev; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci mutex_init(&adc->lock); 32762306a36Sopenharmony_ci adc->regs = devm_platform_ioremap_resource(pdev, 0); 32862306a36Sopenharmony_ci if (IS_ERR(adc->regs)) 32962306a36Sopenharmony_ci return PTR_ERR(adc->regs); 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci irq = platform_get_irq(pdev, 0); 33262306a36Sopenharmony_ci if (irq < 0) 33362306a36Sopenharmony_ci return irq; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci adc->clk = devm_clk_get(dev, "per"); 33662306a36Sopenharmony_ci if (IS_ERR(adc->clk)) 33762306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(adc->clk), "Failed getting clock\n"); 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci adc->ipg_clk = devm_clk_get(dev, "ipg"); 34062306a36Sopenharmony_ci if (IS_ERR(adc->ipg_clk)) 34162306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(adc->ipg_clk), "Failed getting clock\n"); 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci adc->vref = devm_regulator_get(dev, "vref"); 34462306a36Sopenharmony_ci if (IS_ERR(adc->vref)) 34562306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(adc->vref), "Failed getting reference voltage\n"); 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci ret = regulator_enable(adc->vref); 34862306a36Sopenharmony_ci if (ret) { 34962306a36Sopenharmony_ci dev_err(dev, "Can't enable adc reference top voltage\n"); 35062306a36Sopenharmony_ci return ret; 35162306a36Sopenharmony_ci } 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci platform_set_drvdata(pdev, indio_dev); 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci init_completion(&adc->completion); 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci indio_dev->name = ADC_DRIVER_NAME; 35862306a36Sopenharmony_ci indio_dev->info = &imx8qxp_adc_iio_info; 35962306a36Sopenharmony_ci indio_dev->modes = INDIO_DIRECT_MODE; 36062306a36Sopenharmony_ci indio_dev->channels = imx8qxp_adc_iio_channels; 36162306a36Sopenharmony_ci indio_dev->num_channels = ARRAY_SIZE(imx8qxp_adc_iio_channels); 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci ret = clk_prepare_enable(adc->clk); 36462306a36Sopenharmony_ci if (ret) { 36562306a36Sopenharmony_ci dev_err(&pdev->dev, "Could not prepare or enable the clock.\n"); 36662306a36Sopenharmony_ci goto error_regulator_disable; 36762306a36Sopenharmony_ci } 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci ret = clk_prepare_enable(adc->ipg_clk); 37062306a36Sopenharmony_ci if (ret) { 37162306a36Sopenharmony_ci dev_err(&pdev->dev, "Could not prepare or enable the clock.\n"); 37262306a36Sopenharmony_ci goto error_adc_clk_disable; 37362306a36Sopenharmony_ci } 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci ret = devm_request_irq(dev, irq, imx8qxp_adc_isr, 0, ADC_DRIVER_NAME, adc); 37662306a36Sopenharmony_ci if (ret < 0) { 37762306a36Sopenharmony_ci dev_err(dev, "Failed requesting irq, irq = %d\n", irq); 37862306a36Sopenharmony_ci goto error_ipg_clk_disable; 37962306a36Sopenharmony_ci } 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci imx8qxp_adc_reset(adc); 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci ret = iio_device_register(indio_dev); 38462306a36Sopenharmony_ci if (ret) { 38562306a36Sopenharmony_ci imx8qxp_adc_disable(adc); 38662306a36Sopenharmony_ci dev_err(dev, "Couldn't register the device.\n"); 38762306a36Sopenharmony_ci goto error_ipg_clk_disable; 38862306a36Sopenharmony_ci } 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci pm_runtime_set_active(dev); 39162306a36Sopenharmony_ci pm_runtime_set_autosuspend_delay(dev, 50); 39262306a36Sopenharmony_ci pm_runtime_use_autosuspend(dev); 39362306a36Sopenharmony_ci pm_runtime_enable(dev); 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci return 0; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_cierror_ipg_clk_disable: 39862306a36Sopenharmony_ci clk_disable_unprepare(adc->ipg_clk); 39962306a36Sopenharmony_cierror_adc_clk_disable: 40062306a36Sopenharmony_ci clk_disable_unprepare(adc->clk); 40162306a36Sopenharmony_cierror_regulator_disable: 40262306a36Sopenharmony_ci regulator_disable(adc->vref); 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci return ret; 40562306a36Sopenharmony_ci} 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_cistatic int imx8qxp_adc_remove(struct platform_device *pdev) 40862306a36Sopenharmony_ci{ 40962306a36Sopenharmony_ci struct iio_dev *indio_dev = platform_get_drvdata(pdev); 41062306a36Sopenharmony_ci struct imx8qxp_adc *adc = iio_priv(indio_dev); 41162306a36Sopenharmony_ci struct device *dev = adc->dev; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci pm_runtime_get_sync(dev); 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci iio_device_unregister(indio_dev); 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci imx8qxp_adc_disable(adc); 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci clk_disable_unprepare(adc->clk); 42062306a36Sopenharmony_ci clk_disable_unprepare(adc->ipg_clk); 42162306a36Sopenharmony_ci regulator_disable(adc->vref); 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci pm_runtime_disable(dev); 42462306a36Sopenharmony_ci pm_runtime_put_noidle(dev); 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci return 0; 42762306a36Sopenharmony_ci} 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_cistatic int imx8qxp_adc_runtime_suspend(struct device *dev) 43062306a36Sopenharmony_ci{ 43162306a36Sopenharmony_ci struct iio_dev *indio_dev = dev_get_drvdata(dev); 43262306a36Sopenharmony_ci struct imx8qxp_adc *adc = iio_priv(indio_dev); 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci imx8qxp_adc_disable(adc); 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci clk_disable_unprepare(adc->clk); 43762306a36Sopenharmony_ci clk_disable_unprepare(adc->ipg_clk); 43862306a36Sopenharmony_ci regulator_disable(adc->vref); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci return 0; 44162306a36Sopenharmony_ci} 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_cistatic int imx8qxp_adc_runtime_resume(struct device *dev) 44462306a36Sopenharmony_ci{ 44562306a36Sopenharmony_ci struct iio_dev *indio_dev = dev_get_drvdata(dev); 44662306a36Sopenharmony_ci struct imx8qxp_adc *adc = iio_priv(indio_dev); 44762306a36Sopenharmony_ci int ret; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci ret = regulator_enable(adc->vref); 45062306a36Sopenharmony_ci if (ret) { 45162306a36Sopenharmony_ci dev_err(dev, "Can't enable adc reference top voltage, err = %d\n", ret); 45262306a36Sopenharmony_ci return ret; 45362306a36Sopenharmony_ci } 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci ret = clk_prepare_enable(adc->clk); 45662306a36Sopenharmony_ci if (ret) { 45762306a36Sopenharmony_ci dev_err(dev, "Could not prepare or enable clock.\n"); 45862306a36Sopenharmony_ci goto err_disable_reg; 45962306a36Sopenharmony_ci } 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci ret = clk_prepare_enable(adc->ipg_clk); 46262306a36Sopenharmony_ci if (ret) { 46362306a36Sopenharmony_ci dev_err(dev, "Could not prepare or enable clock.\n"); 46462306a36Sopenharmony_ci goto err_unprepare_clk; 46562306a36Sopenharmony_ci } 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci imx8qxp_adc_reset(adc); 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci return 0; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_cierr_unprepare_clk: 47262306a36Sopenharmony_ci clk_disable_unprepare(adc->clk); 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_cierr_disable_reg: 47562306a36Sopenharmony_ci regulator_disable(adc->vref); 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci return ret; 47862306a36Sopenharmony_ci} 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_cistatic DEFINE_RUNTIME_DEV_PM_OPS(imx8qxp_adc_pm_ops, 48162306a36Sopenharmony_ci imx8qxp_adc_runtime_suspend, 48262306a36Sopenharmony_ci imx8qxp_adc_runtime_resume, NULL); 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_cistatic const struct of_device_id imx8qxp_adc_match[] = { 48562306a36Sopenharmony_ci { .compatible = "nxp,imx8qxp-adc", }, 48662306a36Sopenharmony_ci { /* sentinel */ } 48762306a36Sopenharmony_ci}; 48862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, imx8qxp_adc_match); 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_cistatic struct platform_driver imx8qxp_adc_driver = { 49162306a36Sopenharmony_ci .probe = imx8qxp_adc_probe, 49262306a36Sopenharmony_ci .remove = imx8qxp_adc_remove, 49362306a36Sopenharmony_ci .driver = { 49462306a36Sopenharmony_ci .name = ADC_DRIVER_NAME, 49562306a36Sopenharmony_ci .of_match_table = imx8qxp_adc_match, 49662306a36Sopenharmony_ci .pm = pm_ptr(&imx8qxp_adc_pm_ops), 49762306a36Sopenharmony_ci }, 49862306a36Sopenharmony_ci}; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_cimodule_platform_driver(imx8qxp_adc_driver); 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ciMODULE_DESCRIPTION("i.MX8QuadXPlus ADC driver"); 50362306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 504