aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTushar Dave <tushar.n.dave@intel.com>2015-06-10 16:34:24 -0400
committerZhang Rui <rui.zhang@intel.com>2015-08-03 22:06:08 -0400
commitd0a12625d2ff2c63321b3cf48c48184748ab577a (patch)
tree62862e5ff7093cd72f4e64f4d318a76067468fb6
parent1f22dc4494e203d6987fc708f414b4adf8614036 (diff)
thermal: Add Intel PCH thermal driver
This change adds a thermal driver for Wildcat Point platform controller hub. This driver register PCH thermal sensor as a thermal zone and associate critical and hot trips if present. Signed-off-by: Tushar Dave <tushar.n.dave@intel.com> Reviewed-by: Pandruvada, Srinivas <srinivas.pandruvada@intel.com> Signed-off-by: Zhang Rui <rui.zhang@intel.com>
-rw-r--r--drivers/thermal/Kconfig8
-rw-r--r--drivers/thermal/Makefile1
-rw-r--r--drivers/thermal/intel_pch_thermal.c286
3 files changed, 295 insertions, 0 deletions
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index 118938ee8552..039004400987 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -340,6 +340,14 @@ config ACPI_THERMAL_REL
340 tristate 340 tristate
341 depends on ACPI 341 depends on ACPI
342 342
343config INTEL_PCH_THERMAL
344 tristate "Intel PCH Thermal Reporting Driver"
345 depends on X86 && PCI
346 help
347 Enable this to support thermal reporting on certain intel PCHs.
348 Thermal reporting device will provide temperature reading,
349 programmable trip points and other information.
350
343menu "Texas Instruments thermal drivers" 351menu "Texas Instruments thermal drivers"
344source "drivers/thermal/ti-soc-thermal/Kconfig" 352source "drivers/thermal/ti-soc-thermal/Kconfig"
345endmenu 353endmenu
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
index 535dfee1496f..26f160809959 100644
--- a/drivers/thermal/Makefile
+++ b/drivers/thermal/Makefile
@@ -41,6 +41,7 @@ obj-$(CONFIG_INTEL_SOC_DTS_THERMAL) += intel_soc_dts_thermal.o
41obj-$(CONFIG_INTEL_QUARK_DTS_THERMAL) += intel_quark_dts_thermal.o 41obj-$(CONFIG_INTEL_QUARK_DTS_THERMAL) += intel_quark_dts_thermal.o
42obj-$(CONFIG_TI_SOC_THERMAL) += ti-soc-thermal/ 42obj-$(CONFIG_TI_SOC_THERMAL) += ti-soc-thermal/
43obj-$(CONFIG_INT340X_THERMAL) += int340x_thermal/ 43obj-$(CONFIG_INT340X_THERMAL) += int340x_thermal/
44obj-$(CONFIG_INTEL_PCH_THERMAL) += intel_pch_thermal.o
44obj-$(CONFIG_ST_THERMAL) += st/ 45obj-$(CONFIG_ST_THERMAL) += st/
45obj-$(CONFIG_TEGRA_SOCTHERM) += tegra_soctherm.o 46obj-$(CONFIG_TEGRA_SOCTHERM) += tegra_soctherm.o
46obj-$(CONFIG_HISI_THERMAL) += hisi_thermal.o 47obj-$(CONFIG_HISI_THERMAL) += hisi_thermal.o
diff --git a/drivers/thermal/intel_pch_thermal.c b/drivers/thermal/intel_pch_thermal.c
new file mode 100644
index 000000000000..1650a62484bd
--- /dev/null
+++ b/drivers/thermal/intel_pch_thermal.c
@@ -0,0 +1,286 @@
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/thermal.h>
24
25/* Intel PCH thermal Device IDs */
26#define PCH_THERMAL_DID_WPT 0x9CA4 /* Wildcat Point */
27
28/* Wildcat Point-LP PCH Thermal registers */
29#define WPT_TEMP 0x0000 /* Temperature */
30#define WPT_TSC 0x04 /* Thermal Sensor Control */
31#define WPT_TSS 0x06 /* Thermal Sensor Status */
32#define WPT_TSEL 0x08 /* Thermal Sensor Enable and Lock */
33#define WPT_TSREL 0x0A /* Thermal Sensor Report Enable and Lock */
34#define WPT_TSMIC 0x0C /* Thermal Sensor SMI Control */
35#define WPT_CTT 0x0010 /* Catastrophic Trip Point */
36#define WPT_TAHV 0x0014 /* Thermal Alert High Value */
37#define WPT_TALV 0x0018 /* Thermal Alert Low Value */
38#define WPT_TL 0x00000040 /* Throttle Value */
39#define WPT_PHL 0x0060 /* PCH Hot Level */
40#define WPT_PHLC 0x62 /* PHL Control */
41#define WPT_TAS 0x80 /* Thermal Alert Status */
42#define WPT_TSPIEN 0x82 /* PCI Interrupt Event Enables */
43#define WPT_TSGPEN 0x84 /* General Purpose Event Enables */
44
45/* Wildcat Point-LP PCH Thermal Register bit definitions */
46#define WPT_TEMP_TSR 0x00ff /* Temp TS Reading */
47#define WPT_TSC_CPDE 0x01 /* Catastrophic Power-Down Enable */
48#define WPT_TSS_TSDSS 0x10 /* Thermal Sensor Dynamic Shutdown Status */
49#define WPT_TSS_GPES 0x08 /* GPE status */
50#define WPT_TSEL_ETS 0x01 /* Enable TS */
51#define WPT_TSEL_PLDB 0x80 /* TSEL Policy Lock-Down Bit */
52#define WPT_TL_TOL 0x000001FF /* T0 Level */
53#define WPT_TL_T1L 0x1ff00000 /* T1 Level */
54#define WPT_TL_TTEN 0x20000000 /* TT Enable */
55
56static char driver_name[] = "Intel PCH thermal driver";
57
58struct pch_thermal_device {
59 void __iomem *hw_base;
60 const struct pch_dev_ops *ops;
61 struct pci_dev *pdev;
62 struct thermal_zone_device *tzd;
63 int crt_trip_id;
64 unsigned long crt_temp;
65 int hot_trip_id;
66 unsigned long hot_temp;
67};
68
69static int pch_wpt_init(struct pch_thermal_device *ptd, int *nr_trips)
70{
71 u8 tsel;
72 u16 trip_temp;
73
74 *nr_trips = 0;
75
76 /* Check if BIOS has already enabled thermal sensor */
77 if (WPT_TSS_TSDSS & readb(ptd->hw_base + WPT_TSS))
78 goto read_trips;
79
80 tsel = readb(ptd->hw_base + WPT_TSEL);
81 /*
82 * When TSEL's Policy Lock-Down bit is 1, TSEL become RO.
83 * If so, thermal sensor cannot enable. Bail out.
84 */
85 if (tsel & WPT_TSEL_PLDB) {
86 dev_err(&ptd->pdev->dev, "Sensor can't be enabled\n");
87 return -ENODEV;
88 }
89
90 writeb(tsel|WPT_TSEL_ETS, ptd->hw_base + WPT_TSEL);
91 if (!(WPT_TSS_TSDSS & readb(ptd->hw_base + WPT_TSS))) {
92 dev_err(&ptd->pdev->dev, "Sensor can't be enabled\n");
93 return -ENODEV;
94 }
95
96read_trips:
97 ptd->crt_trip_id = -1;
98 trip_temp = readw(ptd->hw_base + WPT_CTT);
99 trip_temp &= 0x1FF;
100 if (trip_temp) {
101 /* Resolution of 1/2 degree C and an offset of -50C */
102 ptd->crt_temp = trip_temp * 1000 / 2 - 50000;
103 ptd->crt_trip_id = 0;
104 ++(*nr_trips);
105 }
106
107 ptd->hot_trip_id = -1;
108 trip_temp = readw(ptd->hw_base + WPT_PHL);
109 trip_temp &= 0x1FF;
110 if (trip_temp) {
111 /* Resolution of 1/2 degree C and an offset of -50C */
112 ptd->hot_temp = trip_temp * 1000 / 2 - 50000;
113 ptd->hot_trip_id = *nr_trips;
114 ++(*nr_trips);
115 }
116
117 return 0;
118}
119
120static int pch_wpt_get_temp(struct pch_thermal_device *ptd,
121 unsigned long *temp)
122{
123 u8 wpt_temp;
124
125 wpt_temp = WPT_TEMP_TSR & readl(ptd->hw_base + WPT_TEMP);
126
127 /* Resolution of 1/2 degree C and an offset of -50C */
128 *temp = (wpt_temp * 1000 / 2 - 50000);
129
130 return 0;
131}
132
133struct pch_dev_ops {
134 int (*hw_init)(struct pch_thermal_device *ptd, int *nr_trips);
135 int (*get_temp)(struct pch_thermal_device *ptd, unsigned long *temp);
136};
137
138
139/* dev ops for Wildcat Point */
140static struct pch_dev_ops pch_dev_ops_wpt = {
141 .hw_init = pch_wpt_init,
142 .get_temp = pch_wpt_get_temp,
143};
144
145static int pch_thermal_get_temp(struct thermal_zone_device *tzd,
146 unsigned long *temp)
147{
148 struct pch_thermal_device *ptd = tzd->devdata;
149
150 return ptd->ops->get_temp(ptd, temp);
151}
152
153static int pch_get_trip_type(struct thermal_zone_device *tzd, int trip,
154 enum thermal_trip_type *type)
155{
156 struct pch_thermal_device *ptd = tzd->devdata;
157
158 if (ptd->crt_trip_id == trip)
159 *type = THERMAL_TRIP_CRITICAL;
160 else if (ptd->hot_trip_id == trip)
161 *type = THERMAL_TRIP_HOT;
162 else
163 return -EINVAL;
164
165 return 0;
166}
167
168static int pch_get_trip_temp(struct thermal_zone_device *tzd, int trip,
169 unsigned long *temp)
170{
171 struct pch_thermal_device *ptd = tzd->devdata;
172
173 if (ptd->crt_trip_id == trip)
174 *temp = ptd->crt_temp;
175 else if (ptd->hot_trip_id == trip)
176 *temp = ptd->hot_temp;
177 else
178 return -EINVAL;
179
180 return 0;
181}
182
183static struct thermal_zone_device_ops tzd_ops = {
184 .get_temp = pch_thermal_get_temp,
185 .get_trip_type = pch_get_trip_type,
186 .get_trip_temp = pch_get_trip_temp,
187};
188
189
190static int intel_pch_thermal_probe(struct pci_dev *pdev,
191 const struct pci_device_id *id)
192{
193 struct pch_thermal_device *ptd;
194 int err;
195 int nr_trips;
196 char *dev_name;
197
198 ptd = devm_kzalloc(&pdev->dev, sizeof(*ptd), GFP_KERNEL);
199 if (!ptd)
200 return -ENOMEM;
201
202 switch (pdev->device) {
203 case PCH_THERMAL_DID_WPT:
204 ptd->ops = &pch_dev_ops_wpt;
205 dev_name = "pch_wildcat_point";
206 break;
207 default:
208 dev_err(&pdev->dev, "unknown pch thermal device\n");
209 return -ENODEV;
210 }
211
212 pci_set_drvdata(pdev, ptd);
213 ptd->pdev = pdev;
214
215 err = pci_enable_device(pdev);
216 if (err) {
217 dev_err(&pdev->dev, "failed to enable pci device\n");
218 return err;
219 }
220
221 err = pci_request_regions(pdev, driver_name);
222 if (err) {
223 dev_err(&pdev->dev, "failed to request pci region\n");
224 goto error_disable;
225 }
226
227 ptd->hw_base = pci_ioremap_bar(pdev, 0);
228 if (!ptd->hw_base) {
229 err = -ENOMEM;
230 dev_err(&pdev->dev, "failed to map mem base\n");
231 goto error_release;
232 }
233
234 err = ptd->ops->hw_init(ptd, &nr_trips);
235 if (err)
236 goto error_cleanup;
237
238 ptd->tzd = thermal_zone_device_register(dev_name, nr_trips, 0, ptd,
239 &tzd_ops, NULL, 0, 0);
240 if (IS_ERR(ptd->tzd)) {
241 dev_err(&pdev->dev, "Failed to register thermal zone %s\n",
242 dev_name);
243 err = PTR_ERR(ptd->tzd);
244 goto error_cleanup;
245 }
246
247 return 0;
248
249error_cleanup:
250 iounmap(ptd->hw_base);
251error_release:
252 pci_release_regions(pdev);
253error_disable:
254 pci_disable_device(pdev);
255 dev_err(&pdev->dev, "pci device failed to probe\n");
256 return err;
257}
258
259static void intel_pch_thermal_remove(struct pci_dev *pdev)
260{
261 struct pch_thermal_device *ptd = pci_get_drvdata(pdev);
262
263 thermal_zone_device_unregister(ptd->tzd);
264 iounmap(ptd->hw_base);
265 pci_set_drvdata(pdev, NULL);
266 pci_release_region(pdev, 0);
267 pci_disable_device(pdev);
268}
269
270static struct pci_device_id intel_pch_thermal_id[] = {
271 { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_WPT) },
272 { 0, },
273};
274MODULE_DEVICE_TABLE(pci, intel_pch_thermal_id);
275
276static struct pci_driver intel_pch_thermal_driver = {
277 .name = "intel_pch_thermal",
278 .id_table = intel_pch_thermal_id,
279 .probe = intel_pch_thermal_probe,
280 .remove = intel_pch_thermal_remove,
281};
282
283module_pci_driver(intel_pch_thermal_driver);
284
285MODULE_LICENSE("GPL v2");
286MODULE_DESCRIPTION("Intel PCH Thermal driver");