• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1commit 7ed7d34dfe4edf3ac928ed431e30371ee7e3b22f
2Author: zhaoxc0502 <zhaoxc0502@thundersoft.com>
3Date:   Thu Jun 16 17:21:08 2022 +0800
4
5    linux_drivers_iio
6
7    Change-Id: I60767bd38b95d43a7415b9e21575ad772380ae56
8
9diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
10index e39b67912..3c68e5bdc 100644
11--- a/drivers/iio/adc/Kconfig
12+++ b/drivers/iio/adc/Kconfig
13@@ -530,6 +530,16 @@ config IMX7D_ADC
14 	  This driver can also be built as a module. If so, the module will be
15 	  called imx7d_adc.
16
17+config IMX8QXP_ADC
18+	tristate "IMX8QXP ADC driver"
19+	depends on ARCH_MXC || COMPILE_TEST
20+	depends on HAS_IOMEM
21+	help
22+	  Say yes here to build support for IMX8QXP ADC.
23+
24+	  This driver can also be built as a module. If so, the module will be
25+	  called imx8qxp_adc.
26+
27 config LP8788_ADC
28 	tristate "LP8788 ADC driver"
29 	depends on MFD_LP8788
30diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
31index 90f94ada7..0a9709c8c 100644
32--- a/drivers/iio/adc/Makefile
33+++ b/drivers/iio/adc/Makefile
34@@ -46,6 +46,7 @@ obj-$(CONFIG_FSL_MX25_ADC) += fsl-imx25-gcq.o
35 obj-$(CONFIG_HI8435) += hi8435.o
36 obj-$(CONFIG_HX711) += hx711.o
37 obj-$(CONFIG_IMX7D_ADC) += imx7d_adc.o
38+obj-$(CONFIG_IMX8QXP_ADC) += imx8qxp_adc.o
39 obj-$(CONFIG_INA2XX_ADC) += ina2xx-adc.o
40 obj-$(CONFIG_INGENIC_ADC) += ingenic-adc.o
41 obj-$(CONFIG_INTEL_MRFLD_ADC) += intel_mrfld_adc.o
42diff --git a/drivers/iio/adc/imx8qxp_adc.c b/drivers/iio/adc/imx8qxp_adc.c
43new file mode 100644
44index 000000000..a6a9c42d0
45--- /dev/null
46+++ b/drivers/iio/adc/imx8qxp_adc.c
47@@ -0,0 +1,723 @@
48+/*
49+ * NXP i.MX8QXP ADC driver
50+ *
51+ * Copyright (C) 2018 NXP
52+ *
53+ * This program is free software; you can redistribute it and/or modify
54+ * it under the terms of the GNU General Public License as published by
55+ * the Free Software Foundation; either version 2 of the License, or
56+ * (at your option) any later version.
57+ */
58+
59+#include <linux/clk.h>
60+#include <linux/completion.h>
61+#include <linux/err.h>
62+#include <linux/interrupt.h>
63+#include <linux/io.h>
64+#include <linux/kernel.h>
65+#include <linux/delay.h>
66+#include <linux/module.h>
67+#include <linux/pm_runtime.h>
68+#include <linux/platform_device.h>
69+#include <linux/regulator/consumer.h>
70+
71+#include <linux/iio/iio.h>
72+#include <linux/iio/driver.h>
73+#include <linux/iio/sysfs.h>
74+
75+/* ADC register */
76+#define IMX8QXP_REG_ADC_VERID		0x00
77+#define IMX8QXP_REG_ADC_PARAM		0x04
78+#define IMX8QXP_REG_ADC_CTRL		0x10
79+#define IMX8QXP_REG_ADC_STAT		0x14
80+#define IMX8QXP_REG_ADC_IE		0x18
81+#define IMX8QXP_REG_ADC_DE		0x1c
82+#define IMX8QXP_REG_ADC_CFG		0x20
83+#define IMX8QXP_REG_ADC_PAUSE		0x24
84+#define IMX8QXP_REG_ADC_FCTRL		0x30
85+#define IMX8QXP_REG_ADC_SWTRIG		0x34
86+#define IMX8QXP_REG_ADC_TCTRL0		0xc0
87+#define IMX8QXP_REG_ADC_TCTRL1		0xc4
88+#define IMX8QXP_REG_ADC_TCTRL2		0xc8
89+#define IMX8QXP_REG_ADC_TCTRL3		0xcc
90+#define IMX8QXP_REG_ADC_TCTRL4		0xd0
91+#define IMX8QXP_REG_ADC_TCTRL5		0xd4
92+#define IMX8QXP_REG_ADC_TCTRL6		0xd8
93+#define IMX8QXP_REG_ADC_TCTRL7		0xdc
94+#define IMX8QXP_REG_ADC_CMDL1		0x100
95+#define IMX8QXP_REG_ADC_CMDH1		0x104
96+#define IMX8QXP_REG_ADC_CMDL2		0x108
97+#define IMX8QXP_REG_ADC_CMDH2		0x10c
98+#define IMX8QXP_REG_ADC_CMDL3		0x110
99+#define IMX8QXP_REG_ADC_CMDH3		0x114
100+#define IMX8QXP_REG_ADC_CMDL4		0x118
101+#define IMX8QXP_REG_ADC_CMDH4		0x11c
102+#define IMX8QXP_REG_ADC_CMDL5		0x120
103+#define IMX8QXP_REG_ADC_CMDH5		0x124
104+#define IMX8QXP_REG_ADC_CMDL6		0x128
105+#define IMX8QXP_REG_ADC_CMDH6		0x12c
106+#define IMX8QXP_REG_ADC_CMDL7		0x130
107+#define IMX8QXP_REG_ADC_CMDH7		0x134
108+#define IMX8QXP_REG_ADC_CMDL8		0x138
109+#define IMX8QXP_REG_ADC_CMDH8		0x13c
110+#define IMX8QXP_REG_ADC_CV1		0x200
111+#define IMX8QXP_REG_ADC_CV2		0x204
112+#define IMX8QXP_REG_ADC_CV3		0x208
113+#define IMX8QXP_REG_ADC_CV4		0x20c
114+#define IMX8QXP_REG_ADC_RESFIFO		0x300
115+#define IMX8QXP_REG_ADC_TST		0xffc
116+
117+/* ADC IE bit shift */
118+#define IMX8QXP_REG_ADC_IE_FOFIE_SHIFT		(1)
119+#define IMX8QXP_REG_ADC_IE_FWMIE_SHIFT		(0)
120+/* ADC CTRL bit shift*/
121+#define IMX8QXP_REG_ADC_CTRL_FIFO_RESET_SHIFT		(8)
122+#define IMX8QXP_REG_ADC_CTRL_SOFTWARE_RESET_SHIFT	(1)
123+#define IMX8QXP_REG_ADC_CTRL_ADC_ENABLE			(0)
124+/* ADC TCTRL bit shift*/
125+#define IMX8QXP_REG_ADC_TCTRL_TCMD_SHIFT	(24)
126+#define IMX8QXP_REG_ADC_TCTRL_TDLY_SHIFT	(16)
127+#define IMX8QXP_REG_ADC_TCTRL_TPRI_SHIFT	(8)
128+#define IMX8QXP_REG_ADC_TCTRL_HTEN_SHIFT	(0)
129+/* ADC CMDL bit shift*/
130+#define IMX8QXP_REG_ADC_CMDL_CSCALE_SHIFT	(8)
131+#define IMX8QXP_REG_ADC_CMDL_MODE_SHIFT		(7)
132+#define IMX8QXP_REG_ADC_CMDL_DIFF_SHIFT		(6)
133+#define IMX8QXP_REG_ADC_CMDL_ABSEL_SHIFT	(5)
134+#define IMX8QXP_REG_ADC_CMDL_ADCH_SHIFT		(0)
135+/* ADC CMDH bit shift*/
136+#define IMX8QXP_REG_ADC_CMDH_NEXT_SHIFT		(24)
137+#define IMX8QXP_REG_ADC_CMDH_LOOP_SHIFT		(16)
138+#define IMX8QXP_REG_ADC_CMDH_AVGS_SHIFT		(12)
139+#define IMX8QXP_REG_ADC_CMDH_STS_SHIFT		(8)
140+#define IMX8QXP_REG_ADC_CMDH_LWI_SHIFT		(7)
141+#define IMX8QXP_REG_ADC_CMDH_CMPEN_SHIFT	(0)
142+/* ADC CFG bit shift*/
143+#define IMX8QXP_REG_ADC_CFG_PWREN_SHIFT		(28)
144+#define IMX8QXP_REG_ADC_CFG_PUDLY_SHIFT		(16)
145+#define IMX8QXP_REG_ADC_CFG_REFSEL_SHIFT	(6)
146+#define IMX8QXP_REG_ADC_CFG_PWRSEL_SHIFT	(4)
147+#define IMX8QXP_REG_ADC_CFG_TPRICTRL_SHIFT	(0)
148+/* ADC FCTRL bit shift*/
149+#define IMX8QXP_ADC_FCTRL_FWMARK_SHIFT		(16)
150+#define IMX8QXP_ADC_FCTRL_FWMARK_MASK		(0x1f << 16)
151+#define IMX8QXP_ADC_FCTRL_FCOUNT_MASK		(0x1f)
152+/* ADC STAT bit shift*/
153+#define IMX8QXP_REG_ADC_STAT_CMDACT_SHIFT	(24)
154+#define IMX8QXP_REG_ADC_STAT_TRGACT_SHIFT	(16)
155+#define IMX8QXP_REG_ADC_STAT_FOF_SHIFT		(1)
156+#define IMX8QXP_REG_ADC_STAT_RDY_SHIFT		(0)
157+
158+/* ADC CMD PARAMETER*/
159+#define IMX8QXP_REG_ADC_CMDL_CNANNEL_SCALE_DOWN		(0)
160+#define IMX8QXP_REG_ADC_CMDL_CHANNEL_SCALE_FULL		(0x3f)
161+#define IMX8QXP_REG_ADC_CMDL_SEL_A_A_B_CHANNEL		(0)
162+#define IMX8QXP_REG_ADC_CMDL_SEL_B_B_A_CHANNEL		(1)
163+#define IMX8QXP_REG_ADC_CMDL_STANDARD_RESOLUTION	(0)
164+#define IMX8QXP_REG_ADC_CMDL_HIGH_RESOLUTION		(1)
165+#define IMX8QXP_REG_ADC_CMDL_MODE_SINGLE		(0)
166+#define IMX8QXP_REG_ADC_CMDL_MODE_DIFF			(1)
167+
168+#define IMX8QXP_REG_ADC_CMDH_LWI_INCREMENT_ENABLE	(1)
169+#define IMX8QXP_REG_ADC_CMDH_LWI_INCREMENT_DISABLE	(0)
170+#define IMX8QXP_REG_ADC_CMDH_CMPEN_DISABLE		(0)
171+#define IMX8QXP_REG_ADC_CMDH_CMPEN_ENABLE_TRUE		(2)
172+#define IMX8QXP_REG_ADC_CMDH_CMPEN_ENABLE_UNTILE_TRUE	(0x3)
173+
174+/* ADC PAUSE PARAMETER*/
175+#define IMX8QXP_REG_ADC_PAUSE_ENABLE		(0x80000000)
176+/* ADC TRIGGER PARAMETER*/
177+#define IMX8QXP_REG_ADC_TCTRL_TPRI_PRIORITY_HIGH	(0)
178+#define IMX8QXP_REG_ADC_TCTRL_TPRI_PRIORITY_LOW	(1)
179+
180+#define IMX8QXP_REG_ADC_TCTRL_HTEN_HARDWARE_TIRGGER_DISABLE	(0)
181+#define IMX8QXP_REG_ADC_TCTRC_HTEN_HARDWARE_TIRGGER_ENABLE	(1)
182+
183+#define MAX_CMD (8)
184+#define MAX_TRIG (8)
185+
186+#define IMX8QXP_ADC_TIMEOUT		msecs_to_jiffies(100)
187+
188+struct imx8qxp_adc_trigger_ctrl {
189+	u32 tcmd;
190+	u32 tdly;
191+	u32 tpri;
192+	u32 hten;
193+};
194+
195+struct imx8qxp_adc_cmd_l {
196+	u32 scale;
197+	u32 mode;
198+	u32 diff;
199+	u32 absel;
200+	u32 adch;
201+};
202+
203+struct imx8qxp_adc_cmd_h {
204+	u32 next;
205+	u32 loop;
206+	u32 avgs;
207+	u32 sts;
208+	u32 lwi;
209+	u32 cmpen;
210+};
211+
212+struct imx8qxp_adc_cfg {
213+	u32 pwren;
214+	u32 pudly;
215+	u32 refsel;
216+	u32 pwrsel;
217+	u32 tprictrl;
218+};
219+
220+struct imx8qxp_adc {
221+	struct device *dev;
222+	void __iomem *regs;
223+	struct clk *clk;
224+	struct clk *ipg_clk;
225+
226+	u32 vref_uv;
227+	u32 value;
228+	u32 channel_id;
229+	u32 trigger_id;
230+	u32 cmd_id;
231+
232+	struct regulator *vref;
233+	struct imx8qxp_adc_cmd_l adc_cmd_l[MAX_CMD + 1];
234+	struct imx8qxp_adc_cmd_h adc_cmd_h[MAX_CMD + 1];
235+	struct imx8qxp_adc_trigger_ctrl adc_trigger_ctrl[MAX_TRIG + 1];
236+	struct imx8qxp_adc_cfg adc_cfg;
237+	struct completion completion;
238+};
239+
240+#define IMX8QXP_ADC_CHAN(_idx) {					\
241+	.type = IIO_VOLTAGE,					\
242+	.indexed = 1,						\
243+	.channel = (_idx),					\
244+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),		\
245+	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) |	\
246+				BIT(IIO_CHAN_INFO_SAMP_FREQ),	\
247+}
248+
249+static const struct iio_chan_spec imx8qxp_adc_iio_channels[] = {
250+	IMX8QXP_ADC_CHAN(0),
251+	IMX8QXP_ADC_CHAN(1),
252+	IMX8QXP_ADC_CHAN(2),
253+	IMX8QXP_ADC_CHAN(3),
254+	IMX8QXP_ADC_CHAN(4),
255+	IMX8QXP_ADC_CHAN(5),
256+	IMX8QXP_ADC_CHAN(6),
257+	IMX8QXP_ADC_CHAN(7),
258+};
259+
260+static void imx8qxp_adc_feature_prepare(struct imx8qxp_adc *adc)
261+{
262+	u32 i;
263+
264+	adc->trigger_id = 0;
265+	adc->cmd_id = 0;
266+	adc->channel_id = 0;
267+
268+	for (i = 0; i < MAX_CMD + 1; i++) {
269+		adc->adc_cmd_l[i].scale = 1;
270+		adc->adc_cmd_l[i].mode = 0;
271+		adc->adc_cmd_l[i].diff = 0;
272+		adc->adc_cmd_l[i].absel = 0;
273+		adc->adc_cmd_l[i].adch = 0;
274+		adc->adc_cmd_h[i].next = 0;
275+		adc->adc_cmd_h[i].loop = 0;
276+		adc->adc_cmd_h[i].avgs = 0;
277+		adc->adc_cmd_h[i].sts = 0;
278+		adc->adc_cmd_h[i].lwi = 0;
279+		adc->adc_cmd_h[i].cmpen = 0;
280+	}
281+
282+	for (i = 0; i < MAX_TRIG; i++) {
283+		adc->adc_trigger_ctrl[i].tcmd = 0;
284+		adc->adc_trigger_ctrl[i].tdly = 0;
285+		adc->adc_trigger_ctrl[i].tpri = 0;
286+		adc->adc_trigger_ctrl[i].hten = 0;
287+	}
288+
289+	adc->adc_cfg.pwren = 0;
290+	adc->adc_cfg.pudly = 0;
291+	adc->adc_cfg.refsel = 0;
292+	adc->adc_cfg.pwrsel = 0;
293+	adc->adc_cfg.tprictrl = 0;
294+}
295+
296+static void imx8qxp_adc_reset(struct imx8qxp_adc *adc)
297+{
298+	u32 ctrl;
299+
300+	/*software reset, need to clear the set bit*/
301+	ctrl = readl(adc->regs + IMX8QXP_REG_ADC_CTRL);
302+	ctrl |= 1 << IMX8QXP_REG_ADC_CTRL_SOFTWARE_RESET_SHIFT;
303+	writel(ctrl, adc->regs + IMX8QXP_REG_ADC_CTRL);
304+	udelay(10);
305+	ctrl &= ~(1 << IMX8QXP_REG_ADC_CTRL_SOFTWARE_RESET_SHIFT);
306+	writel(ctrl, adc->regs + IMX8QXP_REG_ADC_CTRL);
307+
308+	/* reset the fifo */
309+	ctrl |= 1 << IMX8QXP_REG_ADC_CTRL_FIFO_RESET_SHIFT;
310+	writel(ctrl, adc->regs + IMX8QXP_REG_ADC_CTRL);
311+}
312+
313+static void imx8qxp_adc_reg_config(struct imx8qxp_adc *adc)
314+{
315+	u32 adc_cfg, adc_tctrl, adc_cmdl, adc_cmdh;
316+	u32 t_id, c_id;
317+
318+	adc_cfg = adc->adc_cfg.pwren << IMX8QXP_REG_ADC_CFG_PWREN_SHIFT
319+			| adc->adc_cfg.pudly << IMX8QXP_REG_ADC_CFG_PUDLY_SHIFT
320+			| adc->adc_cfg.refsel << IMX8QXP_REG_ADC_CFG_REFSEL_SHIFT
321+			| adc->adc_cfg.pwrsel << IMX8QXP_REG_ADC_CFG_PWRSEL_SHIFT
322+			| adc->adc_cfg.tprictrl << IMX8QXP_REG_ADC_CFG_TPRICTRL_SHIFT;
323+	writel(adc_cfg, adc->regs + IMX8QXP_REG_ADC_CFG);
324+
325+	t_id = adc->trigger_id;
326+	adc_tctrl = adc->adc_trigger_ctrl[t_id].tcmd << IMX8QXP_REG_ADC_TCTRL_TCMD_SHIFT
327+			| adc->adc_trigger_ctrl[t_id].tdly << IMX8QXP_REG_ADC_TCTRL_TDLY_SHIFT
328+			| adc->adc_trigger_ctrl[t_id].tpri << IMX8QXP_REG_ADC_TCTRL_TPRI_SHIFT
329+			| adc->adc_trigger_ctrl[t_id].hten << IMX8QXP_REG_ADC_TCTRL_HTEN_SHIFT;
330+	writel(adc_tctrl, adc->regs + IMX8QXP_REG_ADC_TCTRL0 + t_id * 4);
331+
332+	c_id = adc->cmd_id - 1;
333+	adc_cmdl = adc->adc_cmd_l[c_id].scale << IMX8QXP_REG_ADC_CMDL_CSCALE_SHIFT
334+			| adc->adc_cmd_l[c_id].mode << IMX8QXP_REG_ADC_CMDL_MODE_SHIFT
335+			| adc->adc_cmd_l[c_id].diff << IMX8QXP_REG_ADC_CMDL_DIFF_SHIFT
336+			| adc->adc_cmd_l[c_id].absel << IMX8QXP_REG_ADC_CMDL_ABSEL_SHIFT
337+			| adc->adc_cmd_l[c_id].adch << IMX8QXP_REG_ADC_CMDL_ADCH_SHIFT;
338+	writel(adc_cmdl, adc->regs + IMX8QXP_REG_ADC_CMDL1 + c_id * 8);
339+
340+	adc_cmdh = adc->adc_cmd_h[c_id].next << IMX8QXP_REG_ADC_CMDH_NEXT_SHIFT
341+			| adc->adc_cmd_h[c_id].loop << IMX8QXP_REG_ADC_CMDH_LOOP_SHIFT
342+			| adc->adc_cmd_h[c_id].avgs << IMX8QXP_REG_ADC_CMDH_AVGS_SHIFT
343+			| adc->adc_cmd_h[c_id].sts << IMX8QXP_REG_ADC_CMDH_STS_SHIFT
344+			| adc->adc_cmd_h[c_id].lwi << IMX8QXP_REG_ADC_CMDH_LWI_SHIFT
345+			| adc->adc_cmd_h[c_id].cmpen << IMX8QXP_REG_ADC_CMDH_CMPEN_SHIFT;
346+	writel(adc_cmdh, adc->regs + IMX8QXP_REG_ADC_CMDH1 + c_id * 8);
347+}
348+
349+static void imx8qxp_adc_mode_config(struct imx8qxp_adc *adc)
350+{
351+	u32 cmd_id, trigger_id, channel_id;
352+
353+	channel_id = adc->channel_id;
354+	cmd_id = adc->cmd_id - 1;
355+	trigger_id = adc->trigger_id;
356+
357+	/* config the cmd */
358+	adc->adc_cmd_l[cmd_id].scale = IMX8QXP_REG_ADC_CMDL_CHANNEL_SCALE_FULL;
359+	adc->adc_cmd_l[cmd_id].mode = IMX8QXP_REG_ADC_CMDL_STANDARD_RESOLUTION;
360+	adc->adc_cmd_l[cmd_id].diff = IMX8QXP_REG_ADC_CMDL_MODE_SINGLE;
361+	adc->adc_cmd_l[cmd_id].absel = IMX8QXP_REG_ADC_CMDL_SEL_A_A_B_CHANNEL;
362+	adc->adc_cmd_l[cmd_id].adch = channel_id;
363+
364+	adc->adc_cmd_h[cmd_id].next = 0;
365+	adc->adc_cmd_h[cmd_id].loop = 0;
366+	adc->adc_cmd_h[cmd_id].avgs = 7;  // 128 times conversion
367+	adc->adc_cmd_h[cmd_id].sts = 0;
368+	adc->adc_cmd_h[cmd_id].lwi = IMX8QXP_REG_ADC_CMDH_LWI_INCREMENT_DISABLE;
369+	adc->adc_cmd_h[cmd_id].cmpen = IMX8QXP_REG_ADC_CMDH_CMPEN_DISABLE;
370+
371+	/* config the trigger control */
372+	adc->adc_trigger_ctrl[trigger_id].tcmd = adc->cmd_id;  //point to cmd1
373+	adc->adc_trigger_ctrl[trigger_id].tdly = 0;
374+	adc->adc_trigger_ctrl[trigger_id].tpri = IMX8QXP_REG_ADC_TCTRL_TPRI_PRIORITY_HIGH;
375+	adc->adc_trigger_ctrl[trigger_id].hten = IMX8QXP_REG_ADC_TCTRL_HTEN_HARDWARE_TIRGGER_DISABLE;
376+
377+	/* ADC configuration */
378+	adc->adc_cfg.pwren = 1;
379+	adc->adc_cfg.pudly = 0x80;
380+	adc->adc_cfg.refsel = 0;
381+	adc->adc_cfg.pwrsel = 3;
382+	adc->adc_cfg.tprictrl = 0;
383+
384+	imx8qxp_adc_reg_config(adc);
385+}
386+
387+static void imx8qxp_adc_fifo_config(struct imx8qxp_adc *adc)
388+{
389+	u32 fifo_ctrl, interrupt_en;
390+
391+	fifo_ctrl = readl(adc->regs + IMX8QXP_REG_ADC_FCTRL);
392+	fifo_ctrl &= ~IMX8QXP_ADC_FCTRL_FWMARK_MASK;
393+	/* set the watermark level to 1 */
394+	fifo_ctrl |= 0 << IMX8QXP_ADC_FCTRL_FWMARK_SHIFT;
395+	writel(fifo_ctrl, adc->regs + IMX8QXP_REG_ADC_FCTRL);
396+
397+	/* FIFO Watermark Interrupt Enable */
398+	interrupt_en = readl(adc->regs + IMX8QXP_REG_ADC_IE);
399+	interrupt_en |= 1 << IMX8QXP_REG_ADC_IE_FWMIE_SHIFT;
400+	writel(interrupt_en, adc->regs + IMX8QXP_REG_ADC_IE);
401+}
402+
403+static void imx8qxp_adc_disable(struct imx8qxp_adc *adc)
404+{
405+	u32 ctrl;
406+
407+	ctrl = readl(adc->regs + IMX8QXP_REG_ADC_CTRL);
408+	ctrl &=  ~(1 << IMX8QXP_REG_ADC_CTRL_ADC_ENABLE);
409+	writel(ctrl, adc->regs + IMX8QXP_REG_ADC_CTRL);
410+}
411+
412+static void imx8qxp_adc_enable(struct imx8qxp_adc *adc)
413+{
414+	u32 ctrl;
415+
416+	ctrl = readl(adc->regs + IMX8QXP_REG_ADC_CTRL);
417+	ctrl |= 1 << IMX8QXP_REG_ADC_CTRL_ADC_ENABLE;
418+	writel(ctrl, adc->regs + IMX8QXP_REG_ADC_CTRL);
419+
420+}
421+
422+static void imx8qxp_adc_start_trigger(struct imx8qxp_adc *adc)
423+{
424+	writel(1 << adc->trigger_id, adc->regs + IMX8QXP_REG_ADC_SWTRIG);
425+}
426+
427+static u32 imx8qxp_adc_get_sample_rate(struct imx8qxp_adc *adc)
428+{
429+
430+	u32 input_clk;
431+
432+	input_clk = clk_get_rate(adc->clk);
433+
434+	return input_clk / 3;
435+}
436+
437+static int imx8qxp_adc_read_raw(struct iio_dev *indio_dev,
438+			struct iio_chan_spec const *chan,
439+			int *val,
440+			int *val2,
441+			long mask)
442+{
443+
444+	struct imx8qxp_adc *adc = iio_priv(indio_dev);
445+
446+	u32 channel;
447+	long ret;
448+
449+	switch (mask) {
450+	case IIO_CHAN_INFO_RAW:
451+		pm_runtime_get_sync(adc->dev);
452+
453+		mutex_lock(&indio_dev->mlock);
454+		reinit_completion(&adc->completion);
455+
456+		channel = chan->channel & 0x07;
457+		adc->channel_id = channel;
458+		adc->cmd_id = 1;
459+		adc->trigger_id = 0;
460+		imx8qxp_adc_mode_config(adc);
461+
462+		imx8qxp_adc_fifo_config(adc);
463+
464+		imx8qxp_adc_enable(adc);
465+
466+		imx8qxp_adc_start_trigger(adc);
467+
468+		ret = wait_for_completion_interruptible_timeout
469+				(&adc->completion, IMX8QXP_ADC_TIMEOUT);
470+
471+		pm_runtime_mark_last_busy(adc->dev);
472+		pm_runtime_put_sync_autosuspend(adc->dev);
473+
474+		if (ret == 0) {
475+			mutex_unlock(&indio_dev->mlock);
476+			return -ETIMEDOUT;
477+		}
478+		if (ret < 0) {
479+			mutex_unlock(&indio_dev->mlock);
480+			return ret;
481+		}
482+
483+		*val = adc->value;
484+		mutex_unlock(&indio_dev->mlock);
485+		return IIO_VAL_INT;
486+
487+	case IIO_CHAN_INFO_SCALE:
488+		adc->vref_uv = regulator_get_voltage(adc->vref);
489+		*val = adc->vref_uv / 1000;
490+		*val2 = 12;
491+		return IIO_VAL_FRACTIONAL_LOG2;
492+
493+	case IIO_CHAN_INFO_SAMP_FREQ:
494+		*val = imx8qxp_adc_get_sample_rate(adc);
495+		return IIO_VAL_INT;
496+
497+	default:
498+		return -EINVAL;
499+	}
500+}
501+
502+static int imx8qxp_adc_read_data(struct imx8qxp_adc *adc)
503+{
504+	u32 value;
505+
506+	value = readl(adc->regs + IMX8QXP_REG_ADC_RESFIFO);
507+	value &= 0xffff;
508+	return (value >> 3);
509+}
510+
511+static irqreturn_t imx8qxp_adc_isr(int irq, void *dev_id)
512+{
513+	struct imx8qxp_adc *adc = (struct imx8qxp_adc *)dev_id;
514+
515+	u32 fifo_count;
516+
517+	fifo_count = readl(adc->regs + IMX8QXP_REG_ADC_FCTRL)
518+			& IMX8QXP_ADC_FCTRL_FCOUNT_MASK;
519+
520+	if (fifo_count) {
521+		adc->value = imx8qxp_adc_read_data(adc);
522+		complete(&adc->completion);
523+	}
524+
525+	return IRQ_HANDLED;
526+}
527+
528+static int imx8qxp_adc_reg_access(struct iio_dev *indio_dev,
529+			unsigned int reg, unsigned int writeval,
530+			unsigned int *readval)
531+{
532+	struct imx8qxp_adc *adc = iio_priv(indio_dev);
533+
534+	if (!readval || reg % 4 || reg > IMX8QXP_REG_ADC_TST)
535+		return -EINVAL;
536+
537+	pm_runtime_get_sync(adc->dev);
538+
539+	*readval = readl(adc->regs + reg);
540+
541+	pm_runtime_mark_last_busy(adc->dev);
542+	pm_runtime_put_sync_autosuspend(adc->dev);
543+
544+	return 0;
545+}
546+
547+static const struct iio_info imx8qxp_adc_iio_info = {
548+	.read_raw = &imx8qxp_adc_read_raw,
549+	.debugfs_reg_access = &imx8qxp_adc_reg_access,
550+};
551+
552+static const struct of_device_id imx8qxp_adc_match[] = {
553+	{ .compatible = "fsl,imx8qxp-adc", },
554+	{ /* sentinel */ }
555+};
556+MODULE_DEVICE_TABLE(of, imx8qxp_adc_match);
557+
558+static int imx8qxp_adc_probe(struct platform_device *pdev)
559+{
560+	struct imx8qxp_adc *adc;
561+	struct iio_dev *indio_dev;
562+	struct resource *mem;
563+	int irq;
564+	int ret;
565+
566+	indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc));
567+	if (!indio_dev) {
568+		dev_err(&pdev->dev, "Failed allocating iio device\n");
569+		return -ENOMEM;
570+	}
571+
572+	adc = iio_priv(indio_dev);
573+	adc->dev = &pdev->dev;
574+
575+	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
576+	adc->regs = devm_ioremap_resource(&pdev->dev, mem);
577+	if (IS_ERR(adc->regs)) {
578+		ret = PTR_ERR(adc->regs);
579+		dev_err(&pdev->dev,
580+			"Failed to remap adc memory, err = %d\n", ret);
581+		return ret;
582+	}
583+
584+	irq = platform_get_irq(pdev, 0);
585+	if (irq < 0) {
586+		dev_err(&pdev->dev, "No irq resource?\n");
587+		return irq;
588+	}
589+
590+	adc->clk = devm_clk_get(&pdev->dev, "per");
591+	if (IS_ERR(adc->clk)) {
592+		ret = PTR_ERR(adc->clk);
593+		dev_err(&pdev->dev, "Failed getting clock, err = %d\n", ret);
594+		return ret;
595+	}
596+
597+	adc->ipg_clk = devm_clk_get(&pdev->dev, "ipg");
598+	if (IS_ERR(adc->ipg_clk)) {
599+		ret = PTR_ERR(adc->ipg_clk);
600+		dev_err(&pdev->dev, "Failed getting clock, err = %d\n", ret);
601+		return ret;
602+	}
603+
604+	adc->vref = devm_regulator_get(&pdev->dev, "vref");
605+	if (IS_ERR(adc->vref)) {
606+		ret = PTR_ERR(adc->vref);
607+		dev_err(&pdev->dev,
608+			"Failed getting reference voltage, err = %d\n", ret);
609+		return ret;
610+	}
611+
612+	ret = regulator_enable(adc->vref);
613+	if (ret) {
614+		dev_err(&pdev->dev,
615+			"Can't enable adc reference top voltage, err = %d\n",
616+			ret);
617+		return ret;
618+	}
619+
620+	platform_set_drvdata(pdev, indio_dev);
621+
622+	init_completion(&adc->completion);
623+
624+	indio_dev->name = dev_name(&pdev->dev);
625+	indio_dev->dev.parent = &pdev->dev;
626+	indio_dev->info = &imx8qxp_adc_iio_info;
627+	indio_dev->modes = INDIO_DIRECT_MODE;
628+	indio_dev->channels = imx8qxp_adc_iio_channels;
629+	indio_dev->num_channels = ARRAY_SIZE(imx8qxp_adc_iio_channels);
630+
631+	ret = clk_prepare_enable(adc->clk);
632+	if (ret) {
633+		dev_err(&pdev->dev,
634+			"Could not prepare or enable the clock.\n");
635+		goto error_adc_clk_enable;
636+	}
637+
638+	ret = clk_prepare_enable(adc->ipg_clk);
639+	if (ret) {
640+		dev_err(&pdev->dev,
641+			"Could not prepare or enable the clock.\n");
642+		goto error_adc_ipg_clk_enable;
643+	}
644+
645+	ret = devm_request_irq(adc->dev, irq,
646+				imx8qxp_adc_isr, 0,
647+				dev_name(&pdev->dev), adc);
648+	if (ret < 0) {
649+		dev_err(&pdev->dev, "Failed requesting irq, irq = %d\n", irq);
650+		goto error_iio_device_register;
651+	}
652+
653+	imx8qxp_adc_feature_prepare(adc);
654+	imx8qxp_adc_reset(adc);
655+
656+	ret = iio_device_register(indio_dev);
657+	if (ret) {
658+		imx8qxp_adc_disable(adc);
659+		dev_err(&pdev->dev, "Couldn't register the device.\n");
660+		goto error_iio_device_register;
661+	}
662+
663+	pm_runtime_set_active(&pdev->dev);
664+	pm_runtime_set_autosuspend_delay(&pdev->dev, 50);
665+	pm_runtime_use_autosuspend(&pdev->dev);
666+	pm_runtime_enable(&pdev->dev);
667+
668+	return 0;
669+
670+error_iio_device_register:
671+	clk_disable_unprepare(adc->ipg_clk);
672+error_adc_ipg_clk_enable:
673+	clk_disable_unprepare(adc->clk);
674+error_adc_clk_enable:
675+	regulator_disable(adc->vref);
676+
677+	return ret;
678+}
679+
680+static int imx8qxp_adc_remove(struct platform_device *pdev)
681+{
682+	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
683+	struct imx8qxp_adc *adc = iio_priv(indio_dev);
684+
685+	pm_runtime_get_sync(&pdev->dev);
686+
687+	iio_device_unregister(indio_dev);
688+
689+	imx8qxp_adc_disable(adc);
690+
691+	clk_disable_unprepare(adc->clk);
692+	clk_disable_unprepare(adc->ipg_clk);
693+	regulator_disable(adc->vref);
694+
695+	pm_runtime_disable(&pdev->dev);
696+	pm_runtime_put_noidle(&pdev->dev);
697+
698+
699+	return 0;
700+}
701+
702+static int imx8qxp_adc_runtime_suspend(struct device *dev)
703+{
704+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
705+	struct imx8qxp_adc *adc = iio_priv(indio_dev);
706+
707+	imx8qxp_adc_disable(adc);
708+
709+	clk_disable_unprepare(adc->clk);
710+	clk_disable_unprepare(adc->ipg_clk);
711+	regulator_disable(adc->vref);
712+
713+	return 0;
714+}
715+
716+static int imx8qxp_adc_runtime_resume(struct device *dev)
717+{
718+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
719+	struct imx8qxp_adc *adc = iio_priv(indio_dev);
720+	int ret;
721+
722+	ret = regulator_enable(adc->vref);
723+	if (ret) {
724+		dev_err(adc->dev,
725+			"Can't enable adc reference top voltage, err = %d\n",
726+			ret);
727+		return ret;
728+	}
729+
730+	ret = clk_prepare_enable(adc->clk);
731+	if (ret) {
732+		dev_err(adc->dev,
733+			"Could not prepare or enable clock.\n");
734+		regulator_disable(adc->vref);
735+		return ret;
736+	}
737+
738+	ret = clk_prepare_enable(adc->ipg_clk);
739+	if (ret) {
740+		dev_err(adc->dev,
741+			"Could not prepare or enable clock.\n");
742+		clk_disable_unprepare(adc->clk);
743+		regulator_disable(adc->vref);
744+		return ret;
745+	}
746+	imx8qxp_adc_reset(adc);
747+
748+	return 0;
749+}
750+
751+static const struct dev_pm_ops imx8qxp_adc_pm_ops = {
752+	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
753+	SET_RUNTIME_PM_OPS(imx8qxp_adc_runtime_suspend, imx8qxp_adc_runtime_resume, NULL)
754+};
755+
756+static struct platform_driver imx8qxp_adc_driver = {
757+	.probe		= imx8qxp_adc_probe,
758+	.remove		= imx8qxp_adc_remove,
759+	.driver		= {
760+		.name	= "imx8qxp_adc",
761+		.of_match_table = imx8qxp_adc_match,
762+		.pm	= &imx8qxp_adc_pm_ops,
763+	},
764+};
765+
766+module_platform_driver(imx8qxp_adc_driver);
767+
768+MODULE_AUTHOR("Haibo Chen <haibo.chen@nxp.com>");
769+MODULE_DESCRIPTION("NXP IMX8QXP ADC driver");
770+MODULE_LICENSE("GPL v2");
771