Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/rzhang/linux

Pull thermal management updates from Zhang Rui:
"Sorry that I missed the merge window as there is a bug found in the
last minute, and I have to fix it and wait for the code to be tested
in linux-next tree for a few days. Now the buggy patch has been
dropped entirely from my next branch. Thus I hope those changes can
still be merged in 3.18-rc2 as most of them are platform thermal
driver changes.

Specifics:

- introduce ACPI INT340X thermal drivers.

Newer laptops and tablets may have thermal sensors and other
devices with thermal control capabilities that are exposed for the
OS to use via the ACPI INT340x device objects. Several drivers are
introduced to expose the temperature information and cooling
ability from these objects to user-space via the normal thermal
framework.

From: Lu Aaron, Lan Tianyu, Jacob Pan and Zhang Rui.

- introduce a new thermal governor, which just uses a hysteresis to
switch abruptly on/off a cooling device. This governor can be used
to control certain fan devices that can not be throttled but just
switched on or off. From: Peter Feuerer.

- introduce support for some new thermal interrupt functions on
i.MX6SX, in IMX thermal driver. From: Anson, Huang.

- introduce tracing support on thermal framework. From: Punit
Agrawal.

- small fixes in OF thermal and thermal step_wise governor"

* 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/rzhang/linux: (25 commits)
Thermal: int340x thermal: select ACPI fan driver
Thermal: int3400_thermal: use acpi_thermal_rel parsing APIs
Thermal: int340x_thermal: expose acpi thermal relationship tables
Thermal: introduce int3403 thermal driver
Thermal: introduce INT3402 thermal driver
Thermal: move the KELVIN_TO_MILLICELSIUS macro to thermal.h
ACPI / Fan: support INT3404 thermal device
ACPI / Fan: add ACPI 4.0 style fan support
ACPI / fan: convert to platform driver
ACPI / fan: use acpi_device_xxx_power instead of acpi_bus equivelant
ACPI / fan: remove no need check for device pointer
ACPI / fan: remove unused macro
Thermal: int3400 thermal: register to thermal framework
Thermal: int3400 thermal: add capability to detect supporting UUIDs
Thermal: introduce int3400 thermal driver
ACPI: add ACPI_TYPE_LOCAL_REFERENCE support to acpi_extract_package()
ACPI: make acpi_create_platform_device() an external API
thermal: step_wise: fix: Prevent from binary overflow when trend is dropping
ACPI: introduce ACPI int340x thermal scan handler
thermal: Added Bang-bang thermal governor
...

+2223 -423
+4 -1
Documentation/devicetree/bindings/thermal/imx-thermal.txt
··· 1 1 * Temperature Monitor (TEMPMON) on Freescale i.MX SoCs 2 2 3 3 Required properties: 4 - - compatible : "fsl,imx6q-thermal" 4 + - compatible : "fsl,imx6q-tempmon" for i.MX6Q, "fsl,imx6sx-tempmon" for i.MX6SX. 5 + i.MX6SX has two more IRQs than i.MX6Q, one is IRQ_LOW and the other is IRQ_PANIC, 6 + when temperature is below than low threshold, IRQ_LOW will be triggered, when temperature 7 + is higher than panic threshold, system will auto reboot by SRC module. 5 8 - fsl,tempmon : phandle pointer to system controller that contains TEMPMON 6 9 control registers, e.g. ANATOP on imx6q. 7 10 - fsl,tempmon-data : phandle pointer to fuse controller that contains TEMPMON
+1 -1
drivers/acpi/Kconfig
··· 144 144 145 145 config ACPI_FAN 146 146 tristate "Fan" 147 - select THERMAL 147 + depends on THERMAL 148 148 default y 149 149 help 150 150 This driver supports ACPI fan devices, allowing user-mode
+1
drivers/acpi/Makefile
··· 43 43 acpi-y += acpi_lpss.o 44 44 acpi-y += acpi_platform.o 45 45 acpi-y += acpi_pnp.o 46 + acpi-y += int340x_thermal.o 46 47 acpi-y += power.o 47 48 acpi-y += event.o 48 49 acpi-y += sysfs.o
+1
drivers/acpi/acpi_platform.c
··· 113 113 kfree(resources); 114 114 return pdev; 115 115 } 116 + EXPORT_SYMBOL_GPL(acpi_create_platform_device);
+1
drivers/acpi/device_pm.c
··· 343 343 344 344 return 0; 345 345 } 346 + EXPORT_SYMBOL_GPL(acpi_device_update_power); 346 347 347 348 int acpi_bus_update_power(acpi_handle handle, int *state_p) 348 349 {
+265 -75
drivers/acpi/fan.c
··· 30 30 #include <linux/uaccess.h> 31 31 #include <linux/thermal.h> 32 32 #include <linux/acpi.h> 33 - 34 - #define ACPI_FAN_CLASS "fan" 35 - #define ACPI_FAN_FILE_STATE "state" 36 - 37 - #define _COMPONENT ACPI_FAN_COMPONENT 38 - ACPI_MODULE_NAME("fan"); 33 + #include <linux/platform_device.h> 34 + #include <linux/sort.h> 39 35 40 36 MODULE_AUTHOR("Paul Diefenbaugh"); 41 37 MODULE_DESCRIPTION("ACPI Fan Driver"); 42 38 MODULE_LICENSE("GPL"); 43 39 44 - static int acpi_fan_add(struct acpi_device *device); 45 - static int acpi_fan_remove(struct acpi_device *device); 40 + static int acpi_fan_probe(struct platform_device *pdev); 41 + static int acpi_fan_remove(struct platform_device *pdev); 46 42 47 43 static const struct acpi_device_id fan_device_ids[] = { 48 44 {"PNP0C0B", 0}, 45 + {"INT3404", 0}, 49 46 {"", 0}, 50 47 }; 51 48 MODULE_DEVICE_TABLE(acpi, fan_device_ids); ··· 61 64 #define FAN_PM_OPS_PTR NULL 62 65 #endif 63 66 64 - static struct acpi_driver acpi_fan_driver = { 65 - .name = "fan", 66 - .class = ACPI_FAN_CLASS, 67 - .ids = fan_device_ids, 68 - .ops = { 69 - .add = acpi_fan_add, 70 - .remove = acpi_fan_remove, 71 - }, 72 - .drv.pm = FAN_PM_OPS_PTR, 67 + struct acpi_fan_fps { 68 + u64 control; 69 + u64 trip_point; 70 + u64 speed; 71 + u64 noise_level; 72 + u64 power; 73 + }; 74 + 75 + struct acpi_fan_fif { 76 + u64 revision; 77 + u64 fine_grain_ctrl; 78 + u64 step_size; 79 + u64 low_speed_notification; 80 + }; 81 + 82 + struct acpi_fan { 83 + bool acpi4; 84 + struct acpi_fan_fif fif; 85 + struct acpi_fan_fps *fps; 86 + int fps_count; 87 + struct thermal_cooling_device *cdev; 88 + }; 89 + 90 + static struct platform_driver acpi_fan_driver = { 91 + .probe = acpi_fan_probe, 92 + .remove = acpi_fan_remove, 93 + .driver = { 94 + .name = "acpi-fan", 95 + .acpi_match_table = fan_device_ids, 96 + .pm = FAN_PM_OPS_PTR, 97 + }, 73 98 }; 74 99 75 100 /* thermal cooling device callbacks */ 76 101 static int fan_get_max_state(struct thermal_cooling_device *cdev, unsigned long 77 102 *state) 78 103 { 79 - /* ACPI fan device only support two states: ON/OFF */ 80 - *state = 1; 104 + struct acpi_device *device = cdev->devdata; 105 + struct acpi_fan *fan = acpi_driver_data(device); 106 + 107 + if (fan->acpi4) 108 + *state = fan->fps_count - 1; 109 + else 110 + *state = 1; 81 111 return 0; 82 112 } 83 113 84 - static int fan_get_cur_state(struct thermal_cooling_device *cdev, unsigned long 85 - *state) 114 + static int fan_get_state_acpi4(struct acpi_device *device, unsigned long *state) 86 115 { 87 - struct acpi_device *device = cdev->devdata; 116 + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; 117 + struct acpi_fan *fan = acpi_driver_data(device); 118 + union acpi_object *obj; 119 + acpi_status status; 120 + int control, i; 121 + 122 + status = acpi_evaluate_object(device->handle, "_FST", NULL, &buffer); 123 + if (ACPI_FAILURE(status)) { 124 + dev_err(&device->dev, "Get fan state failed\n"); 125 + return status; 126 + } 127 + 128 + obj = buffer.pointer; 129 + if (!obj || obj->type != ACPI_TYPE_PACKAGE || 130 + obj->package.count != 3 || 131 + obj->package.elements[1].type != ACPI_TYPE_INTEGER) { 132 + dev_err(&device->dev, "Invalid _FST data\n"); 133 + status = -EINVAL; 134 + goto err; 135 + } 136 + 137 + control = obj->package.elements[1].integer.value; 138 + for (i = 0; i < fan->fps_count; i++) { 139 + if (control == fan->fps[i].control) 140 + break; 141 + } 142 + if (i == fan->fps_count) { 143 + dev_dbg(&device->dev, "Invalid control value returned\n"); 144 + status = -EINVAL; 145 + goto err; 146 + } 147 + 148 + *state = i; 149 + 150 + err: 151 + kfree(obj); 152 + return status; 153 + } 154 + 155 + static int fan_get_state(struct acpi_device *device, unsigned long *state) 156 + { 88 157 int result; 89 158 int acpi_state = ACPI_STATE_D0; 90 159 91 - if (!device) 92 - return -EINVAL; 93 - 94 - result = acpi_bus_update_power(device->handle, &acpi_state); 160 + result = acpi_device_update_power(device, &acpi_state); 95 161 if (result) 96 162 return result; 97 163 ··· 163 103 return 0; 164 104 } 165 105 106 + static int fan_get_cur_state(struct thermal_cooling_device *cdev, unsigned long 107 + *state) 108 + { 109 + struct acpi_device *device = cdev->devdata; 110 + struct acpi_fan *fan = acpi_driver_data(device); 111 + 112 + if (fan->acpi4) 113 + return fan_get_state_acpi4(device, state); 114 + else 115 + return fan_get_state(device, state); 116 + } 117 + 118 + static int fan_set_state(struct acpi_device *device, unsigned long state) 119 + { 120 + if (state != 0 && state != 1) 121 + return -EINVAL; 122 + 123 + return acpi_device_set_power(device, 124 + state ? ACPI_STATE_D0 : ACPI_STATE_D3_COLD); 125 + } 126 + 127 + static int fan_set_state_acpi4(struct acpi_device *device, unsigned long state) 128 + { 129 + struct acpi_fan *fan = acpi_driver_data(device); 130 + acpi_status status; 131 + 132 + if (state >= fan->fps_count) 133 + return -EINVAL; 134 + 135 + status = acpi_execute_simple_method(device->handle, "_FSL", 136 + fan->fps[state].control); 137 + if (ACPI_FAILURE(status)) { 138 + dev_dbg(&device->dev, "Failed to set state by _FSL\n"); 139 + return status; 140 + } 141 + 142 + return 0; 143 + } 144 + 166 145 static int 167 146 fan_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state) 168 147 { 169 148 struct acpi_device *device = cdev->devdata; 170 - int result; 149 + struct acpi_fan *fan = acpi_driver_data(device); 171 150 172 - if (!device || (state != 0 && state != 1)) 173 - return -EINVAL; 174 - 175 - result = acpi_bus_set_power(device->handle, 176 - state ? ACPI_STATE_D0 : ACPI_STATE_D3_COLD); 177 - 178 - return result; 179 - } 151 + if (fan->acpi4) 152 + return fan_set_state_acpi4(device, state); 153 + else 154 + return fan_set_state(device, state); 155 + } 180 156 181 157 static const struct thermal_cooling_device_ops fan_cooling_ops = { 182 158 .get_max_state = fan_get_max_state, ··· 225 129 * -------------------------------------------------------------------------- 226 130 */ 227 131 228 - static int acpi_fan_add(struct acpi_device *device) 132 + static bool acpi_fan_is_acpi4(struct acpi_device *device) 133 + { 134 + return acpi_has_method(device->handle, "_FIF") && 135 + acpi_has_method(device->handle, "_FPS") && 136 + acpi_has_method(device->handle, "_FSL") && 137 + acpi_has_method(device->handle, "_FST"); 138 + } 139 + 140 + static int acpi_fan_get_fif(struct acpi_device *device) 141 + { 142 + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; 143 + struct acpi_fan *fan = acpi_driver_data(device); 144 + struct acpi_buffer format = { sizeof("NNNN"), "NNNN" }; 145 + struct acpi_buffer fif = { sizeof(fan->fif), &fan->fif }; 146 + union acpi_object *obj; 147 + acpi_status status; 148 + 149 + status = acpi_evaluate_object(device->handle, "_FIF", NULL, &buffer); 150 + if (ACPI_FAILURE(status)) 151 + return status; 152 + 153 + obj = buffer.pointer; 154 + if (!obj || obj->type != ACPI_TYPE_PACKAGE) { 155 + dev_err(&device->dev, "Invalid _FIF data\n"); 156 + status = -EINVAL; 157 + goto err; 158 + } 159 + 160 + status = acpi_extract_package(obj, &format, &fif); 161 + if (ACPI_FAILURE(status)) { 162 + dev_err(&device->dev, "Invalid _FIF element\n"); 163 + status = -EINVAL; 164 + } 165 + 166 + err: 167 + kfree(obj); 168 + return status; 169 + } 170 + 171 + static int acpi_fan_speed_cmp(const void *a, const void *b) 172 + { 173 + const struct acpi_fan_fps *fps1 = a; 174 + const struct acpi_fan_fps *fps2 = b; 175 + return fps1->speed - fps2->speed; 176 + } 177 + 178 + static int acpi_fan_get_fps(struct acpi_device *device) 179 + { 180 + struct acpi_fan *fan = acpi_driver_data(device); 181 + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; 182 + union acpi_object *obj; 183 + acpi_status status; 184 + int i; 185 + 186 + status = acpi_evaluate_object(device->handle, "_FPS", NULL, &buffer); 187 + if (ACPI_FAILURE(status)) 188 + return status; 189 + 190 + obj = buffer.pointer; 191 + if (!obj || obj->type != ACPI_TYPE_PACKAGE || obj->package.count < 2) { 192 + dev_err(&device->dev, "Invalid _FPS data\n"); 193 + status = -EINVAL; 194 + goto err; 195 + } 196 + 197 + fan->fps_count = obj->package.count - 1; /* minus revision field */ 198 + fan->fps = devm_kzalloc(&device->dev, 199 + fan->fps_count * sizeof(struct acpi_fan_fps), 200 + GFP_KERNEL); 201 + if (!fan->fps) { 202 + dev_err(&device->dev, "Not enough memory\n"); 203 + status = -ENOMEM; 204 + goto err; 205 + } 206 + for (i = 0; i < fan->fps_count; i++) { 207 + struct acpi_buffer format = { sizeof("NNNNN"), "NNNNN" }; 208 + struct acpi_buffer fps = { sizeof(fan->fps[i]), &fan->fps[i] }; 209 + status = acpi_extract_package(&obj->package.elements[i + 1], 210 + &format, &fps); 211 + if (ACPI_FAILURE(status)) { 212 + dev_err(&device->dev, "Invalid _FPS element\n"); 213 + break; 214 + } 215 + } 216 + 217 + /* sort the state array according to fan speed in increase order */ 218 + sort(fan->fps, fan->fps_count, sizeof(*fan->fps), 219 + acpi_fan_speed_cmp, NULL); 220 + 221 + err: 222 + kfree(obj); 223 + return status; 224 + } 225 + 226 + static int acpi_fan_probe(struct platform_device *pdev) 229 227 { 230 228 int result = 0; 231 229 struct thermal_cooling_device *cdev; 230 + struct acpi_fan *fan; 231 + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); 232 232 233 - if (!device) 234 - return -EINVAL; 233 + fan = devm_kzalloc(&pdev->dev, sizeof(*fan), GFP_KERNEL); 234 + if (!fan) { 235 + dev_err(&device->dev, "No memory for fan\n"); 236 + return -ENOMEM; 237 + } 238 + device->driver_data = fan; 239 + platform_set_drvdata(pdev, fan); 235 240 236 - strcpy(acpi_device_name(device), "Fan"); 237 - strcpy(acpi_device_class(device), ACPI_FAN_CLASS); 238 - 239 - result = acpi_bus_update_power(device->handle, NULL); 240 - if (result) { 241 - dev_err(&device->dev, "Setting initial power state\n"); 242 - goto end; 241 + if (acpi_fan_is_acpi4(device)) { 242 + if (acpi_fan_get_fif(device) || acpi_fan_get_fps(device)) 243 + goto end; 244 + fan->acpi4 = true; 245 + } else { 246 + result = acpi_device_update_power(device, NULL); 247 + if (result) { 248 + dev_err(&device->dev, "Setting initial power state\n"); 249 + goto end; 250 + } 243 251 } 244 252 245 253 cdev = thermal_cooling_device_register("Fan", device, ··· 353 153 goto end; 354 154 } 355 155 356 - dev_dbg(&device->dev, "registered as cooling_device%d\n", cdev->id); 156 + dev_dbg(&pdev->dev, "registered as cooling_device%d\n", cdev->id); 357 157 358 - device->driver_data = cdev; 359 - result = sysfs_create_link(&device->dev.kobj, 158 + fan->cdev = cdev; 159 + result = sysfs_create_link(&pdev->dev.kobj, 360 160 &cdev->device.kobj, 361 161 "thermal_cooling"); 362 162 if (result) 363 - dev_err(&device->dev, "Failed to create sysfs link " 364 - "'thermal_cooling'\n"); 163 + dev_err(&pdev->dev, "Failed to create sysfs link 'thermal_cooling'\n"); 365 164 366 165 result = sysfs_create_link(&cdev->device.kobj, 367 - &device->dev.kobj, 166 + &pdev->dev.kobj, 368 167 "device"); 369 168 if (result) 370 - dev_err(&device->dev, "Failed to create sysfs link 'device'\n"); 371 - 372 - dev_info(&device->dev, "ACPI: %s [%s] (%s)\n", 373 - acpi_device_name(device), acpi_device_bid(device), 374 - !device->power.state ? "on" : "off"); 169 + dev_err(&pdev->dev, "Failed to create sysfs link 'device'\n"); 375 170 376 171 end: 377 172 return result; 378 173 } 379 174 380 - static int acpi_fan_remove(struct acpi_device *device) 175 + static int acpi_fan_remove(struct platform_device *pdev) 381 176 { 382 - struct thermal_cooling_device *cdev; 177 + struct acpi_fan *fan = platform_get_drvdata(pdev); 383 178 384 - if (!device) 385 - return -EINVAL; 386 - 387 - cdev = acpi_driver_data(device); 388 - if (!cdev) 389 - return -EINVAL; 390 - 391 - sysfs_remove_link(&device->dev.kobj, "thermal_cooling"); 392 - sysfs_remove_link(&cdev->device.kobj, "device"); 393 - thermal_cooling_device_unregister(cdev); 179 + sysfs_remove_link(&pdev->dev.kobj, "thermal_cooling"); 180 + sysfs_remove_link(&fan->cdev->device.kobj, "device"); 181 + thermal_cooling_device_unregister(fan->cdev); 394 182 395 183 return 0; 396 184 } ··· 386 198 #ifdef CONFIG_PM_SLEEP 387 199 static int acpi_fan_suspend(struct device *dev) 388 200 { 389 - if (!dev) 390 - return -EINVAL; 201 + struct acpi_fan *fan = dev_get_drvdata(dev); 202 + if (fan->acpi4) 203 + return 0; 391 204 392 - acpi_bus_set_power(to_acpi_device(dev)->handle, ACPI_STATE_D0); 205 + acpi_device_set_power(ACPI_COMPANION(dev), ACPI_STATE_D0); 393 206 394 207 return AE_OK; 395 208 } ··· 398 209 static int acpi_fan_resume(struct device *dev) 399 210 { 400 211 int result; 212 + struct acpi_fan *fan = dev_get_drvdata(dev); 401 213 402 - if (!dev) 403 - return -EINVAL; 214 + if (fan->acpi4) 215 + return 0; 404 216 405 - result = acpi_bus_update_power(to_acpi_device(dev)->handle, NULL); 217 + result = acpi_device_update_power(ACPI_COMPANION(dev), NULL); 406 218 if (result) 407 219 dev_err(dev, "Error updating fan power state\n"); 408 220 ··· 411 221 } 412 222 #endif 413 223 414 - module_acpi_driver(acpi_fan_driver); 224 + module_platform_driver(acpi_fan_driver);
+51
drivers/acpi/int340x_thermal.c
··· 1 + /* 2 + * ACPI support for int340x thermal drivers 3 + * 4 + * Copyright (C) 2014, Intel Corporation 5 + * Authors: Zhang Rui <rui.zhang@intel.com> 6 + * 7 + * This program is free software; you can redistribute it and/or modify 8 + * it under the terms of the GNU General Public License version 2 as 9 + * published by the Free Software Foundation. 10 + */ 11 + 12 + #include <linux/acpi.h> 13 + #include <linux/module.h> 14 + 15 + #include "internal.h" 16 + 17 + #define DO_ENUMERATION 0x01 18 + static const struct acpi_device_id int340x_thermal_device_ids[] = { 19 + {"INT3400", DO_ENUMERATION }, 20 + {"INT3401"}, 21 + {"INT3402"}, 22 + {"INT3403"}, 23 + {"INT3404"}, 24 + {"INT3406"}, 25 + {"INT3407"}, 26 + {"INT3408"}, 27 + {"INT3409"}, 28 + {"INT340A"}, 29 + {"INT340B"}, 30 + {""}, 31 + }; 32 + 33 + static int int340x_thermal_handler_attach(struct acpi_device *adev, 34 + const struct acpi_device_id *id) 35 + { 36 + #if defined(CONFIG_INT340X_THERMAL) || defined(CONFIG_INT340X_THERMAL_MODULE) 37 + if (id->driver_data == DO_ENUMERATION) 38 + acpi_create_platform_device(adev); 39 + #endif 40 + return 1; 41 + } 42 + 43 + static struct acpi_scan_handler int340x_thermal_handler = { 44 + .ids = int340x_thermal_device_ids, 45 + .attach = int340x_thermal_handler_attach, 46 + }; 47 + 48 + void __init acpi_int340x_thermal_init(void) 49 + { 50 + acpi_scan_add_handler(&int340x_thermal_handler); 51 + }
+1 -9
drivers/acpi/internal.h
··· 31 31 void acpi_processor_init(void); 32 32 void acpi_platform_init(void); 33 33 void acpi_pnp_init(void); 34 + void acpi_int340x_thermal_init(void); 34 35 int acpi_sysfs_init(void); 35 36 void acpi_container_init(void); 36 37 void acpi_memory_hotplug_init(void); ··· 104 103 int acpi_power_on_resources(struct acpi_device *device, int state); 105 104 int acpi_power_transition(struct acpi_device *device, int state); 106 105 107 - int acpi_device_update_power(struct acpi_device *device, int *state_p); 108 - 109 106 int acpi_wakeup_device_init(void); 110 107 111 108 #ifdef CONFIG_ARCH_MIGHT_HAVE_ACPI_PDC ··· 165 166 static inline int suspend_nvs_save(void) { return 0; } 166 167 static inline void suspend_nvs_restore(void) {} 167 168 #endif 168 - 169 - /*-------------------------------------------------------------------------- 170 - Platform bus support 171 - -------------------------------------------------------------------------- */ 172 - struct platform_device; 173 - 174 - struct platform_device *acpi_create_platform_device(struct acpi_device *adev); 175 169 176 170 /*-------------------------------------------------------------------------- 177 171 Video
+1
drivers/acpi/scan.c
··· 2315 2315 acpi_container_init(); 2316 2316 acpi_memory_hotplug_init(); 2317 2317 acpi_pnp_init(); 2318 + acpi_int340x_thermal_init(); 2318 2319 2319 2320 mutex_lock(&acpi_scan_lock); 2320 2321 /*
+9 -9
drivers/acpi/thermal.c
··· 528 528 } 529 529 530 530 /* sys I/F for generic thermal sysfs support */ 531 - #define KELVIN_TO_MILLICELSIUS(t, off) (((t) - (off)) * 100) 532 531 533 532 static int thermal_get_temp(struct thermal_zone_device *thermal, 534 533 unsigned long *temp) ··· 542 543 if (result) 543 544 return result; 544 545 545 - *temp = KELVIN_TO_MILLICELSIUS(tz->temperature, tz->kelvin_offset); 546 + *temp = DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET(tz->temperature, 547 + tz->kelvin_offset); 546 548 return 0; 547 549 } 548 550 ··· 647 647 648 648 if (tz->trips.critical.flags.valid) { 649 649 if (!trip) { 650 - *temp = KELVIN_TO_MILLICELSIUS( 650 + *temp = DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET( 651 651 tz->trips.critical.temperature, 652 652 tz->kelvin_offset); 653 653 return 0; ··· 657 657 658 658 if (tz->trips.hot.flags.valid) { 659 659 if (!trip) { 660 - *temp = KELVIN_TO_MILLICELSIUS( 660 + *temp = DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET( 661 661 tz->trips.hot.temperature, 662 662 tz->kelvin_offset); 663 663 return 0; ··· 667 667 668 668 if (tz->trips.passive.flags.valid) { 669 669 if (!trip) { 670 - *temp = KELVIN_TO_MILLICELSIUS( 670 + *temp = DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET( 671 671 tz->trips.passive.temperature, 672 672 tz->kelvin_offset); 673 673 return 0; ··· 678 678 for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE && 679 679 tz->trips.active[i].flags.valid; i++) { 680 680 if (!trip) { 681 - *temp = KELVIN_TO_MILLICELSIUS( 681 + *temp = DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET( 682 682 tz->trips.active[i].temperature, 683 683 tz->kelvin_offset); 684 684 return 0; ··· 694 694 struct acpi_thermal *tz = thermal->devdata; 695 695 696 696 if (tz->trips.critical.flags.valid) { 697 - *temperature = KELVIN_TO_MILLICELSIUS( 697 + *temperature = DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET( 698 698 tz->trips.critical.temperature, 699 699 tz->kelvin_offset); 700 700 return 0; ··· 714 714 715 715 if (type == THERMAL_TRIP_ACTIVE) { 716 716 unsigned long trip_temp; 717 - unsigned long temp = KELVIN_TO_MILLICELSIUS(tz->temperature, 718 - tz->kelvin_offset); 717 + unsigned long temp = DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET( 718 + tz->temperature, tz->kelvin_offset); 719 719 if (thermal_get_trip_temp(thermal, trip, &trip_temp)) 720 720 return -EINVAL; 721 721
+27 -1
drivers/acpi/utils.c
··· 149 149 break; 150 150 } 151 151 break; 152 + case ACPI_TYPE_LOCAL_REFERENCE: 153 + switch (format_string[i]) { 154 + case 'R': 155 + size_required += sizeof(void *); 156 + tail_offset += sizeof(void *); 157 + break; 158 + default: 159 + printk(KERN_WARNING PREFIX "Invalid package element" 160 + " [%d] got reference," 161 + " expecting [%c]\n", 162 + i, format_string[i]); 163 + return AE_BAD_DATA; 164 + break; 165 + } 166 + break; 152 167 153 168 case ACPI_TYPE_PACKAGE: 154 169 default: ··· 262 247 break; 263 248 } 264 249 break; 265 - 250 + case ACPI_TYPE_LOCAL_REFERENCE: 251 + switch (format_string[i]) { 252 + case 'R': 253 + *(void **)head = 254 + (void *)element->reference.handle; 255 + head += sizeof(void *); 256 + break; 257 + default: 258 + /* Should never get here */ 259 + break; 260 + } 261 + break; 266 262 case ACPI_TYPE_PACKAGE: 267 263 /* TBD: handle nested packages... */ 268 264 default:
+34 -15
drivers/thermal/Kconfig
··· 84 84 Enable this to manage platform thermals using a simple linear 85 85 governor. 86 86 87 + config THERMAL_GOV_BANG_BANG 88 + bool "Bang Bang thermal governor" 89 + default n 90 + help 91 + Enable this to manage platform thermals using bang bang governor. 92 + 93 + Say 'Y' here if you want to use two point temperature regulation 94 + used for fans without throttling. Some fan drivers depend on this 95 + governor to be enabled (e.g. acerhdf). 96 + 87 97 config THERMAL_GOV_USER_SPACE 88 98 bool "User_space thermal governor" 89 99 help ··· 217 207 two trip points which can be set by user to get notifications via thermal 218 208 notification methods. 219 209 220 - config ACPI_INT3403_THERMAL 221 - tristate "ACPI INT3403 thermal driver" 222 - depends on X86 && ACPI 223 - help 224 - Newer laptops and tablets that use ACPI may have thermal sensors 225 - outside the core CPU/SOC for thermal safety reasons. These 226 - temperature sensors are also exposed for the OS to use via the so 227 - called INT3403 ACPI object. This driver will, on devices that have 228 - such sensors, expose the temperature information from these sensors 229 - to userspace via the normal thermal framework. This means that a wide 230 - range of applications and GUI widgets can show this information to 231 - the user or use this information for making decisions. For example, 232 - the Intel Thermal Daemon can use this information to allow the user 233 - to select his laptop to run without turning on the fans. 234 - 235 210 config INTEL_SOC_DTS_THERMAL 236 211 tristate "Intel SoCs DTS thermal driver" 237 212 depends on X86 && IOSF_MBI ··· 228 233 be set by user mode programs to get notifications via Linux thermal 229 234 notification methods.The other trip is a critical trip point, which 230 235 was set by the driver based on the TJ MAX temperature. 236 + 237 + config INT340X_THERMAL 238 + tristate "ACPI INT340X thermal drivers" 239 + depends on X86 && ACPI 240 + select THERMAL_GOV_USER_SPACE 241 + select ACPI_THERMAL_REL 242 + select ACPI_FAN 243 + help 244 + Newer laptops and tablets that use ACPI may have thermal sensors and 245 + other devices with thermal control capabilities outside the core 246 + CPU/SOC, for thermal safety reasons. 247 + They are exposed for the OS to use via the INT3400 ACPI device object 248 + as the master, and INT3401~INT340B ACPI device objects as the slaves. 249 + Enable this to expose the temperature information and cooling ability 250 + from these objects to userspace via the normal thermal framework. 251 + This means that a wide range of applications and GUI widgets can show 252 + the information to the user or use this information for making 253 + decisions. For example, the Intel Thermal Daemon can use this 254 + information to allow the user to select his laptop to run without 255 + turning on the fans. 256 + 257 + config ACPI_THERMAL_REL 258 + tristate 259 + depends on ACPI 231 260 232 261 menu "Texas Instruments thermal drivers" 233 262 source "drivers/thermal/ti-soc-thermal/Kconfig"
+2 -1
drivers/thermal/Makefile
··· 11 11 12 12 # governors 13 13 thermal_sys-$(CONFIG_THERMAL_GOV_FAIR_SHARE) += fair_share.o 14 + thermal_sys-$(CONFIG_THERMAL_GOV_BANG_BANG) += gov_bang_bang.o 14 15 thermal_sys-$(CONFIG_THERMAL_GOV_STEP_WISE) += step_wise.o 15 16 thermal_sys-$(CONFIG_THERMAL_GOV_USER_SPACE) += user_space.o 16 17 ··· 32 31 obj-$(CONFIG_X86_PKG_TEMP_THERMAL) += x86_pkg_temp_thermal.o 33 32 obj-$(CONFIG_INTEL_SOC_DTS_THERMAL) += intel_soc_dts_thermal.o 34 33 obj-$(CONFIG_TI_SOC_THERMAL) += ti-soc-thermal/ 35 - obj-$(CONFIG_ACPI_INT3403_THERMAL) += int3403_thermal.o 34 + obj-$(CONFIG_INT340X_THERMAL) += int340x_thermal/ 36 35 obj-$(CONFIG_ST_THERMAL) += st/
+12
drivers/thermal/fair_share.c
··· 23 23 */ 24 24 25 25 #include <linux/thermal.h> 26 + #include <trace/events/thermal.h> 26 27 27 28 #include "thermal_core.h" 28 29 ··· 35 34 { 36 35 int count = 0; 37 36 unsigned long trip_temp; 37 + enum thermal_trip_type trip_type; 38 38 39 39 if (tz->trips == 0 || !tz->ops->get_trip_temp) 40 40 return 0; ··· 45 43 if (tz->temperature < trip_temp) 46 44 break; 47 45 } 46 + 47 + /* 48 + * count > 0 only if temperature is greater than first trip 49 + * point, in which case, trip_point = count - 1 50 + */ 51 + if (count > 0) { 52 + tz->ops->get_trip_type(tz, count - 1, &trip_type); 53 + trace_thermal_zone_trip(tz, count - 1, trip_type); 54 + } 55 + 48 56 return count; 49 57 } 50 58
+131
drivers/thermal/gov_bang_bang.c
··· 1 + /* 2 + * gov_bang_bang.c - A simple thermal throttling governor using hysteresis 3 + * 4 + * Copyright (C) 2014 Peter Feuerer <peter@piie.net> 5 + * 6 + * Based on step_wise.c with following Copyrights: 7 + * Copyright (C) 2012 Intel Corp 8 + * Copyright (C) 2012 Durgadoss R <durgadoss.r@intel.com> 9 + * 10 + * 11 + * This program is free software; you can redistribute it and/or modify 12 + * it under the terms of the GNU General Public License as published by 13 + * the Free Software Foundation, version 2. 14 + * 15 + * This program is distributed in the hope that it will be useful, 16 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See 18 + * the GNU General Public License for more details. 19 + * 20 + */ 21 + 22 + #include <linux/thermal.h> 23 + 24 + #include "thermal_core.h" 25 + 26 + static void thermal_zone_trip_update(struct thermal_zone_device *tz, int trip) 27 + { 28 + long trip_temp; 29 + unsigned long trip_hyst; 30 + struct thermal_instance *instance; 31 + 32 + tz->ops->get_trip_temp(tz, trip, &trip_temp); 33 + tz->ops->get_trip_hyst(tz, trip, &trip_hyst); 34 + 35 + dev_dbg(&tz->device, "Trip%d[temp=%ld]:temp=%d:hyst=%ld\n", 36 + trip, trip_temp, tz->temperature, 37 + trip_hyst); 38 + 39 + mutex_lock(&tz->lock); 40 + 41 + list_for_each_entry(instance, &tz->thermal_instances, tz_node) { 42 + if (instance->trip != trip) 43 + continue; 44 + 45 + /* in case fan is in initial state, switch the fan off */ 46 + if (instance->target == THERMAL_NO_TARGET) 47 + instance->target = 0; 48 + 49 + /* in case fan is neither on nor off set the fan to active */ 50 + if (instance->target != 0 && instance->target != 1) { 51 + pr_warn("Thermal instance %s controlled by bang-bang has unexpected state: %ld\n", 52 + instance->name, instance->target); 53 + instance->target = 1; 54 + } 55 + 56 + /* 57 + * enable fan when temperature exceeds trip_temp and disable 58 + * the fan in case it falls below trip_temp minus hysteresis 59 + */ 60 + if (instance->target == 0 && tz->temperature >= trip_temp) 61 + instance->target = 1; 62 + else if (instance->target == 1 && 63 + tz->temperature < trip_temp - trip_hyst) 64 + instance->target = 0; 65 + 66 + dev_dbg(&instance->cdev->device, "target=%d\n", 67 + (int)instance->target); 68 + 69 + instance->cdev->updated = false; /* cdev needs update */ 70 + } 71 + 72 + mutex_unlock(&tz->lock); 73 + } 74 + 75 + /** 76 + * bang_bang_control - controls devices associated with the given zone 77 + * @tz - thermal_zone_device 78 + * @trip - the trip point 79 + * 80 + * Regulation Logic: a two point regulation, deliver cooling state depending 81 + * on the previous state shown in this diagram: 82 + * 83 + * Fan: OFF ON 84 + * 85 + * | 86 + * | 87 + * trip_temp: +---->+ 88 + * | | ^ 89 + * | | | 90 + * | | Temperature 91 + * (trip_temp - hyst): +<----+ 92 + * | 93 + * | 94 + * | 95 + * 96 + * * If the fan is not running and temperature exceeds trip_temp, the fan 97 + * gets turned on. 98 + * * In case the fan is running, temperature must fall below 99 + * (trip_temp - hyst) so that the fan gets turned off again. 100 + * 101 + */ 102 + static int bang_bang_control(struct thermal_zone_device *tz, int trip) 103 + { 104 + struct thermal_instance *instance; 105 + 106 + thermal_zone_trip_update(tz, trip); 107 + 108 + mutex_lock(&tz->lock); 109 + 110 + list_for_each_entry(instance, &tz->thermal_instances, tz_node) 111 + thermal_cdev_update(instance->cdev); 112 + 113 + mutex_unlock(&tz->lock); 114 + 115 + return 0; 116 + } 117 + 118 + static struct thermal_governor thermal_gov_bang_bang = { 119 + .name = "bang_bang", 120 + .throttle = bang_bang_control, 121 + }; 122 + 123 + int thermal_gov_bang_bang_register(void) 124 + { 125 + return thermal_register_governor(&thermal_gov_bang_bang); 126 + } 127 + 128 + void thermal_gov_bang_bang_unregister(void) 129 + { 130 + thermal_unregister_governor(&thermal_gov_bang_bang); 131 + }
+78 -13
drivers/thermal/imx_thermal.c
··· 19 19 #include <linux/mfd/syscon.h> 20 20 #include <linux/module.h> 21 21 #include <linux/of.h> 22 + #include <linux/of_device.h> 22 23 #include <linux/platform_device.h> 23 24 #include <linux/regmap.h> 24 25 #include <linux/slab.h> ··· 32 31 33 32 #define MISC0 0x0150 34 33 #define MISC0_REFTOP_SELBIASOFF (1 << 3) 34 + #define MISC1 0x0160 35 + #define MISC1_IRQ_TEMPHIGH (1 << 29) 36 + /* Below LOW and PANIC bits are only for TEMPMON_IMX6SX */ 37 + #define MISC1_IRQ_TEMPLOW (1 << 28) 38 + #define MISC1_IRQ_TEMPPANIC (1 << 27) 35 39 36 40 #define TEMPSENSE0 0x0180 37 41 #define TEMPSENSE0_ALARM_VALUE_SHIFT 20 ··· 49 43 50 44 #define TEMPSENSE1 0x0190 51 45 #define TEMPSENSE1_MEASURE_FREQ 0xffff 46 + /* Below TEMPSENSE2 is only for TEMPMON_IMX6SX */ 47 + #define TEMPSENSE2 0x0290 48 + #define TEMPSENSE2_LOW_VALUE_SHIFT 0 49 + #define TEMPSENSE2_LOW_VALUE_MASK 0xfff 50 + #define TEMPSENSE2_PANIC_VALUE_SHIFT 16 51 + #define TEMPSENSE2_PANIC_VALUE_MASK 0xfff0000 52 52 53 53 #define OCOTP_ANA1 0x04e0 54 54 ··· 78 66 #define FACTOR1 15976 79 67 #define FACTOR2 4297157 80 68 69 + #define TEMPMON_IMX6Q 1 70 + #define TEMPMON_IMX6SX 2 71 + 72 + struct thermal_soc_data { 73 + u32 version; 74 + }; 75 + 76 + static struct thermal_soc_data thermal_imx6q_data = { 77 + .version = TEMPMON_IMX6Q, 78 + }; 79 + 80 + static struct thermal_soc_data thermal_imx6sx_data = { 81 + .version = TEMPMON_IMX6SX, 82 + }; 83 + 81 84 struct imx_thermal_data { 82 85 struct thermal_zone_device *tz; 83 86 struct thermal_cooling_device *cdev; ··· 106 79 bool irq_enabled; 107 80 int irq; 108 81 struct clk *thermal_clk; 82 + const struct thermal_soc_data *socdata; 109 83 }; 84 + 85 + static void imx_set_panic_temp(struct imx_thermal_data *data, 86 + signed long panic_temp) 87 + { 88 + struct regmap *map = data->tempmon; 89 + int critical_value; 90 + 91 + critical_value = (data->c2 - panic_temp) / data->c1; 92 + regmap_write(map, TEMPSENSE2 + REG_CLR, TEMPSENSE2_PANIC_VALUE_MASK); 93 + regmap_write(map, TEMPSENSE2 + REG_SET, critical_value << 94 + TEMPSENSE2_PANIC_VALUE_SHIFT); 95 + } 110 96 111 97 static void imx_set_alarm_temp(struct imx_thermal_data *data, 112 98 signed long alarm_temp) ··· 182 142 /* See imx_get_sensor_data() for formula derivation */ 183 143 *temp = data->c2 - n_meas * data->c1; 184 144 185 - /* Update alarm value to next higher trip point */ 186 - if (data->alarm_temp == data->temp_passive && *temp >= data->temp_passive) 187 - imx_set_alarm_temp(data, data->temp_critical); 188 - if (data->alarm_temp == data->temp_critical && *temp < data->temp_passive) { 189 - imx_set_alarm_temp(data, data->temp_passive); 190 - dev_dbg(&tz->device, "thermal alarm off: T < %lu\n", 191 - data->alarm_temp / 1000); 145 + /* Update alarm value to next higher trip point for TEMPMON_IMX6Q */ 146 + if (data->socdata->version == TEMPMON_IMX6Q) { 147 + if (data->alarm_temp == data->temp_passive && 148 + *temp >= data->temp_passive) 149 + imx_set_alarm_temp(data, data->temp_critical); 150 + if (data->alarm_temp == data->temp_critical && 151 + *temp < data->temp_passive) { 152 + imx_set_alarm_temp(data, data->temp_passive); 153 + dev_dbg(&tz->device, "thermal alarm off: T < %lu\n", 154 + data->alarm_temp / 1000); 155 + } 192 156 } 193 157 194 158 if (*temp != data->last_temp) { ··· 442 398 return IRQ_HANDLED; 443 399 } 444 400 401 + static const struct of_device_id of_imx_thermal_match[] = { 402 + { .compatible = "fsl,imx6q-tempmon", .data = &thermal_imx6q_data, }, 403 + { .compatible = "fsl,imx6sx-tempmon", .data = &thermal_imx6sx_data, }, 404 + { /* end */ } 405 + }; 406 + MODULE_DEVICE_TABLE(of, of_imx_thermal_match); 407 + 445 408 static int imx_thermal_probe(struct platform_device *pdev) 446 409 { 410 + const struct of_device_id *of_id = 411 + of_match_device(of_imx_thermal_match, &pdev->dev); 447 412 struct imx_thermal_data *data; 448 413 struct cpumask clip_cpus; 449 414 struct regmap *map; ··· 470 417 return ret; 471 418 } 472 419 data->tempmon = map; 420 + 421 + data->socdata = of_id->data; 422 + 423 + /* make sure the IRQ flag is clear before enabling irq on i.MX6SX */ 424 + if (data->socdata->version == TEMPMON_IMX6SX) { 425 + regmap_write(map, MISC1 + REG_CLR, MISC1_IRQ_TEMPHIGH | 426 + MISC1_IRQ_TEMPLOW | MISC1_IRQ_TEMPPANIC); 427 + /* 428 + * reset value of LOW ALARM is incorrect, set it to lowest 429 + * value to avoid false trigger of low alarm. 430 + */ 431 + regmap_write(map, TEMPSENSE2 + REG_SET, 432 + TEMPSENSE2_LOW_VALUE_MASK); 433 + } 473 434 474 435 data->irq = platform_get_irq(pdev, 0); 475 436 if (data->irq < 0) ··· 556 489 measure_freq = DIV_ROUND_UP(32768, 10); /* 10 Hz */ 557 490 regmap_write(map, TEMPSENSE1 + REG_SET, measure_freq); 558 491 imx_set_alarm_temp(data, data->temp_passive); 492 + 493 + if (data->socdata->version == TEMPMON_IMX6SX) 494 + imx_set_panic_temp(data, data->temp_critical); 495 + 559 496 regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_POWER_DOWN); 560 497 regmap_write(map, TEMPSENSE0 + REG_SET, TEMPSENSE0_MEASURE_TEMP); 561 498 ··· 620 549 621 550 static SIMPLE_DEV_PM_OPS(imx_thermal_pm_ops, 622 551 imx_thermal_suspend, imx_thermal_resume); 623 - 624 - static const struct of_device_id of_imx_thermal_match[] = { 625 - { .compatible = "fsl,imx6q-tempmon", }, 626 - { /* end */ } 627 - }; 628 - MODULE_DEVICE_TABLE(of, of_imx_thermal_match); 629 552 630 553 static struct platform_driver imx_thermal = { 631 554 .driver = {
-296
drivers/thermal/int3403_thermal.c
··· 1 - /* 2 - * ACPI INT3403 thermal driver 3 - * Copyright (c) 2013, 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 - #include <linux/kernel.h> 16 - #include <linux/module.h> 17 - #include <linux/init.h> 18 - #include <linux/types.h> 19 - #include <linux/acpi.h> 20 - #include <linux/thermal.h> 21 - 22 - #define INT3403_TYPE_SENSOR 0x03 23 - #define INT3403_PERF_CHANGED_EVENT 0x80 24 - #define INT3403_THERMAL_EVENT 0x90 25 - 26 - #define DECI_KELVIN_TO_MILLI_CELSIUS(t, off) (((t) - (off)) * 100) 27 - #define KELVIN_OFFSET 2732 28 - #define MILLI_CELSIUS_TO_DECI_KELVIN(t, off) (((t) / 100) + (off)) 29 - 30 - #define ACPI_INT3403_CLASS "int3403" 31 - #define ACPI_INT3403_FILE_STATE "state" 32 - 33 - struct int3403_sensor { 34 - struct thermal_zone_device *tzone; 35 - unsigned long *thresholds; 36 - unsigned long crit_temp; 37 - int crit_trip_id; 38 - unsigned long psv_temp; 39 - int psv_trip_id; 40 - }; 41 - 42 - static int sys_get_curr_temp(struct thermal_zone_device *tzone, 43 - unsigned long *temp) 44 - { 45 - struct acpi_device *device = tzone->devdata; 46 - unsigned long long tmp; 47 - acpi_status status; 48 - 49 - status = acpi_evaluate_integer(device->handle, "_TMP", NULL, &tmp); 50 - if (ACPI_FAILURE(status)) 51 - return -EIO; 52 - 53 - *temp = DECI_KELVIN_TO_MILLI_CELSIUS(tmp, KELVIN_OFFSET); 54 - 55 - return 0; 56 - } 57 - 58 - static int sys_get_trip_hyst(struct thermal_zone_device *tzone, 59 - int trip, unsigned long *temp) 60 - { 61 - struct acpi_device *device = tzone->devdata; 62 - unsigned long long hyst; 63 - acpi_status status; 64 - 65 - status = acpi_evaluate_integer(device->handle, "GTSH", NULL, &hyst); 66 - if (ACPI_FAILURE(status)) 67 - return -EIO; 68 - 69 - /* 70 - * Thermal hysteresis represents a temperature difference. 71 - * Kelvin and Celsius have same degree size. So the 72 - * conversion here between tenths of degree Kelvin unit 73 - * and Milli-Celsius unit is just to multiply 100. 74 - */ 75 - *temp = hyst * 100; 76 - 77 - return 0; 78 - } 79 - 80 - static int sys_get_trip_temp(struct thermal_zone_device *tzone, 81 - int trip, unsigned long *temp) 82 - { 83 - struct acpi_device *device = tzone->devdata; 84 - struct int3403_sensor *obj = acpi_driver_data(device); 85 - 86 - if (trip == obj->crit_trip_id) 87 - *temp = obj->crit_temp; 88 - else if (trip == obj->psv_trip_id) 89 - *temp = obj->psv_temp; 90 - else { 91 - /* 92 - * get_trip_temp is a mandatory callback but 93 - * PATx method doesn't return any value, so return 94 - * cached value, which was last set from user space. 95 - */ 96 - *temp = obj->thresholds[trip]; 97 - } 98 - 99 - return 0; 100 - } 101 - 102 - static int sys_get_trip_type(struct thermal_zone_device *thermal, 103 - int trip, enum thermal_trip_type *type) 104 - { 105 - struct acpi_device *device = thermal->devdata; 106 - struct int3403_sensor *obj = acpi_driver_data(device); 107 - 108 - /* Mandatory callback, may not mean much here */ 109 - if (trip == obj->crit_trip_id) 110 - *type = THERMAL_TRIP_CRITICAL; 111 - else 112 - *type = THERMAL_TRIP_PASSIVE; 113 - 114 - return 0; 115 - } 116 - 117 - int sys_set_trip_temp(struct thermal_zone_device *tzone, int trip, 118 - unsigned long temp) 119 - { 120 - struct acpi_device *device = tzone->devdata; 121 - acpi_status status; 122 - char name[10]; 123 - int ret = 0; 124 - struct int3403_sensor *obj = acpi_driver_data(device); 125 - 126 - snprintf(name, sizeof(name), "PAT%d", trip); 127 - if (acpi_has_method(device->handle, name)) { 128 - status = acpi_execute_simple_method(device->handle, name, 129 - MILLI_CELSIUS_TO_DECI_KELVIN(temp, 130 - KELVIN_OFFSET)); 131 - if (ACPI_FAILURE(status)) 132 - ret = -EIO; 133 - else 134 - obj->thresholds[trip] = temp; 135 - } else { 136 - ret = -EIO; 137 - dev_err(&device->dev, "sys_set_trip_temp: method not found\n"); 138 - } 139 - 140 - return ret; 141 - } 142 - 143 - static struct thermal_zone_device_ops tzone_ops = { 144 - .get_temp = sys_get_curr_temp, 145 - .get_trip_temp = sys_get_trip_temp, 146 - .get_trip_type = sys_get_trip_type, 147 - .set_trip_temp = sys_set_trip_temp, 148 - .get_trip_hyst = sys_get_trip_hyst, 149 - }; 150 - 151 - static void acpi_thermal_notify(struct acpi_device *device, u32 event) 152 - { 153 - struct int3403_sensor *obj; 154 - 155 - if (!device) 156 - return; 157 - 158 - obj = acpi_driver_data(device); 159 - if (!obj) 160 - return; 161 - 162 - switch (event) { 163 - case INT3403_PERF_CHANGED_EVENT: 164 - break; 165 - case INT3403_THERMAL_EVENT: 166 - thermal_zone_device_update(obj->tzone); 167 - break; 168 - default: 169 - dev_err(&device->dev, "Unsupported event [0x%x]\n", event); 170 - break; 171 - } 172 - } 173 - 174 - static int sys_get_trip_crt(struct acpi_device *device, unsigned long *temp) 175 - { 176 - unsigned long long crt; 177 - acpi_status status; 178 - 179 - status = acpi_evaluate_integer(device->handle, "_CRT", NULL, &crt); 180 - if (ACPI_FAILURE(status)) 181 - return -EIO; 182 - 183 - *temp = DECI_KELVIN_TO_MILLI_CELSIUS(crt, KELVIN_OFFSET); 184 - 185 - return 0; 186 - } 187 - 188 - static int sys_get_trip_psv(struct acpi_device *device, unsigned long *temp) 189 - { 190 - unsigned long long psv; 191 - acpi_status status; 192 - 193 - status = acpi_evaluate_integer(device->handle, "_PSV", NULL, &psv); 194 - if (ACPI_FAILURE(status)) 195 - return -EIO; 196 - 197 - *temp = DECI_KELVIN_TO_MILLI_CELSIUS(psv, KELVIN_OFFSET); 198 - 199 - return 0; 200 - } 201 - 202 - static int acpi_int3403_add(struct acpi_device *device) 203 - { 204 - int result = 0; 205 - unsigned long long ptyp; 206 - acpi_status status; 207 - struct int3403_sensor *obj; 208 - unsigned long long trip_cnt; 209 - int trip_mask = 0; 210 - 211 - if (!device) 212 - return -EINVAL; 213 - 214 - status = acpi_evaluate_integer(device->handle, "PTYP", NULL, &ptyp); 215 - if (ACPI_FAILURE(status)) 216 - return -EINVAL; 217 - 218 - if (ptyp != INT3403_TYPE_SENSOR) 219 - return -EINVAL; 220 - 221 - obj = devm_kzalloc(&device->dev, sizeof(*obj), GFP_KERNEL); 222 - if (!obj) 223 - return -ENOMEM; 224 - 225 - device->driver_data = obj; 226 - 227 - status = acpi_evaluate_integer(device->handle, "PATC", NULL, 228 - &trip_cnt); 229 - if (ACPI_FAILURE(status)) 230 - trip_cnt = 0; 231 - 232 - if (trip_cnt) { 233 - /* We have to cache, thresholds can't be readback */ 234 - obj->thresholds = devm_kzalloc(&device->dev, 235 - sizeof(*obj->thresholds) * trip_cnt, 236 - GFP_KERNEL); 237 - if (!obj->thresholds) 238 - return -ENOMEM; 239 - trip_mask = BIT(trip_cnt) - 1; 240 - } 241 - 242 - obj->psv_trip_id = -1; 243 - if (!sys_get_trip_psv(device, &obj->psv_temp)) 244 - obj->psv_trip_id = trip_cnt++; 245 - 246 - obj->crit_trip_id = -1; 247 - if (!sys_get_trip_crt(device, &obj->crit_temp)) 248 - obj->crit_trip_id = trip_cnt++; 249 - 250 - obj->tzone = thermal_zone_device_register(acpi_device_bid(device), 251 - trip_cnt, trip_mask, device, &tzone_ops, 252 - NULL, 0, 0); 253 - if (IS_ERR(obj->tzone)) { 254 - result = PTR_ERR(obj->tzone); 255 - return result; 256 - } 257 - 258 - strcpy(acpi_device_name(device), "INT3403"); 259 - strcpy(acpi_device_class(device), ACPI_INT3403_CLASS); 260 - 261 - return 0; 262 - } 263 - 264 - static int acpi_int3403_remove(struct acpi_device *device) 265 - { 266 - struct int3403_sensor *obj; 267 - 268 - obj = acpi_driver_data(device); 269 - thermal_zone_device_unregister(obj->tzone); 270 - 271 - return 0; 272 - } 273 - 274 - ACPI_MODULE_NAME("int3403"); 275 - static const struct acpi_device_id int3403_device_ids[] = { 276 - {"INT3403", 0}, 277 - {"", 0}, 278 - }; 279 - MODULE_DEVICE_TABLE(acpi, int3403_device_ids); 280 - 281 - static struct acpi_driver acpi_int3403_driver = { 282 - .name = "INT3403", 283 - .class = ACPI_INT3403_CLASS, 284 - .ids = int3403_device_ids, 285 - .ops = { 286 - .add = acpi_int3403_add, 287 - .remove = acpi_int3403_remove, 288 - .notify = acpi_thermal_notify, 289 - }, 290 - }; 291 - 292 - module_acpi_driver(acpi_int3403_driver); 293 - 294 - MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>"); 295 - MODULE_LICENSE("GPL v2"); 296 - MODULE_DESCRIPTION("ACPI INT3403 thermal driver");
+4
drivers/thermal/int340x_thermal/Makefile
··· 1 + obj-$(CONFIG_INT340X_THERMAL) += int3400_thermal.o 2 + obj-$(CONFIG_INT340X_THERMAL) += int3402_thermal.o 3 + obj-$(CONFIG_INT340X_THERMAL) += int3403_thermal.o 4 + obj-$(CONFIG_ACPI_THERMAL_REL) += acpi_thermal_rel.o
+400
drivers/thermal/int340x_thermal/acpi_thermal_rel.c
··· 1 + /* acpi_thermal_rel.c driver for exporting ACPI thermal relationship 2 + * 3 + * Copyright (c) 2014 Intel Corp 4 + * 5 + * This program is free software; you can redistribute it and/or modify it 6 + * under the terms of the GNU General Public License version 2 as published by 7 + * the Free Software Foundation. 8 + * 9 + */ 10 + 11 + /* 12 + * Two functionalities included: 13 + * 1. Export _TRT, _ART, via misc device interface to the userspace. 14 + * 2. Provide parsing result to kernel drivers 15 + * 16 + */ 17 + #include <linux/init.h> 18 + #include <linux/export.h> 19 + #include <linux/module.h> 20 + #include <linux/device.h> 21 + #include <linux/platform_device.h> 22 + #include <linux/io.h> 23 + #include <linux/acpi.h> 24 + #include <linux/uaccess.h> 25 + #include <linux/miscdevice.h> 26 + #include "acpi_thermal_rel.h" 27 + 28 + static acpi_handle acpi_thermal_rel_handle; 29 + static DEFINE_SPINLOCK(acpi_thermal_rel_chrdev_lock); 30 + static int acpi_thermal_rel_chrdev_count; /* #times opened */ 31 + static int acpi_thermal_rel_chrdev_exclu; /* already open exclusive? */ 32 + 33 + static int acpi_thermal_rel_open(struct inode *inode, struct file *file) 34 + { 35 + spin_lock(&acpi_thermal_rel_chrdev_lock); 36 + if (acpi_thermal_rel_chrdev_exclu || 37 + (acpi_thermal_rel_chrdev_count && (file->f_flags & O_EXCL))) { 38 + spin_unlock(&acpi_thermal_rel_chrdev_lock); 39 + return -EBUSY; 40 + } 41 + 42 + if (file->f_flags & O_EXCL) 43 + acpi_thermal_rel_chrdev_exclu = 1; 44 + acpi_thermal_rel_chrdev_count++; 45 + 46 + spin_unlock(&acpi_thermal_rel_chrdev_lock); 47 + 48 + return nonseekable_open(inode, file); 49 + } 50 + 51 + static int acpi_thermal_rel_release(struct inode *inode, struct file *file) 52 + { 53 + spin_lock(&acpi_thermal_rel_chrdev_lock); 54 + acpi_thermal_rel_chrdev_count--; 55 + acpi_thermal_rel_chrdev_exclu = 0; 56 + spin_unlock(&acpi_thermal_rel_chrdev_lock); 57 + 58 + return 0; 59 + } 60 + 61 + /** 62 + * acpi_parse_trt - Thermal Relationship Table _TRT for passive cooling 63 + * 64 + * @handle: ACPI handle of the device contains _TRT 65 + * @art_count: the number of valid entries resulted from parsing _TRT 66 + * @artp: pointer to pointer of array of art entries in parsing result 67 + * @create_dev: whether to create platform devices for target and source 68 + * 69 + */ 70 + int acpi_parse_trt(acpi_handle handle, int *trt_count, struct trt **trtp, 71 + bool create_dev) 72 + { 73 + acpi_status status; 74 + int result = 0; 75 + int i; 76 + int nr_bad_entries = 0; 77 + struct trt *trts; 78 + struct acpi_device *adev; 79 + union acpi_object *p; 80 + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; 81 + struct acpi_buffer element = { 0, NULL }; 82 + struct acpi_buffer trt_format = { sizeof("RRNNNNNN"), "RRNNNNNN" }; 83 + 84 + if (!acpi_has_method(handle, "_TRT")) 85 + return 0; 86 + 87 + status = acpi_evaluate_object(handle, "_TRT", NULL, &buffer); 88 + if (ACPI_FAILURE(status)) 89 + return -ENODEV; 90 + 91 + p = buffer.pointer; 92 + if (!p || (p->type != ACPI_TYPE_PACKAGE)) { 93 + pr_err("Invalid _TRT data\n"); 94 + result = -EFAULT; 95 + goto end; 96 + } 97 + 98 + *trt_count = p->package.count; 99 + trts = kzalloc(*trt_count * sizeof(struct trt), GFP_KERNEL); 100 + if (!trts) { 101 + result = -ENOMEM; 102 + goto end; 103 + } 104 + 105 + for (i = 0; i < *trt_count; i++) { 106 + struct trt *trt = &trts[i - nr_bad_entries]; 107 + 108 + element.length = sizeof(struct trt); 109 + element.pointer = trt; 110 + 111 + status = acpi_extract_package(&(p->package.elements[i]), 112 + &trt_format, &element); 113 + if (ACPI_FAILURE(status)) { 114 + nr_bad_entries++; 115 + pr_warn("_TRT package %d is invalid, ignored\n", i); 116 + continue; 117 + } 118 + if (!create_dev) 119 + continue; 120 + 121 + result = acpi_bus_get_device(trt->source, &adev); 122 + if (!result) 123 + acpi_create_platform_device(adev); 124 + else 125 + pr_warn("Failed to get source ACPI device\n"); 126 + 127 + result = acpi_bus_get_device(trt->target, &adev); 128 + if (!result) 129 + acpi_create_platform_device(adev); 130 + else 131 + pr_warn("Failed to get target ACPI device\n"); 132 + } 133 + 134 + *trtp = trts; 135 + /* don't count bad entries */ 136 + *trt_count -= nr_bad_entries; 137 + end: 138 + kfree(buffer.pointer); 139 + return result; 140 + } 141 + EXPORT_SYMBOL(acpi_parse_trt); 142 + 143 + /** 144 + * acpi_parse_art - Parse Active Relationship Table _ART 145 + * 146 + * @handle: ACPI handle of the device contains _ART 147 + * @art_count: the number of valid entries resulted from parsing _ART 148 + * @artp: pointer to pointer of array of art entries in parsing result 149 + * @create_dev: whether to create platform devices for target and source 150 + * 151 + */ 152 + int acpi_parse_art(acpi_handle handle, int *art_count, struct art **artp, 153 + bool create_dev) 154 + { 155 + acpi_status status; 156 + int result = 0; 157 + int i; 158 + int nr_bad_entries = 0; 159 + struct art *arts; 160 + struct acpi_device *adev; 161 + union acpi_object *p; 162 + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; 163 + struct acpi_buffer element = { 0, NULL }; 164 + struct acpi_buffer art_format = { 165 + sizeof("RRNNNNNNNNNNN"), "RRNNNNNNNNNNN" }; 166 + 167 + if (!acpi_has_method(handle, "_ART")) 168 + return 0; 169 + 170 + status = acpi_evaluate_object(handle, "_ART", NULL, &buffer); 171 + if (ACPI_FAILURE(status)) 172 + return -ENODEV; 173 + 174 + p = buffer.pointer; 175 + if (!p || (p->type != ACPI_TYPE_PACKAGE)) { 176 + pr_err("Invalid _ART data\n"); 177 + result = -EFAULT; 178 + goto end; 179 + } 180 + 181 + /* ignore p->package.elements[0], as this is _ART Revision field */ 182 + *art_count = p->package.count - 1; 183 + arts = kzalloc(*art_count * sizeof(struct art), GFP_KERNEL); 184 + if (!arts) { 185 + result = -ENOMEM; 186 + goto end; 187 + } 188 + 189 + for (i = 0; i < *art_count; i++) { 190 + struct art *art = &arts[i - nr_bad_entries]; 191 + 192 + element.length = sizeof(struct art); 193 + element.pointer = art; 194 + 195 + status = acpi_extract_package(&(p->package.elements[i + 1]), 196 + &art_format, &element); 197 + if (ACPI_FAILURE(status)) { 198 + pr_warn("_ART package %d is invalid, ignored", i); 199 + nr_bad_entries++; 200 + continue; 201 + } 202 + if (!create_dev) 203 + continue; 204 + 205 + if (art->source) { 206 + result = acpi_bus_get_device(art->source, &adev); 207 + if (!result) 208 + acpi_create_platform_device(adev); 209 + else 210 + pr_warn("Failed to get source ACPI device\n"); 211 + } 212 + if (art->target) { 213 + result = acpi_bus_get_device(art->target, &adev); 214 + if (!result) 215 + acpi_create_platform_device(adev); 216 + else 217 + pr_warn("Failed to get source ACPI device\n"); 218 + } 219 + } 220 + 221 + *artp = arts; 222 + /* don't count bad entries */ 223 + *art_count -= nr_bad_entries; 224 + end: 225 + kfree(buffer.pointer); 226 + return result; 227 + } 228 + EXPORT_SYMBOL(acpi_parse_art); 229 + 230 + 231 + /* get device name from acpi handle */ 232 + static void get_single_name(acpi_handle handle, char *name) 233 + { 234 + struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER}; 235 + 236 + if (ACPI_FAILURE(acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer))) 237 + pr_warn("Failed get name from handle\n"); 238 + else { 239 + memcpy(name, buffer.pointer, ACPI_NAME_SIZE); 240 + kfree(buffer.pointer); 241 + } 242 + } 243 + 244 + static int fill_art(char __user *ubuf) 245 + { 246 + int i; 247 + int ret; 248 + int count; 249 + int art_len; 250 + struct art *arts = NULL; 251 + union art_object *art_user; 252 + 253 + ret = acpi_parse_art(acpi_thermal_rel_handle, &count, &arts, false); 254 + if (ret) 255 + goto free_art; 256 + art_len = count * sizeof(union art_object); 257 + art_user = kzalloc(art_len, GFP_KERNEL); 258 + if (!art_user) { 259 + ret = -ENOMEM; 260 + goto free_art; 261 + } 262 + /* now fill in user art data */ 263 + for (i = 0; i < count; i++) { 264 + /* userspace art needs device name instead of acpi reference */ 265 + get_single_name(arts[i].source, art_user[i].source_device); 266 + get_single_name(arts[i].target, art_user[i].target_device); 267 + /* copy the rest int data in addition to source and target */ 268 + memcpy(&art_user[i].weight, &arts[i].weight, 269 + sizeof(u64) * (ACPI_NR_ART_ELEMENTS - 2)); 270 + } 271 + 272 + if (copy_to_user(ubuf, art_user, art_len)) 273 + ret = -EFAULT; 274 + kfree(art_user); 275 + free_art: 276 + kfree(arts); 277 + return ret; 278 + } 279 + 280 + static int fill_trt(char __user *ubuf) 281 + { 282 + int i; 283 + int ret; 284 + int count; 285 + int trt_len; 286 + struct trt *trts = NULL; 287 + union trt_object *trt_user; 288 + 289 + ret = acpi_parse_trt(acpi_thermal_rel_handle, &count, &trts, false); 290 + if (ret) 291 + goto free_trt; 292 + trt_len = count * sizeof(union trt_object); 293 + trt_user = kzalloc(trt_len, GFP_KERNEL); 294 + if (!trt_user) { 295 + ret = -ENOMEM; 296 + goto free_trt; 297 + } 298 + /* now fill in user trt data */ 299 + for (i = 0; i < count; i++) { 300 + /* userspace trt needs device name instead of acpi reference */ 301 + get_single_name(trts[i].source, trt_user[i].source_device); 302 + get_single_name(trts[i].target, trt_user[i].target_device); 303 + trt_user[i].sample_period = trts[i].sample_period; 304 + trt_user[i].influence = trts[i].influence; 305 + } 306 + 307 + if (copy_to_user(ubuf, trt_user, trt_len)) 308 + ret = -EFAULT; 309 + kfree(trt_user); 310 + free_trt: 311 + kfree(trts); 312 + return ret; 313 + } 314 + 315 + static long acpi_thermal_rel_ioctl(struct file *f, unsigned int cmd, 316 + unsigned long __arg) 317 + { 318 + int ret = 0; 319 + unsigned long length = 0; 320 + unsigned long count = 0; 321 + char __user *arg = (void __user *)__arg; 322 + struct trt *trts; 323 + struct art *arts; 324 + 325 + switch (cmd) { 326 + case ACPI_THERMAL_GET_TRT_COUNT: 327 + ret = acpi_parse_trt(acpi_thermal_rel_handle, (int *)&count, 328 + &trts, false); 329 + kfree(trts); 330 + if (!ret) 331 + return put_user(count, (unsigned long __user *)__arg); 332 + return ret; 333 + case ACPI_THERMAL_GET_TRT_LEN: 334 + ret = acpi_parse_trt(acpi_thermal_rel_handle, (int *)&count, 335 + &trts, false); 336 + kfree(trts); 337 + length = count * sizeof(union trt_object); 338 + if (!ret) 339 + return put_user(length, (unsigned long __user *)__arg); 340 + return ret; 341 + case ACPI_THERMAL_GET_TRT: 342 + return fill_trt(arg); 343 + case ACPI_THERMAL_GET_ART_COUNT: 344 + ret = acpi_parse_art(acpi_thermal_rel_handle, (int *)&count, 345 + &arts, false); 346 + kfree(arts); 347 + if (!ret) 348 + return put_user(count, (unsigned long __user *)__arg); 349 + return ret; 350 + case ACPI_THERMAL_GET_ART_LEN: 351 + ret = acpi_parse_art(acpi_thermal_rel_handle, (int *)&count, 352 + &arts, false); 353 + kfree(arts); 354 + length = count * sizeof(union art_object); 355 + if (!ret) 356 + return put_user(length, (unsigned long __user *)__arg); 357 + return ret; 358 + 359 + case ACPI_THERMAL_GET_ART: 360 + return fill_art(arg); 361 + 362 + default: 363 + return -ENOTTY; 364 + } 365 + } 366 + 367 + static const struct file_operations acpi_thermal_rel_fops = { 368 + .owner = THIS_MODULE, 369 + .open = acpi_thermal_rel_open, 370 + .release = acpi_thermal_rel_release, 371 + .unlocked_ioctl = acpi_thermal_rel_ioctl, 372 + .llseek = no_llseek, 373 + }; 374 + 375 + static struct miscdevice acpi_thermal_rel_misc_device = { 376 + .minor = MISC_DYNAMIC_MINOR, 377 + "acpi_thermal_rel", 378 + &acpi_thermal_rel_fops 379 + }; 380 + 381 + int acpi_thermal_rel_misc_device_add(acpi_handle handle) 382 + { 383 + acpi_thermal_rel_handle = handle; 384 + 385 + return misc_register(&acpi_thermal_rel_misc_device); 386 + } 387 + EXPORT_SYMBOL(acpi_thermal_rel_misc_device_add); 388 + 389 + int acpi_thermal_rel_misc_device_remove(acpi_handle handle) 390 + { 391 + misc_deregister(&acpi_thermal_rel_misc_device); 392 + 393 + return 0; 394 + } 395 + EXPORT_SYMBOL(acpi_thermal_rel_misc_device_remove); 396 + 397 + MODULE_AUTHOR("Zhang Rui <rui.zhang@intel.com>"); 398 + MODULE_AUTHOR("Jacob Pan <jacob.jun.pan@intel.com"); 399 + MODULE_DESCRIPTION("Intel acpi thermal rel misc dev driver"); 400 + MODULE_LICENSE("GPL v2");
+84
drivers/thermal/int340x_thermal/acpi_thermal_rel.h
··· 1 + #ifndef __ACPI_ACPI_THERMAL_H 2 + #define __ACPI_ACPI_THERMAL_H 3 + 4 + #include <asm/ioctl.h> 5 + 6 + #define ACPI_THERMAL_MAGIC 's' 7 + 8 + #define ACPI_THERMAL_GET_TRT_LEN _IOR(ACPI_THERMAL_MAGIC, 1, unsigned long) 9 + #define ACPI_THERMAL_GET_ART_LEN _IOR(ACPI_THERMAL_MAGIC, 2, unsigned long) 10 + #define ACPI_THERMAL_GET_TRT_COUNT _IOR(ACPI_THERMAL_MAGIC, 3, unsigned long) 11 + #define ACPI_THERMAL_GET_ART_COUNT _IOR(ACPI_THERMAL_MAGIC, 4, unsigned long) 12 + 13 + #define ACPI_THERMAL_GET_TRT _IOR(ACPI_THERMAL_MAGIC, 5, unsigned long) 14 + #define ACPI_THERMAL_GET_ART _IOR(ACPI_THERMAL_MAGIC, 6, unsigned long) 15 + 16 + struct art { 17 + acpi_handle source; 18 + acpi_handle target; 19 + u64 weight; 20 + u64 ac0_max; 21 + u64 ac1_max; 22 + u64 ac2_max; 23 + u64 ac3_max; 24 + u64 ac4_max; 25 + u64 ac5_max; 26 + u64 ac6_max; 27 + u64 ac7_max; 28 + u64 ac8_max; 29 + u64 ac9_max; 30 + } __packed; 31 + 32 + struct trt { 33 + acpi_handle source; 34 + acpi_handle target; 35 + u64 influence; 36 + u64 sample_period; 37 + u64 reverved1; 38 + u64 reverved2; 39 + u64 reverved3; 40 + u64 reverved4; 41 + } __packed; 42 + 43 + #define ACPI_NR_ART_ELEMENTS 13 44 + /* for usrspace */ 45 + union art_object { 46 + struct { 47 + char source_device[8]; /* ACPI single name */ 48 + char target_device[8]; /* ACPI single name */ 49 + u64 weight; 50 + u64 ac0_max_level; 51 + u64 ac1_max_level; 52 + u64 ac2_max_level; 53 + u64 ac3_max_level; 54 + u64 ac4_max_level; 55 + u64 ac5_max_level; 56 + u64 ac6_max_level; 57 + u64 ac7_max_level; 58 + u64 ac8_max_level; 59 + u64 ac9_max_level; 60 + }; 61 + u64 __data[ACPI_NR_ART_ELEMENTS]; 62 + }; 63 + 64 + union trt_object { 65 + struct { 66 + char source_device[8]; /* ACPI single name */ 67 + char target_device[8]; /* ACPI single name */ 68 + u64 influence; 69 + u64 sample_period; 70 + u64 reserved[4]; 71 + }; 72 + u64 __data[8]; 73 + }; 74 + 75 + #ifdef __KERNEL__ 76 + int acpi_thermal_rel_misc_device_add(acpi_handle handle); 77 + int acpi_thermal_rel_misc_device_remove(acpi_handle handle); 78 + int acpi_parse_art(acpi_handle handle, int *art_count, struct art **arts, 79 + bool create_dev); 80 + int acpi_parse_trt(acpi_handle handle, int *trt_count, struct trt **trts, 81 + bool create_dev); 82 + #endif 83 + 84 + #endif /* __ACPI_ACPI_THERMAL_H */
+271
drivers/thermal/int340x_thermal/int3400_thermal.c
··· 1 + /* 2 + * INT3400 thermal driver 3 + * 4 + * Copyright (C) 2014, Intel Corporation 5 + * Authors: Zhang Rui <rui.zhang@intel.com> 6 + * 7 + * This program is free software; you can redistribute it and/or modify 8 + * it under the terms of the GNU General Public License version 2 as 9 + * published by the Free Software Foundation. 10 + * 11 + */ 12 + 13 + #include <linux/module.h> 14 + #include <linux/platform_device.h> 15 + #include <linux/acpi.h> 16 + #include <linux/thermal.h> 17 + #include "acpi_thermal_rel.h" 18 + 19 + enum int3400_thermal_uuid { 20 + INT3400_THERMAL_PASSIVE_1, 21 + INT3400_THERMAL_PASSIVE_2, 22 + INT3400_THERMAL_ACTIVE, 23 + INT3400_THERMAL_CRITICAL, 24 + INT3400_THERMAL_COOLING_MODE, 25 + INT3400_THERMAL_MAXIMUM_UUID, 26 + }; 27 + 28 + static u8 *int3400_thermal_uuids[INT3400_THERMAL_MAXIMUM_UUID] = { 29 + "42A441D6-AE6A-462b-A84B-4A8CE79027D3", 30 + "9E04115A-AE87-4D1C-9500-0F3E340BFE75", 31 + "3A95C389-E4B8-4629-A526-C52C88626BAE", 32 + "97C68AE7-15FA-499c-B8C9-5DA81D606E0A", 33 + "16CAF1B7-DD38-40ed-B1C1-1B8A1913D531", 34 + }; 35 + 36 + struct int3400_thermal_priv { 37 + struct acpi_device *adev; 38 + struct thermal_zone_device *thermal; 39 + int mode; 40 + int art_count; 41 + struct art *arts; 42 + int trt_count; 43 + struct trt *trts; 44 + u8 uuid_bitmap; 45 + int rel_misc_dev_res; 46 + }; 47 + 48 + static int int3400_thermal_get_uuids(struct int3400_thermal_priv *priv) 49 + { 50 + struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL}; 51 + union acpi_object *obja, *objb; 52 + int i, j; 53 + int result = 0; 54 + acpi_status status; 55 + 56 + status = acpi_evaluate_object(priv->adev->handle, "IDSP", NULL, &buf); 57 + if (ACPI_FAILURE(status)) 58 + return -ENODEV; 59 + 60 + obja = (union acpi_object *)buf.pointer; 61 + if (obja->type != ACPI_TYPE_PACKAGE) { 62 + result = -EINVAL; 63 + goto end; 64 + } 65 + 66 + for (i = 0; i < obja->package.count; i++) { 67 + objb = &obja->package.elements[i]; 68 + if (objb->type != ACPI_TYPE_BUFFER) { 69 + result = -EINVAL; 70 + goto end; 71 + } 72 + 73 + /* UUID must be 16 bytes */ 74 + if (objb->buffer.length != 16) { 75 + result = -EINVAL; 76 + goto end; 77 + } 78 + 79 + for (j = 0; j < INT3400_THERMAL_MAXIMUM_UUID; j++) { 80 + u8 uuid[16]; 81 + 82 + acpi_str_to_uuid(int3400_thermal_uuids[j], uuid); 83 + if (!strncmp(uuid, objb->buffer.pointer, 16)) { 84 + priv->uuid_bitmap |= (1 << j); 85 + break; 86 + } 87 + } 88 + } 89 + 90 + end: 91 + kfree(buf.pointer); 92 + return result; 93 + } 94 + 95 + static int int3400_thermal_run_osc(acpi_handle handle, 96 + enum int3400_thermal_uuid uuid, bool enable) 97 + { 98 + u32 ret, buf[2]; 99 + acpi_status status; 100 + int result = 0; 101 + struct acpi_osc_context context = { 102 + .uuid_str = int3400_thermal_uuids[uuid], 103 + .rev = 1, 104 + .cap.length = 8, 105 + }; 106 + 107 + buf[OSC_QUERY_DWORD] = 0; 108 + buf[OSC_SUPPORT_DWORD] = enable; 109 + 110 + context.cap.pointer = buf; 111 + 112 + status = acpi_run_osc(handle, &context); 113 + if (ACPI_SUCCESS(status)) { 114 + ret = *((u32 *)(context.ret.pointer + 4)); 115 + if (ret != enable) 116 + result = -EPERM; 117 + } else 118 + result = -EPERM; 119 + 120 + kfree(context.ret.pointer); 121 + return result; 122 + } 123 + 124 + static int int3400_thermal_get_temp(struct thermal_zone_device *thermal, 125 + unsigned long *temp) 126 + { 127 + *temp = 20 * 1000; /* faked temp sensor with 20C */ 128 + return 0; 129 + } 130 + 131 + static int int3400_thermal_get_mode(struct thermal_zone_device *thermal, 132 + enum thermal_device_mode *mode) 133 + { 134 + struct int3400_thermal_priv *priv = thermal->devdata; 135 + 136 + if (!priv) 137 + return -EINVAL; 138 + 139 + *mode = priv->mode; 140 + 141 + return 0; 142 + } 143 + 144 + static int int3400_thermal_set_mode(struct thermal_zone_device *thermal, 145 + enum thermal_device_mode mode) 146 + { 147 + struct int3400_thermal_priv *priv = thermal->devdata; 148 + bool enable; 149 + int result = 0; 150 + 151 + if (!priv) 152 + return -EINVAL; 153 + 154 + if (mode == THERMAL_DEVICE_ENABLED) 155 + enable = true; 156 + else if (mode == THERMAL_DEVICE_DISABLED) 157 + enable = false; 158 + else 159 + return -EINVAL; 160 + 161 + if (enable != priv->mode) { 162 + priv->mode = enable; 163 + /* currently, only PASSIVE COOLING is supported */ 164 + result = int3400_thermal_run_osc(priv->adev->handle, 165 + INT3400_THERMAL_PASSIVE_1, enable); 166 + } 167 + return result; 168 + } 169 + 170 + static struct thermal_zone_device_ops int3400_thermal_ops = { 171 + .get_temp = int3400_thermal_get_temp, 172 + }; 173 + 174 + static struct thermal_zone_params int3400_thermal_params = { 175 + .governor_name = "user_space", 176 + .no_hwmon = true, 177 + }; 178 + 179 + static int int3400_thermal_probe(struct platform_device *pdev) 180 + { 181 + struct acpi_device *adev = ACPI_COMPANION(&pdev->dev); 182 + struct int3400_thermal_priv *priv; 183 + int result; 184 + 185 + if (!adev) 186 + return -ENODEV; 187 + 188 + priv = kzalloc(sizeof(struct int3400_thermal_priv), GFP_KERNEL); 189 + if (!priv) 190 + return -ENOMEM; 191 + 192 + priv->adev = adev; 193 + 194 + result = int3400_thermal_get_uuids(priv); 195 + if (result) 196 + goto free_priv; 197 + 198 + result = acpi_parse_art(priv->adev->handle, &priv->art_count, 199 + &priv->arts, true); 200 + if (result) 201 + goto free_priv; 202 + 203 + 204 + result = acpi_parse_trt(priv->adev->handle, &priv->trt_count, 205 + &priv->trts, true); 206 + if (result) 207 + goto free_art; 208 + 209 + platform_set_drvdata(pdev, priv); 210 + 211 + if (priv->uuid_bitmap & 1 << INT3400_THERMAL_PASSIVE_1) { 212 + int3400_thermal_ops.get_mode = int3400_thermal_get_mode; 213 + int3400_thermal_ops.set_mode = int3400_thermal_set_mode; 214 + } 215 + priv->thermal = thermal_zone_device_register("INT3400 Thermal", 0, 0, 216 + priv, &int3400_thermal_ops, 217 + &int3400_thermal_params, 0, 0); 218 + if (IS_ERR(priv->thermal)) { 219 + result = PTR_ERR(priv->thermal); 220 + goto free_trt; 221 + } 222 + 223 + priv->rel_misc_dev_res = acpi_thermal_rel_misc_device_add( 224 + priv->adev->handle); 225 + 226 + return 0; 227 + free_trt: 228 + kfree(priv->trts); 229 + free_art: 230 + kfree(priv->arts); 231 + free_priv: 232 + kfree(priv); 233 + return result; 234 + } 235 + 236 + static int int3400_thermal_remove(struct platform_device *pdev) 237 + { 238 + struct int3400_thermal_priv *priv = platform_get_drvdata(pdev); 239 + 240 + if (!priv->rel_misc_dev_res) 241 + acpi_thermal_rel_misc_device_remove(priv->adev->handle); 242 + 243 + thermal_zone_device_unregister(priv->thermal); 244 + kfree(priv->trts); 245 + kfree(priv->arts); 246 + kfree(priv); 247 + return 0; 248 + } 249 + 250 + static const struct acpi_device_id int3400_thermal_match[] = { 251 + {"INT3400", 0}, 252 + {} 253 + }; 254 + 255 + MODULE_DEVICE_TABLE(acpi, int3400_thermal_match); 256 + 257 + static struct platform_driver int3400_thermal_driver = { 258 + .probe = int3400_thermal_probe, 259 + .remove = int3400_thermal_remove, 260 + .driver = { 261 + .name = "int3400 thermal", 262 + .owner = THIS_MODULE, 263 + .acpi_match_table = ACPI_PTR(int3400_thermal_match), 264 + }, 265 + }; 266 + 267 + module_platform_driver(int3400_thermal_driver); 268 + 269 + MODULE_DESCRIPTION("INT3400 Thermal driver"); 270 + MODULE_AUTHOR("Zhang Rui <rui.zhang@intel.com>"); 271 + MODULE_LICENSE("GPL");
+242
drivers/thermal/int340x_thermal/int3402_thermal.c
··· 1 + /* 2 + * INT3402 thermal driver for memory temperature reporting 3 + * 4 + * Copyright (C) 2014, Intel Corporation 5 + * Authors: Aaron Lu <aaron.lu@intel.com> 6 + * 7 + * This program is free software; you can redistribute it and/or modify 8 + * it under the terms of the GNU General Public License version 2 as 9 + * published by the Free Software Foundation. 10 + * 11 + */ 12 + 13 + #include <linux/module.h> 14 + #include <linux/platform_device.h> 15 + #include <linux/acpi.h> 16 + #include <linux/thermal.h> 17 + 18 + #define ACPI_ACTIVE_COOLING_MAX_NR 10 19 + 20 + struct active_trip { 21 + unsigned long temp; 22 + int id; 23 + bool valid; 24 + }; 25 + 26 + struct int3402_thermal_data { 27 + unsigned long *aux_trips; 28 + int aux_trip_nr; 29 + unsigned long psv_temp; 30 + int psv_trip_id; 31 + unsigned long crt_temp; 32 + int crt_trip_id; 33 + unsigned long hot_temp; 34 + int hot_trip_id; 35 + struct active_trip act_trips[ACPI_ACTIVE_COOLING_MAX_NR]; 36 + acpi_handle *handle; 37 + }; 38 + 39 + static int int3402_thermal_get_zone_temp(struct thermal_zone_device *zone, 40 + unsigned long *temp) 41 + { 42 + struct int3402_thermal_data *d = zone->devdata; 43 + unsigned long long tmp; 44 + acpi_status status; 45 + 46 + status = acpi_evaluate_integer(d->handle, "_TMP", NULL, &tmp); 47 + if (ACPI_FAILURE(status)) 48 + return -ENODEV; 49 + 50 + /* _TMP returns the temperature in tenths of degrees Kelvin */ 51 + *temp = DECI_KELVIN_TO_MILLICELSIUS(tmp); 52 + 53 + return 0; 54 + } 55 + 56 + static int int3402_thermal_get_trip_temp(struct thermal_zone_device *zone, 57 + int trip, unsigned long *temp) 58 + { 59 + struct int3402_thermal_data *d = zone->devdata; 60 + int i; 61 + 62 + if (trip < d->aux_trip_nr) 63 + *temp = d->aux_trips[trip]; 64 + else if (trip == d->crt_trip_id) 65 + *temp = d->crt_temp; 66 + else if (trip == d->psv_trip_id) 67 + *temp = d->psv_temp; 68 + else if (trip == d->hot_trip_id) 69 + *temp = d->hot_temp; 70 + else { 71 + for (i = 0; i < ACPI_ACTIVE_COOLING_MAX_NR; i++) { 72 + if (d->act_trips[i].valid && 73 + d->act_trips[i].id == trip) { 74 + *temp = d->act_trips[i].temp; 75 + break; 76 + } 77 + } 78 + if (i == ACPI_ACTIVE_COOLING_MAX_NR) 79 + return -EINVAL; 80 + } 81 + return 0; 82 + } 83 + 84 + static int int3402_thermal_get_trip_type(struct thermal_zone_device *zone, 85 + int trip, enum thermal_trip_type *type) 86 + { 87 + struct int3402_thermal_data *d = zone->devdata; 88 + int i; 89 + 90 + if (trip < d->aux_trip_nr) 91 + *type = THERMAL_TRIP_PASSIVE; 92 + else if (trip == d->crt_trip_id) 93 + *type = THERMAL_TRIP_CRITICAL; 94 + else if (trip == d->hot_trip_id) 95 + *type = THERMAL_TRIP_HOT; 96 + else if (trip == d->psv_trip_id) 97 + *type = THERMAL_TRIP_PASSIVE; 98 + else { 99 + for (i = 0; i < ACPI_ACTIVE_COOLING_MAX_NR; i++) { 100 + if (d->act_trips[i].valid && 101 + d->act_trips[i].id == trip) { 102 + *type = THERMAL_TRIP_ACTIVE; 103 + break; 104 + } 105 + } 106 + if (i == ACPI_ACTIVE_COOLING_MAX_NR) 107 + return -EINVAL; 108 + } 109 + return 0; 110 + } 111 + 112 + static int int3402_thermal_set_trip_temp(struct thermal_zone_device *zone, int trip, 113 + unsigned long temp) 114 + { 115 + struct int3402_thermal_data *d = zone->devdata; 116 + acpi_status status; 117 + char name[10]; 118 + 119 + snprintf(name, sizeof(name), "PAT%d", trip); 120 + status = acpi_execute_simple_method(d->handle, name, 121 + MILLICELSIUS_TO_DECI_KELVIN(temp)); 122 + if (ACPI_FAILURE(status)) 123 + return -EIO; 124 + 125 + d->aux_trips[trip] = temp; 126 + return 0; 127 + } 128 + 129 + static struct thermal_zone_device_ops int3402_thermal_zone_ops = { 130 + .get_temp = int3402_thermal_get_zone_temp, 131 + .get_trip_temp = int3402_thermal_get_trip_temp, 132 + .get_trip_type = int3402_thermal_get_trip_type, 133 + .set_trip_temp = int3402_thermal_set_trip_temp, 134 + }; 135 + 136 + static struct thermal_zone_params int3402_thermal_params = { 137 + .governor_name = "user_space", 138 + .no_hwmon = true, 139 + }; 140 + 141 + static int int3402_thermal_get_temp(acpi_handle handle, char *name, 142 + unsigned long *temp) 143 + { 144 + unsigned long long r; 145 + acpi_status status; 146 + 147 + status = acpi_evaluate_integer(handle, name, NULL, &r); 148 + if (ACPI_FAILURE(status)) 149 + return -EIO; 150 + 151 + *temp = DECI_KELVIN_TO_MILLICELSIUS(r); 152 + return 0; 153 + } 154 + 155 + static int int3402_thermal_probe(struct platform_device *pdev) 156 + { 157 + struct acpi_device *adev = ACPI_COMPANION(&pdev->dev); 158 + struct int3402_thermal_data *d; 159 + struct thermal_zone_device *zone; 160 + acpi_status status; 161 + unsigned long long trip_cnt; 162 + int trip_mask = 0, i; 163 + 164 + if (!acpi_has_method(adev->handle, "_TMP")) 165 + return -ENODEV; 166 + 167 + d = devm_kzalloc(&pdev->dev, sizeof(*d), GFP_KERNEL); 168 + if (!d) 169 + return -ENOMEM; 170 + 171 + status = acpi_evaluate_integer(adev->handle, "PATC", NULL, &trip_cnt); 172 + if (ACPI_FAILURE(status)) 173 + trip_cnt = 0; 174 + else { 175 + d->aux_trips = devm_kzalloc(&pdev->dev, 176 + sizeof(*d->aux_trips) * trip_cnt, GFP_KERNEL); 177 + if (!d->aux_trips) 178 + return -ENOMEM; 179 + trip_mask = trip_cnt - 1; 180 + d->handle = adev->handle; 181 + d->aux_trip_nr = trip_cnt; 182 + } 183 + 184 + d->crt_trip_id = -1; 185 + if (!int3402_thermal_get_temp(adev->handle, "_CRT", &d->crt_temp)) 186 + d->crt_trip_id = trip_cnt++; 187 + d->hot_trip_id = -1; 188 + if (!int3402_thermal_get_temp(adev->handle, "_HOT", &d->hot_temp)) 189 + d->hot_trip_id = trip_cnt++; 190 + d->psv_trip_id = -1; 191 + if (!int3402_thermal_get_temp(adev->handle, "_PSV", &d->psv_temp)) 192 + d->psv_trip_id = trip_cnt++; 193 + for (i = 0; i < ACPI_ACTIVE_COOLING_MAX_NR; i++) { 194 + char name[5] = { '_', 'A', 'C', '0' + i, '\0' }; 195 + if (int3402_thermal_get_temp(adev->handle, name, 196 + &d->act_trips[i].temp)) 197 + break; 198 + d->act_trips[i].id = trip_cnt++; 199 + d->act_trips[i].valid = true; 200 + } 201 + 202 + zone = thermal_zone_device_register(acpi_device_bid(adev), trip_cnt, 203 + trip_mask, d, 204 + &int3402_thermal_zone_ops, 205 + &int3402_thermal_params, 206 + 0, 0); 207 + if (IS_ERR(zone)) 208 + return PTR_ERR(zone); 209 + platform_set_drvdata(pdev, zone); 210 + 211 + return 0; 212 + } 213 + 214 + static int int3402_thermal_remove(struct platform_device *pdev) 215 + { 216 + struct thermal_zone_device *zone = platform_get_drvdata(pdev); 217 + 218 + thermal_zone_device_unregister(zone); 219 + return 0; 220 + } 221 + 222 + static const struct acpi_device_id int3402_thermal_match[] = { 223 + {"INT3402", 0}, 224 + {} 225 + }; 226 + 227 + MODULE_DEVICE_TABLE(acpi, int3402_thermal_match); 228 + 229 + static struct platform_driver int3402_thermal_driver = { 230 + .probe = int3402_thermal_probe, 231 + .remove = int3402_thermal_remove, 232 + .driver = { 233 + .name = "int3402 thermal", 234 + .owner = THIS_MODULE, 235 + .acpi_match_table = int3402_thermal_match, 236 + }, 237 + }; 238 + 239 + module_platform_driver(int3402_thermal_driver); 240 + 241 + MODULE_DESCRIPTION("INT3402 Thermal driver"); 242 + MODULE_LICENSE("GPL");
+477
drivers/thermal/int340x_thermal/int3403_thermal.c
··· 1 + /* 2 + * ACPI INT3403 thermal driver 3 + * Copyright (c) 2013, 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 + #include <linux/kernel.h> 16 + #include <linux/module.h> 17 + #include <linux/init.h> 18 + #include <linux/types.h> 19 + #include <linux/acpi.h> 20 + #include <linux/thermal.h> 21 + #include <linux/platform_device.h> 22 + 23 + #define INT3403_TYPE_SENSOR 0x03 24 + #define INT3403_TYPE_CHARGER 0x0B 25 + #define INT3403_TYPE_BATTERY 0x0C 26 + #define INT3403_PERF_CHANGED_EVENT 0x80 27 + #define INT3403_THERMAL_EVENT 0x90 28 + 29 + #define DECI_KELVIN_TO_MILLI_CELSIUS(t, off) (((t) - (off)) * 100) 30 + #define KELVIN_OFFSET 2732 31 + #define MILLI_CELSIUS_TO_DECI_KELVIN(t, off) (((t) / 100) + (off)) 32 + 33 + struct int3403_sensor { 34 + struct thermal_zone_device *tzone; 35 + unsigned long *thresholds; 36 + unsigned long crit_temp; 37 + int crit_trip_id; 38 + unsigned long psv_temp; 39 + int psv_trip_id; 40 + 41 + }; 42 + 43 + struct int3403_performance_state { 44 + u64 performance; 45 + u64 power; 46 + u64 latency; 47 + u64 linear; 48 + u64 control; 49 + u64 raw_performace; 50 + char *raw_unit; 51 + int reserved; 52 + }; 53 + 54 + struct int3403_cdev { 55 + struct thermal_cooling_device *cdev; 56 + unsigned long max_state; 57 + }; 58 + 59 + struct int3403_priv { 60 + struct platform_device *pdev; 61 + struct acpi_device *adev; 62 + unsigned long long type; 63 + void *priv; 64 + }; 65 + 66 + static int sys_get_curr_temp(struct thermal_zone_device *tzone, 67 + unsigned long *temp) 68 + { 69 + struct int3403_priv *priv = tzone->devdata; 70 + struct acpi_device *device = priv->adev; 71 + unsigned long long tmp; 72 + acpi_status status; 73 + 74 + status = acpi_evaluate_integer(device->handle, "_TMP", NULL, &tmp); 75 + if (ACPI_FAILURE(status)) 76 + return -EIO; 77 + 78 + *temp = DECI_KELVIN_TO_MILLI_CELSIUS(tmp, KELVIN_OFFSET); 79 + 80 + return 0; 81 + } 82 + 83 + static int sys_get_trip_hyst(struct thermal_zone_device *tzone, 84 + int trip, unsigned long *temp) 85 + { 86 + struct int3403_priv *priv = tzone->devdata; 87 + struct acpi_device *device = priv->adev; 88 + unsigned long long hyst; 89 + acpi_status status; 90 + 91 + status = acpi_evaluate_integer(device->handle, "GTSH", NULL, &hyst); 92 + if (ACPI_FAILURE(status)) 93 + return -EIO; 94 + 95 + *temp = DECI_KELVIN_TO_MILLI_CELSIUS(hyst, KELVIN_OFFSET); 96 + 97 + return 0; 98 + } 99 + 100 + static int sys_get_trip_temp(struct thermal_zone_device *tzone, 101 + int trip, unsigned long *temp) 102 + { 103 + struct int3403_priv *priv = tzone->devdata; 104 + struct int3403_sensor *obj = priv->priv; 105 + 106 + if (priv->type != INT3403_TYPE_SENSOR || !obj) 107 + return -EINVAL; 108 + 109 + if (trip == obj->crit_trip_id) 110 + *temp = obj->crit_temp; 111 + else if (trip == obj->psv_trip_id) 112 + *temp = obj->psv_temp; 113 + else { 114 + /* 115 + * get_trip_temp is a mandatory callback but 116 + * PATx method doesn't return any value, so return 117 + * cached value, which was last set from user space 118 + */ 119 + *temp = obj->thresholds[trip]; 120 + } 121 + 122 + return 0; 123 + } 124 + 125 + static int sys_get_trip_type(struct thermal_zone_device *thermal, 126 + int trip, enum thermal_trip_type *type) 127 + { 128 + struct int3403_priv *priv = thermal->devdata; 129 + struct int3403_sensor *obj = priv->priv; 130 + 131 + /* Mandatory callback, may not mean much here */ 132 + if (trip == obj->crit_trip_id) 133 + *type = THERMAL_TRIP_CRITICAL; 134 + else 135 + *type = THERMAL_TRIP_PASSIVE; 136 + 137 + return 0; 138 + } 139 + 140 + int sys_set_trip_temp(struct thermal_zone_device *tzone, int trip, 141 + unsigned long temp) 142 + { 143 + struct int3403_priv *priv = tzone->devdata; 144 + struct acpi_device *device = priv->adev; 145 + struct int3403_sensor *obj = priv->priv; 146 + acpi_status status; 147 + char name[10]; 148 + int ret = 0; 149 + 150 + snprintf(name, sizeof(name), "PAT%d", trip); 151 + if (acpi_has_method(device->handle, name)) { 152 + status = acpi_execute_simple_method(device->handle, name, 153 + MILLI_CELSIUS_TO_DECI_KELVIN(temp, 154 + KELVIN_OFFSET)); 155 + if (ACPI_FAILURE(status)) 156 + ret = -EIO; 157 + else 158 + obj->thresholds[trip] = temp; 159 + } else { 160 + ret = -EIO; 161 + dev_err(&device->dev, "sys_set_trip_temp: method not found\n"); 162 + } 163 + 164 + return ret; 165 + } 166 + 167 + static struct thermal_zone_device_ops tzone_ops = { 168 + .get_temp = sys_get_curr_temp, 169 + .get_trip_temp = sys_get_trip_temp, 170 + .get_trip_type = sys_get_trip_type, 171 + .set_trip_temp = sys_set_trip_temp, 172 + .get_trip_hyst = sys_get_trip_hyst, 173 + }; 174 + 175 + static struct thermal_zone_params int3403_thermal_params = { 176 + .governor_name = "user_space", 177 + .no_hwmon = true, 178 + }; 179 + 180 + static void int3403_notify(acpi_handle handle, 181 + u32 event, void *data) 182 + { 183 + struct int3403_priv *priv = data; 184 + struct int3403_sensor *obj; 185 + 186 + if (!priv) 187 + return; 188 + 189 + obj = priv->priv; 190 + if (priv->type != INT3403_TYPE_SENSOR || !obj) 191 + return; 192 + 193 + switch (event) { 194 + case INT3403_PERF_CHANGED_EVENT: 195 + break; 196 + case INT3403_THERMAL_EVENT: 197 + thermal_zone_device_update(obj->tzone); 198 + break; 199 + default: 200 + dev_err(&priv->pdev->dev, "Unsupported event [0x%x]\n", event); 201 + break; 202 + } 203 + } 204 + 205 + static int sys_get_trip_crt(struct acpi_device *device, unsigned long *temp) 206 + { 207 + unsigned long long crt; 208 + acpi_status status; 209 + 210 + status = acpi_evaluate_integer(device->handle, "_CRT", NULL, &crt); 211 + if (ACPI_FAILURE(status)) 212 + return -EIO; 213 + 214 + *temp = DECI_KELVIN_TO_MILLI_CELSIUS(crt, KELVIN_OFFSET); 215 + 216 + return 0; 217 + } 218 + 219 + static int sys_get_trip_psv(struct acpi_device *device, unsigned long *temp) 220 + { 221 + unsigned long long psv; 222 + acpi_status status; 223 + 224 + status = acpi_evaluate_integer(device->handle, "_PSV", NULL, &psv); 225 + if (ACPI_FAILURE(status)) 226 + return -EIO; 227 + 228 + *temp = DECI_KELVIN_TO_MILLI_CELSIUS(psv, KELVIN_OFFSET); 229 + 230 + return 0; 231 + } 232 + 233 + static int int3403_sensor_add(struct int3403_priv *priv) 234 + { 235 + int result = 0; 236 + acpi_status status; 237 + struct int3403_sensor *obj; 238 + unsigned long long trip_cnt; 239 + int trip_mask = 0; 240 + 241 + obj = devm_kzalloc(&priv->pdev->dev, sizeof(*obj), GFP_KERNEL); 242 + if (!obj) 243 + return -ENOMEM; 244 + 245 + priv->priv = obj; 246 + 247 + status = acpi_evaluate_integer(priv->adev->handle, "PATC", NULL, 248 + &trip_cnt); 249 + if (ACPI_FAILURE(status)) 250 + trip_cnt = 0; 251 + 252 + if (trip_cnt) { 253 + /* We have to cache, thresholds can't be readback */ 254 + obj->thresholds = devm_kzalloc(&priv->pdev->dev, 255 + sizeof(*obj->thresholds) * trip_cnt, 256 + GFP_KERNEL); 257 + if (!obj->thresholds) { 258 + result = -ENOMEM; 259 + goto err_free_obj; 260 + } 261 + trip_mask = BIT(trip_cnt) - 1; 262 + } 263 + 264 + obj->psv_trip_id = -1; 265 + if (!sys_get_trip_psv(priv->adev, &obj->psv_temp)) 266 + obj->psv_trip_id = trip_cnt++; 267 + 268 + obj->crit_trip_id = -1; 269 + if (!sys_get_trip_crt(priv->adev, &obj->crit_temp)) 270 + obj->crit_trip_id = trip_cnt++; 271 + 272 + obj->tzone = thermal_zone_device_register(acpi_device_bid(priv->adev), 273 + trip_cnt, trip_mask, priv, &tzone_ops, 274 + &int3403_thermal_params, 0, 0); 275 + if (IS_ERR(obj->tzone)) { 276 + result = PTR_ERR(obj->tzone); 277 + obj->tzone = NULL; 278 + goto err_free_obj; 279 + } 280 + 281 + result = acpi_install_notify_handler(priv->adev->handle, 282 + ACPI_DEVICE_NOTIFY, int3403_notify, 283 + (void *)priv); 284 + if (result) 285 + goto err_free_obj; 286 + 287 + return 0; 288 + 289 + err_free_obj: 290 + if (obj->tzone) 291 + thermal_zone_device_unregister(obj->tzone); 292 + return result; 293 + } 294 + 295 + static int int3403_sensor_remove(struct int3403_priv *priv) 296 + { 297 + struct int3403_sensor *obj = priv->priv; 298 + 299 + thermal_zone_device_unregister(obj->tzone); 300 + return 0; 301 + } 302 + 303 + /* INT3403 Cooling devices */ 304 + static int int3403_get_max_state(struct thermal_cooling_device *cdev, 305 + unsigned long *state) 306 + { 307 + struct int3403_priv *priv = cdev->devdata; 308 + struct int3403_cdev *obj = priv->priv; 309 + 310 + *state = obj->max_state; 311 + return 0; 312 + } 313 + 314 + static int int3403_get_cur_state(struct thermal_cooling_device *cdev, 315 + unsigned long *state) 316 + { 317 + struct int3403_priv *priv = cdev->devdata; 318 + unsigned long long level; 319 + acpi_status status; 320 + 321 + status = acpi_evaluate_integer(priv->adev->handle, "PPPC", NULL, &level); 322 + if (ACPI_SUCCESS(status)) { 323 + *state = level; 324 + return 0; 325 + } else 326 + return -EINVAL; 327 + } 328 + 329 + static int 330 + int3403_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state) 331 + { 332 + struct int3403_priv *priv = cdev->devdata; 333 + acpi_status status; 334 + 335 + status = acpi_execute_simple_method(priv->adev->handle, "SPPC", state); 336 + if (ACPI_SUCCESS(status)) 337 + return 0; 338 + else 339 + return -EINVAL; 340 + } 341 + 342 + static const struct thermal_cooling_device_ops int3403_cooling_ops = { 343 + .get_max_state = int3403_get_max_state, 344 + .get_cur_state = int3403_get_cur_state, 345 + .set_cur_state = int3403_set_cur_state, 346 + }; 347 + 348 + static int int3403_cdev_add(struct int3403_priv *priv) 349 + { 350 + int result = 0; 351 + acpi_status status; 352 + struct int3403_cdev *obj; 353 + struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL }; 354 + union acpi_object *p; 355 + 356 + obj = devm_kzalloc(&priv->pdev->dev, sizeof(*obj), GFP_KERNEL); 357 + if (!obj) 358 + return -ENOMEM; 359 + 360 + status = acpi_evaluate_object(priv->adev->handle, "PPSS", NULL, &buf); 361 + if (ACPI_FAILURE(status)) 362 + return -ENODEV; 363 + 364 + p = buf.pointer; 365 + if (!p || (p->type != ACPI_TYPE_PACKAGE)) { 366 + printk(KERN_WARNING "Invalid PPSS data\n"); 367 + return -EFAULT; 368 + } 369 + 370 + obj->max_state = p->package.count - 1; 371 + obj->cdev = 372 + thermal_cooling_device_register(acpi_device_bid(priv->adev), 373 + priv, &int3403_cooling_ops); 374 + if (IS_ERR(obj->cdev)) 375 + result = PTR_ERR(obj->cdev); 376 + 377 + priv->priv = obj; 378 + 379 + /* TODO: add ACPI notification support */ 380 + 381 + return result; 382 + } 383 + 384 + static int int3403_cdev_remove(struct int3403_priv *priv) 385 + { 386 + struct int3403_cdev *obj = priv->priv; 387 + 388 + thermal_cooling_device_unregister(obj->cdev); 389 + return 0; 390 + } 391 + 392 + static int int3403_add(struct platform_device *pdev) 393 + { 394 + struct int3403_priv *priv; 395 + int result = 0; 396 + acpi_status status; 397 + 398 + priv = devm_kzalloc(&pdev->dev, sizeof(struct int3403_priv), 399 + GFP_KERNEL); 400 + if (!priv) 401 + return -ENOMEM; 402 + 403 + priv->pdev = pdev; 404 + priv->adev = ACPI_COMPANION(&(pdev->dev)); 405 + if (!priv->adev) { 406 + result = -EINVAL; 407 + goto err; 408 + } 409 + 410 + status = acpi_evaluate_integer(priv->adev->handle, "PTYP", 411 + NULL, &priv->type); 412 + if (ACPI_FAILURE(status)) { 413 + result = -EINVAL; 414 + goto err; 415 + } 416 + 417 + platform_set_drvdata(pdev, priv); 418 + switch (priv->type) { 419 + case INT3403_TYPE_SENSOR: 420 + result = int3403_sensor_add(priv); 421 + break; 422 + case INT3403_TYPE_CHARGER: 423 + case INT3403_TYPE_BATTERY: 424 + result = int3403_cdev_add(priv); 425 + break; 426 + default: 427 + result = -EINVAL; 428 + } 429 + 430 + if (result) 431 + goto err; 432 + return result; 433 + 434 + err: 435 + return result; 436 + } 437 + 438 + static int int3403_remove(struct platform_device *pdev) 439 + { 440 + struct int3403_priv *priv = platform_get_drvdata(pdev); 441 + 442 + switch (priv->type) { 443 + case INT3403_TYPE_SENSOR: 444 + int3403_sensor_remove(priv); 445 + break; 446 + case INT3403_TYPE_CHARGER: 447 + case INT3403_TYPE_BATTERY: 448 + int3403_cdev_remove(priv); 449 + break; 450 + default: 451 + break; 452 + } 453 + 454 + return 0; 455 + } 456 + 457 + static const struct acpi_device_id int3403_device_ids[] = { 458 + {"INT3403", 0}, 459 + {"", 0}, 460 + }; 461 + MODULE_DEVICE_TABLE(acpi, int3403_device_ids); 462 + 463 + static struct platform_driver int3403_driver = { 464 + .probe = int3403_add, 465 + .remove = int3403_remove, 466 + .driver = { 467 + .name = "int3403 thermal", 468 + .owner = THIS_MODULE, 469 + .acpi_match_table = int3403_device_ids, 470 + }, 471 + }; 472 + 473 + module_platform_driver(int3403_driver); 474 + 475 + MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>"); 476 + MODULE_LICENSE("GPL v2"); 477 + MODULE_DESCRIPTION("ACPI INT3403 thermal driver");
+12
drivers/thermal/of-thermal.c
··· 401 401 struct of_phandle_args sensor_specs; 402 402 int ret, id; 403 403 404 + /* Check whether child is enabled or not */ 405 + if (!of_device_is_available(child)) 406 + continue; 407 + 404 408 /* For now, thermal framework supports only 1 sensor per zone */ 405 409 ret = of_parse_phandle_with_args(child, "thermal-sensors", 406 410 "#thermal-sensor-cells", ··· 775 771 struct thermal_zone_device *zone; 776 772 struct thermal_zone_params *tzp; 777 773 774 + /* Check whether child is enabled or not */ 775 + if (!of_device_is_available(child)) 776 + continue; 777 + 778 778 tz = thermal_of_build_thermal_zone(child); 779 779 if (IS_ERR(tz)) { 780 780 pr_err("failed to build thermal zone %s: %ld\n", ··· 845 837 846 838 for_each_child_of_node(np, child) { 847 839 struct thermal_zone_device *zone; 840 + 841 + /* Check whether child is enabled or not */ 842 + if (!of_device_is_available(child)) 843 + continue; 848 844 849 845 zone = thermal_zone_get_zone_by_name(child->name); 850 846 if (IS_ERR(zone))
+5 -2
drivers/thermal/step_wise.c
··· 23 23 */ 24 24 25 25 #include <linux/thermal.h> 26 + #include <trace/events/thermal.h> 26 27 27 28 #include "thermal_core.h" 28 29 ··· 77 76 next_target = instance->upper; 78 77 break; 79 78 case THERMAL_TREND_DROPPING: 80 - if (cur_state == instance->lower) { 79 + if (cur_state <= instance->lower) { 81 80 if (!throttle) 82 81 next_target = THERMAL_NO_TARGET; 83 82 } else { ··· 130 129 131 130 trend = get_tz_trend(tz, trip); 132 131 133 - if (tz->temperature >= trip_temp) 132 + if (tz->temperature >= trip_temp) { 134 133 throttle = true; 134 + trace_thermal_zone_trip(tz, trip, trip_type); 135 + } 135 136 136 137 dev_dbg(&tz->device, "Trip%d[type=%d,temp=%ld]:trend=%d,throttle=%d\n", 137 138 trip, trip_type, trip_temp, trend, throttle);
+12
drivers/thermal/thermal_core.c
··· 38 38 #include <net/netlink.h> 39 39 #include <net/genetlink.h> 40 40 41 + #define CREATE_TRACE_POINTS 42 + #include <trace/events/thermal.h> 43 + 41 44 #include "thermal_core.h" 42 45 #include "thermal_hwmon.h" 43 46 ··· 371 368 if (tz->temperature < trip_temp) 372 369 return; 373 370 371 + trace_thermal_zone_trip(tz, trip, trip_type); 372 + 374 373 if (tz->ops->notify) 375 374 tz->ops->notify(tz, trip, trip_type); 376 375 ··· 468 463 tz->temperature = temp; 469 464 mutex_unlock(&tz->lock); 470 465 466 + trace_thermal_temperature(tz); 471 467 dev_dbg(&tz->device, "last_temperature=%d, current_temperature=%d\n", 472 468 tz->last_temperature, tz->temperature); 473 469 } ··· 1293 1287 mutex_unlock(&cdev->lock); 1294 1288 cdev->ops->set_cur_state(cdev, target); 1295 1289 cdev->updated = true; 1290 + trace_cdev_update(cdev, target); 1296 1291 dev_dbg(&cdev->device, "set to state %lu\n", target); 1297 1292 } 1298 1293 EXPORT_SYMBOL(thermal_cdev_update); ··· 1797 1790 if (result) 1798 1791 return result; 1799 1792 1793 + result = thermal_gov_bang_bang_register(); 1794 + if (result) 1795 + return result; 1796 + 1800 1797 return thermal_gov_user_space_register(); 1801 1798 } 1802 1799 ··· 1808 1797 { 1809 1798 thermal_gov_step_wise_unregister(); 1810 1799 thermal_gov_fair_share_unregister(); 1800 + thermal_gov_bang_bang_unregister(); 1811 1801 thermal_gov_user_space_unregister(); 1812 1802 } 1813 1803
+8
drivers/thermal/thermal_core.h
··· 69 69 static inline void thermal_gov_fair_share_unregister(void) {} 70 70 #endif /* CONFIG_THERMAL_GOV_FAIR_SHARE */ 71 71 72 + #ifdef CONFIG_THERMAL_GOV_BANG_BANG 73 + int thermal_gov_bang_bang_register(void); 74 + void thermal_gov_bang_bang_unregister(void); 75 + #else 76 + static inline int thermal_gov_bang_bang_register(void) { return 0; } 77 + static inline void thermal_gov_bang_bang_unregister(void) {} 78 + #endif /* CONFIG_THERMAL_GOV_BANG_BANG */ 79 + 72 80 #ifdef CONFIG_THERMAL_GOV_USER_SPACE 73 81 int thermal_gov_user_space_register(void); 74 82 void thermal_gov_user_space_unregister(void);
+1
include/acpi/acpi_bus.h
··· 433 433 int acpi_bus_init_power(struct acpi_device *device); 434 434 int acpi_device_fix_up_power(struct acpi_device *device); 435 435 int acpi_bus_update_power(acpi_handle handle, int *state_p); 436 + int acpi_device_update_power(struct acpi_device *device, int *state_p); 436 437 bool acpi_bus_power_manageable(acpi_handle handle); 437 438 438 439 #ifdef CONFIG_PM
+1
include/linux/acpi.h
··· 432 432 int acpi_device_uevent_modalias(struct device *, struct kobj_uevent_env *); 433 433 int acpi_device_modalias(struct device *, char *, int); 434 434 435 + struct platform_device *acpi_create_platform_device(struct acpi_device *); 435 436 #define ACPI_PTR(_ptr) (_ptr) 436 437 437 438 #else /* !CONFIG_ACPI */
+4
include/linux/thermal.h
··· 44 44 #define KELVIN_TO_CELSIUS(t) (long)(((long)t-2732 >= 0) ? \ 45 45 ((long)t-2732+5)/10 : ((long)t-2732-5)/10) 46 46 #define CELSIUS_TO_KELVIN(t) ((t)*10+2732) 47 + #define DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET(t, off) (((t) - (off)) * 100) 48 + #define DECI_KELVIN_TO_MILLICELSIUS(t) DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET(t, 2732) 49 + #define MILLICELSIUS_TO_DECI_KELVIN_WITH_OFFSET(t, off) (((t) / 100) + (off)) 50 + #define MILLICELSIUS_TO_DECI_KELVIN(t) MILLICELSIUS_TO_DECI_KELVIN_WITH_OFFSET(t, 2732) 47 51 48 52 /* Adding event notification support elements */ 49 53 #define THERMAL_GENL_FAMILY_NAME "thermal_event"
+83
include/trace/events/thermal.h
··· 1 + #undef TRACE_SYSTEM 2 + #define TRACE_SYSTEM thermal 3 + 4 + #if !defined(_TRACE_THERMAL_H) || defined(TRACE_HEADER_MULTI_READ) 5 + #define _TRACE_THERMAL_H 6 + 7 + #include <linux/thermal.h> 8 + #include <linux/tracepoint.h> 9 + 10 + TRACE_EVENT(thermal_temperature, 11 + 12 + TP_PROTO(struct thermal_zone_device *tz), 13 + 14 + TP_ARGS(tz), 15 + 16 + TP_STRUCT__entry( 17 + __string(thermal_zone, tz->type) 18 + __field(int, id) 19 + __field(int, temp_prev) 20 + __field(int, temp) 21 + ), 22 + 23 + TP_fast_assign( 24 + __assign_str(thermal_zone, tz->type); 25 + __entry->id = tz->id; 26 + __entry->temp_prev = tz->last_temperature; 27 + __entry->temp = tz->temperature; 28 + ), 29 + 30 + TP_printk("thermal_zone=%s id=%d temp_prev=%d temp=%d", 31 + __get_str(thermal_zone), __entry->id, __entry->temp_prev, 32 + __entry->temp) 33 + ); 34 + 35 + TRACE_EVENT(cdev_update, 36 + 37 + TP_PROTO(struct thermal_cooling_device *cdev, unsigned long target), 38 + 39 + TP_ARGS(cdev, target), 40 + 41 + TP_STRUCT__entry( 42 + __string(type, cdev->type) 43 + __field(unsigned long, target) 44 + ), 45 + 46 + TP_fast_assign( 47 + __assign_str(type, cdev->type); 48 + __entry->target = target; 49 + ), 50 + 51 + TP_printk("type=%s target=%lu", __get_str(type), __entry->target) 52 + ); 53 + 54 + TRACE_EVENT(thermal_zone_trip, 55 + 56 + TP_PROTO(struct thermal_zone_device *tz, int trip, 57 + enum thermal_trip_type trip_type), 58 + 59 + TP_ARGS(tz, trip, trip_type), 60 + 61 + TP_STRUCT__entry( 62 + __string(thermal_zone, tz->type) 63 + __field(int, id) 64 + __field(int, trip) 65 + __field(enum thermal_trip_type, trip_type) 66 + ), 67 + 68 + TP_fast_assign( 69 + __assign_str(thermal_zone, tz->type); 70 + __entry->id = tz->id; 71 + __entry->trip = trip; 72 + __entry->trip_type = trip_type; 73 + ), 74 + 75 + TP_printk("thermal_zone=%s id=%d trip=%d trip_type=%d", 76 + __get_str(thermal_zone), __entry->id, __entry->trip, 77 + __entry->trip_type) 78 + ); 79 + 80 + #endif /* _TRACE_THERMAL_H */ 81 + 82 + /* This part must be outside protection */ 83 + #include <trace/define_trace.h>