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