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