GNU Linux-libre 4.14.262-gnu1
[releases.git] / drivers / thermal / intel_pch_thermal.c
1 /* intel_pch_thermal.c - Intel PCH Thermal driver
2  *
3  * Copyright (c) 2015, Intel Corporation.
4  *
5  * Authors:
6  *     Tushar Dave <tushar.n.dave@intel.com>
7  *
8  * This program is free software; you can redistribute it and/or modify it
9  * under the terms and conditions of the GNU General Public License,
10  * version 2, as published by the Free Software Foundation.
11  *
12  * This program is distributed in the hope it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
15  * more details.
16  *
17  */
18
19 #include <linux/module.h>
20 #include <linux/types.h>
21 #include <linux/init.h>
22 #include <linux/pci.h>
23 #include <linux/acpi.h>
24 #include <linux/thermal.h>
25 #include <linux/pm.h>
26
27 /* Intel PCH thermal Device IDs */
28 #define PCH_THERMAL_DID_HSW_1   0x9C24 /* Haswell PCH */
29 #define PCH_THERMAL_DID_HSW_2   0x8C24 /* Haswell PCH */
30 #define PCH_THERMAL_DID_WPT     0x9CA4 /* Wildcat Point */
31 #define PCH_THERMAL_DID_SKL     0x9D31 /* Skylake PCH */
32 #define PCH_THERMAL_DID_SKL_H   0xA131 /* Skylake PCH 100 series */
33
34 /* Wildcat Point-LP  PCH Thermal registers */
35 #define WPT_TEMP        0x0000  /* Temperature */
36 #define WPT_TSC 0x04    /* Thermal Sensor Control */
37 #define WPT_TSS 0x06    /* Thermal Sensor Status */
38 #define WPT_TSEL        0x08    /* Thermal Sensor Enable and Lock */
39 #define WPT_TSREL       0x0A    /* Thermal Sensor Report Enable and Lock */
40 #define WPT_TSMIC       0x0C    /* Thermal Sensor SMI Control */
41 #define WPT_CTT 0x0010  /* Catastrophic Trip Point */
42 #define WPT_TAHV        0x0014  /* Thermal Alert High Value */
43 #define WPT_TALV        0x0018  /* Thermal Alert Low Value */
44 #define WPT_TL          0x00000040      /* Throttle Value */
45 #define WPT_PHL 0x0060  /* PCH Hot Level */
46 #define WPT_PHLC        0x62    /* PHL Control */
47 #define WPT_TAS 0x80    /* Thermal Alert Status */
48 #define WPT_TSPIEN      0x82    /* PCI Interrupt Event Enables */
49 #define WPT_TSGPEN      0x84    /* General Purpose Event Enables */
50
51 /*  Wildcat Point-LP  PCH Thermal Register bit definitions */
52 #define WPT_TEMP_TSR    0x01ff  /* Temp TS Reading */
53 #define WPT_TSC_CPDE    0x01    /* Catastrophic Power-Down Enable */
54 #define WPT_TSS_TSDSS   0x10    /* Thermal Sensor Dynamic Shutdown Status */
55 #define WPT_TSS_GPES    0x08    /* GPE status */
56 #define WPT_TSEL_ETS    0x01    /* Enable TS */
57 #define WPT_TSEL_PLDB   0x80    /* TSEL Policy Lock-Down Bit */
58 #define WPT_TL_TOL      0x000001FF      /* T0 Level */
59 #define WPT_TL_T1L      0x1ff00000      /* T1 Level */
60 #define WPT_TL_TTEN     0x20000000      /* TT Enable */
61
62 static char driver_name[] = "Intel PCH thermal driver";
63
64 struct pch_thermal_device {
65         void __iomem *hw_base;
66         const struct pch_dev_ops *ops;
67         struct pci_dev *pdev;
68         struct thermal_zone_device *tzd;
69         int crt_trip_id;
70         unsigned long crt_temp;
71         int hot_trip_id;
72         unsigned long hot_temp;
73         int psv_trip_id;
74         unsigned long psv_temp;
75         bool bios_enabled;
76 };
77
78 #ifdef CONFIG_ACPI
79
80 /*
81  * On some platforms, there is a companion ACPI device, which adds
82  * passive trip temperature using _PSV method. There is no specific
83  * passive temperature setting in MMIO interface of this PCI device.
84  */
85 static void pch_wpt_add_acpi_psv_trip(struct pch_thermal_device *ptd,
86                                       int *nr_trips)
87 {
88         struct acpi_device *adev;
89
90         ptd->psv_trip_id = -1;
91
92         adev = ACPI_COMPANION(&ptd->pdev->dev);
93         if (adev) {
94                 unsigned long long r;
95                 acpi_status status;
96
97                 status = acpi_evaluate_integer(adev->handle, "_PSV", NULL,
98                                                &r);
99                 if (ACPI_SUCCESS(status)) {
100                         unsigned long trip_temp;
101
102                         trip_temp = DECI_KELVIN_TO_MILLICELSIUS(r);
103                         if (trip_temp) {
104                                 ptd->psv_temp = trip_temp;
105                                 ptd->psv_trip_id = *nr_trips;
106                                 ++(*nr_trips);
107                         }
108                 }
109         }
110 }
111 #else
112 static void pch_wpt_add_acpi_psv_trip(struct pch_thermal_device *ptd,
113                                       int *nr_trips)
114 {
115         ptd->psv_trip_id = -1;
116
117 }
118 #endif
119
120 static int pch_wpt_init(struct pch_thermal_device *ptd, int *nr_trips)
121 {
122         u8 tsel;
123         u16 trip_temp;
124
125         *nr_trips = 0;
126
127         /* Check if BIOS has already enabled thermal sensor */
128         if (WPT_TSEL_ETS & readb(ptd->hw_base + WPT_TSEL)) {
129                 ptd->bios_enabled = true;
130                 goto read_trips;
131         }
132
133         tsel = readb(ptd->hw_base + WPT_TSEL);
134         /*
135          * When TSEL's Policy Lock-Down bit is 1, TSEL become RO.
136          * If so, thermal sensor cannot enable. Bail out.
137          */
138         if (tsel & WPT_TSEL_PLDB) {
139                 dev_err(&ptd->pdev->dev, "Sensor can't be enabled\n");
140                 return -ENODEV;
141         }
142
143         writeb(tsel|WPT_TSEL_ETS, ptd->hw_base + WPT_TSEL);
144         if (!(WPT_TSEL_ETS & readb(ptd->hw_base + WPT_TSEL))) {
145                 dev_err(&ptd->pdev->dev, "Sensor can't be enabled\n");
146                 return -ENODEV;
147         }
148
149 read_trips:
150         ptd->crt_trip_id = -1;
151         trip_temp = readw(ptd->hw_base + WPT_CTT);
152         trip_temp &= 0x1FF;
153         if (trip_temp) {
154                 /* Resolution of 1/2 degree C and an offset of -50C */
155                 ptd->crt_temp = trip_temp * 1000 / 2 - 50000;
156                 ptd->crt_trip_id = 0;
157                 ++(*nr_trips);
158         }
159
160         ptd->hot_trip_id = -1;
161         trip_temp = readw(ptd->hw_base + WPT_PHL);
162         trip_temp &= 0x1FF;
163         if (trip_temp) {
164                 /* Resolution of 1/2 degree C and an offset of -50C */
165                 ptd->hot_temp = trip_temp * 1000 / 2 - 50000;
166                 ptd->hot_trip_id = *nr_trips;
167                 ++(*nr_trips);
168         }
169
170         pch_wpt_add_acpi_psv_trip(ptd, nr_trips);
171
172         return 0;
173 }
174
175 static int pch_wpt_get_temp(struct pch_thermal_device *ptd, int *temp)
176 {
177         u16 wpt_temp;
178
179         wpt_temp = WPT_TEMP_TSR & readw(ptd->hw_base + WPT_TEMP);
180
181         /* Resolution of 1/2 degree C and an offset of -50C */
182         *temp = (wpt_temp * 1000 / 2 - 50000);
183
184         return 0;
185 }
186
187 static int pch_wpt_suspend(struct pch_thermal_device *ptd)
188 {
189         u8 tsel;
190
191         if (ptd->bios_enabled)
192                 return 0;
193
194         tsel = readb(ptd->hw_base + WPT_TSEL);
195
196         writeb(tsel & 0xFE, ptd->hw_base + WPT_TSEL);
197
198         return 0;
199 }
200
201 static int pch_wpt_resume(struct pch_thermal_device *ptd)
202 {
203         u8 tsel;
204
205         if (ptd->bios_enabled)
206                 return 0;
207
208         tsel = readb(ptd->hw_base + WPT_TSEL);
209
210         writeb(tsel | WPT_TSEL_ETS, ptd->hw_base + WPT_TSEL);
211
212         return 0;
213 }
214
215 struct pch_dev_ops {
216         int (*hw_init)(struct pch_thermal_device *ptd, int *nr_trips);
217         int (*get_temp)(struct pch_thermal_device *ptd, int *temp);
218         int (*suspend)(struct pch_thermal_device *ptd);
219         int (*resume)(struct pch_thermal_device *ptd);
220 };
221
222
223 /* dev ops for Wildcat Point */
224 static const struct pch_dev_ops pch_dev_ops_wpt = {
225         .hw_init = pch_wpt_init,
226         .get_temp = pch_wpt_get_temp,
227         .suspend = pch_wpt_suspend,
228         .resume = pch_wpt_resume,
229 };
230
231 static int pch_thermal_get_temp(struct thermal_zone_device *tzd, int *temp)
232 {
233         struct pch_thermal_device *ptd = tzd->devdata;
234
235         return  ptd->ops->get_temp(ptd, temp);
236 }
237
238 static int pch_get_trip_type(struct thermal_zone_device *tzd, int trip,
239                              enum thermal_trip_type *type)
240 {
241         struct pch_thermal_device *ptd = tzd->devdata;
242
243         if (ptd->crt_trip_id == trip)
244                 *type = THERMAL_TRIP_CRITICAL;
245         else if (ptd->hot_trip_id == trip)
246                 *type = THERMAL_TRIP_HOT;
247         else if (ptd->psv_trip_id == trip)
248                 *type = THERMAL_TRIP_PASSIVE;
249         else
250                 return -EINVAL;
251
252         return 0;
253 }
254
255 static int pch_get_trip_temp(struct thermal_zone_device *tzd, int trip, int *temp)
256 {
257         struct pch_thermal_device *ptd = tzd->devdata;
258
259         if (ptd->crt_trip_id == trip)
260                 *temp = ptd->crt_temp;
261         else if (ptd->hot_trip_id == trip)
262                 *temp = ptd->hot_temp;
263         else if (ptd->psv_trip_id == trip)
264                 *temp = ptd->psv_temp;
265         else
266                 return -EINVAL;
267
268         return 0;
269 }
270
271 static struct thermal_zone_device_ops tzd_ops = {
272         .get_temp = pch_thermal_get_temp,
273         .get_trip_type = pch_get_trip_type,
274         .get_trip_temp = pch_get_trip_temp,
275 };
276
277 enum board_ids {
278         board_hsw,
279         board_wpt,
280         board_skl,
281 };
282
283 static const struct board_info {
284         const char *name;
285         const struct pch_dev_ops *ops;
286 } board_info[] = {
287         [board_hsw] = {
288                 .name = "pch_haswell",
289                 .ops = &pch_dev_ops_wpt,
290         },
291         [board_wpt] = {
292                 .name = "pch_wildcat_point",
293                 .ops = &pch_dev_ops_wpt,
294         },
295         [board_skl] = {
296                 .name = "pch_skylake",
297                 .ops = &pch_dev_ops_wpt,
298         },
299 };
300
301 static int intel_pch_thermal_probe(struct pci_dev *pdev,
302                                    const struct pci_device_id *id)
303 {
304         enum board_ids board_id = id->driver_data;
305         const struct board_info *bi = &board_info[board_id];
306         struct pch_thermal_device *ptd;
307         int err;
308         int nr_trips;
309
310         ptd = devm_kzalloc(&pdev->dev, sizeof(*ptd), GFP_KERNEL);
311         if (!ptd)
312                 return -ENOMEM;
313
314         ptd->ops = bi->ops;
315
316         pci_set_drvdata(pdev, ptd);
317         ptd->pdev = pdev;
318
319         err = pci_enable_device(pdev);
320         if (err) {
321                 dev_err(&pdev->dev, "failed to enable pci device\n");
322                 return err;
323         }
324
325         err = pci_request_regions(pdev, driver_name);
326         if (err) {
327                 dev_err(&pdev->dev, "failed to request pci region\n");
328                 goto error_disable;
329         }
330
331         ptd->hw_base = pci_ioremap_bar(pdev, 0);
332         if (!ptd->hw_base) {
333                 err = -ENOMEM;
334                 dev_err(&pdev->dev, "failed to map mem base\n");
335                 goto error_release;
336         }
337
338         err = ptd->ops->hw_init(ptd, &nr_trips);
339         if (err)
340                 goto error_cleanup;
341
342         ptd->tzd = thermal_zone_device_register(bi->name, nr_trips, 0, ptd,
343                                                 &tzd_ops, NULL, 0, 0);
344         if (IS_ERR(ptd->tzd)) {
345                 dev_err(&pdev->dev, "Failed to register thermal zone %s\n",
346                         bi->name);
347                 err = PTR_ERR(ptd->tzd);
348                 goto error_cleanup;
349         }
350
351         return 0;
352
353 error_cleanup:
354         iounmap(ptd->hw_base);
355 error_release:
356         pci_release_regions(pdev);
357 error_disable:
358         pci_disable_device(pdev);
359         dev_err(&pdev->dev, "pci device failed to probe\n");
360         return err;
361 }
362
363 static void intel_pch_thermal_remove(struct pci_dev *pdev)
364 {
365         struct pch_thermal_device *ptd = pci_get_drvdata(pdev);
366
367         thermal_zone_device_unregister(ptd->tzd);
368         iounmap(ptd->hw_base);
369         pci_set_drvdata(pdev, NULL);
370         pci_release_region(pdev, 0);
371         pci_disable_device(pdev);
372 }
373
374 static int intel_pch_thermal_suspend(struct device *device)
375 {
376         struct pci_dev *pdev = to_pci_dev(device);
377         struct pch_thermal_device *ptd = pci_get_drvdata(pdev);
378
379         return ptd->ops->suspend(ptd);
380 }
381
382 static int intel_pch_thermal_resume(struct device *device)
383 {
384         struct pci_dev *pdev = to_pci_dev(device);
385         struct pch_thermal_device *ptd = pci_get_drvdata(pdev);
386
387         return ptd->ops->resume(ptd);
388 }
389
390 static const struct pci_device_id intel_pch_thermal_id[] = {
391         { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_HSW_1),
392                 .driver_data = board_hsw, },
393         { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_HSW_2),
394                 .driver_data = board_hsw, },
395         { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_WPT),
396                 .driver_data = board_wpt, },
397         { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_SKL),
398                 .driver_data = board_skl, },
399         { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_SKL_H),
400                 .driver_data = board_skl, },
401         { 0, },
402 };
403 MODULE_DEVICE_TABLE(pci, intel_pch_thermal_id);
404
405 static const struct dev_pm_ops intel_pch_pm_ops = {
406         .suspend = intel_pch_thermal_suspend,
407         .resume = intel_pch_thermal_resume,
408 };
409
410 static struct pci_driver intel_pch_thermal_driver = {
411         .name           = "intel_pch_thermal",
412         .id_table       = intel_pch_thermal_id,
413         .probe          = intel_pch_thermal_probe,
414         .remove         = intel_pch_thermal_remove,
415         .driver.pm      = &intel_pch_pm_ops,
416 };
417
418 module_pci_driver(intel_pch_thermal_driver);
419
420 MODULE_LICENSE("GPL v2");
421 MODULE_DESCRIPTION("Intel PCH Thermal driver");