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