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

Thermal: ACPI INT3403 thermal driver

The ACPI INT3403 device objects present on some systems can be used to retrieve
temperature data from thermal sensors. Add a driver registering each INT3403
device object as a thermal zone device and exposing its _TMP, PATx and GTSH
method via the standard thermal control interface under /sys/class/thermal/.

Signed-off-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
Signed-off-by: Zhang Rui <rui.zhang@intel.com>

authored by

Srinivas Pandruvada and committed by
Zhang Rui
925c36bb dea4f48a

+245
+7
drivers/thermal/Kconfig
··· 192 192 two trip points which can be set by user to get notifications via thermal 193 193 notification methods. 194 194 195 + config ACPI_INT3403_THERMAL 196 + tristate "ACPI INT3403 thermal driver" 197 + depends on X86 && ACPI 198 + help 199 + This driver uses ACPI INT3403 device objects. If present, it will 200 + register each INT3403 thermal sensor as a thermal zone. 201 + 195 202 menu "Texas Instruments thermal drivers" 196 203 source "drivers/thermal/ti-soc-thermal/Kconfig" 197 204 endmenu
+1
drivers/thermal/Makefile
··· 29 29 obj-$(CONFIG_INTEL_POWERCLAMP) += intel_powerclamp.o 30 30 obj-$(CONFIG_X86_PKG_TEMP_THERMAL) += x86_pkg_temp_thermal.o 31 31 obj-$(CONFIG_TI_SOC_THERMAL) += ti-soc-thermal/ 32 + obj-$(CONFIG_ACPI_INT3403_THERMAL) += int3403_thermal.o
+237
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 + }; 37 + 38 + static int sys_get_curr_temp(struct thermal_zone_device *tzone, 39 + unsigned long *temp) 40 + { 41 + struct acpi_device *device = tzone->devdata; 42 + unsigned long long tmp; 43 + acpi_status status; 44 + 45 + status = acpi_evaluate_integer(device->handle, "_TMP", NULL, &tmp); 46 + if (ACPI_FAILURE(status)) 47 + return -EIO; 48 + 49 + *temp = DECI_KELVIN_TO_MILLI_CELSIUS(tmp, KELVIN_OFFSET); 50 + 51 + return 0; 52 + } 53 + 54 + static int sys_get_trip_hyst(struct thermal_zone_device *tzone, 55 + int trip, unsigned long *temp) 56 + { 57 + struct acpi_device *device = tzone->devdata; 58 + unsigned long long hyst; 59 + acpi_status status; 60 + 61 + status = acpi_evaluate_integer(device->handle, "GTSH", NULL, &hyst); 62 + if (ACPI_FAILURE(status)) 63 + return -EIO; 64 + 65 + *temp = DECI_KELVIN_TO_MILLI_CELSIUS(hyst, KELVIN_OFFSET); 66 + 67 + return 0; 68 + } 69 + 70 + static int sys_get_trip_temp(struct thermal_zone_device *tzone, 71 + int trip, unsigned long *temp) 72 + { 73 + struct acpi_device *device = tzone->devdata; 74 + struct int3403_sensor *obj = acpi_driver_data(device); 75 + 76 + /* 77 + * get_trip_temp is a mandatory callback but 78 + * PATx method doesn't return any value, so return 79 + * cached value, which was last set from user space. 80 + */ 81 + *temp = obj->thresholds[trip]; 82 + 83 + return 0; 84 + } 85 + 86 + static int sys_get_trip_type(struct thermal_zone_device *thermal, 87 + int trip, enum thermal_trip_type *type) 88 + { 89 + /* Mandatory callback, may not mean much here */ 90 + *type = THERMAL_TRIP_PASSIVE; 91 + 92 + return 0; 93 + } 94 + 95 + int sys_set_trip_temp(struct thermal_zone_device *tzone, int trip, 96 + unsigned long temp) 97 + { 98 + struct acpi_device *device = tzone->devdata; 99 + acpi_status status; 100 + char name[10]; 101 + int ret = 0; 102 + struct int3403_sensor *obj = acpi_driver_data(device); 103 + 104 + snprintf(name, sizeof(name), "PAT%d", trip); 105 + if (acpi_has_method(device->handle, name)) { 106 + status = acpi_execute_simple_method(device->handle, name, 107 + MILLI_CELSIUS_TO_DECI_KELVIN(temp, 108 + KELVIN_OFFSET)); 109 + if (ACPI_FAILURE(status)) 110 + ret = -EIO; 111 + else 112 + obj->thresholds[trip] = temp; 113 + } else { 114 + ret = -EIO; 115 + dev_err(&device->dev, "sys_set_trip_temp: method not found\n"); 116 + } 117 + 118 + return ret; 119 + } 120 + 121 + static struct thermal_zone_device_ops tzone_ops = { 122 + .get_temp = sys_get_curr_temp, 123 + .get_trip_temp = sys_get_trip_temp, 124 + .get_trip_type = sys_get_trip_type, 125 + .set_trip_temp = sys_set_trip_temp, 126 + .get_trip_hyst = sys_get_trip_hyst, 127 + }; 128 + 129 + static void acpi_thermal_notify(struct acpi_device *device, u32 event) 130 + { 131 + struct int3403_sensor *obj; 132 + 133 + if (!device) 134 + return; 135 + 136 + obj = acpi_driver_data(device); 137 + if (!obj) 138 + return; 139 + 140 + switch (event) { 141 + case INT3403_PERF_CHANGED_EVENT: 142 + break; 143 + case INT3403_THERMAL_EVENT: 144 + thermal_zone_device_update(obj->tzone); 145 + break; 146 + default: 147 + dev_err(&device->dev, "Unsupported event [0x%x]\n", event); 148 + break; 149 + } 150 + } 151 + 152 + static int acpi_int3403_add(struct acpi_device *device) 153 + { 154 + int result = 0; 155 + unsigned long long ptyp; 156 + acpi_status status; 157 + struct int3403_sensor *obj; 158 + unsigned long long trip_cnt; 159 + int trip_mask = 0; 160 + 161 + if (!device) 162 + return -EINVAL; 163 + 164 + status = acpi_evaluate_integer(device->handle, "PTYP", NULL, &ptyp); 165 + if (ACPI_FAILURE(status)) 166 + return -EINVAL; 167 + 168 + if (ptyp != INT3403_TYPE_SENSOR) 169 + return -EINVAL; 170 + 171 + obj = devm_kzalloc(&device->dev, sizeof(*obj), GFP_KERNEL); 172 + if (!obj) 173 + return -ENOMEM; 174 + 175 + device->driver_data = obj; 176 + 177 + status = acpi_evaluate_integer(device->handle, "PATC", NULL, 178 + &trip_cnt); 179 + if (ACPI_FAILURE(status)) 180 + trip_cnt = 0; 181 + 182 + if (trip_cnt) { 183 + /* We have to cache, thresholds can't be readback */ 184 + obj->thresholds = devm_kzalloc(&device->dev, 185 + sizeof(*obj->thresholds) * trip_cnt, 186 + GFP_KERNEL); 187 + if (!obj->thresholds) 188 + return -ENOMEM; 189 + trip_mask = BIT(trip_cnt) - 1; 190 + } 191 + obj->tzone = thermal_zone_device_register(acpi_device_bid(device), 192 + trip_cnt, trip_mask, device, &tzone_ops, 193 + NULL, 0, 0); 194 + if (IS_ERR(obj->tzone)) { 195 + result = PTR_ERR(obj->tzone); 196 + return result; 197 + } 198 + 199 + strcpy(acpi_device_name(device), "INT3403"); 200 + strcpy(acpi_device_class(device), ACPI_INT3403_CLASS); 201 + 202 + return 0; 203 + } 204 + 205 + static int acpi_int3403_remove(struct acpi_device *device) 206 + { 207 + struct int3403_sensor *obj; 208 + 209 + obj = acpi_driver_data(device); 210 + thermal_zone_device_unregister(obj->tzone); 211 + 212 + return 0; 213 + } 214 + 215 + ACPI_MODULE_NAME("int3403"); 216 + static const struct acpi_device_id int3403_device_ids[] = { 217 + {"INT3403", 0}, 218 + {"", 0}, 219 + }; 220 + MODULE_DEVICE_TABLE(acpi, int3403_device_ids); 221 + 222 + static struct acpi_driver acpi_int3403_driver = { 223 + .name = "INT3403", 224 + .class = ACPI_INT3403_CLASS, 225 + .ids = int3403_device_ids, 226 + .ops = { 227 + .add = acpi_int3403_add, 228 + .remove = acpi_int3403_remove, 229 + .notify = acpi_thermal_notify, 230 + }, 231 + }; 232 + 233 + module_acpi_driver(acpi_int3403_driver); 234 + 235 + MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>"); 236 + MODULE_LICENSE("GPL v2"); 237 + MODULE_DESCRIPTION("ACPI INT3403 thermal driver");