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