xref: /linux/drivers/thermal/imx91_thermal.c (revision 24f171c7e145f43b9f187578e89b0982ce87e54c)
1*c411d8bfSPengfei Li // SPDX-License-Identifier: GPL-2.0
2*c411d8bfSPengfei Li /*
3*c411d8bfSPengfei Li  * Copyright 2025 NXP.
4*c411d8bfSPengfei Li  */
5*c411d8bfSPengfei Li 
6*c411d8bfSPengfei Li #include <linux/bitfield.h>
7*c411d8bfSPengfei Li #include <linux/clk.h>
8*c411d8bfSPengfei Li #include <linux/err.h>
9*c411d8bfSPengfei Li #include <linux/interrupt.h>
10*c411d8bfSPengfei Li #include <linux/iopoll.h>
11*c411d8bfSPengfei Li #include <linux/nvmem-consumer.h>
12*c411d8bfSPengfei Li #include <linux/module.h>
13*c411d8bfSPengfei Li #include <linux/of.h>
14*c411d8bfSPengfei Li #include <linux/of_device.h>
15*c411d8bfSPengfei Li #include <linux/platform_device.h>
16*c411d8bfSPengfei Li #include <linux/pm_runtime.h>
17*c411d8bfSPengfei Li #include <linux/thermal.h>
18*c411d8bfSPengfei Li #include <linux/units.h>
19*c411d8bfSPengfei Li 
20*c411d8bfSPengfei Li #define REG_SET					0x4
21*c411d8bfSPengfei Li #define REG_CLR					0x8
22*c411d8bfSPengfei Li #define REG_TOG					0xc
23*c411d8bfSPengfei Li 
24*c411d8bfSPengfei Li #define IMX91_TMU_CTRL0				0x0
25*c411d8bfSPengfei Li #define   IMX91_TMU_CTRL0_THR1_IE		BIT(9)
26*c411d8bfSPengfei Li #define   IMX91_TMU_CTRL0_THR1_MASK		GENMASK(3, 2)
27*c411d8bfSPengfei Li #define   IMX91_TMU_CTRL0_CLR_FLT1		BIT(21)
28*c411d8bfSPengfei Li 
29*c411d8bfSPengfei Li #define IMX91_TMU_THR_MODE_LE			0
30*c411d8bfSPengfei Li #define IMX91_TMU_THR_MODE_GE			1
31*c411d8bfSPengfei Li 
32*c411d8bfSPengfei Li #define IMX91_TMU_STAT0				0x10
33*c411d8bfSPengfei Li #define   IMX91_TMU_STAT0_THR1_IF		BIT(9)
34*c411d8bfSPengfei Li #define   IMX91_TMU_STAT0_THR1_STAT		BIT(13)
35*c411d8bfSPengfei Li #define   IMX91_TMU_STAT0_DRDY0_IF_MASK		BIT(16)
36*c411d8bfSPengfei Li 
37*c411d8bfSPengfei Li #define IMX91_TMU_DATA0				0x20
38*c411d8bfSPengfei Li 
39*c411d8bfSPengfei Li #define IMX91_TMU_CTRL1				0x200
40*c411d8bfSPengfei Li #define IMX91_TMU_CTRL1_EN			BIT(31)
41*c411d8bfSPengfei Li #define IMX91_TMU_CTRL1_START			BIT(30)
42*c411d8bfSPengfei Li #define IMX91_TMU_CTRL1_STOP			BIT(29)
43*c411d8bfSPengfei Li #define IMX91_TMU_CTRL1_RES_MASK		GENMASK(19, 18)
44*c411d8bfSPengfei Li #define IMX91_TMU_CTRL1_MEAS_MODE_MASK		GENMASK(25, 24)
45*c411d8bfSPengfei Li #define   IMX91_TMU_CTRL1_MEAS_MODE_SINGLE	0
46*c411d8bfSPengfei Li #define   IMX91_TMU_CTRL1_MEAS_MODE_CONTINUES	1
47*c411d8bfSPengfei Li #define   IMX91_TMU_CTRL1_MEAS_MODE_PERIODIC	2
48*c411d8bfSPengfei Li 
49*c411d8bfSPengfei Li #define IMX91_TMU_THR_CTRL01			0x30
50*c411d8bfSPengfei Li #define   IMX91_TMU_THR_CTRL01_THR1_MASK	GENMASK(31, 16)
51*c411d8bfSPengfei Li 
52*c411d8bfSPengfei Li #define IMX91_TMU_REF_DIV			0x280
53*c411d8bfSPengfei Li #define IMX91_TMU_DIV_EN			BIT(31)
54*c411d8bfSPengfei Li #define IMX91_TMU_DIV_MASK			GENMASK(23, 16)
55*c411d8bfSPengfei Li #define IMX91_TMU_DIV_MAX			255
56*c411d8bfSPengfei Li 
57*c411d8bfSPengfei Li #define IMX91_TMU_PUD_ST_CTRL			0x2b0
58*c411d8bfSPengfei Li #define IMX91_TMU_PUDL_MASK			GENMASK(23, 16)
59*c411d8bfSPengfei Li 
60*c411d8bfSPengfei Li #define IMX91_TMU_TRIM1				0x2e0
61*c411d8bfSPengfei Li #define IMX91_TMU_TRIM2				0x2f0
62*c411d8bfSPengfei Li 
63*c411d8bfSPengfei Li #define IMX91_TMU_TEMP_LOW_LIMIT		-40000
64*c411d8bfSPengfei Li #define IMX91_TMU_TEMP_HIGH_LIMIT		125000
65*c411d8bfSPengfei Li 
66*c411d8bfSPengfei Li #define IMX91_TMU_DEFAULT_TRIM1_CONFIG		0xb561bc2d
67*c411d8bfSPengfei Li #define IMX91_TMU_DEFAULT_TRIM2_CONFIG		0x65d4
68*c411d8bfSPengfei Li 
69*c411d8bfSPengfei Li #define IMX91_TMU_PERIOD_CTRL			0x270
70*c411d8bfSPengfei Li #define   IMX91_TMU_PERIOD_CTRL_MEAS_MASK	GENMASK(23, 0)
71*c411d8bfSPengfei Li 
72*c411d8bfSPengfei Li #define IMX91_TMP_FRAC				64
73*c411d8bfSPengfei Li 
74*c411d8bfSPengfei Li struct imx91_tmu {
75*c411d8bfSPengfei Li 	void __iomem *base;
76*c411d8bfSPengfei Li 	struct clk *clk;
77*c411d8bfSPengfei Li 	struct device *dev;
78*c411d8bfSPengfei Li 	struct thermal_zone_device *tzd;
79*c411d8bfSPengfei Li };
80*c411d8bfSPengfei Li 
81*c411d8bfSPengfei Li static void imx91_tmu_start(struct imx91_tmu *tmu, bool start)
82*c411d8bfSPengfei Li {
83*c411d8bfSPengfei Li 	u32 val = start ? IMX91_TMU_CTRL1_START : IMX91_TMU_CTRL1_STOP;
84*c411d8bfSPengfei Li 
85*c411d8bfSPengfei Li 	writel_relaxed(val, tmu->base + IMX91_TMU_CTRL1 + REG_SET);
86*c411d8bfSPengfei Li }
87*c411d8bfSPengfei Li 
88*c411d8bfSPengfei Li static void imx91_tmu_enable(struct imx91_tmu *tmu, bool enable)
89*c411d8bfSPengfei Li {
90*c411d8bfSPengfei Li 	u32 reg = IMX91_TMU_CTRL1;
91*c411d8bfSPengfei Li 
92*c411d8bfSPengfei Li 	reg += enable ? REG_SET : REG_CLR;
93*c411d8bfSPengfei Li 
94*c411d8bfSPengfei Li 	writel_relaxed(IMX91_TMU_CTRL1_EN, tmu->base + reg);
95*c411d8bfSPengfei Li }
96*c411d8bfSPengfei Li 
97*c411d8bfSPengfei Li static int imx91_tmu_to_mcelsius(int x)
98*c411d8bfSPengfei Li {
99*c411d8bfSPengfei Li 	return x * MILLIDEGREE_PER_DEGREE / IMX91_TMP_FRAC;
100*c411d8bfSPengfei Li }
101*c411d8bfSPengfei Li 
102*c411d8bfSPengfei Li static int imx91_tmu_from_mcelsius(int x)
103*c411d8bfSPengfei Li {
104*c411d8bfSPengfei Li 	return x * IMX91_TMP_FRAC / MILLIDEGREE_PER_DEGREE;
105*c411d8bfSPengfei Li }
106*c411d8bfSPengfei Li 
107*c411d8bfSPengfei Li static int imx91_tmu_get_temp(struct thermal_zone_device *tz, int *temp)
108*c411d8bfSPengfei Li {
109*c411d8bfSPengfei Li 	struct imx91_tmu *tmu = thermal_zone_device_priv(tz);
110*c411d8bfSPengfei Li 	s16 data;
111*c411d8bfSPengfei Li 
112*c411d8bfSPengfei Li 	/* DATA0 is 16bit signed number */
113*c411d8bfSPengfei Li 	data = readw_relaxed(tmu->base + IMX91_TMU_DATA0);
114*c411d8bfSPengfei Li 	*temp = imx91_tmu_to_mcelsius(data);
115*c411d8bfSPengfei Li 
116*c411d8bfSPengfei Li 	return 0;
117*c411d8bfSPengfei Li }
118*c411d8bfSPengfei Li 
119*c411d8bfSPengfei Li static int imx91_tmu_set_trips(struct thermal_zone_device *tz, int low, int high)
120*c411d8bfSPengfei Li {
121*c411d8bfSPengfei Li 	struct imx91_tmu *tmu = thermal_zone_device_priv(tz);
122*c411d8bfSPengfei Li 	int val;
123*c411d8bfSPengfei Li 
124*c411d8bfSPengfei Li 	if (high >= IMX91_TMU_TEMP_HIGH_LIMIT)
125*c411d8bfSPengfei Li 		return -EINVAL;
126*c411d8bfSPengfei Li 
127*c411d8bfSPengfei Li 	writel_relaxed(IMX91_TMU_CTRL0_THR1_IE, tmu->base + IMX91_TMU_CTRL0 + REG_CLR);
128*c411d8bfSPengfei Li 
129*c411d8bfSPengfei Li 	/* Comparator1 for temperature threshold */
130*c411d8bfSPengfei Li 	writel_relaxed(IMX91_TMU_THR_CTRL01_THR1_MASK, tmu->base + IMX91_TMU_THR_CTRL01 + REG_CLR);
131*c411d8bfSPengfei Li 	val = FIELD_PREP(IMX91_TMU_THR_CTRL01_THR1_MASK, imx91_tmu_from_mcelsius(high));
132*c411d8bfSPengfei Li 
133*c411d8bfSPengfei Li 	writel_relaxed(val, tmu->base + IMX91_TMU_THR_CTRL01 + REG_SET);
134*c411d8bfSPengfei Li 
135*c411d8bfSPengfei Li 	writel_relaxed(IMX91_TMU_STAT0_THR1_IF, tmu->base + IMX91_TMU_STAT0 + REG_CLR);
136*c411d8bfSPengfei Li 
137*c411d8bfSPengfei Li 	writel_relaxed(IMX91_TMU_CTRL0_THR1_IE, tmu->base + IMX91_TMU_CTRL0 + REG_SET);
138*c411d8bfSPengfei Li 
139*c411d8bfSPengfei Li 	return 0;
140*c411d8bfSPengfei Li }
141*c411d8bfSPengfei Li 
142*c411d8bfSPengfei Li static int imx91_init_from_nvmem_cells(struct imx91_tmu *tmu)
143*c411d8bfSPengfei Li {
144*c411d8bfSPengfei Li 	struct device *dev = tmu->dev;
145*c411d8bfSPengfei Li 	u32 trim1, trim2;
146*c411d8bfSPengfei Li 	int ret;
147*c411d8bfSPengfei Li 
148*c411d8bfSPengfei Li 	ret = nvmem_cell_read_u32(dev, "trim1", &trim1);
149*c411d8bfSPengfei Li 	if (ret)
150*c411d8bfSPengfei Li 		return ret;
151*c411d8bfSPengfei Li 
152*c411d8bfSPengfei Li 	ret = nvmem_cell_read_u32(dev, "trim2", &trim2);
153*c411d8bfSPengfei Li 	if (ret)
154*c411d8bfSPengfei Li 		return ret;
155*c411d8bfSPengfei Li 
156*c411d8bfSPengfei Li 	if (trim1 == 0 || trim2 == 0)
157*c411d8bfSPengfei Li 		return -EINVAL;
158*c411d8bfSPengfei Li 
159*c411d8bfSPengfei Li 	writel_relaxed(trim1, tmu->base + IMX91_TMU_TRIM1);
160*c411d8bfSPengfei Li 	writel_relaxed(trim2, tmu->base + IMX91_TMU_TRIM2);
161*c411d8bfSPengfei Li 
162*c411d8bfSPengfei Li 	return 0;
163*c411d8bfSPengfei Li }
164*c411d8bfSPengfei Li 
165*c411d8bfSPengfei Li static void imx91_tmu_action_remove(void *data)
166*c411d8bfSPengfei Li {
167*c411d8bfSPengfei Li 	struct imx91_tmu *tmu = data;
168*c411d8bfSPengfei Li 
169*c411d8bfSPengfei Li 	/* disable tmu */
170*c411d8bfSPengfei Li 	imx91_tmu_enable(tmu, false);
171*c411d8bfSPengfei Li }
172*c411d8bfSPengfei Li 
173*c411d8bfSPengfei Li static irqreturn_t imx91_tmu_alarm_irq(int irq, void *data)
174*c411d8bfSPengfei Li {
175*c411d8bfSPengfei Li 	struct imx91_tmu *tmu = data;
176*c411d8bfSPengfei Li 	u32 val;
177*c411d8bfSPengfei Li 
178*c411d8bfSPengfei Li 	val = readl_relaxed(tmu->base + IMX91_TMU_STAT0);
179*c411d8bfSPengfei Li 
180*c411d8bfSPengfei Li 	/* Check if comparison interrupt occurred */
181*c411d8bfSPengfei Li 	if (val & IMX91_TMU_STAT0_THR1_IF) {
182*c411d8bfSPengfei Li 		/* Clear irq flag and disable interrupt until reconfigured */
183*c411d8bfSPengfei Li 		writel(IMX91_TMU_STAT0_THR1_IF, tmu->base + IMX91_TMU_STAT0 + REG_CLR);
184*c411d8bfSPengfei Li 		writel_relaxed(IMX91_TMU_CTRL0_THR1_IE, tmu->base + IMX91_TMU_CTRL0 + REG_CLR);
185*c411d8bfSPengfei Li 
186*c411d8bfSPengfei Li 		return IRQ_WAKE_THREAD;
187*c411d8bfSPengfei Li 	}
188*c411d8bfSPengfei Li 
189*c411d8bfSPengfei Li 	return IRQ_NONE;
190*c411d8bfSPengfei Li }
191*c411d8bfSPengfei Li 
192*c411d8bfSPengfei Li static irqreturn_t imx91_tmu_alarm_irq_thread(int irq, void *data)
193*c411d8bfSPengfei Li {
194*c411d8bfSPengfei Li 	struct imx91_tmu *tmu = data;
195*c411d8bfSPengfei Li 
196*c411d8bfSPengfei Li 	thermal_zone_device_update(tmu->tzd, THERMAL_EVENT_UNSPECIFIED);
197*c411d8bfSPengfei Li 
198*c411d8bfSPengfei Li 	return IRQ_HANDLED;
199*c411d8bfSPengfei Li }
200*c411d8bfSPengfei Li 
201*c411d8bfSPengfei Li static int imx91_tmu_change_mode(struct thermal_zone_device *tz, enum thermal_device_mode mode)
202*c411d8bfSPengfei Li {
203*c411d8bfSPengfei Li 	struct imx91_tmu *tmu = thermal_zone_device_priv(tz);
204*c411d8bfSPengfei Li 	int ret;
205*c411d8bfSPengfei Li 
206*c411d8bfSPengfei Li 	if (mode == THERMAL_DEVICE_ENABLED) {
207*c411d8bfSPengfei Li 		ret = pm_runtime_get(tmu->dev);
208*c411d8bfSPengfei Li 		if (ret < 0)
209*c411d8bfSPengfei Li 			return ret;
210*c411d8bfSPengfei Li 
211*c411d8bfSPengfei Li 		writel_relaxed(IMX91_TMU_CTRL0_THR1_IE | IMX91_TMU_CTRL0_THR1_MASK,
212*c411d8bfSPengfei Li 			       tmu->base + IMX91_TMU_CTRL0 + REG_CLR);
213*c411d8bfSPengfei Li 
214*c411d8bfSPengfei Li 		writel_relaxed(FIELD_PREP(IMX91_TMU_CTRL0_THR1_MASK, IMX91_TMU_THR_MODE_GE),
215*c411d8bfSPengfei Li 			       tmu->base + IMX91_TMU_CTRL0 + REG_SET);
216*c411d8bfSPengfei Li 		imx91_tmu_start(tmu, true);
217*c411d8bfSPengfei Li 	} else {
218*c411d8bfSPengfei Li 		writel_relaxed(IMX91_TMU_CTRL0_THR1_IE, tmu->base + IMX91_TMU_CTRL0 + REG_CLR);
219*c411d8bfSPengfei Li 		imx91_tmu_start(tmu, false);
220*c411d8bfSPengfei Li 		pm_runtime_put(tmu->dev);
221*c411d8bfSPengfei Li 	}
222*c411d8bfSPengfei Li 
223*c411d8bfSPengfei Li 	return 0;
224*c411d8bfSPengfei Li }
225*c411d8bfSPengfei Li 
226*c411d8bfSPengfei Li static struct thermal_zone_device_ops tmu_tz_ops = {
227*c411d8bfSPengfei Li 	.get_temp = imx91_tmu_get_temp,
228*c411d8bfSPengfei Li 	.change_mode = imx91_tmu_change_mode,
229*c411d8bfSPengfei Li 	.set_trips = imx91_tmu_set_trips,
230*c411d8bfSPengfei Li };
231*c411d8bfSPengfei Li 
232*c411d8bfSPengfei Li static int imx91_tmu_probe(struct platform_device *pdev)
233*c411d8bfSPengfei Li {
234*c411d8bfSPengfei Li 	struct device *dev = &pdev->dev;
235*c411d8bfSPengfei Li 	struct imx91_tmu *tmu;
236*c411d8bfSPengfei Li 	unsigned long rate;
237*c411d8bfSPengfei Li 	int irq, ret;
238*c411d8bfSPengfei Li 	u32 div;
239*c411d8bfSPengfei Li 
240*c411d8bfSPengfei Li 	tmu = devm_kzalloc(dev, sizeof(struct imx91_tmu), GFP_KERNEL);
241*c411d8bfSPengfei Li 	if (!tmu)
242*c411d8bfSPengfei Li 		return -ENOMEM;
243*c411d8bfSPengfei Li 
244*c411d8bfSPengfei Li 	tmu->dev = dev;
245*c411d8bfSPengfei Li 
246*c411d8bfSPengfei Li 	tmu->base = devm_platform_ioremap_resource(pdev, 0);
247*c411d8bfSPengfei Li 	if (IS_ERR(tmu->base))
248*c411d8bfSPengfei Li 		return dev_err_probe(dev, PTR_ERR(tmu->base), "failed to get io resource");
249*c411d8bfSPengfei Li 
250*c411d8bfSPengfei Li 	tmu->clk = devm_clk_get_enabled(dev, NULL);
251*c411d8bfSPengfei Li 	if (IS_ERR(tmu->clk))
252*c411d8bfSPengfei Li 		return dev_err_probe(dev, PTR_ERR(tmu->clk), "failed to get tmu clock\n");
253*c411d8bfSPengfei Li 
254*c411d8bfSPengfei Li 	platform_set_drvdata(pdev, tmu);
255*c411d8bfSPengfei Li 
256*c411d8bfSPengfei Li 	/* disable the monitor during initialization */
257*c411d8bfSPengfei Li 	imx91_tmu_enable(tmu, false);
258*c411d8bfSPengfei Li 	imx91_tmu_start(tmu, false);
259*c411d8bfSPengfei Li 
260*c411d8bfSPengfei Li 	ret = imx91_init_from_nvmem_cells(tmu);
261*c411d8bfSPengfei Li 	if (ret) {
262*c411d8bfSPengfei Li 		dev_warn(dev, "can't get trim value, use default settings\n");
263*c411d8bfSPengfei Li 
264*c411d8bfSPengfei Li 		writel_relaxed(IMX91_TMU_DEFAULT_TRIM1_CONFIG, tmu->base + IMX91_TMU_TRIM1);
265*c411d8bfSPengfei Li 		writel_relaxed(IMX91_TMU_DEFAULT_TRIM2_CONFIG, tmu->base + IMX91_TMU_TRIM2);
266*c411d8bfSPengfei Li 	}
267*c411d8bfSPengfei Li 
268*c411d8bfSPengfei Li 	/* The typical conv clk is 4MHz, the output freq is 'rate / (div + 1)' */
269*c411d8bfSPengfei Li 	rate = clk_get_rate(tmu->clk);
270*c411d8bfSPengfei Li 	div = (rate / (4 * HZ_PER_MHZ)) - 1;
271*c411d8bfSPengfei Li 	if (div > IMX91_TMU_DIV_MAX)
272*c411d8bfSPengfei Li 		return dev_err_probe(dev, -EINVAL, "clock divider exceed hardware limitation");
273*c411d8bfSPengfei Li 
274*c411d8bfSPengfei Li 	/* Set divider value and enable divider */
275*c411d8bfSPengfei Li 	writel_relaxed(IMX91_TMU_DIV_EN | FIELD_PREP(IMX91_TMU_DIV_MASK, div),
276*c411d8bfSPengfei Li 		       tmu->base + IMX91_TMU_REF_DIV);
277*c411d8bfSPengfei Li 
278*c411d8bfSPengfei Li 	/* Set max power up delay: 'Tpud(ms) = 0xFF * 1000 / 4000000' */
279*c411d8bfSPengfei Li 	writel_relaxed(FIELD_PREP(IMX91_TMU_PUDL_MASK, 100U), tmu->base + IMX91_TMU_PUD_ST_CTRL);
280*c411d8bfSPengfei Li 
281*c411d8bfSPengfei Li 	/*
282*c411d8bfSPengfei Li 	 * Set resolution mode
283*c411d8bfSPengfei Li 	 * 00b - Conversion time = 0.59325 ms
284*c411d8bfSPengfei Li 	 * 01b - Conversion time = 1.10525 ms
285*c411d8bfSPengfei Li 	 * 10b - Conversion time = 2.12925 ms
286*c411d8bfSPengfei Li 	 * 11b - Conversion time = 4.17725 ms
287*c411d8bfSPengfei Li 	 */
288*c411d8bfSPengfei Li 	writel_relaxed(FIELD_PREP(IMX91_TMU_CTRL1_RES_MASK, 0x3),
289*c411d8bfSPengfei Li 		       tmu->base + IMX91_TMU_CTRL1 + REG_CLR);
290*c411d8bfSPengfei Li 	writel_relaxed(FIELD_PREP(IMX91_TMU_CTRL1_RES_MASK, 0x1),
291*c411d8bfSPengfei Li 		       tmu->base + IMX91_TMU_CTRL1 + REG_SET);
292*c411d8bfSPengfei Li 
293*c411d8bfSPengfei Li 	writel_relaxed(IMX91_TMU_CTRL1_MEAS_MODE_MASK, tmu->base + IMX91_TMU_CTRL1 + REG_CLR);
294*c411d8bfSPengfei Li 	writel_relaxed(FIELD_PREP(IMX91_TMU_CTRL1_MEAS_MODE_MASK,
295*c411d8bfSPengfei Li 				  IMX91_TMU_CTRL1_MEAS_MODE_PERIODIC),
296*c411d8bfSPengfei Li 		       tmu->base + IMX91_TMU_CTRL1 + REG_SET);
297*c411d8bfSPengfei Li 
298*c411d8bfSPengfei Li 	/*
299*c411d8bfSPengfei Li 	 * Set Periodic Measurement Frequency to 25Hz:
300*c411d8bfSPengfei Li 	 * tMEAS_FREQ = tCONV_CLK * PERIOD_CTRL[MEAS_FREQ]
301*c411d8bfSPengfei Li 	 */
302*c411d8bfSPengfei Li 	writel_relaxed(FIELD_PREP(IMX91_TMU_PERIOD_CTRL_MEAS_MASK, 4 * HZ_PER_MHZ / 25),
303*c411d8bfSPengfei Li 		       tmu->base + IMX91_TMU_PERIOD_CTRL);
304*c411d8bfSPengfei Li 
305*c411d8bfSPengfei Li 	imx91_tmu_enable(tmu, true);
306*c411d8bfSPengfei Li 	ret = devm_add_action(dev, imx91_tmu_action_remove, tmu);
307*c411d8bfSPengfei Li 	if (ret)
308*c411d8bfSPengfei Li 		return dev_err_probe(dev, ret, "Failure to add action imx91_tmu_action_remove()\n");
309*c411d8bfSPengfei Li 
310*c411d8bfSPengfei Li 	pm_runtime_set_active(dev);
311*c411d8bfSPengfei Li 	pm_runtime_get_noresume(dev);
312*c411d8bfSPengfei Li 	ret = devm_pm_runtime_enable(dev);
313*c411d8bfSPengfei Li 	if (ret)
314*c411d8bfSPengfei Li 		return ret;
315*c411d8bfSPengfei Li 
316*c411d8bfSPengfei Li 	tmu->tzd = devm_thermal_of_zone_register(dev, 0, tmu, &tmu_tz_ops);
317*c411d8bfSPengfei Li 	if (IS_ERR(tmu->tzd))
318*c411d8bfSPengfei Li 		return dev_err_probe(dev, PTR_ERR(tmu->tzd),
319*c411d8bfSPengfei Li 				     "failed to register thermal zone sensor\n");
320*c411d8bfSPengfei Li 
321*c411d8bfSPengfei Li 	irq = platform_get_irq(pdev, 0);
322*c411d8bfSPengfei Li 	if (irq < 0)
323*c411d8bfSPengfei Li 		return irq;
324*c411d8bfSPengfei Li 
325*c411d8bfSPengfei Li 	ret = devm_request_threaded_irq(dev, irq, imx91_tmu_alarm_irq,
326*c411d8bfSPengfei Li 					imx91_tmu_alarm_irq_thread,
327*c411d8bfSPengfei Li 					IRQF_ONESHOT, "imx91_thermal", tmu);
328*c411d8bfSPengfei Li 
329*c411d8bfSPengfei Li 	if (ret < 0)
330*c411d8bfSPengfei Li 		return dev_err_probe(dev, ret, "failed to request alarm irq\n");
331*c411d8bfSPengfei Li 
332*c411d8bfSPengfei Li 	pm_runtime_put(dev);
333*c411d8bfSPengfei Li 
334*c411d8bfSPengfei Li 	return 0;
335*c411d8bfSPengfei Li }
336*c411d8bfSPengfei Li 
337*c411d8bfSPengfei Li static int imx91_tmu_runtime_suspend(struct device *dev)
338*c411d8bfSPengfei Li {
339*c411d8bfSPengfei Li 	struct imx91_tmu *tmu = dev_get_drvdata(dev);
340*c411d8bfSPengfei Li 
341*c411d8bfSPengfei Li 	/* disable tmu */
342*c411d8bfSPengfei Li 	imx91_tmu_enable(tmu, false);
343*c411d8bfSPengfei Li 
344*c411d8bfSPengfei Li 	clk_disable_unprepare(tmu->clk);
345*c411d8bfSPengfei Li 
346*c411d8bfSPengfei Li 	return 0;
347*c411d8bfSPengfei Li }
348*c411d8bfSPengfei Li 
349*c411d8bfSPengfei Li static int imx91_tmu_runtime_resume(struct device *dev)
350*c411d8bfSPengfei Li {
351*c411d8bfSPengfei Li 	struct imx91_tmu *tmu = dev_get_drvdata(dev);
352*c411d8bfSPengfei Li 	int ret;
353*c411d8bfSPengfei Li 
354*c411d8bfSPengfei Li 	ret = clk_prepare_enable(tmu->clk);
355*c411d8bfSPengfei Li 	if (ret)
356*c411d8bfSPengfei Li 		return ret;
357*c411d8bfSPengfei Li 
358*c411d8bfSPengfei Li 	imx91_tmu_enable(tmu, true);
359*c411d8bfSPengfei Li 
360*c411d8bfSPengfei Li 	return 0;
361*c411d8bfSPengfei Li }
362*c411d8bfSPengfei Li 
363*c411d8bfSPengfei Li static DEFINE_RUNTIME_DEV_PM_OPS(imx91_tmu_pm_ops, imx91_tmu_runtime_suspend,
364*c411d8bfSPengfei Li 				 imx91_tmu_runtime_resume, NULL);
365*c411d8bfSPengfei Li 
366*c411d8bfSPengfei Li static const struct of_device_id imx91_tmu_table[] = {
367*c411d8bfSPengfei Li 	{ .compatible = "fsl,imx91-tmu", },
368*c411d8bfSPengfei Li 	{ },
369*c411d8bfSPengfei Li };
370*c411d8bfSPengfei Li MODULE_DEVICE_TABLE(of, imx91_tmu_table);
371*c411d8bfSPengfei Li 
372*c411d8bfSPengfei Li static struct platform_driver imx91_tmu = {
373*c411d8bfSPengfei Li 	.driver = {
374*c411d8bfSPengfei Li 		.name	= "imx91_thermal",
375*c411d8bfSPengfei Li 		.pm	= pm_ptr(&imx91_tmu_pm_ops),
376*c411d8bfSPengfei Li 		.of_match_table = imx91_tmu_table,
377*c411d8bfSPengfei Li 	},
378*c411d8bfSPengfei Li 	.probe = imx91_tmu_probe,
379*c411d8bfSPengfei Li };
380*c411d8bfSPengfei Li module_platform_driver(imx91_tmu);
381*c411d8bfSPengfei Li 
382*c411d8bfSPengfei Li MODULE_AUTHOR("Peng Fan <peng.fan@nxp.com>");
383*c411d8bfSPengfei Li MODULE_DESCRIPTION("i.MX91 Thermal Monitor Unit driver");
384*c411d8bfSPengfei Li MODULE_LICENSE("GPL");
385