]>
Commit | Line | Data |
---|---|---|
ee073604 SP |
1 | /* |
2 | * intel_soc_dts_iosf.c | |
3 | * Copyright (c) 2015, Intel Corporation. | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify it | |
6 | * under the terms and conditions of the GNU General Public License, | |
7 | * version 2, as published by the Free Software Foundation. | |
8 | * | |
9 | * This program is distributed in the hope it will be useful, but WITHOUT | |
10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
12 | * more details. | |
13 | * | |
14 | */ | |
15 | ||
16 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | |
17 | ||
18 | #include <linux/module.h> | |
19 | #include <linux/slab.h> | |
20 | #include <linux/interrupt.h> | |
21 | #include <asm/iosf_mbi.h> | |
22 | #include "intel_soc_dts_iosf.h" | |
23 | ||
24 | #define SOC_DTS_OFFSET_ENABLE 0xB0 | |
25 | #define SOC_DTS_OFFSET_TEMP 0xB1 | |
26 | ||
27 | #define SOC_DTS_OFFSET_PTPS 0xB2 | |
28 | #define SOC_DTS_OFFSET_PTTS 0xB3 | |
29 | #define SOC_DTS_OFFSET_PTTSS 0xB4 | |
30 | #define SOC_DTS_OFFSET_PTMC 0x80 | |
31 | #define SOC_DTS_TE_AUX0 0xB5 | |
32 | #define SOC_DTS_TE_AUX1 0xB6 | |
33 | ||
34 | #define SOC_DTS_AUX0_ENABLE_BIT BIT(0) | |
35 | #define SOC_DTS_AUX1_ENABLE_BIT BIT(1) | |
36 | #define SOC_DTS_CPU_MODULE0_ENABLE_BIT BIT(16) | |
37 | #define SOC_DTS_CPU_MODULE1_ENABLE_BIT BIT(17) | |
38 | #define SOC_DTS_TE_SCI_ENABLE BIT(9) | |
39 | #define SOC_DTS_TE_SMI_ENABLE BIT(10) | |
40 | #define SOC_DTS_TE_MSI_ENABLE BIT(11) | |
41 | #define SOC_DTS_TE_APICA_ENABLE BIT(14) | |
42 | #define SOC_DTS_PTMC_APIC_DEASSERT_BIT BIT(4) | |
43 | ||
44 | /* DTS encoding for TJ MAX temperature */ | |
45 | #define SOC_DTS_TJMAX_ENCODING 0x7F | |
46 | ||
47 | /* Only 2 out of 4 is allowed for OSPM */ | |
48 | #define SOC_MAX_DTS_TRIPS 2 | |
49 | ||
50 | /* Mask for two trips in status bits */ | |
51 | #define SOC_DTS_TRIP_MASK 0x03 | |
52 | ||
53 | /* DTS0 and DTS 1 */ | |
54 | #define SOC_MAX_DTS_SENSORS 2 | |
55 | ||
56 | static int get_tj_max(u32 *tj_max) | |
57 | { | |
58 | u32 eax, edx; | |
59 | u32 val; | |
60 | int err; | |
61 | ||
62 | err = rdmsr_safe(MSR_IA32_TEMPERATURE_TARGET, &eax, &edx); | |
63 | if (err) | |
64 | goto err_ret; | |
65 | else { | |
66 | val = (eax >> 16) & 0xff; | |
67 | if (val) | |
68 | *tj_max = val * 1000; | |
69 | else { | |
70 | err = -EINVAL; | |
71 | goto err_ret; | |
72 | } | |
73 | } | |
74 | ||
75 | return 0; | |
76 | err_ret: | |
77 | *tj_max = 0; | |
78 | ||
79 | return err; | |
80 | } | |
81 | ||
82 | static int sys_get_trip_temp(struct thermal_zone_device *tzd, int trip, | |
17e8351a | 83 | int *temp) |
ee073604 SP |
84 | { |
85 | int status; | |
86 | u32 out; | |
87 | struct intel_soc_dts_sensor_entry *dts; | |
88 | struct intel_soc_dts_sensors *sensors; | |
89 | ||
90 | dts = tzd->devdata; | |
91 | sensors = dts->sensors; | |
92 | mutex_lock(&sensors->dts_update_lock); | |
93 | status = iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_READ, | |
94 | SOC_DTS_OFFSET_PTPS, &out); | |
95 | mutex_unlock(&sensors->dts_update_lock); | |
96 | if (status) | |
97 | return status; | |
98 | ||
99 | out = (out >> (trip * 8)) & SOC_DTS_TJMAX_ENCODING; | |
100 | if (!out) | |
101 | *temp = 0; | |
102 | else | |
103 | *temp = sensors->tj_max - out * 1000; | |
104 | ||
105 | return 0; | |
106 | } | |
107 | ||
108 | static int update_trip_temp(struct intel_soc_dts_sensor_entry *dts, | |
17e8351a | 109 | int thres_index, int temp, |
ee073604 SP |
110 | enum thermal_trip_type trip_type) |
111 | { | |
112 | int status; | |
113 | u32 temp_out; | |
114 | u32 out; | |
115 | u32 store_ptps; | |
116 | u32 store_ptmc; | |
117 | u32 store_te_out; | |
118 | u32 te_out; | |
119 | u32 int_enable_bit = SOC_DTS_TE_APICA_ENABLE; | |
120 | struct intel_soc_dts_sensors *sensors = dts->sensors; | |
121 | ||
122 | if (sensors->intr_type == INTEL_SOC_DTS_INTERRUPT_MSI) | |
123 | int_enable_bit |= SOC_DTS_TE_MSI_ENABLE; | |
124 | ||
125 | temp_out = (sensors->tj_max - temp) / 1000; | |
126 | ||
127 | status = iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_READ, | |
128 | SOC_DTS_OFFSET_PTPS, &store_ptps); | |
129 | if (status) | |
130 | return status; | |
131 | ||
132 | out = (store_ptps & ~(0xFF << (thres_index * 8))); | |
133 | out |= (temp_out & 0xFF) << (thres_index * 8); | |
134 | status = iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_WRITE, | |
135 | SOC_DTS_OFFSET_PTPS, out); | |
136 | if (status) | |
137 | return status; | |
138 | ||
139 | pr_debug("update_trip_temp PTPS = %x\n", out); | |
140 | status = iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_READ, | |
141 | SOC_DTS_OFFSET_PTMC, &out); | |
142 | if (status) | |
143 | goto err_restore_ptps; | |
144 | ||
145 | store_ptmc = out; | |
146 | ||
147 | status = iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_READ, | |
148 | SOC_DTS_TE_AUX0 + thres_index, | |
149 | &te_out); | |
150 | if (status) | |
151 | goto err_restore_ptmc; | |
152 | ||
153 | store_te_out = te_out; | |
154 | /* Enable for CPU module 0 and module 1 */ | |
155 | out |= (SOC_DTS_CPU_MODULE0_ENABLE_BIT | | |
156 | SOC_DTS_CPU_MODULE1_ENABLE_BIT); | |
157 | if (temp) { | |
158 | if (thres_index) | |
159 | out |= SOC_DTS_AUX1_ENABLE_BIT; | |
160 | else | |
161 | out |= SOC_DTS_AUX0_ENABLE_BIT; | |
162 | te_out |= int_enable_bit; | |
163 | } else { | |
164 | if (thres_index) | |
165 | out &= ~SOC_DTS_AUX1_ENABLE_BIT; | |
166 | else | |
167 | out &= ~SOC_DTS_AUX0_ENABLE_BIT; | |
168 | te_out &= ~int_enable_bit; | |
169 | } | |
170 | status = iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_WRITE, | |
171 | SOC_DTS_OFFSET_PTMC, out); | |
172 | if (status) | |
173 | goto err_restore_te_out; | |
174 | ||
175 | status = iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_WRITE, | |
176 | SOC_DTS_TE_AUX0 + thres_index, | |
177 | te_out); | |
178 | if (status) | |
179 | goto err_restore_te_out; | |
180 | ||
181 | dts->trip_types[thres_index] = trip_type; | |
182 | ||
183 | return 0; | |
184 | err_restore_te_out: | |
185 | iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_WRITE, | |
186 | SOC_DTS_OFFSET_PTMC, store_te_out); | |
187 | err_restore_ptmc: | |
188 | iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_WRITE, | |
189 | SOC_DTS_OFFSET_PTMC, store_ptmc); | |
190 | err_restore_ptps: | |
191 | iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_WRITE, | |
192 | SOC_DTS_OFFSET_PTPS, store_ptps); | |
193 | /* Nothing we can do if restore fails */ | |
194 | ||
195 | return status; | |
196 | } | |
197 | ||
198 | static int sys_set_trip_temp(struct thermal_zone_device *tzd, int trip, | |
17e8351a | 199 | int temp) |
ee073604 SP |
200 | { |
201 | struct intel_soc_dts_sensor_entry *dts = tzd->devdata; | |
202 | struct intel_soc_dts_sensors *sensors = dts->sensors; | |
203 | int status; | |
204 | ||
205 | if (temp > sensors->tj_max) | |
206 | return -EINVAL; | |
207 | ||
208 | mutex_lock(&sensors->dts_update_lock); | |
209 | status = update_trip_temp(tzd->devdata, trip, temp, | |
210 | dts->trip_types[trip]); | |
211 | mutex_unlock(&sensors->dts_update_lock); | |
212 | ||
213 | return status; | |
214 | } | |
215 | ||
216 | static int sys_get_trip_type(struct thermal_zone_device *tzd, | |
217 | int trip, enum thermal_trip_type *type) | |
218 | { | |
219 | struct intel_soc_dts_sensor_entry *dts; | |
220 | ||
221 | dts = tzd->devdata; | |
222 | ||
223 | *type = dts->trip_types[trip]; | |
224 | ||
225 | return 0; | |
226 | } | |
227 | ||
228 | static int sys_get_curr_temp(struct thermal_zone_device *tzd, | |
17e8351a | 229 | int *temp) |
ee073604 SP |
230 | { |
231 | int status; | |
232 | u32 out; | |
233 | struct intel_soc_dts_sensor_entry *dts; | |
234 | struct intel_soc_dts_sensors *sensors; | |
235 | ||
236 | dts = tzd->devdata; | |
237 | sensors = dts->sensors; | |
238 | status = iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_READ, | |
239 | SOC_DTS_OFFSET_TEMP, &out); | |
240 | if (status) | |
241 | return status; | |
242 | ||
243 | out = (out & dts->temp_mask) >> dts->temp_shift; | |
244 | out -= SOC_DTS_TJMAX_ENCODING; | |
245 | *temp = sensors->tj_max - out * 1000; | |
246 | ||
247 | return 0; | |
248 | } | |
249 | ||
250 | static struct thermal_zone_device_ops tzone_ops = { | |
251 | .get_temp = sys_get_curr_temp, | |
252 | .get_trip_temp = sys_get_trip_temp, | |
253 | .get_trip_type = sys_get_trip_type, | |
254 | .set_trip_temp = sys_set_trip_temp, | |
255 | }; | |
256 | ||
257 | static int soc_dts_enable(int id) | |
258 | { | |
259 | u32 out; | |
260 | int ret; | |
261 | ||
262 | ret = iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_READ, | |
263 | SOC_DTS_OFFSET_ENABLE, &out); | |
264 | if (ret) | |
265 | return ret; | |
266 | ||
267 | if (!(out & BIT(id))) { | |
268 | out |= BIT(id); | |
269 | ret = iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_WRITE, | |
270 | SOC_DTS_OFFSET_ENABLE, out); | |
271 | if (ret) | |
272 | return ret; | |
273 | } | |
274 | ||
275 | return ret; | |
276 | } | |
277 | ||
278 | static void remove_dts_thermal_zone(struct intel_soc_dts_sensor_entry *dts) | |
279 | { | |
280 | if (dts) { | |
281 | iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_WRITE, | |
282 | SOC_DTS_OFFSET_ENABLE, dts->store_status); | |
283 | thermal_zone_device_unregister(dts->tzone); | |
284 | } | |
285 | } | |
286 | ||
287 | static int add_dts_thermal_zone(int id, struct intel_soc_dts_sensor_entry *dts, | |
288 | bool notification_support, int trip_cnt, | |
289 | int read_only_trip_cnt) | |
290 | { | |
291 | char name[10]; | |
292 | int trip_count = 0; | |
293 | int trip_mask = 0; | |
294 | u32 store_ptps; | |
295 | int ret; | |
296 | int i; | |
297 | ||
298 | /* Store status to restor on exit */ | |
299 | ret = iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_READ, | |
300 | SOC_DTS_OFFSET_ENABLE, | |
301 | &dts->store_status); | |
302 | if (ret) | |
303 | goto err_ret; | |
304 | ||
305 | dts->id = id; | |
306 | dts->temp_mask = 0x00FF << (id * 8); | |
307 | dts->temp_shift = id * 8; | |
308 | if (notification_support) { | |
309 | trip_count = min(SOC_MAX_DTS_TRIPS, trip_cnt); | |
310 | trip_mask = BIT(trip_count - read_only_trip_cnt) - 1; | |
311 | } | |
312 | ||
313 | /* Check if the writable trip we provide is not used by BIOS */ | |
314 | ret = iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_READ, | |
315 | SOC_DTS_OFFSET_PTPS, &store_ptps); | |
316 | if (ret) | |
317 | trip_mask = 0; | |
318 | else { | |
319 | for (i = 0; i < trip_count; ++i) { | |
320 | if (trip_mask & BIT(i)) | |
321 | if (store_ptps & (0xff << (i * 8))) | |
322 | trip_mask &= ~BIT(i); | |
323 | } | |
324 | } | |
325 | dts->trip_mask = trip_mask; | |
326 | dts->trip_count = trip_count; | |
327 | snprintf(name, sizeof(name), "soc_dts%d", id); | |
328 | dts->tzone = thermal_zone_device_register(name, | |
329 | trip_count, | |
330 | trip_mask, | |
331 | dts, &tzone_ops, | |
332 | NULL, 0, 0); | |
333 | if (IS_ERR(dts->tzone)) { | |
334 | ret = PTR_ERR(dts->tzone); | |
335 | goto err_ret; | |
336 | } | |
337 | ||
338 | ret = soc_dts_enable(id); | |
339 | if (ret) | |
340 | goto err_enable; | |
341 | ||
342 | return 0; | |
343 | err_enable: | |
344 | thermal_zone_device_unregister(dts->tzone); | |
345 | err_ret: | |
346 | return ret; | |
347 | } | |
348 | ||
349 | int intel_soc_dts_iosf_add_read_only_critical_trip( | |
350 | struct intel_soc_dts_sensors *sensors, int critical_offset) | |
351 | { | |
352 | int i, j; | |
ee073604 SP |
353 | |
354 | for (i = 0; i < SOC_MAX_DTS_SENSORS; ++i) { | |
355 | for (j = 0; j < sensors->soc_dts[i].trip_count; ++j) { | |
356 | if (!(sensors->soc_dts[i].trip_mask & BIT(j))) { | |
bd7081ae | 357 | return update_trip_temp(&sensors->soc_dts[i], j, |
ee073604 SP |
358 | sensors->tj_max - critical_offset, |
359 | THERMAL_TRIP_CRITICAL); | |
ee073604 SP |
360 | } |
361 | } | |
362 | } | |
363 | ||
364 | return -EINVAL; | |
365 | } | |
366 | EXPORT_SYMBOL_GPL(intel_soc_dts_iosf_add_read_only_critical_trip); | |
367 | ||
368 | void intel_soc_dts_iosf_interrupt_handler(struct intel_soc_dts_sensors *sensors) | |
369 | { | |
370 | u32 sticky_out; | |
371 | int status; | |
372 | u32 ptmc_out; | |
373 | unsigned long flags; | |
374 | ||
375 | spin_lock_irqsave(&sensors->intr_notify_lock, flags); | |
376 | ||
377 | status = iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_READ, | |
378 | SOC_DTS_OFFSET_PTMC, &ptmc_out); | |
379 | ptmc_out |= SOC_DTS_PTMC_APIC_DEASSERT_BIT; | |
380 | status = iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_WRITE, | |
381 | SOC_DTS_OFFSET_PTMC, ptmc_out); | |
382 | ||
383 | status = iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_READ, | |
384 | SOC_DTS_OFFSET_PTTSS, &sticky_out); | |
385 | pr_debug("status %d PTTSS %x\n", status, sticky_out); | |
386 | if (sticky_out & SOC_DTS_TRIP_MASK) { | |
387 | int i; | |
388 | /* reset sticky bit */ | |
389 | status = iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_WRITE, | |
390 | SOC_DTS_OFFSET_PTTSS, sticky_out); | |
391 | spin_unlock_irqrestore(&sensors->intr_notify_lock, flags); | |
392 | ||
393 | for (i = 0; i < SOC_MAX_DTS_SENSORS; ++i) { | |
394 | pr_debug("TZD update for zone %d\n", i); | |
395 | thermal_zone_device_update(sensors->soc_dts[i].tzone); | |
396 | } | |
397 | } else | |
398 | spin_unlock_irqrestore(&sensors->intr_notify_lock, flags); | |
399 | } | |
400 | EXPORT_SYMBOL_GPL(intel_soc_dts_iosf_interrupt_handler); | |
401 | ||
402 | struct intel_soc_dts_sensors *intel_soc_dts_iosf_init( | |
403 | enum intel_soc_dts_interrupt_type intr_type, int trip_count, | |
404 | int read_only_trip_count) | |
405 | { | |
406 | struct intel_soc_dts_sensors *sensors; | |
407 | bool notification; | |
408 | u32 tj_max; | |
409 | int ret; | |
410 | int i; | |
411 | ||
412 | if (!iosf_mbi_available()) | |
413 | return ERR_PTR(-ENODEV); | |
414 | ||
415 | if (!trip_count || read_only_trip_count > trip_count) | |
416 | return ERR_PTR(-EINVAL); | |
417 | ||
418 | if (get_tj_max(&tj_max)) | |
419 | return ERR_PTR(-EINVAL); | |
420 | ||
421 | sensors = kzalloc(sizeof(*sensors), GFP_KERNEL); | |
422 | if (!sensors) | |
423 | return ERR_PTR(-ENOMEM); | |
424 | ||
425 | spin_lock_init(&sensors->intr_notify_lock); | |
426 | mutex_init(&sensors->dts_update_lock); | |
427 | sensors->intr_type = intr_type; | |
428 | sensors->tj_max = tj_max; | |
429 | if (intr_type == INTEL_SOC_DTS_INTERRUPT_NONE) | |
430 | notification = false; | |
431 | else | |
432 | notification = true; | |
433 | for (i = 0; i < SOC_MAX_DTS_SENSORS; ++i) { | |
434 | sensors->soc_dts[i].sensors = sensors; | |
435 | ret = add_dts_thermal_zone(i, &sensors->soc_dts[i], | |
436 | notification, trip_count, | |
437 | read_only_trip_count); | |
438 | if (ret) | |
439 | goto err_free; | |
440 | } | |
441 | ||
442 | for (i = 0; i < SOC_MAX_DTS_SENSORS; ++i) { | |
443 | ret = update_trip_temp(&sensors->soc_dts[i], 0, 0, | |
444 | THERMAL_TRIP_PASSIVE); | |
445 | if (ret) | |
446 | goto err_remove_zone; | |
447 | ||
448 | ret = update_trip_temp(&sensors->soc_dts[i], 1, 0, | |
449 | THERMAL_TRIP_PASSIVE); | |
450 | if (ret) | |
451 | goto err_remove_zone; | |
452 | } | |
453 | ||
454 | return sensors; | |
455 | err_remove_zone: | |
456 | for (i = 0; i < SOC_MAX_DTS_SENSORS; ++i) | |
457 | remove_dts_thermal_zone(&sensors->soc_dts[i]); | |
458 | ||
459 | err_free: | |
460 | kfree(sensors); | |
461 | return ERR_PTR(ret); | |
462 | } | |
463 | EXPORT_SYMBOL_GPL(intel_soc_dts_iosf_init); | |
464 | ||
465 | void intel_soc_dts_iosf_exit(struct intel_soc_dts_sensors *sensors) | |
466 | { | |
467 | int i; | |
468 | ||
469 | for (i = 0; i < SOC_MAX_DTS_SENSORS; ++i) { | |
470 | update_trip_temp(&sensors->soc_dts[i], 0, 0, 0); | |
471 | update_trip_temp(&sensors->soc_dts[i], 1, 0, 0); | |
472 | remove_dts_thermal_zone(&sensors->soc_dts[i]); | |
473 | } | |
474 | kfree(sensors); | |
475 | } | |
476 | EXPORT_SYMBOL_GPL(intel_soc_dts_iosf_exit); | |
477 | ||
478 | MODULE_LICENSE("GPL v2"); |