aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBin Gao <bin.gao@linux.intel.com>2016-08-29 12:55:04 -0400
committerZhang Rui <rui.zhang@intel.com>2016-09-05 18:55:33 -0400
commitb474303ffd57e0a379ce73ca10232350f866f77b (patch)
treeee0fcc4b1914cdc75e3473fc5d352144b9d60f06
parentc6935931c1894ff857616ff8549b61236a19148f (diff)
thermal: add Intel BXT WhiskeyCove PMIC thermal driver
This change adds support for Intel BXT Whiskey Cove PMIC thermal driver which is intended to handle the alert interrupts triggered upon thermal trip point cross and notify the thermal framework appropriately with the zone, temp, crossed trip and event details. Signed-off-by: Yegnesh S Iyer <yegnesh.s.iyer@intel.com> Signed-off-by: Bin Gao <bin.gao@intel.com> Signed-off-by: Zhang Rui <rui.zhang@intel.com>
-rw-r--r--drivers/thermal/Kconfig10
-rw-r--r--drivers/thermal/Makefile1
-rw-r--r--drivers/thermal/intel_bxt_pmic_thermal.c299
3 files changed, 310 insertions, 0 deletions
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index 2d702ca6556f..9bdd62462f0b 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -332,6 +332,16 @@ menu "ACPI INT340X thermal drivers"
332source drivers/thermal/int340x_thermal/Kconfig 332source drivers/thermal/int340x_thermal/Kconfig
333endmenu 333endmenu
334 334
335config INTEL_BXT_PMIC_THERMAL
336 tristate "Intel Broxton PMIC thermal driver"
337 depends on X86 && INTEL_SOC_PMIC && REGMAP
338 help
339 Select this driver for Intel Broxton PMIC with ADC channels monitoring
340 system temperature measurements and alerts.
341 This driver is used for monitoring the ADC channels of PMIC and handles
342 the alert trip point interrupts and notifies the thermal framework with
343 the trip point and temperature details of the zone.
344
335config INTEL_PCH_THERMAL 345config INTEL_PCH_THERMAL
336 tristate "Intel PCH Thermal Reporting Driver" 346 tristate "Intel PCH Thermal Reporting Driver"
337 depends on X86 && PCI 347 depends on X86 && PCI
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
index 10b07c14f8a9..7aa7f4c2e3da 100644
--- a/drivers/thermal/Makefile
+++ b/drivers/thermal/Makefile
@@ -45,6 +45,7 @@ obj-$(CONFIG_INTEL_SOC_DTS_THERMAL) += intel_soc_dts_thermal.o
45obj-$(CONFIG_INTEL_QUARK_DTS_THERMAL) += intel_quark_dts_thermal.o 45obj-$(CONFIG_INTEL_QUARK_DTS_THERMAL) += intel_quark_dts_thermal.o
46obj-$(CONFIG_TI_SOC_THERMAL) += ti-soc-thermal/ 46obj-$(CONFIG_TI_SOC_THERMAL) += ti-soc-thermal/
47obj-$(CONFIG_INT340X_THERMAL) += int340x_thermal/ 47obj-$(CONFIG_INT340X_THERMAL) += int340x_thermal/
48obj-$(CONFIG_INTEL_BXT_PMIC_THERMAL) += intel_bxt_pmic_thermal.o
48obj-$(CONFIG_INTEL_PCH_THERMAL) += intel_pch_thermal.o 49obj-$(CONFIG_INTEL_PCH_THERMAL) += intel_pch_thermal.o
49obj-$(CONFIG_ST_THERMAL) += st/ 50obj-$(CONFIG_ST_THERMAL) += st/
50obj-$(CONFIG_TEGRA_SOCTHERM) += tegra/ 51obj-$(CONFIG_TEGRA_SOCTHERM) += tegra/
diff --git a/drivers/thermal/intel_bxt_pmic_thermal.c b/drivers/thermal/intel_bxt_pmic_thermal.c
new file mode 100644
index 000000000000..4ae3e0c1576a
--- /dev/null
+++ b/drivers/thermal/intel_bxt_pmic_thermal.c
@@ -0,0 +1,299 @@
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
38struct 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
48struct thermal_irq_map {
49 char handle[20];
50 int num_trips;
51 const struct trip_config_map *trip_config;
52};
53
54struct pmic_thermal_data {
55 const struct thermal_irq_map *maps;
56 int num_maps;
57};
58
59static 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
80static 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
101static 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
122static 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
134static 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
157static 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
162static 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
209 /* Clear the appropriate irq */
210 regmap_write(regmap, reg, reg_val & mask);
211 }
212 }
213
214 return IRQ_HANDLED;
215}
216
217static int pmic_thermal_probe(struct platform_device *pdev)
218{
219 struct regmap_irq_chip_data *regmap_irq_chip;
220 struct pmic_thermal_data *thermal_data;
221 int ret, irq, virq, i, j, pmic_irq_count;
222 struct intel_soc_pmic *pmic;
223 struct regmap *regmap;
224 struct device *dev;
225 u16 reg;
226 u8 mask;
227
228 dev = &pdev->dev;
229 pmic = dev_get_drvdata(pdev->dev.parent);
230 if (!pmic) {
231 dev_err(dev, "Failed to get struct intel_soc_pmic pointer\n");
232 return -ENODEV;
233 }
234
235 thermal_data = (struct pmic_thermal_data *)
236 platform_get_device_id(pdev)->driver_data;
237 if (!thermal_data) {
238 dev_err(dev, "No thermal data initialized!!\n");
239 return -ENODEV;
240 }
241
242 regmap = pmic->regmap;
243 regmap_irq_chip = pmic->irq_chip_data_level2;
244
245 pmic_irq_count = 0;
246 while ((irq = platform_get_irq(pdev, pmic_irq_count)) != -ENXIO) {
247 virq = regmap_irq_get_virq(regmap_irq_chip, irq);
248 if (virq < 0) {
249 dev_err(dev, "failed to get virq by irq %d\n", irq);
250 return virq;
251 }
252
253 ret = devm_request_threaded_irq(&pdev->dev, virq,
254 NULL, pmic_thermal_irq_handler,
255 IRQF_ONESHOT, "pmic_thermal", pdev);
256
257 if (ret) {
258 dev_err(dev, "request irq(%d) failed: %d\n", virq, ret);
259 return ret;
260 }
261 pmic_irq_count++;
262 }
263
264 /* Enable thermal interrupts */
265 for (i = 0; i < thermal_data->num_maps; i++) {
266 for (j = 0; j < thermal_data->maps[i].num_trips; j++) {
267 reg = thermal_data->maps[i].trip_config[j].irq_en;
268 mask = thermal_data->maps[i].trip_config[j].irq_en_mask;
269 ret = regmap_update_bits(regmap, reg, mask, 0x00);
270 if (ret)
271 return ret;
272 }
273 }
274
275 return 0;
276}
277
278static const struct platform_device_id pmic_thermal_id_table[] = {
279 {
280 .name = "bxt_wcove_thermal",
281 .driver_data = (kernel_ulong_t)&bxtwc_thermal_data,
282 },
283 {},
284};
285
286static struct platform_driver pmic_thermal_driver = {
287 .probe = pmic_thermal_probe,
288 .driver = {
289 .name = "pmic_thermal",
290 },
291 .id_table = pmic_thermal_id_table,
292};
293
294MODULE_DEVICE_TABLE(platform, pmic_thermal_id_table);
295module_platform_driver(pmic_thermal_driver);
296
297MODULE_AUTHOR("Yegnesh S Iyer <yegnesh.s.iyer@intel.com>");
298MODULE_DESCRIPTION("Intel Broxton PMIC Thermal Driver");
299MODULE_LICENSE("GPL v2");