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