diff options
| -rw-r--r-- | drivers/thermal/Kconfig | 12 | ||||
| -rw-r--r-- | drivers/thermal/Makefile | 1 | ||||
| -rw-r--r-- | drivers/thermal/intel_soc_dts_thermal.c | 479 |
3 files changed, 492 insertions, 0 deletions
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index 2d51912a6e40..74323d5e3425 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig | |||
| @@ -222,6 +222,18 @@ config ACPI_INT3403_THERMAL | |||
| 222 | the Intel Thermal Daemon can use this information to allow the user | 222 | the Intel Thermal Daemon can use this information to allow the user |
| 223 | to select his laptop to run without turning on the fans. | 223 | to select his laptop to run without turning on the fans. |
| 224 | 224 | ||
| 225 | config INTEL_SOC_DTS_THERMAL | ||
| 226 | tristate "Intel SoCs DTS thermal driver" | ||
| 227 | depends on X86 && IOSF_MBI | ||
| 228 | help | ||
| 229 | Enable this to register Intel SoCs (e.g. Bay Trail) platform digital | ||
| 230 | temperature sensor (DTS). These SoCs have two additional DTSs in | ||
| 231 | addition to DTSs on CPU cores. Each DTS will be registered as a | ||
| 232 | thermal zone. There are two trip points. One of the trip point can | ||
| 233 | be set by user mode programs to get notifications via Linux thermal | ||
| 234 | notification methods.The other trip is a critical trip point, which | ||
| 235 | was set by the driver based on the TJ MAX temperature. | ||
| 236 | |||
| 225 | menu "Texas Instruments thermal drivers" | 237 | menu "Texas Instruments thermal drivers" |
| 226 | source "drivers/thermal/ti-soc-thermal/Kconfig" | 238 | source "drivers/thermal/ti-soc-thermal/Kconfig" |
| 227 | endmenu | 239 | endmenu |
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile index 54e4ec9eb5df..de0636a57a64 100644 --- a/drivers/thermal/Makefile +++ b/drivers/thermal/Makefile | |||
| @@ -29,5 +29,6 @@ obj-$(CONFIG_IMX_THERMAL) += imx_thermal.o | |||
| 29 | obj-$(CONFIG_DB8500_CPUFREQ_COOLING) += db8500_cpufreq_cooling.o | 29 | obj-$(CONFIG_DB8500_CPUFREQ_COOLING) += db8500_cpufreq_cooling.o |
| 30 | obj-$(CONFIG_INTEL_POWERCLAMP) += intel_powerclamp.o | 30 | obj-$(CONFIG_INTEL_POWERCLAMP) += intel_powerclamp.o |
| 31 | obj-$(CONFIG_X86_PKG_TEMP_THERMAL) += x86_pkg_temp_thermal.o | 31 | obj-$(CONFIG_X86_PKG_TEMP_THERMAL) += x86_pkg_temp_thermal.o |
| 32 | obj-$(CONFIG_INTEL_SOC_DTS_THERMAL) += intel_soc_dts_thermal.o | ||
| 32 | obj-$(CONFIG_TI_SOC_THERMAL) += ti-soc-thermal/ | 33 | obj-$(CONFIG_TI_SOC_THERMAL) += ti-soc-thermal/ |
| 33 | obj-$(CONFIG_ACPI_INT3403_THERMAL) += int3403_thermal.o | 34 | obj-$(CONFIG_ACPI_INT3403_THERMAL) += int3403_thermal.o |
diff --git a/drivers/thermal/intel_soc_dts_thermal.c b/drivers/thermal/intel_soc_dts_thermal.c new file mode 100644 index 000000000000..a6a0a18ec0aa --- /dev/null +++ b/drivers/thermal/intel_soc_dts_thermal.c | |||
| @@ -0,0 +1,479 @@ | |||
| 1 | /* | ||
| 2 | * intel_soc_dts_thermal.c | ||
| 3 | * Copyright (c) 2014, 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 <linux/thermal.h> | ||
| 22 | #include <asm/cpu_device_id.h> | ||
| 23 | #include <asm/iosf_mbi.h> | ||
| 24 | |||
| 25 | #define SOC_DTS_OFFSET_ENABLE 0xB0 | ||
| 26 | #define SOC_DTS_OFFSET_TEMP 0xB1 | ||
| 27 | |||
| 28 | #define SOC_DTS_OFFSET_PTPS 0xB2 | ||
| 29 | #define SOC_DTS_OFFSET_PTTS 0xB3 | ||
| 30 | #define SOC_DTS_OFFSET_PTTSS 0xB4 | ||
| 31 | #define SOC_DTS_OFFSET_PTMC 0x80 | ||
| 32 | #define SOC_DTS_TE_AUX0 0xB5 | ||
| 33 | #define SOC_DTS_TE_AUX1 0xB6 | ||
| 34 | |||
| 35 | #define SOC_DTS_AUX0_ENABLE_BIT BIT(0) | ||
| 36 | #define SOC_DTS_AUX1_ENABLE_BIT BIT(1) | ||
| 37 | #define SOC_DTS_CPU_MODULE0_ENABLE_BIT BIT(16) | ||
| 38 | #define SOC_DTS_CPU_MODULE1_ENABLE_BIT BIT(17) | ||
| 39 | #define SOC_DTS_TE_SCI_ENABLE BIT(9) | ||
| 40 | #define SOC_DTS_TE_SMI_ENABLE BIT(10) | ||
| 41 | #define SOC_DTS_TE_MSI_ENABLE BIT(11) | ||
| 42 | #define SOC_DTS_TE_APICA_ENABLE BIT(14) | ||
| 43 | #define SOC_DTS_PTMC_APIC_DEASSERT_BIT BIT(4) | ||
| 44 | |||
| 45 | /* DTS encoding for TJ MAX temperature */ | ||
| 46 | #define SOC_DTS_TJMAX_ENCODING 0x7F | ||
| 47 | |||
| 48 | /* IRQ 86 is a fixed APIC interrupt for BYT DTS Aux threshold notifications */ | ||
| 49 | #define BYT_SOC_DTS_APIC_IRQ 86 | ||
| 50 | |||
| 51 | /* Only 2 out of 4 is allowed for OSPM */ | ||
| 52 | #define SOC_MAX_DTS_TRIPS 2 | ||
| 53 | |||
| 54 | /* Mask for two trips in status bits */ | ||
| 55 | #define SOC_DTS_TRIP_MASK 0x03 | ||
| 56 | |||
| 57 | /* DTS0 and DTS 1 */ | ||
| 58 | #define SOC_MAX_DTS_SENSORS 2 | ||
| 59 | |||
| 60 | #define CRITICAL_OFFSET_FROM_TJ_MAX 5000 | ||
| 61 | |||
| 62 | struct soc_sensor_entry { | ||
| 63 | int id; | ||
| 64 | u32 tj_max; | ||
| 65 | u32 temp_mask; | ||
| 66 | u32 temp_shift; | ||
| 67 | u32 store_status; | ||
| 68 | struct thermal_zone_device *tzone; | ||
| 69 | }; | ||
| 70 | |||
| 71 | static struct soc_sensor_entry *soc_dts[SOC_MAX_DTS_SENSORS]; | ||
| 72 | |||
| 73 | static int crit_offset = CRITICAL_OFFSET_FROM_TJ_MAX; | ||
| 74 | module_param(crit_offset, int, 0644); | ||
| 75 | MODULE_PARM_DESC(crit_offset, | ||
| 76 | "Critical Temperature offset from tj max in millidegree Celsius."); | ||
| 77 | |||
| 78 | static DEFINE_MUTEX(aux_update_mutex); | ||
| 79 | static spinlock_t intr_notify_lock; | ||
| 80 | static int soc_dts_thres_irq; | ||
| 81 | |||
| 82 | static int get_tj_max(u32 *tj_max) | ||
| 83 | { | ||
| 84 | u32 eax, edx; | ||
| 85 | u32 val; | ||
| 86 | int err; | ||
| 87 | |||
| 88 | err = rdmsr_safe(MSR_IA32_TEMPERATURE_TARGET, &eax, &edx); | ||
| 89 | if (err) | ||
| 90 | goto err_ret; | ||
| 91 | else { | ||
| 92 | val = (eax >> 16) & 0xff; | ||
| 93 | if (val) | ||
| 94 | *tj_max = val * 1000; | ||
| 95 | else { | ||
| 96 | err = -EINVAL; | ||
| 97 | goto err_ret; | ||
| 98 | } | ||
| 99 | } | ||
| 100 | |||
| 101 | return 0; | ||
| 102 | err_ret: | ||
| 103 | *tj_max = 0; | ||
| 104 | |||
| 105 | return err; | ||
| 106 | } | ||
| 107 | |||
| 108 | static int sys_get_trip_temp(struct thermal_zone_device *tzd, | ||
| 109 | int trip, unsigned long *temp) | ||
| 110 | { | ||
| 111 | int status; | ||
| 112 | u32 out; | ||
| 113 | struct soc_sensor_entry *aux_entry; | ||
| 114 | |||
| 115 | aux_entry = tzd->devdata; | ||
| 116 | |||
| 117 | if (!trip) { | ||
| 118 | /* Just return the critical temp */ | ||
| 119 | *temp = aux_entry->tj_max - crit_offset; | ||
| 120 | return 0; | ||
| 121 | } | ||
| 122 | |||
| 123 | mutex_lock(&aux_update_mutex); | ||
| 124 | status = iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_READ, | ||
| 125 | SOC_DTS_OFFSET_PTPS, &out); | ||
| 126 | mutex_unlock(&aux_update_mutex); | ||
| 127 | if (status) | ||
| 128 | return status; | ||
| 129 | |||
| 130 | out = (out >> (trip * 8)) & SOC_DTS_TJMAX_ENCODING; | ||
| 131 | |||
| 132 | if (!out) | ||
| 133 | *temp = 0; | ||
| 134 | else | ||
| 135 | *temp = aux_entry->tj_max - out * 1000; | ||
| 136 | |||
| 137 | return 0; | ||
| 138 | } | ||
| 139 | |||
| 140 | static int update_trip_temp(struct soc_sensor_entry *aux_entry, | ||
| 141 | int thres_index, unsigned long temp) | ||
| 142 | { | ||
| 143 | int status; | ||
| 144 | u32 temp_out; | ||
| 145 | u32 out; | ||
| 146 | u32 store_ptps; | ||
| 147 | u32 store_ptmc; | ||
| 148 | u32 store_te_out; | ||
| 149 | u32 te_out; | ||
| 150 | |||
| 151 | u32 int_enable_bit = SOC_DTS_TE_APICA_ENABLE | | ||
| 152 | SOC_DTS_TE_MSI_ENABLE; | ||
| 153 | |||
| 154 | temp_out = (aux_entry->tj_max - temp) / 1000; | ||
| 155 | |||
| 156 | status = iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_READ, | ||
| 157 | SOC_DTS_OFFSET_PTPS, &store_ptps); | ||
| 158 | if (status) | ||
| 159 | return status; | ||
| 160 | |||
| 161 | out = (store_ptps & ~(0xFF << (thres_index * 8))); | ||
| 162 | out |= (temp_out & 0xFF) << (thres_index * 8); | ||
| 163 | status = iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_WRITE, | ||
| 164 | SOC_DTS_OFFSET_PTPS, out); | ||
| 165 | if (status) | ||
| 166 | return status; | ||
| 167 | pr_debug("update_trip_temp PTPS = %x\n", out); | ||
| 168 | status = iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_READ, | ||
| 169 | SOC_DTS_OFFSET_PTMC, &out); | ||
| 170 | if (status) | ||
| 171 | goto err_restore_ptps; | ||
| 172 | |||
| 173 | store_ptmc = out; | ||
| 174 | |||
| 175 | status = iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_READ, | ||
| 176 | SOC_DTS_TE_AUX0 + thres_index, | ||
| 177 | &te_out); | ||
| 178 | if (status) | ||
| 179 | goto err_restore_ptmc; | ||
| 180 | |||
| 181 | store_te_out = te_out; | ||
| 182 | |||
| 183 | /* Enable for CPU module 0 and module 1 */ | ||
| 184 | out |= (SOC_DTS_CPU_MODULE0_ENABLE_BIT | | ||
| 185 | SOC_DTS_CPU_MODULE1_ENABLE_BIT); | ||
| 186 | if (temp) { | ||
| 187 | if (thres_index) | ||
| 188 | out |= SOC_DTS_AUX1_ENABLE_BIT; | ||
| 189 | else | ||
| 190 | out |= SOC_DTS_AUX0_ENABLE_BIT; | ||
| 191 | te_out |= int_enable_bit; | ||
| 192 | } else { | ||
| 193 | if (thres_index) | ||
| 194 | out &= ~SOC_DTS_AUX1_ENABLE_BIT; | ||
| 195 | else | ||
| 196 | out &= ~SOC_DTS_AUX0_ENABLE_BIT; | ||
| 197 | te_out &= ~int_enable_bit; | ||
| 198 | } | ||
| 199 | status = iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_WRITE, | ||
| 200 | SOC_DTS_OFFSET_PTMC, out); | ||
| 201 | if (status) | ||
| 202 | goto err_restore_te_out; | ||
| 203 | |||
| 204 | status = iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_WRITE, | ||
| 205 | SOC_DTS_TE_AUX0 + thres_index, | ||
| 206 | te_out); | ||
| 207 | if (status) | ||
| 208 | goto err_restore_te_out; | ||
| 209 | |||
| 210 | return 0; | ||
| 211 | |||
| 212 | err_restore_te_out: | ||
| 213 | iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_WRITE, | ||
| 214 | SOC_DTS_OFFSET_PTMC, store_te_out); | ||
| 215 | err_restore_ptmc: | ||
| 216 | iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_WRITE, | ||
| 217 | SOC_DTS_OFFSET_PTMC, store_ptmc); | ||
| 218 | err_restore_ptps: | ||
| 219 | iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_WRITE, | ||
| 220 | SOC_DTS_OFFSET_PTPS, store_ptps); | ||
| 221 | /* Nothing we can do if restore fails */ | ||
| 222 | |||
| 223 | return status; | ||
| 224 | } | ||
| 225 | |||
| 226 | static int sys_set_trip_temp(struct thermal_zone_device *tzd, int trip, | ||
| 227 | unsigned long temp) | ||
| 228 | { | ||
| 229 | struct soc_sensor_entry *aux_entry = tzd->devdata; | ||
| 230 | int status; | ||
| 231 | |||
| 232 | if (temp > (aux_entry->tj_max - crit_offset)) | ||
| 233 | return -EINVAL; | ||
| 234 | |||
| 235 | mutex_lock(&aux_update_mutex); | ||
| 236 | status = update_trip_temp(tzd->devdata, trip, temp); | ||
| 237 | mutex_unlock(&aux_update_mutex); | ||
| 238 | |||
| 239 | return status; | ||
| 240 | } | ||
| 241 | |||
| 242 | static int sys_get_trip_type(struct thermal_zone_device *thermal, | ||
| 243 | int trip, enum thermal_trip_type *type) | ||
| 244 | { | ||
| 245 | if (trip) | ||
| 246 | *type = THERMAL_TRIP_PASSIVE; | ||
| 247 | else | ||
| 248 | *type = THERMAL_TRIP_CRITICAL; | ||
| 249 | |||
| 250 | return 0; | ||
| 251 | } | ||
| 252 | |||
| 253 | static int sys_get_curr_temp(struct thermal_zone_device *tzd, | ||
| 254 | unsigned long *temp) | ||
| 255 | { | ||
| 256 | int status; | ||
| 257 | u32 out; | ||
| 258 | struct soc_sensor_entry *aux_entry; | ||
| 259 | |||
| 260 | aux_entry = tzd->devdata; | ||
| 261 | |||
| 262 | status = iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_READ, | ||
| 263 | SOC_DTS_OFFSET_TEMP, &out); | ||
| 264 | if (status) | ||
| 265 | return status; | ||
| 266 | |||
| 267 | out = (out & aux_entry->temp_mask) >> aux_entry->temp_shift; | ||
| 268 | out -= SOC_DTS_TJMAX_ENCODING; | ||
| 269 | *temp = aux_entry->tj_max - out * 1000; | ||
| 270 | |||
| 271 | return 0; | ||
| 272 | } | ||
| 273 | |||
| 274 | static struct thermal_zone_device_ops tzone_ops = { | ||
| 275 | .get_temp = sys_get_curr_temp, | ||
| 276 | .get_trip_temp = sys_get_trip_temp, | ||
| 277 | .get_trip_type = sys_get_trip_type, | ||
| 278 | .set_trip_temp = sys_set_trip_temp, | ||
| 279 | }; | ||
| 280 | |||
| 281 | static void free_soc_dts(struct soc_sensor_entry *aux_entry) | ||
| 282 | { | ||
| 283 | if (aux_entry) { | ||
| 284 | iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_WRITE, | ||
| 285 | SOC_DTS_OFFSET_ENABLE, aux_entry->store_status); | ||
| 286 | thermal_zone_device_unregister(aux_entry->tzone); | ||
| 287 | kfree(aux_entry); | ||
| 288 | } | ||
| 289 | } | ||
| 290 | |||
| 291 | static int soc_dts_enable(int id) | ||
| 292 | { | ||
| 293 | u32 out; | ||
| 294 | int ret; | ||
| 295 | |||
| 296 | ret = iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_READ, | ||
| 297 | SOC_DTS_OFFSET_ENABLE, &out); | ||
| 298 | if (ret) | ||
| 299 | return ret; | ||
| 300 | |||
| 301 | if (!(out & BIT(id))) { | ||
| 302 | out |= BIT(id); | ||
| 303 | ret = iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_WRITE, | ||
| 304 | SOC_DTS_OFFSET_ENABLE, out); | ||
| 305 | if (ret) | ||
| 306 | return ret; | ||
| 307 | } | ||
| 308 | |||
| 309 | return ret; | ||
| 310 | } | ||
| 311 | |||
| 312 | static struct soc_sensor_entry *alloc_soc_dts(int id, u32 tj_max) | ||
| 313 | { | ||
| 314 | struct soc_sensor_entry *aux_entry; | ||
| 315 | char name[10]; | ||
| 316 | int err; | ||
| 317 | |||
| 318 | aux_entry = kzalloc(sizeof(*aux_entry), GFP_KERNEL); | ||
| 319 | if (!aux_entry) { | ||
| 320 | err = -ENOMEM; | ||
| 321 | return ERR_PTR(-ENOMEM); | ||
| 322 | } | ||
| 323 | |||
| 324 | /* Store status to restor on exit */ | ||
| 325 | err = iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_READ, | ||
| 326 | SOC_DTS_OFFSET_ENABLE, | ||
| 327 | &aux_entry->store_status); | ||
| 328 | if (err) | ||
| 329 | goto err_ret; | ||
| 330 | |||
| 331 | aux_entry->id = id; | ||
| 332 | aux_entry->tj_max = tj_max; | ||
| 333 | aux_entry->temp_mask = 0x00FF << (id * 8); | ||
| 334 | aux_entry->temp_shift = id * 8; | ||
| 335 | snprintf(name, sizeof(name), "soc_dts%d", id); | ||
| 336 | aux_entry->tzone = thermal_zone_device_register(name, | ||
| 337 | SOC_MAX_DTS_TRIPS, | ||
| 338 | 0x02, | ||
| 339 | aux_entry, &tzone_ops, NULL, 0, 0); | ||
| 340 | if (IS_ERR(aux_entry->tzone)) { | ||
| 341 | err = PTR_ERR(aux_entry->tzone); | ||
| 342 | goto err_ret; | ||
| 343 | } | ||
| 344 | |||
| 345 | err = soc_dts_enable(id); | ||
| 346 | if (err) | ||
| 347 | goto err_aux_status; | ||
| 348 | |||
| 349 | return aux_entry; | ||
| 350 | |||
| 351 | err_aux_status: | ||
| 352 | thermal_zone_device_unregister(aux_entry->tzone); | ||
| 353 | err_ret: | ||
| 354 | kfree(aux_entry); | ||
| 355 | return ERR_PTR(err); | ||
| 356 | } | ||
| 357 | |||
| 358 | static void proc_thermal_interrupt(void) | ||
| 359 | { | ||
| 360 | u32 sticky_out; | ||
| 361 | int status; | ||
| 362 | u32 ptmc_out; | ||
| 363 | |||
| 364 | /* Clear APIC interrupt */ | ||
| 365 | status = iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_READ, | ||
| 366 | SOC_DTS_OFFSET_PTMC, &ptmc_out); | ||
| 367 | |||
| 368 | ptmc_out |= SOC_DTS_PTMC_APIC_DEASSERT_BIT; | ||
| 369 | status = iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_WRITE, | ||
| 370 | SOC_DTS_OFFSET_PTMC, ptmc_out); | ||
| 371 | |||
| 372 | /* Read status here */ | ||
| 373 | status = iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_READ, | ||
| 374 | SOC_DTS_OFFSET_PTTSS, &sticky_out); | ||
| 375 | pr_debug("status %d PTTSS %x\n", status, sticky_out); | ||
| 376 | if (sticky_out & SOC_DTS_TRIP_MASK) { | ||
| 377 | int i; | ||
| 378 | /* reset sticky bit */ | ||
| 379 | status = iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_WRITE, | ||
| 380 | SOC_DTS_OFFSET_PTTSS, sticky_out); | ||
| 381 | for (i = 0; i < SOC_MAX_DTS_SENSORS; ++i) { | ||
| 382 | pr_debug("TZD update for zone %d\n", i); | ||
| 383 | thermal_zone_device_update(soc_dts[i]->tzone); | ||
| 384 | } | ||
| 385 | } | ||
| 386 | |||
| 387 | } | ||
| 388 | |||
| 389 | static irqreturn_t soc_irq_thread_fn(int irq, void *dev_data) | ||
| 390 | { | ||
| 391 | unsigned long flags; | ||
| 392 | |||
| 393 | spin_lock_irqsave(&intr_notify_lock, flags); | ||
| 394 | proc_thermal_interrupt(); | ||
| 395 | spin_unlock_irqrestore(&intr_notify_lock, flags); | ||
| 396 | pr_debug("proc_thermal_interrupt\n"); | ||
| 397 | |||
| 398 | return IRQ_HANDLED; | ||
| 399 | } | ||
| 400 | |||
| 401 | static const struct x86_cpu_id soc_thermal_ids[] = { | ||
| 402 | { X86_VENDOR_INTEL, X86_FAMILY_ANY, 0x37, 0, BYT_SOC_DTS_APIC_IRQ}, | ||
| 403 | {} | ||
| 404 | }; | ||
| 405 | MODULE_DEVICE_TABLE(x86cpu, soc_thermal_ids); | ||
| 406 | |||
| 407 | static int __init intel_soc_thermal_init(void) | ||
| 408 | { | ||
| 409 | u32 tj_max; | ||
| 410 | int err = 0; | ||
| 411 | int i; | ||
| 412 | const struct x86_cpu_id *match_cpu; | ||
| 413 | |||
| 414 | match_cpu = x86_match_cpu(soc_thermal_ids); | ||
| 415 | if (!match_cpu) | ||
| 416 | return -ENODEV; | ||
| 417 | |||
| 418 | if (get_tj_max(&tj_max)) | ||
| 419 | return -EINVAL; | ||
| 420 | |||
| 421 | for (i = 0; i < SOC_MAX_DTS_SENSORS; ++i) { | ||
| 422 | soc_dts[i] = alloc_soc_dts(i, tj_max); | ||
| 423 | if (IS_ERR(soc_dts[i])) { | ||
| 424 | err = PTR_ERR(soc_dts[i]); | ||
| 425 | goto err_free; | ||
| 426 | } | ||
| 427 | } | ||
| 428 | |||
| 429 | spin_lock_init(&intr_notify_lock); | ||
| 430 | |||
| 431 | soc_dts_thres_irq = (int)match_cpu->driver_data; | ||
| 432 | |||
| 433 | err = request_threaded_irq(soc_dts_thres_irq, NULL, | ||
| 434 | soc_irq_thread_fn, | ||
| 435 | IRQF_TRIGGER_RISING | IRQF_ONESHOT, | ||
| 436 | "soc_dts", soc_dts); | ||
| 437 | if (err) { | ||
| 438 | pr_err("request_threaded_irq ret %d\n", err); | ||
| 439 | goto err_free; | ||
| 440 | } | ||
| 441 | |||
| 442 | for (i = 0; i < SOC_MAX_DTS_SENSORS; ++i) { | ||
| 443 | err = update_trip_temp(soc_dts[i], 0, tj_max - crit_offset); | ||
| 444 | if (err) | ||
| 445 | goto err_trip_temp; | ||
| 446 | } | ||
| 447 | |||
| 448 | return 0; | ||
| 449 | |||
| 450 | err_trip_temp: | ||
| 451 | i = SOC_MAX_DTS_SENSORS; | ||
| 452 | free_irq(soc_dts_thres_irq, soc_dts); | ||
| 453 | err_free: | ||
| 454 | while (--i >= 0) | ||
| 455 | free_soc_dts(soc_dts[i]); | ||
| 456 | |||
| 457 | return err; | ||
| 458 | } | ||
| 459 | |||
| 460 | static void __exit intel_soc_thermal_exit(void) | ||
| 461 | { | ||
| 462 | int i; | ||
| 463 | |||
| 464 | for (i = 0; i < SOC_MAX_DTS_SENSORS; ++i) | ||
| 465 | update_trip_temp(soc_dts[i], 0, 0); | ||
| 466 | |||
| 467 | free_irq(soc_dts_thres_irq, soc_dts); | ||
| 468 | |||
| 469 | for (i = 0; i < SOC_MAX_DTS_SENSORS; ++i) | ||
| 470 | free_soc_dts(soc_dts[i]); | ||
| 471 | |||
| 472 | } | ||
| 473 | |||
| 474 | module_init(intel_soc_thermal_init) | ||
| 475 | module_exit(intel_soc_thermal_exit) | ||
| 476 | |||
| 477 | MODULE_DESCRIPTION("Intel SoC DTS Thermal Driver"); | ||
| 478 | MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>"); | ||
| 479 | MODULE_LICENSE("GPL v2"); | ||
