1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * int340x_thermal_zone.c 4 * Copyright (c) 2015, Intel Corporation. 5 */ 6 #include <linux/kernel.h> 7 #include <linux/module.h> 8 #include <linux/init.h> 9 #include <linux/acpi.h> 10 #include <linux/thermal.h> 11 #include <linux/units.h> 12 #include "int340x_thermal_zone.h" 13 14 static int int340x_thermal_get_zone_temp(struct thermal_zone_device *zone, 15 int *temp) 16 { 17 struct int34x_thermal_zone *d = zone->devdata; 18 unsigned long long tmp; 19 acpi_status status; 20 21 if (d->override_ops && d->override_ops->get_temp) 22 return d->override_ops->get_temp(zone, temp); 23 24 status = acpi_evaluate_integer(d->adev->handle, "_TMP", NULL, &tmp); 25 if (ACPI_FAILURE(status)) 26 return -EIO; 27 28 if (d->lpat_table) { 29 int conv_temp; 30 31 conv_temp = acpi_lpat_raw_to_temp(d->lpat_table, (int)tmp); 32 if (conv_temp < 0) 33 return conv_temp; 34 35 *temp = (unsigned long)conv_temp * 10; 36 } else 37 /* _TMP returns the temperature in tenths of degrees Kelvin */ 38 *temp = deci_kelvin_to_millicelsius(tmp); 39 40 return 0; 41 } 42 43 static int int340x_thermal_get_trip_temp(struct thermal_zone_device *zone, 44 int trip, int *temp) 45 { 46 struct int34x_thermal_zone *d = zone->devdata; 47 int i, ret = 0; 48 49 if (d->override_ops && d->override_ops->get_trip_temp) 50 return d->override_ops->get_trip_temp(zone, trip, temp); 51 52 mutex_lock(&d->trip_mutex); 53 54 if (trip < d->aux_trip_nr) 55 *temp = d->aux_trips[trip]; 56 else if (trip == d->crt_trip_id) 57 *temp = d->crt_temp; 58 else if (trip == d->psv_trip_id) 59 *temp = d->psv_temp; 60 else if (trip == d->hot_trip_id) 61 *temp = d->hot_temp; 62 else { 63 for (i = 0; i < INT340X_THERMAL_MAX_ACT_TRIP_COUNT; i++) { 64 if (d->act_trips[i].valid && 65 d->act_trips[i].id == trip) { 66 *temp = d->act_trips[i].temp; 67 break; 68 } 69 } 70 if (i == INT340X_THERMAL_MAX_ACT_TRIP_COUNT) 71 ret = -EINVAL; 72 } 73 74 mutex_unlock(&d->trip_mutex); 75 76 return ret; 77 } 78 79 static int int340x_thermal_get_trip_type(struct thermal_zone_device *zone, 80 int trip, 81 enum thermal_trip_type *type) 82 { 83 struct int34x_thermal_zone *d = zone->devdata; 84 int i, ret = 0; 85 86 if (d->override_ops && d->override_ops->get_trip_type) 87 return d->override_ops->get_trip_type(zone, trip, type); 88 89 mutex_lock(&d->trip_mutex); 90 91 if (trip < d->aux_trip_nr) 92 *type = THERMAL_TRIP_PASSIVE; 93 else if (trip == d->crt_trip_id) 94 *type = THERMAL_TRIP_CRITICAL; 95 else if (trip == d->hot_trip_id) 96 *type = THERMAL_TRIP_HOT; 97 else if (trip == d->psv_trip_id) 98 *type = THERMAL_TRIP_PASSIVE; 99 else { 100 for (i = 0; i < INT340X_THERMAL_MAX_ACT_TRIP_COUNT; i++) { 101 if (d->act_trips[i].valid && 102 d->act_trips[i].id == trip) { 103 *type = THERMAL_TRIP_ACTIVE; 104 break; 105 } 106 } 107 if (i == INT340X_THERMAL_MAX_ACT_TRIP_COUNT) 108 ret = -EINVAL; 109 } 110 111 mutex_unlock(&d->trip_mutex); 112 113 return ret; 114 } 115 116 static int int340x_thermal_set_trip_temp(struct thermal_zone_device *zone, 117 int trip, int temp) 118 { 119 struct int34x_thermal_zone *d = zone->devdata; 120 acpi_status status; 121 char name[10]; 122 123 if (d->override_ops && d->override_ops->set_trip_temp) 124 return d->override_ops->set_trip_temp(zone, trip, temp); 125 126 snprintf(name, sizeof(name), "PAT%d", trip); 127 status = acpi_execute_simple_method(d->adev->handle, name, 128 millicelsius_to_deci_kelvin(temp)); 129 if (ACPI_FAILURE(status)) 130 return -EIO; 131 132 d->aux_trips[trip] = temp; 133 134 return 0; 135 } 136 137 138 static int int340x_thermal_get_trip_hyst(struct thermal_zone_device *zone, 139 int trip, int *temp) 140 { 141 struct int34x_thermal_zone *d = zone->devdata; 142 acpi_status status; 143 unsigned long long hyst; 144 145 if (d->override_ops && d->override_ops->get_trip_hyst) 146 return d->override_ops->get_trip_hyst(zone, trip, temp); 147 148 status = acpi_evaluate_integer(d->adev->handle, "GTSH", NULL, &hyst); 149 if (ACPI_FAILURE(status)) 150 *temp = 0; 151 else 152 *temp = hyst * 100; 153 154 return 0; 155 } 156 157 static void int340x_thermal_critical(struct thermal_zone_device *zone) 158 { 159 dev_dbg(&zone->device, "%s: critical temperature reached\n", zone->type); 160 } 161 162 static struct thermal_zone_device_ops int340x_thermal_zone_ops = { 163 .get_temp = int340x_thermal_get_zone_temp, 164 .get_trip_temp = int340x_thermal_get_trip_temp, 165 .get_trip_type = int340x_thermal_get_trip_type, 166 .set_trip_temp = int340x_thermal_set_trip_temp, 167 .get_trip_hyst = int340x_thermal_get_trip_hyst, 168 .critical = int340x_thermal_critical, 169 }; 170 171 static int int340x_thermal_get_trip_config(acpi_handle handle, char *name, 172 int *temp) 173 { 174 unsigned long long r; 175 acpi_status status; 176 177 status = acpi_evaluate_integer(handle, name, NULL, &r); 178 if (ACPI_FAILURE(status)) 179 return -EIO; 180 181 *temp = deci_kelvin_to_millicelsius(r); 182 183 return 0; 184 } 185 186 int int340x_thermal_read_trips(struct int34x_thermal_zone *int34x_zone) 187 { 188 int trip_cnt = int34x_zone->aux_trip_nr; 189 int i; 190 191 mutex_lock(&int34x_zone->trip_mutex); 192 193 int34x_zone->crt_trip_id = -1; 194 if (!int340x_thermal_get_trip_config(int34x_zone->adev->handle, "_CRT", 195 &int34x_zone->crt_temp)) 196 int34x_zone->crt_trip_id = trip_cnt++; 197 198 int34x_zone->hot_trip_id = -1; 199 if (!int340x_thermal_get_trip_config(int34x_zone->adev->handle, "_HOT", 200 &int34x_zone->hot_temp)) 201 int34x_zone->hot_trip_id = trip_cnt++; 202 203 int34x_zone->psv_trip_id = -1; 204 if (!int340x_thermal_get_trip_config(int34x_zone->adev->handle, "_PSV", 205 &int34x_zone->psv_temp)) 206 int34x_zone->psv_trip_id = trip_cnt++; 207 208 for (i = 0; i < INT340X_THERMAL_MAX_ACT_TRIP_COUNT; i++) { 209 char name[5] = { '_', 'A', 'C', '0' + i, '\0' }; 210 211 if (int340x_thermal_get_trip_config(int34x_zone->adev->handle, 212 name, 213 &int34x_zone->act_trips[i].temp)) 214 break; 215 216 int34x_zone->act_trips[i].id = trip_cnt++; 217 int34x_zone->act_trips[i].valid = true; 218 } 219 220 mutex_unlock(&int34x_zone->trip_mutex); 221 222 return trip_cnt; 223 } 224 EXPORT_SYMBOL_GPL(int340x_thermal_read_trips); 225 226 static struct thermal_zone_params int340x_thermal_params = { 227 .governor_name = "user_space", 228 .no_hwmon = true, 229 }; 230 231 struct int34x_thermal_zone *int340x_thermal_zone_add(struct acpi_device *adev, 232 struct thermal_zone_device_ops *override_ops) 233 { 234 struct int34x_thermal_zone *int34x_thermal_zone; 235 acpi_status status; 236 unsigned long long trip_cnt; 237 int trip_mask = 0; 238 int ret; 239 240 int34x_thermal_zone = kzalloc(sizeof(*int34x_thermal_zone), 241 GFP_KERNEL); 242 if (!int34x_thermal_zone) 243 return ERR_PTR(-ENOMEM); 244 245 mutex_init(&int34x_thermal_zone->trip_mutex); 246 247 int34x_thermal_zone->adev = adev; 248 int34x_thermal_zone->override_ops = override_ops; 249 250 status = acpi_evaluate_integer(adev->handle, "PATC", NULL, &trip_cnt); 251 if (ACPI_FAILURE(status)) 252 trip_cnt = 0; 253 else { 254 int i; 255 256 int34x_thermal_zone->aux_trips = 257 kcalloc(trip_cnt, 258 sizeof(*int34x_thermal_zone->aux_trips), 259 GFP_KERNEL); 260 if (!int34x_thermal_zone->aux_trips) { 261 ret = -ENOMEM; 262 goto err_trip_alloc; 263 } 264 trip_mask = BIT(trip_cnt) - 1; 265 int34x_thermal_zone->aux_trip_nr = trip_cnt; 266 for (i = 0; i < trip_cnt; ++i) 267 int34x_thermal_zone->aux_trips[i] = THERMAL_TEMP_INVALID; 268 } 269 270 trip_cnt = int340x_thermal_read_trips(int34x_thermal_zone); 271 272 int34x_thermal_zone->lpat_table = acpi_lpat_get_conversion_table( 273 adev->handle); 274 275 int34x_thermal_zone->zone = thermal_zone_device_register( 276 acpi_device_bid(adev), 277 trip_cnt, 278 trip_mask, int34x_thermal_zone, 279 &int340x_thermal_zone_ops, 280 &int340x_thermal_params, 281 0, 0); 282 if (IS_ERR(int34x_thermal_zone->zone)) { 283 ret = PTR_ERR(int34x_thermal_zone->zone); 284 goto err_thermal_zone; 285 } 286 ret = thermal_zone_device_enable(int34x_thermal_zone->zone); 287 if (ret) 288 goto err_enable; 289 290 return int34x_thermal_zone; 291 292 err_enable: 293 thermal_zone_device_unregister(int34x_thermal_zone->zone); 294 err_thermal_zone: 295 acpi_lpat_free_conversion_table(int34x_thermal_zone->lpat_table); 296 kfree(int34x_thermal_zone->aux_trips); 297 err_trip_alloc: 298 mutex_destroy(&int34x_thermal_zone->trip_mutex); 299 kfree(int34x_thermal_zone); 300 return ERR_PTR(ret); 301 } 302 EXPORT_SYMBOL_GPL(int340x_thermal_zone_add); 303 304 void int340x_thermal_zone_remove(struct int34x_thermal_zone 305 *int34x_thermal_zone) 306 { 307 thermal_zone_device_unregister(int34x_thermal_zone->zone); 308 acpi_lpat_free_conversion_table(int34x_thermal_zone->lpat_table); 309 kfree(int34x_thermal_zone->aux_trips); 310 mutex_destroy(&int34x_thermal_zone->trip_mutex); 311 kfree(int34x_thermal_zone); 312 } 313 EXPORT_SYMBOL_GPL(int340x_thermal_zone_remove); 314 315 MODULE_AUTHOR("Aaron Lu <aaron.lu@intel.com>"); 316 MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>"); 317 MODULE_DESCRIPTION("Intel INT340x common thermal zone handler"); 318 MODULE_LICENSE("GPL v2"); 319