0001
0002
0003
0004
0005
0006 #include <linux/kernel.h>
0007 #include <linux/module.h>
0008 #include <linux/init.h>
0009 #include <linux/acpi.h>
0010 #include <linux/thermal.h>
0011 #include <linux/units.h>
0012 #include "int340x_thermal_zone.h"
0013
0014 static int int340x_thermal_get_zone_temp(struct thermal_zone_device *zone,
0015 int *temp)
0016 {
0017 struct int34x_thermal_zone *d = zone->devdata;
0018 unsigned long long tmp;
0019 acpi_status status;
0020
0021 if (d->override_ops && d->override_ops->get_temp)
0022 return d->override_ops->get_temp(zone, temp);
0023
0024 status = acpi_evaluate_integer(d->adev->handle, "_TMP", NULL, &tmp);
0025 if (ACPI_FAILURE(status))
0026 return -EIO;
0027
0028 if (d->lpat_table) {
0029 int conv_temp;
0030
0031 conv_temp = acpi_lpat_raw_to_temp(d->lpat_table, (int)tmp);
0032 if (conv_temp < 0)
0033 return conv_temp;
0034
0035 *temp = (unsigned long)conv_temp * 10;
0036 } else
0037
0038 *temp = deci_kelvin_to_millicelsius(tmp);
0039
0040 return 0;
0041 }
0042
0043 static int int340x_thermal_get_trip_temp(struct thermal_zone_device *zone,
0044 int trip, int *temp)
0045 {
0046 struct int34x_thermal_zone *d = zone->devdata;
0047 int i;
0048
0049 if (d->override_ops && d->override_ops->get_trip_temp)
0050 return d->override_ops->get_trip_temp(zone, trip, temp);
0051
0052 if (trip < d->aux_trip_nr)
0053 *temp = d->aux_trips[trip];
0054 else if (trip == d->crt_trip_id)
0055 *temp = d->crt_temp;
0056 else if (trip == d->psv_trip_id)
0057 *temp = d->psv_temp;
0058 else if (trip == d->hot_trip_id)
0059 *temp = d->hot_temp;
0060 else {
0061 for (i = 0; i < INT340X_THERMAL_MAX_ACT_TRIP_COUNT; i++) {
0062 if (d->act_trips[i].valid &&
0063 d->act_trips[i].id == trip) {
0064 *temp = d->act_trips[i].temp;
0065 break;
0066 }
0067 }
0068 if (i == INT340X_THERMAL_MAX_ACT_TRIP_COUNT)
0069 return -EINVAL;
0070 }
0071
0072 return 0;
0073 }
0074
0075 static int int340x_thermal_get_trip_type(struct thermal_zone_device *zone,
0076 int trip,
0077 enum thermal_trip_type *type)
0078 {
0079 struct int34x_thermal_zone *d = zone->devdata;
0080 int i;
0081
0082 if (d->override_ops && d->override_ops->get_trip_type)
0083 return d->override_ops->get_trip_type(zone, trip, type);
0084
0085 if (trip < d->aux_trip_nr)
0086 *type = THERMAL_TRIP_PASSIVE;
0087 else if (trip == d->crt_trip_id)
0088 *type = THERMAL_TRIP_CRITICAL;
0089 else if (trip == d->hot_trip_id)
0090 *type = THERMAL_TRIP_HOT;
0091 else if (trip == d->psv_trip_id)
0092 *type = THERMAL_TRIP_PASSIVE;
0093 else {
0094 for (i = 0; i < INT340X_THERMAL_MAX_ACT_TRIP_COUNT; i++) {
0095 if (d->act_trips[i].valid &&
0096 d->act_trips[i].id == trip) {
0097 *type = THERMAL_TRIP_ACTIVE;
0098 break;
0099 }
0100 }
0101 if (i == INT340X_THERMAL_MAX_ACT_TRIP_COUNT)
0102 return -EINVAL;
0103 }
0104
0105 return 0;
0106 }
0107
0108 static int int340x_thermal_set_trip_temp(struct thermal_zone_device *zone,
0109 int trip, int temp)
0110 {
0111 struct int34x_thermal_zone *d = zone->devdata;
0112 acpi_status status;
0113 char name[10];
0114
0115 if (d->override_ops && d->override_ops->set_trip_temp)
0116 return d->override_ops->set_trip_temp(zone, trip, temp);
0117
0118 snprintf(name, sizeof(name), "PAT%d", trip);
0119 status = acpi_execute_simple_method(d->adev->handle, name,
0120 millicelsius_to_deci_kelvin(temp));
0121 if (ACPI_FAILURE(status))
0122 return -EIO;
0123
0124 d->aux_trips[trip] = temp;
0125
0126 return 0;
0127 }
0128
0129
0130 static int int340x_thermal_get_trip_hyst(struct thermal_zone_device *zone,
0131 int trip, int *temp)
0132 {
0133 struct int34x_thermal_zone *d = zone->devdata;
0134 acpi_status status;
0135 unsigned long long hyst;
0136
0137 if (d->override_ops && d->override_ops->get_trip_hyst)
0138 return d->override_ops->get_trip_hyst(zone, trip, temp);
0139
0140 status = acpi_evaluate_integer(d->adev->handle, "GTSH", NULL, &hyst);
0141 if (ACPI_FAILURE(status))
0142 *temp = 0;
0143 else
0144 *temp = hyst * 100;
0145
0146 return 0;
0147 }
0148
0149 static void int340x_thermal_critical(struct thermal_zone_device *zone)
0150 {
0151 dev_dbg(&zone->device, "%s: critical temperature reached\n", zone->type);
0152 }
0153
0154 static struct thermal_zone_device_ops int340x_thermal_zone_ops = {
0155 .get_temp = int340x_thermal_get_zone_temp,
0156 .get_trip_temp = int340x_thermal_get_trip_temp,
0157 .get_trip_type = int340x_thermal_get_trip_type,
0158 .set_trip_temp = int340x_thermal_set_trip_temp,
0159 .get_trip_hyst = int340x_thermal_get_trip_hyst,
0160 .critical = int340x_thermal_critical,
0161 };
0162
0163 static int int340x_thermal_get_trip_config(acpi_handle handle, char *name,
0164 int *temp)
0165 {
0166 unsigned long long r;
0167 acpi_status status;
0168
0169 status = acpi_evaluate_integer(handle, name, NULL, &r);
0170 if (ACPI_FAILURE(status))
0171 return -EIO;
0172
0173 *temp = deci_kelvin_to_millicelsius(r);
0174
0175 return 0;
0176 }
0177
0178 int int340x_thermal_read_trips(struct int34x_thermal_zone *int34x_zone)
0179 {
0180 int trip_cnt = int34x_zone->aux_trip_nr;
0181 int i;
0182
0183 int34x_zone->crt_trip_id = -1;
0184 if (!int340x_thermal_get_trip_config(int34x_zone->adev->handle, "_CRT",
0185 &int34x_zone->crt_temp))
0186 int34x_zone->crt_trip_id = trip_cnt++;
0187
0188 int34x_zone->hot_trip_id = -1;
0189 if (!int340x_thermal_get_trip_config(int34x_zone->adev->handle, "_HOT",
0190 &int34x_zone->hot_temp))
0191 int34x_zone->hot_trip_id = trip_cnt++;
0192
0193 int34x_zone->psv_trip_id = -1;
0194 if (!int340x_thermal_get_trip_config(int34x_zone->adev->handle, "_PSV",
0195 &int34x_zone->psv_temp))
0196 int34x_zone->psv_trip_id = trip_cnt++;
0197
0198 for (i = 0; i < INT340X_THERMAL_MAX_ACT_TRIP_COUNT; i++) {
0199 char name[5] = { '_', 'A', 'C', '0' + i, '\0' };
0200
0201 if (int340x_thermal_get_trip_config(int34x_zone->adev->handle,
0202 name,
0203 &int34x_zone->act_trips[i].temp))
0204 break;
0205
0206 int34x_zone->act_trips[i].id = trip_cnt++;
0207 int34x_zone->act_trips[i].valid = true;
0208 }
0209
0210 return trip_cnt;
0211 }
0212 EXPORT_SYMBOL_GPL(int340x_thermal_read_trips);
0213
0214 static struct thermal_zone_params int340x_thermal_params = {
0215 .governor_name = "user_space",
0216 .no_hwmon = true,
0217 };
0218
0219 struct int34x_thermal_zone *int340x_thermal_zone_add(struct acpi_device *adev,
0220 struct thermal_zone_device_ops *override_ops)
0221 {
0222 struct int34x_thermal_zone *int34x_thermal_zone;
0223 acpi_status status;
0224 unsigned long long trip_cnt;
0225 int trip_mask = 0;
0226 int ret;
0227
0228 int34x_thermal_zone = kzalloc(sizeof(*int34x_thermal_zone),
0229 GFP_KERNEL);
0230 if (!int34x_thermal_zone)
0231 return ERR_PTR(-ENOMEM);
0232
0233 int34x_thermal_zone->adev = adev;
0234 int34x_thermal_zone->override_ops = override_ops;
0235
0236 status = acpi_evaluate_integer(adev->handle, "PATC", NULL, &trip_cnt);
0237 if (ACPI_FAILURE(status))
0238 trip_cnt = 0;
0239 else {
0240 int i;
0241
0242 int34x_thermal_zone->aux_trips =
0243 kcalloc(trip_cnt,
0244 sizeof(*int34x_thermal_zone->aux_trips),
0245 GFP_KERNEL);
0246 if (!int34x_thermal_zone->aux_trips) {
0247 ret = -ENOMEM;
0248 goto err_trip_alloc;
0249 }
0250 trip_mask = BIT(trip_cnt) - 1;
0251 int34x_thermal_zone->aux_trip_nr = trip_cnt;
0252 for (i = 0; i < trip_cnt; ++i)
0253 int34x_thermal_zone->aux_trips[i] = THERMAL_TEMP_INVALID;
0254 }
0255
0256 trip_cnt = int340x_thermal_read_trips(int34x_thermal_zone);
0257
0258 int34x_thermal_zone->lpat_table = acpi_lpat_get_conversion_table(
0259 adev->handle);
0260
0261 int34x_thermal_zone->zone = thermal_zone_device_register(
0262 acpi_device_bid(adev),
0263 trip_cnt,
0264 trip_mask, int34x_thermal_zone,
0265 &int340x_thermal_zone_ops,
0266 &int340x_thermal_params,
0267 0, 0);
0268 if (IS_ERR(int34x_thermal_zone->zone)) {
0269 ret = PTR_ERR(int34x_thermal_zone->zone);
0270 goto err_thermal_zone;
0271 }
0272 ret = thermal_zone_device_enable(int34x_thermal_zone->zone);
0273 if (ret)
0274 goto err_enable;
0275
0276 return int34x_thermal_zone;
0277
0278 err_enable:
0279 thermal_zone_device_unregister(int34x_thermal_zone->zone);
0280 err_thermal_zone:
0281 acpi_lpat_free_conversion_table(int34x_thermal_zone->lpat_table);
0282 kfree(int34x_thermal_zone->aux_trips);
0283 err_trip_alloc:
0284 kfree(int34x_thermal_zone);
0285 return ERR_PTR(ret);
0286 }
0287 EXPORT_SYMBOL_GPL(int340x_thermal_zone_add);
0288
0289 void int340x_thermal_zone_remove(struct int34x_thermal_zone
0290 *int34x_thermal_zone)
0291 {
0292 thermal_zone_device_unregister(int34x_thermal_zone->zone);
0293 acpi_lpat_free_conversion_table(int34x_thermal_zone->lpat_table);
0294 kfree(int34x_thermal_zone->aux_trips);
0295 kfree(int34x_thermal_zone);
0296 }
0297 EXPORT_SYMBOL_GPL(int340x_thermal_zone_remove);
0298
0299 MODULE_AUTHOR("Aaron Lu <aaron.lu@intel.com>");
0300 MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
0301 MODULE_DESCRIPTION("Intel INT340x common thermal zone handler");
0302 MODULE_LICENSE("GPL v2");