xref: /OK3568_Linux_fs/kernel/drivers/thermal/intel/intel_bxt_pmic_thermal.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * Intel Broxton PMIC thermal driver
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Copyright (C) 2016 Intel Corporation. All rights reserved.
6*4882a593Smuzhiyun  */
7*4882a593Smuzhiyun 
8*4882a593Smuzhiyun #include <linux/module.h>
9*4882a593Smuzhiyun #include <linux/kernel.h>
10*4882a593Smuzhiyun #include <linux/slab.h>
11*4882a593Smuzhiyun #include <linux/delay.h>
12*4882a593Smuzhiyun #include <linux/interrupt.h>
13*4882a593Smuzhiyun #include <linux/device.h>
14*4882a593Smuzhiyun #include <linux/thermal.h>
15*4882a593Smuzhiyun #include <linux/platform_device.h>
16*4882a593Smuzhiyun #include <linux/sched.h>
17*4882a593Smuzhiyun #include <linux/mfd/intel_soc_pmic.h>
18*4882a593Smuzhiyun 
19*4882a593Smuzhiyun #define BXTWC_THRM0IRQ		0x4E04
20*4882a593Smuzhiyun #define BXTWC_THRM1IRQ		0x4E05
21*4882a593Smuzhiyun #define BXTWC_THRM2IRQ		0x4E06
22*4882a593Smuzhiyun #define BXTWC_MTHRM0IRQ		0x4E12
23*4882a593Smuzhiyun #define BXTWC_MTHRM1IRQ		0x4E13
24*4882a593Smuzhiyun #define BXTWC_MTHRM2IRQ		0x4E14
25*4882a593Smuzhiyun #define BXTWC_STHRM0IRQ		0x4F19
26*4882a593Smuzhiyun #define BXTWC_STHRM1IRQ		0x4F1A
27*4882a593Smuzhiyun #define BXTWC_STHRM2IRQ		0x4F1B
28*4882a593Smuzhiyun 
29*4882a593Smuzhiyun struct trip_config_map {
30*4882a593Smuzhiyun 	u16 irq_reg;
31*4882a593Smuzhiyun 	u16 irq_en;
32*4882a593Smuzhiyun 	u16 evt_stat;
33*4882a593Smuzhiyun 	u8 irq_mask;
34*4882a593Smuzhiyun 	u8 irq_en_mask;
35*4882a593Smuzhiyun 	u8 evt_mask;
36*4882a593Smuzhiyun 	u8 trip_num;
37*4882a593Smuzhiyun };
38*4882a593Smuzhiyun 
39*4882a593Smuzhiyun struct thermal_irq_map {
40*4882a593Smuzhiyun 	char handle[20];
41*4882a593Smuzhiyun 	int num_trips;
42*4882a593Smuzhiyun 	const struct trip_config_map *trip_config;
43*4882a593Smuzhiyun };
44*4882a593Smuzhiyun 
45*4882a593Smuzhiyun struct pmic_thermal_data {
46*4882a593Smuzhiyun 	const struct thermal_irq_map *maps;
47*4882a593Smuzhiyun 	int num_maps;
48*4882a593Smuzhiyun };
49*4882a593Smuzhiyun 
50*4882a593Smuzhiyun static const struct trip_config_map bxtwc_str0_trip_config[] = {
51*4882a593Smuzhiyun 	{
52*4882a593Smuzhiyun 		.irq_reg = BXTWC_THRM0IRQ,
53*4882a593Smuzhiyun 		.irq_mask = 0x01,
54*4882a593Smuzhiyun 		.irq_en = BXTWC_MTHRM0IRQ,
55*4882a593Smuzhiyun 		.irq_en_mask = 0x01,
56*4882a593Smuzhiyun 		.evt_stat = BXTWC_STHRM0IRQ,
57*4882a593Smuzhiyun 		.evt_mask = 0x01,
58*4882a593Smuzhiyun 		.trip_num = 0
59*4882a593Smuzhiyun 	},
60*4882a593Smuzhiyun 	{
61*4882a593Smuzhiyun 		.irq_reg = BXTWC_THRM0IRQ,
62*4882a593Smuzhiyun 		.irq_mask = 0x10,
63*4882a593Smuzhiyun 		.irq_en = BXTWC_MTHRM0IRQ,
64*4882a593Smuzhiyun 		.irq_en_mask = 0x10,
65*4882a593Smuzhiyun 		.evt_stat = BXTWC_STHRM0IRQ,
66*4882a593Smuzhiyun 		.evt_mask = 0x10,
67*4882a593Smuzhiyun 		.trip_num = 1
68*4882a593Smuzhiyun 	}
69*4882a593Smuzhiyun };
70*4882a593Smuzhiyun 
71*4882a593Smuzhiyun static const struct trip_config_map bxtwc_str1_trip_config[] = {
72*4882a593Smuzhiyun 	{
73*4882a593Smuzhiyun 		.irq_reg = BXTWC_THRM0IRQ,
74*4882a593Smuzhiyun 		.irq_mask = 0x02,
75*4882a593Smuzhiyun 		.irq_en = BXTWC_MTHRM0IRQ,
76*4882a593Smuzhiyun 		.irq_en_mask = 0x02,
77*4882a593Smuzhiyun 		.evt_stat = BXTWC_STHRM0IRQ,
78*4882a593Smuzhiyun 		.evt_mask = 0x02,
79*4882a593Smuzhiyun 		.trip_num = 0
80*4882a593Smuzhiyun 	},
81*4882a593Smuzhiyun 	{
82*4882a593Smuzhiyun 		.irq_reg = BXTWC_THRM0IRQ,
83*4882a593Smuzhiyun 		.irq_mask = 0x20,
84*4882a593Smuzhiyun 		.irq_en = BXTWC_MTHRM0IRQ,
85*4882a593Smuzhiyun 		.irq_en_mask = 0x20,
86*4882a593Smuzhiyun 		.evt_stat = BXTWC_STHRM0IRQ,
87*4882a593Smuzhiyun 		.evt_mask = 0x20,
88*4882a593Smuzhiyun 		.trip_num = 1
89*4882a593Smuzhiyun 	},
90*4882a593Smuzhiyun };
91*4882a593Smuzhiyun 
92*4882a593Smuzhiyun static const struct trip_config_map bxtwc_str2_trip_config[] = {
93*4882a593Smuzhiyun 	{
94*4882a593Smuzhiyun 		.irq_reg = BXTWC_THRM0IRQ,
95*4882a593Smuzhiyun 		.irq_mask = 0x04,
96*4882a593Smuzhiyun 		.irq_en = BXTWC_MTHRM0IRQ,
97*4882a593Smuzhiyun 		.irq_en_mask = 0x04,
98*4882a593Smuzhiyun 		.evt_stat = BXTWC_STHRM0IRQ,
99*4882a593Smuzhiyun 		.evt_mask = 0x04,
100*4882a593Smuzhiyun 		.trip_num = 0
101*4882a593Smuzhiyun 	},
102*4882a593Smuzhiyun 	{
103*4882a593Smuzhiyun 		.irq_reg = BXTWC_THRM0IRQ,
104*4882a593Smuzhiyun 		.irq_mask = 0x40,
105*4882a593Smuzhiyun 		.irq_en = BXTWC_MTHRM0IRQ,
106*4882a593Smuzhiyun 		.irq_en_mask = 0x40,
107*4882a593Smuzhiyun 		.evt_stat = BXTWC_STHRM0IRQ,
108*4882a593Smuzhiyun 		.evt_mask = 0x40,
109*4882a593Smuzhiyun 		.trip_num = 1
110*4882a593Smuzhiyun 	},
111*4882a593Smuzhiyun };
112*4882a593Smuzhiyun 
113*4882a593Smuzhiyun static const struct trip_config_map bxtwc_str3_trip_config[] = {
114*4882a593Smuzhiyun 	{
115*4882a593Smuzhiyun 		.irq_reg = BXTWC_THRM2IRQ,
116*4882a593Smuzhiyun 		.irq_mask = 0x10,
117*4882a593Smuzhiyun 		.irq_en = BXTWC_MTHRM2IRQ,
118*4882a593Smuzhiyun 		.irq_en_mask = 0x10,
119*4882a593Smuzhiyun 		.evt_stat = BXTWC_STHRM2IRQ,
120*4882a593Smuzhiyun 		.evt_mask = 0x10,
121*4882a593Smuzhiyun 		.trip_num = 0
122*4882a593Smuzhiyun 	},
123*4882a593Smuzhiyun };
124*4882a593Smuzhiyun 
125*4882a593Smuzhiyun static const struct thermal_irq_map bxtwc_thermal_irq_map[] = {
126*4882a593Smuzhiyun 	{
127*4882a593Smuzhiyun 		.handle = "STR0",
128*4882a593Smuzhiyun 		.trip_config = bxtwc_str0_trip_config,
129*4882a593Smuzhiyun 		.num_trips = ARRAY_SIZE(bxtwc_str0_trip_config),
130*4882a593Smuzhiyun 	},
131*4882a593Smuzhiyun 	{
132*4882a593Smuzhiyun 		.handle = "STR1",
133*4882a593Smuzhiyun 		.trip_config = bxtwc_str1_trip_config,
134*4882a593Smuzhiyun 		.num_trips = ARRAY_SIZE(bxtwc_str1_trip_config),
135*4882a593Smuzhiyun 	},
136*4882a593Smuzhiyun 	{
137*4882a593Smuzhiyun 		.handle = "STR2",
138*4882a593Smuzhiyun 		.trip_config = bxtwc_str2_trip_config,
139*4882a593Smuzhiyun 		.num_trips = ARRAY_SIZE(bxtwc_str2_trip_config),
140*4882a593Smuzhiyun 	},
141*4882a593Smuzhiyun 	{
142*4882a593Smuzhiyun 		.handle = "STR3",
143*4882a593Smuzhiyun 		.trip_config = bxtwc_str3_trip_config,
144*4882a593Smuzhiyun 		.num_trips = ARRAY_SIZE(bxtwc_str3_trip_config),
145*4882a593Smuzhiyun 	},
146*4882a593Smuzhiyun };
147*4882a593Smuzhiyun 
148*4882a593Smuzhiyun static const struct pmic_thermal_data bxtwc_thermal_data = {
149*4882a593Smuzhiyun 	.maps = bxtwc_thermal_irq_map,
150*4882a593Smuzhiyun 	.num_maps = ARRAY_SIZE(bxtwc_thermal_irq_map),
151*4882a593Smuzhiyun };
152*4882a593Smuzhiyun 
pmic_thermal_irq_handler(int irq,void * data)153*4882a593Smuzhiyun static irqreturn_t pmic_thermal_irq_handler(int irq, void *data)
154*4882a593Smuzhiyun {
155*4882a593Smuzhiyun 	struct platform_device *pdev = data;
156*4882a593Smuzhiyun 	struct thermal_zone_device *tzd;
157*4882a593Smuzhiyun 	struct pmic_thermal_data *td;
158*4882a593Smuzhiyun 	struct intel_soc_pmic *pmic;
159*4882a593Smuzhiyun 	struct regmap *regmap;
160*4882a593Smuzhiyun 	u8 reg_val, mask, irq_stat;
161*4882a593Smuzhiyun 	u16 reg, evt_stat_reg;
162*4882a593Smuzhiyun 	int i, j, ret;
163*4882a593Smuzhiyun 
164*4882a593Smuzhiyun 	pmic = dev_get_drvdata(pdev->dev.parent);
165*4882a593Smuzhiyun 	regmap = pmic->regmap;
166*4882a593Smuzhiyun 	td = (struct pmic_thermal_data *)
167*4882a593Smuzhiyun 		platform_get_device_id(pdev)->driver_data;
168*4882a593Smuzhiyun 
169*4882a593Smuzhiyun 	/* Resolve thermal irqs */
170*4882a593Smuzhiyun 	for (i = 0; i < td->num_maps; i++) {
171*4882a593Smuzhiyun 		for (j = 0; j < td->maps[i].num_trips; j++) {
172*4882a593Smuzhiyun 			reg = td->maps[i].trip_config[j].irq_reg;
173*4882a593Smuzhiyun 			mask = td->maps[i].trip_config[j].irq_mask;
174*4882a593Smuzhiyun 			/*
175*4882a593Smuzhiyun 			 * Read the irq register to resolve whether the
176*4882a593Smuzhiyun 			 * interrupt was triggered for this sensor
177*4882a593Smuzhiyun 			 */
178*4882a593Smuzhiyun 			if (regmap_read(regmap, reg, &ret))
179*4882a593Smuzhiyun 				return IRQ_HANDLED;
180*4882a593Smuzhiyun 
181*4882a593Smuzhiyun 			reg_val = (u8)ret;
182*4882a593Smuzhiyun 			irq_stat = ((u8)ret & mask);
183*4882a593Smuzhiyun 
184*4882a593Smuzhiyun 			if (!irq_stat)
185*4882a593Smuzhiyun 				continue;
186*4882a593Smuzhiyun 
187*4882a593Smuzhiyun 			/*
188*4882a593Smuzhiyun 			 * Read the status register to find out what
189*4882a593Smuzhiyun 			 * event occurred i.e a high or a low
190*4882a593Smuzhiyun 			 */
191*4882a593Smuzhiyun 			evt_stat_reg = td->maps[i].trip_config[j].evt_stat;
192*4882a593Smuzhiyun 			if (regmap_read(regmap, evt_stat_reg, &ret))
193*4882a593Smuzhiyun 				return IRQ_HANDLED;
194*4882a593Smuzhiyun 
195*4882a593Smuzhiyun 			tzd = thermal_zone_get_zone_by_name(td->maps[i].handle);
196*4882a593Smuzhiyun 			if (!IS_ERR(tzd))
197*4882a593Smuzhiyun 				thermal_zone_device_update(tzd,
198*4882a593Smuzhiyun 						THERMAL_EVENT_UNSPECIFIED);
199*4882a593Smuzhiyun 
200*4882a593Smuzhiyun 			/* Clear the appropriate irq */
201*4882a593Smuzhiyun 			regmap_write(regmap, reg, reg_val & mask);
202*4882a593Smuzhiyun 		}
203*4882a593Smuzhiyun 	}
204*4882a593Smuzhiyun 
205*4882a593Smuzhiyun 	return IRQ_HANDLED;
206*4882a593Smuzhiyun }
207*4882a593Smuzhiyun 
pmic_thermal_probe(struct platform_device * pdev)208*4882a593Smuzhiyun static int pmic_thermal_probe(struct platform_device *pdev)
209*4882a593Smuzhiyun {
210*4882a593Smuzhiyun 	struct regmap_irq_chip_data *regmap_irq_chip;
211*4882a593Smuzhiyun 	struct pmic_thermal_data *thermal_data;
212*4882a593Smuzhiyun 	int ret, irq, virq, i, j, pmic_irq_count;
213*4882a593Smuzhiyun 	struct intel_soc_pmic *pmic;
214*4882a593Smuzhiyun 	struct regmap *regmap;
215*4882a593Smuzhiyun 	struct device *dev;
216*4882a593Smuzhiyun 	u16 reg;
217*4882a593Smuzhiyun 	u8 mask;
218*4882a593Smuzhiyun 
219*4882a593Smuzhiyun 	dev = &pdev->dev;
220*4882a593Smuzhiyun 	pmic = dev_get_drvdata(pdev->dev.parent);
221*4882a593Smuzhiyun 	if (!pmic) {
222*4882a593Smuzhiyun 		dev_err(dev, "Failed to get struct intel_soc_pmic pointer\n");
223*4882a593Smuzhiyun 		return -ENODEV;
224*4882a593Smuzhiyun 	}
225*4882a593Smuzhiyun 
226*4882a593Smuzhiyun 	thermal_data = (struct pmic_thermal_data *)
227*4882a593Smuzhiyun 				platform_get_device_id(pdev)->driver_data;
228*4882a593Smuzhiyun 	if (!thermal_data) {
229*4882a593Smuzhiyun 		dev_err(dev, "No thermal data initialized!!\n");
230*4882a593Smuzhiyun 		return -ENODEV;
231*4882a593Smuzhiyun 	}
232*4882a593Smuzhiyun 
233*4882a593Smuzhiyun 	regmap = pmic->regmap;
234*4882a593Smuzhiyun 	regmap_irq_chip = pmic->irq_chip_data;
235*4882a593Smuzhiyun 
236*4882a593Smuzhiyun 	pmic_irq_count = 0;
237*4882a593Smuzhiyun 	while ((irq = platform_get_irq(pdev, pmic_irq_count)) != -ENXIO) {
238*4882a593Smuzhiyun 		virq = regmap_irq_get_virq(regmap_irq_chip, irq);
239*4882a593Smuzhiyun 		if (virq < 0) {
240*4882a593Smuzhiyun 			dev_err(dev, "failed to get virq by irq %d\n", irq);
241*4882a593Smuzhiyun 			return virq;
242*4882a593Smuzhiyun 		}
243*4882a593Smuzhiyun 
244*4882a593Smuzhiyun 		ret = devm_request_threaded_irq(&pdev->dev, virq,
245*4882a593Smuzhiyun 				NULL, pmic_thermal_irq_handler,
246*4882a593Smuzhiyun 				IRQF_ONESHOT, "pmic_thermal", pdev);
247*4882a593Smuzhiyun 
248*4882a593Smuzhiyun 		if (ret) {
249*4882a593Smuzhiyun 			dev_err(dev, "request irq(%d) failed: %d\n", virq, ret);
250*4882a593Smuzhiyun 			return ret;
251*4882a593Smuzhiyun 		}
252*4882a593Smuzhiyun 		pmic_irq_count++;
253*4882a593Smuzhiyun 	}
254*4882a593Smuzhiyun 
255*4882a593Smuzhiyun 	/* Enable thermal interrupts */
256*4882a593Smuzhiyun 	for (i = 0; i < thermal_data->num_maps; i++) {
257*4882a593Smuzhiyun 		for (j = 0; j < thermal_data->maps[i].num_trips; j++) {
258*4882a593Smuzhiyun 			reg = thermal_data->maps[i].trip_config[j].irq_en;
259*4882a593Smuzhiyun 			mask = thermal_data->maps[i].trip_config[j].irq_en_mask;
260*4882a593Smuzhiyun 			ret = regmap_update_bits(regmap, reg, mask, 0x00);
261*4882a593Smuzhiyun 			if (ret)
262*4882a593Smuzhiyun 				return ret;
263*4882a593Smuzhiyun 		}
264*4882a593Smuzhiyun 	}
265*4882a593Smuzhiyun 
266*4882a593Smuzhiyun 	return 0;
267*4882a593Smuzhiyun }
268*4882a593Smuzhiyun 
269*4882a593Smuzhiyun static const struct platform_device_id pmic_thermal_id_table[] = {
270*4882a593Smuzhiyun 	{
271*4882a593Smuzhiyun 		.name = "bxt_wcove_thermal",
272*4882a593Smuzhiyun 		.driver_data = (kernel_ulong_t)&bxtwc_thermal_data,
273*4882a593Smuzhiyun 	},
274*4882a593Smuzhiyun 	{},
275*4882a593Smuzhiyun };
276*4882a593Smuzhiyun 
277*4882a593Smuzhiyun static struct platform_driver pmic_thermal_driver = {
278*4882a593Smuzhiyun 	.probe = pmic_thermal_probe,
279*4882a593Smuzhiyun 	.driver = {
280*4882a593Smuzhiyun 		.name = "pmic_thermal",
281*4882a593Smuzhiyun 	},
282*4882a593Smuzhiyun 	.id_table = pmic_thermal_id_table,
283*4882a593Smuzhiyun };
284*4882a593Smuzhiyun 
285*4882a593Smuzhiyun MODULE_DEVICE_TABLE(platform, pmic_thermal_id_table);
286*4882a593Smuzhiyun module_platform_driver(pmic_thermal_driver);
287*4882a593Smuzhiyun 
288*4882a593Smuzhiyun MODULE_AUTHOR("Yegnesh S Iyer <yegnesh.s.iyer@intel.com>");
289*4882a593Smuzhiyun MODULE_DESCRIPTION("Intel Broxton PMIC Thermal Driver");
290*4882a593Smuzhiyun MODULE_LICENSE("GPL v2");
291