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 status = acpi_evaluate_integer(d->adev->handle, "_TMP", NULL, &tmp); 22 if (ACPI_FAILURE(status)) 23 return -EIO; 24 25 if (d->lpat_table) { 26 int conv_temp; 27 28 conv_temp = acpi_lpat_raw_to_temp(d->lpat_table, (int)tmp); 29 if (conv_temp < 0) 30 return conv_temp; 31 32 *temp = conv_temp * 10; 33 } else { 34 /* _TMP returns the temperature in tenths of degrees Kelvin */ 35 *temp = deci_kelvin_to_millicelsius(tmp); 36 } 37 38 return 0; 39 } 40 41 static int int340x_thermal_set_trip_temp(struct thermal_zone_device *zone, 42 int trip, int temp) 43 { 44 struct int34x_thermal_zone *d = zone->devdata; 45 char name[] = {'P', 'A', 'T', '0' + trip, '\0'}; 46 acpi_status status; 47 48 if (trip > 9) 49 return -EINVAL; 50 51 status = acpi_execute_simple_method(d->adev->handle, name, 52 millicelsius_to_deci_kelvin(temp)); 53 if (ACPI_FAILURE(status)) 54 return -EIO; 55 56 return 0; 57 } 58 59 static void int340x_thermal_critical(struct thermal_zone_device *zone) 60 { 61 dev_dbg(&zone->device, "%s: critical temperature reached\n", zone->type); 62 } 63 64 static struct thermal_zone_device_ops int340x_thermal_zone_ops = { 65 .get_temp = int340x_thermal_get_zone_temp, 66 .set_trip_temp = int340x_thermal_set_trip_temp, 67 .critical = int340x_thermal_critical, 68 }; 69 70 static int int340x_thermal_read_trips(struct acpi_device *zone_adev, 71 struct thermal_trip *zone_trips, 72 int trip_cnt) 73 { 74 int i, ret; 75 76 ret = thermal_acpi_critical_trip_temp(zone_adev, 77 &zone_trips[trip_cnt].temperature); 78 if (!ret) { 79 zone_trips[trip_cnt].type = THERMAL_TRIP_CRITICAL; 80 trip_cnt++; 81 } 82 83 ret = thermal_acpi_hot_trip_temp(zone_adev, 84 &zone_trips[trip_cnt].temperature); 85 if (!ret) { 86 zone_trips[trip_cnt].type = THERMAL_TRIP_HOT; 87 trip_cnt++; 88 } 89 90 ret = thermal_acpi_passive_trip_temp(zone_adev, 91 &zone_trips[trip_cnt].temperature); 92 if (!ret) { 93 zone_trips[trip_cnt].type = THERMAL_TRIP_PASSIVE; 94 trip_cnt++; 95 } 96 97 for (i = 0; i < INT340X_THERMAL_MAX_ACT_TRIP_COUNT; i++) { 98 ret = thermal_acpi_active_trip_temp(zone_adev, i, 99 &zone_trips[trip_cnt].temperature); 100 if (ret) 101 break; 102 103 zone_trips[trip_cnt].type = THERMAL_TRIP_ACTIVE; 104 trip_cnt++; 105 } 106 107 return trip_cnt; 108 } 109 110 static struct thermal_zone_params int340x_thermal_params = { 111 .governor_name = "user_space", 112 .no_hwmon = true, 113 }; 114 115 struct int34x_thermal_zone *int340x_thermal_zone_add(struct acpi_device *adev, 116 int (*get_temp) (struct thermal_zone_device *, int *)) 117 { 118 struct int34x_thermal_zone *int34x_zone; 119 struct thermal_trip *zone_trips; 120 unsigned long long trip_cnt = 0; 121 unsigned long long hyst; 122 int trip_mask = 0; 123 acpi_status status; 124 int i, ret; 125 126 int34x_zone = kzalloc(sizeof(*int34x_zone), GFP_KERNEL); 127 if (!int34x_zone) 128 return ERR_PTR(-ENOMEM); 129 130 int34x_zone->adev = adev; 131 132 int34x_zone->ops = kmemdup(&int340x_thermal_zone_ops, 133 sizeof(int340x_thermal_zone_ops), GFP_KERNEL); 134 if (!int34x_zone->ops) { 135 ret = -ENOMEM; 136 goto err_ops_alloc; 137 } 138 139 if (get_temp) 140 int34x_zone->ops->get_temp = get_temp; 141 142 status = acpi_evaluate_integer(adev->handle, "PATC", NULL, &trip_cnt); 143 if (ACPI_SUCCESS(status)) { 144 int34x_zone->aux_trip_nr = trip_cnt; 145 trip_mask = BIT(trip_cnt) - 1; 146 } 147 148 zone_trips = kzalloc(sizeof(*zone_trips) * (trip_cnt + INT340X_THERMAL_MAX_TRIP_COUNT), 149 GFP_KERNEL); 150 if (!zone_trips) { 151 ret = -ENOMEM; 152 goto err_trips_alloc; 153 } 154 155 for (i = 0; i < trip_cnt; i++) { 156 zone_trips[i].type = THERMAL_TRIP_PASSIVE; 157 zone_trips[i].temperature = THERMAL_TEMP_INVALID; 158 } 159 160 trip_cnt = int340x_thermal_read_trips(adev, zone_trips, trip_cnt); 161 162 status = acpi_evaluate_integer(adev->handle, "GTSH", NULL, &hyst); 163 if (ACPI_SUCCESS(status)) 164 hyst *= 100; 165 else 166 hyst = 0; 167 168 for (i = 0; i < trip_cnt; ++i) 169 zone_trips[i].hysteresis = hyst; 170 171 int34x_zone->trips = zone_trips; 172 173 int34x_zone->lpat_table = acpi_lpat_get_conversion_table(adev->handle); 174 175 int34x_zone->zone = thermal_zone_device_register_with_trips( 176 acpi_device_bid(adev), 177 zone_trips, trip_cnt, 178 trip_mask, int34x_zone, 179 int34x_zone->ops, 180 &int340x_thermal_params, 181 0, 0); 182 if (IS_ERR(int34x_zone->zone)) { 183 ret = PTR_ERR(int34x_zone->zone); 184 goto err_thermal_zone; 185 } 186 ret = thermal_zone_device_enable(int34x_zone->zone); 187 if (ret) 188 goto err_enable; 189 190 return int34x_zone; 191 192 err_enable: 193 thermal_zone_device_unregister(int34x_zone->zone); 194 err_thermal_zone: 195 kfree(int34x_zone->trips); 196 acpi_lpat_free_conversion_table(int34x_zone->lpat_table); 197 err_trips_alloc: 198 kfree(int34x_zone->ops); 199 err_ops_alloc: 200 kfree(int34x_zone); 201 return ERR_PTR(ret); 202 } 203 EXPORT_SYMBOL_GPL(int340x_thermal_zone_add); 204 205 void int340x_thermal_zone_remove(struct int34x_thermal_zone *int34x_zone) 206 { 207 thermal_zone_device_unregister(int34x_zone->zone); 208 acpi_lpat_free_conversion_table(int34x_zone->lpat_table); 209 kfree(int34x_zone->trips); 210 kfree(int34x_zone->ops); 211 kfree(int34x_zone); 212 } 213 EXPORT_SYMBOL_GPL(int340x_thermal_zone_remove); 214 215 void int340x_thermal_update_trips(struct int34x_thermal_zone *int34x_zone) 216 { 217 struct acpi_device *zone_adev = int34x_zone->adev; 218 struct thermal_trip *zone_trips = int34x_zone->trips; 219 int trip_cnt = int34x_zone->zone->num_trips; 220 int act_trip_nr = 0; 221 int i; 222 223 mutex_lock(&int34x_zone->zone->lock); 224 225 for (i = int34x_zone->aux_trip_nr; i < trip_cnt; i++) { 226 int temp, err; 227 228 switch (zone_trips[i].type) { 229 case THERMAL_TRIP_CRITICAL: 230 err = thermal_acpi_critical_trip_temp(zone_adev, &temp); 231 break; 232 case THERMAL_TRIP_HOT: 233 err = thermal_acpi_hot_trip_temp(zone_adev, &temp); 234 break; 235 case THERMAL_TRIP_PASSIVE: 236 err = thermal_acpi_passive_trip_temp(zone_adev, &temp); 237 break; 238 case THERMAL_TRIP_ACTIVE: 239 err = thermal_acpi_active_trip_temp(zone_adev, act_trip_nr++, 240 &temp); 241 break; 242 default: 243 err = -ENODEV; 244 } 245 if (err) { 246 zone_trips[i].temperature = THERMAL_TEMP_INVALID; 247 continue; 248 } 249 250 zone_trips[i].temperature = temp; 251 } 252 253 mutex_unlock(&int34x_zone->zone->lock); 254 } 255 EXPORT_SYMBOL_GPL(int340x_thermal_update_trips); 256 257 MODULE_AUTHOR("Aaron Lu <aaron.lu@intel.com>"); 258 MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>"); 259 MODULE_DESCRIPTION("Intel INT340x common thermal zone handler"); 260 MODULE_LICENSE("GPL v2"); 261