diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2015-02-19 11:28:36 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2015-02-19 11:28:36 -0800 |
commit | 89d3fa45b4add00cd0056361a2498e978cb1e119 (patch) | |
tree | b717ab79c3258c3838d7d0753c5aeae61984e17c /drivers/thermal | |
parent | 477ea1169667a88d8ee12d83a0b0863091fb8670 (diff) | |
parent | 31908f45a583e8f21db37f402b6e8d5739945afd (diff) |
Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/rzhang/linux
Pull thermal managament updates from Zhang Rui:
"Specifics:
- Abstract the code and introduce helper functions for all int340x
thermal drivers. From: Srinivas Pandruvada.
- Reorganize the ACPI LPAT table support code so that it can be
shared for both ACPI PMIC driver and int340x thermal driver.
- Add support for Braswell in intel_soc_dts thermal driver.
- a couple of small fixes/cleanups for step_wise governor and int340x
thermal driver"
* 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/rzhang/linux:
Thermal/int340x_thermal: remove unused uuids.
thermal: step_wise: spelling fixes
thermal: int340x: fix sparse warning
Thermal/int340x: LPAT conversion for temperature
ACPI / PMIC: Use common LPAT table handling functions
ACPI / LPAT: Common table processing functions
thermal: Intel SoC DTS: Add Braswell support
Thermal/int340x/int3402: Provide notification support
Thermal/int340x/processor_thermal: Add thermal zone support
Thermal/int340x/int3403: Use int340x thermal API
Thermal/int340x/int3402: Use int340x thermal API
Thermal/int340x: Add common thermal zone handler
Diffstat (limited to 'drivers/thermal')
-rw-r--r-- | drivers/thermal/int340x_thermal/Makefile | 1 | ||||
-rw-r--r-- | drivers/thermal/int340x_thermal/int3400_thermal.c | 4 | ||||
-rw-r--r-- | drivers/thermal/int340x_thermal/int3402_thermal.c | 208 | ||||
-rw-r--r-- | drivers/thermal/int340x_thermal/int3403_thermal.c | 208 | ||||
-rw-r--r-- | drivers/thermal/int340x_thermal/int340x_thermal_zone.c | 276 | ||||
-rw-r--r-- | drivers/thermal/int340x_thermal/int340x_thermal_zone.h | 68 | ||||
-rw-r--r-- | drivers/thermal/int340x_thermal/processor_thermal_device.c | 92 | ||||
-rw-r--r-- | drivers/thermal/intel_soc_dts_thermal.c | 46 | ||||
-rw-r--r-- | drivers/thermal/step_wise.c | 4 |
9 files changed, 515 insertions, 392 deletions
diff --git a/drivers/thermal/int340x_thermal/Makefile b/drivers/thermal/int340x_thermal/Makefile index d4413698a85f..ba77a34f659f 100644 --- a/drivers/thermal/int340x_thermal/Makefile +++ b/drivers/thermal/int340x_thermal/Makefile @@ -1,4 +1,5 @@ obj-$(CONFIG_INT340X_THERMAL) += int3400_thermal.o +obj-$(CONFIG_INT340X_THERMAL) += int340x_thermal_zone.o obj-$(CONFIG_INT340X_THERMAL) += int3402_thermal.o obj-$(CONFIG_INT340X_THERMAL) += int3403_thermal.o obj-$(CONFIG_INT340X_THERMAL) += processor_thermal_device.o diff --git a/drivers/thermal/int340x_thermal/int3400_thermal.c b/drivers/thermal/int340x_thermal/int3400_thermal.c index 65a98a97df07..25d244cbbe8f 100644 --- a/drivers/thermal/int340x_thermal/int3400_thermal.c +++ b/drivers/thermal/int340x_thermal/int3400_thermal.c @@ -18,19 +18,15 @@ enum int3400_thermal_uuid { INT3400_THERMAL_PASSIVE_1, - INT3400_THERMAL_PASSIVE_2, INT3400_THERMAL_ACTIVE, INT3400_THERMAL_CRITICAL, - INT3400_THERMAL_COOLING_MODE, INT3400_THERMAL_MAXIMUM_UUID, }; static u8 *int3400_thermal_uuids[INT3400_THERMAL_MAXIMUM_UUID] = { "42A441D6-AE6A-462b-A84B-4A8CE79027D3", - "9E04115A-AE87-4D1C-9500-0F3E340BFE75", "3A95C389-E4B8-4629-A526-C52C88626BAE", "97C68AE7-15FA-499c-B8C9-5DA81D606E0A", - "16CAF1B7-DD38-40ed-B1C1-1B8A1913D531", }; struct int3400_thermal_priv { diff --git a/drivers/thermal/int340x_thermal/int3402_thermal.c b/drivers/thermal/int340x_thermal/int3402_thermal.c index c5cbc3af3a05..69df3d960303 100644 --- a/drivers/thermal/int340x_thermal/int3402_thermal.c +++ b/drivers/thermal/int340x_thermal/int3402_thermal.c @@ -14,152 +14,39 @@ #include <linux/platform_device.h> #include <linux/acpi.h> #include <linux/thermal.h> +#include "int340x_thermal_zone.h" -#define ACPI_ACTIVE_COOLING_MAX_NR 10 - -struct active_trip { - unsigned long temp; - int id; - bool valid; -}; +#define INT3402_PERF_CHANGED_EVENT 0x80 +#define INT3402_THERMAL_EVENT 0x90 struct int3402_thermal_data { - unsigned long *aux_trips; - int aux_trip_nr; - unsigned long psv_temp; - int psv_trip_id; - unsigned long crt_temp; - int crt_trip_id; - unsigned long hot_temp; - int hot_trip_id; - struct active_trip act_trips[ACPI_ACTIVE_COOLING_MAX_NR]; acpi_handle *handle; + struct int34x_thermal_zone *int340x_zone; }; -static int int3402_thermal_get_zone_temp(struct thermal_zone_device *zone, - unsigned long *temp) -{ - struct int3402_thermal_data *d = zone->devdata; - unsigned long long tmp; - acpi_status status; - - status = acpi_evaluate_integer(d->handle, "_TMP", NULL, &tmp); - if (ACPI_FAILURE(status)) - return -ENODEV; - - /* _TMP returns the temperature in tenths of degrees Kelvin */ - *temp = DECI_KELVIN_TO_MILLICELSIUS(tmp); - - return 0; -} - -static int int3402_thermal_get_trip_temp(struct thermal_zone_device *zone, - int trip, unsigned long *temp) +static void int3402_notify(acpi_handle handle, u32 event, void *data) { - struct int3402_thermal_data *d = zone->devdata; - int i; - - if (trip < d->aux_trip_nr) - *temp = d->aux_trips[trip]; - else if (trip == d->crt_trip_id) - *temp = d->crt_temp; - else if (trip == d->psv_trip_id) - *temp = d->psv_temp; - else if (trip == d->hot_trip_id) - *temp = d->hot_temp; - else { - for (i = 0; i < ACPI_ACTIVE_COOLING_MAX_NR; i++) { - if (d->act_trips[i].valid && - d->act_trips[i].id == trip) { - *temp = d->act_trips[i].temp; - break; - } - } - if (i == ACPI_ACTIVE_COOLING_MAX_NR) - return -EINVAL; + struct int3402_thermal_data *priv = data; + + if (!priv) + return; + + switch (event) { + case INT3402_PERF_CHANGED_EVENT: + break; + case INT3402_THERMAL_EVENT: + int340x_thermal_zone_device_update(priv->int340x_zone); + break; + default: + break; } - return 0; -} - -static int int3402_thermal_get_trip_type(struct thermal_zone_device *zone, - int trip, enum thermal_trip_type *type) -{ - struct int3402_thermal_data *d = zone->devdata; - int i; - - if (trip < d->aux_trip_nr) - *type = THERMAL_TRIP_PASSIVE; - else if (trip == d->crt_trip_id) - *type = THERMAL_TRIP_CRITICAL; - else if (trip == d->hot_trip_id) - *type = THERMAL_TRIP_HOT; - else if (trip == d->psv_trip_id) - *type = THERMAL_TRIP_PASSIVE; - else { - for (i = 0; i < ACPI_ACTIVE_COOLING_MAX_NR; i++) { - if (d->act_trips[i].valid && - d->act_trips[i].id == trip) { - *type = THERMAL_TRIP_ACTIVE; - break; - } - } - if (i == ACPI_ACTIVE_COOLING_MAX_NR) - return -EINVAL; - } - return 0; -} - -static int int3402_thermal_set_trip_temp(struct thermal_zone_device *zone, int trip, - unsigned long temp) -{ - struct int3402_thermal_data *d = zone->devdata; - acpi_status status; - char name[10]; - - snprintf(name, sizeof(name), "PAT%d", trip); - status = acpi_execute_simple_method(d->handle, name, - MILLICELSIUS_TO_DECI_KELVIN(temp)); - if (ACPI_FAILURE(status)) - return -EIO; - - d->aux_trips[trip] = temp; - return 0; -} - -static struct thermal_zone_device_ops int3402_thermal_zone_ops = { - .get_temp = int3402_thermal_get_zone_temp, - .get_trip_temp = int3402_thermal_get_trip_temp, - .get_trip_type = int3402_thermal_get_trip_type, - .set_trip_temp = int3402_thermal_set_trip_temp, -}; - -static struct thermal_zone_params int3402_thermal_params = { - .governor_name = "user_space", - .no_hwmon = true, -}; - -static int int3402_thermal_get_temp(acpi_handle handle, char *name, - unsigned long *temp) -{ - unsigned long long r; - acpi_status status; - - status = acpi_evaluate_integer(handle, name, NULL, &r); - if (ACPI_FAILURE(status)) - return -EIO; - - *temp = DECI_KELVIN_TO_MILLICELSIUS(r); - return 0; } static int int3402_thermal_probe(struct platform_device *pdev) { struct acpi_device *adev = ACPI_COMPANION(&pdev->dev); struct int3402_thermal_data *d; - struct thermal_zone_device *zone; - acpi_status status; - unsigned long long trip_cnt; - int trip_mask = 0, i; + int ret; if (!acpi_has_method(adev->handle, "_TMP")) return -ENODEV; @@ -168,54 +55,33 @@ static int int3402_thermal_probe(struct platform_device *pdev) if (!d) return -ENOMEM; - status = acpi_evaluate_integer(adev->handle, "PATC", NULL, &trip_cnt); - if (ACPI_FAILURE(status)) - trip_cnt = 0; - else { - d->aux_trips = devm_kzalloc(&pdev->dev, - sizeof(*d->aux_trips) * trip_cnt, GFP_KERNEL); - if (!d->aux_trips) - return -ENOMEM; - trip_mask = trip_cnt - 1; - d->handle = adev->handle; - d->aux_trip_nr = trip_cnt; - } - - d->crt_trip_id = -1; - if (!int3402_thermal_get_temp(adev->handle, "_CRT", &d->crt_temp)) - d->crt_trip_id = trip_cnt++; - d->hot_trip_id = -1; - if (!int3402_thermal_get_temp(adev->handle, "_HOT", &d->hot_temp)) - d->hot_trip_id = trip_cnt++; - d->psv_trip_id = -1; - if (!int3402_thermal_get_temp(adev->handle, "_PSV", &d->psv_temp)) - d->psv_trip_id = trip_cnt++; - for (i = 0; i < ACPI_ACTIVE_COOLING_MAX_NR; i++) { - char name[5] = { '_', 'A', 'C', '0' + i, '\0' }; - if (int3402_thermal_get_temp(adev->handle, name, - &d->act_trips[i].temp)) - break; - d->act_trips[i].id = trip_cnt++; - d->act_trips[i].valid = true; + d->int340x_zone = int340x_thermal_zone_add(adev, NULL); + if (IS_ERR(d->int340x_zone)) + return PTR_ERR(d->int340x_zone); + + ret = acpi_install_notify_handler(adev->handle, + ACPI_DEVICE_NOTIFY, + int3402_notify, + d); + if (ret) { + int340x_thermal_zone_remove(d->int340x_zone); + return ret; } - zone = thermal_zone_device_register(acpi_device_bid(adev), trip_cnt, - trip_mask, d, - &int3402_thermal_zone_ops, - &int3402_thermal_params, - 0, 0); - if (IS_ERR(zone)) - return PTR_ERR(zone); - platform_set_drvdata(pdev, zone); + d->handle = adev->handle; + platform_set_drvdata(pdev, d); return 0; } static int int3402_thermal_remove(struct platform_device *pdev) { - struct thermal_zone_device *zone = platform_get_drvdata(pdev); + struct int3402_thermal_data *d = platform_get_drvdata(pdev); + + acpi_remove_notify_handler(d->handle, + ACPI_DEVICE_NOTIFY, int3402_notify); + int340x_thermal_zone_remove(d->int340x_zone); - thermal_zone_device_unregister(zone); return 0; } diff --git a/drivers/thermal/int340x_thermal/int3403_thermal.c b/drivers/thermal/int340x_thermal/int3403_thermal.c index 0faf500d8a77..50a7a08e3a15 100644 --- a/drivers/thermal/int340x_thermal/int3403_thermal.c +++ b/drivers/thermal/int340x_thermal/int3403_thermal.c @@ -19,6 +19,7 @@ #include <linux/acpi.h> #include <linux/thermal.h> #include <linux/platform_device.h> +#include "int340x_thermal_zone.h" #define INT3403_TYPE_SENSOR 0x03 #define INT3403_TYPE_CHARGER 0x0B @@ -26,18 +27,9 @@ #define INT3403_PERF_CHANGED_EVENT 0x80 #define INT3403_THERMAL_EVENT 0x90 -#define DECI_KELVIN_TO_MILLI_CELSIUS(t, off) (((t) - (off)) * 100) -#define KELVIN_OFFSET 2732 -#define MILLI_CELSIUS_TO_DECI_KELVIN(t, off) (((t) / 100) + (off)) - +/* Preserved structure for future expandbility */ struct int3403_sensor { - struct thermal_zone_device *tzone; - unsigned long *thresholds; - unsigned long crit_temp; - int crit_trip_id; - unsigned long psv_temp; - int psv_trip_id; - + struct int34x_thermal_zone *int340x_zone; }; struct int3403_performance_state { @@ -63,126 +55,6 @@ struct int3403_priv { void *priv; }; -static int sys_get_curr_temp(struct thermal_zone_device *tzone, - unsigned long *temp) -{ - struct int3403_priv *priv = tzone->devdata; - struct acpi_device *device = priv->adev; - unsigned long long tmp; - acpi_status status; - - status = acpi_evaluate_integer(device->handle, "_TMP", NULL, &tmp); - if (ACPI_FAILURE(status)) - return -EIO; - - *temp = DECI_KELVIN_TO_MILLI_CELSIUS(tmp, KELVIN_OFFSET); - - return 0; -} - -static int sys_get_trip_hyst(struct thermal_zone_device *tzone, - int trip, unsigned long *temp) -{ - struct int3403_priv *priv = tzone->devdata; - struct acpi_device *device = priv->adev; - unsigned long long hyst; - acpi_status status; - - status = acpi_evaluate_integer(device->handle, "GTSH", NULL, &hyst); - if (ACPI_FAILURE(status)) - return -EIO; - - /* - * Thermal hysteresis represents a temperature difference. - * Kelvin and Celsius have same degree size. So the - * conversion here between tenths of degree Kelvin unit - * and Milli-Celsius unit is just to multiply 100. - */ - *temp = hyst * 100; - - return 0; -} - -static int sys_get_trip_temp(struct thermal_zone_device *tzone, - int trip, unsigned long *temp) -{ - struct int3403_priv *priv = tzone->devdata; - struct int3403_sensor *obj = priv->priv; - - if (priv->type != INT3403_TYPE_SENSOR || !obj) - return -EINVAL; - - if (trip == obj->crit_trip_id) - *temp = obj->crit_temp; - else if (trip == obj->psv_trip_id) - *temp = obj->psv_temp; - else { - /* - * get_trip_temp is a mandatory callback but - * PATx method doesn't return any value, so return - * cached value, which was last set from user space - */ - *temp = obj->thresholds[trip]; - } - - return 0; -} - -static int sys_get_trip_type(struct thermal_zone_device *thermal, - int trip, enum thermal_trip_type *type) -{ - struct int3403_priv *priv = thermal->devdata; - struct int3403_sensor *obj = priv->priv; - - /* Mandatory callback, may not mean much here */ - if (trip == obj->crit_trip_id) - *type = THERMAL_TRIP_CRITICAL; - else - *type = THERMAL_TRIP_PASSIVE; - - return 0; -} - -int sys_set_trip_temp(struct thermal_zone_device *tzone, int trip, - unsigned long temp) -{ - struct int3403_priv *priv = tzone->devdata; - struct acpi_device *device = priv->adev; - struct int3403_sensor *obj = priv->priv; - acpi_status status; - char name[10]; - int ret = 0; - - snprintf(name, sizeof(name), "PAT%d", trip); - if (acpi_has_method(device->handle, name)) { - status = acpi_execute_simple_method(device->handle, name, - MILLI_CELSIUS_TO_DECI_KELVIN(temp, - KELVIN_OFFSET)); - if (ACPI_FAILURE(status)) - ret = -EIO; - else - obj->thresholds[trip] = temp; - } else { - ret = -EIO; - dev_err(&device->dev, "sys_set_trip_temp: method not found\n"); - } - - return ret; -} - -static struct thermal_zone_device_ops tzone_ops = { - .get_temp = sys_get_curr_temp, - .get_trip_temp = sys_get_trip_temp, - .get_trip_type = sys_get_trip_type, - .set_trip_temp = sys_set_trip_temp, - .get_trip_hyst = sys_get_trip_hyst, -}; - -static struct thermal_zone_params int3403_thermal_params = { - .governor_name = "user_space", - .no_hwmon = true, -}; - static void int3403_notify(acpi_handle handle, u32 event, void *data) { @@ -200,7 +72,7 @@ static void int3403_notify(acpi_handle handle, case INT3403_PERF_CHANGED_EVENT: break; case INT3403_THERMAL_EVENT: - thermal_zone_device_update(obj->tzone); + int340x_thermal_zone_device_update(obj->int340x_zone); break; default: dev_err(&priv->pdev->dev, "Unsupported event [0x%x]\n", event); @@ -208,41 +80,10 @@ static void int3403_notify(acpi_handle handle, } } -static int sys_get_trip_crt(struct acpi_device *device, unsigned long *temp) -{ - unsigned long long crt; - acpi_status status; - - status = acpi_evaluate_integer(device->handle, "_CRT", NULL, &crt); - if (ACPI_FAILURE(status)) - return -EIO; - - *temp = DECI_KELVIN_TO_MILLI_CELSIUS(crt, KELVIN_OFFSET); - - return 0; -} - -static int sys_get_trip_psv(struct acpi_device *device, unsigned long *temp) -{ - unsigned long long psv; - acpi_status status; - - status = acpi_evaluate_integer(device->handle, "_PSV", NULL, &psv); - if (ACPI_FAILURE(status)) - return -EIO; - - *temp = DECI_KELVIN_TO_MILLI_CELSIUS(psv, KELVIN_OFFSET); - - return 0; -} - static int int3403_sensor_add(struct int3403_priv *priv) { int result = 0; - acpi_status status; struct int3403_sensor *obj; - unsigned long long trip_cnt; - int trip_mask = 0; obj = devm_kzalloc(&priv->pdev->dev, sizeof(*obj), GFP_KERNEL); if (!obj) @@ -250,39 +91,9 @@ static int int3403_sensor_add(struct int3403_priv *priv) priv->priv = obj; - status = acpi_evaluate_integer(priv->adev->handle, "PATC", NULL, - &trip_cnt); - if (ACPI_FAILURE(status)) - trip_cnt = 0; - - if (trip_cnt) { - /* We have to cache, thresholds can't be readback */ - obj->thresholds = devm_kzalloc(&priv->pdev->dev, - sizeof(*obj->thresholds) * trip_cnt, - GFP_KERNEL); - if (!obj->thresholds) { - result = -ENOMEM; - goto err_free_obj; - } - trip_mask = BIT(trip_cnt) - 1; - } - - obj->psv_trip_id = -1; - if (!sys_get_trip_psv(priv->adev, &obj->psv_temp)) - obj->psv_trip_id = trip_cnt++; - - obj->crit_trip_id = -1; - if (!sys_get_trip_crt(priv->adev, &obj->crit_temp)) - obj->crit_trip_id = trip_cnt++; - - obj->tzone = thermal_zone_device_register(acpi_device_bid(priv->adev), - trip_cnt, trip_mask, priv, &tzone_ops, - &int3403_thermal_params, 0, 0); - if (IS_ERR(obj->tzone)) { - result = PTR_ERR(obj->tzone); - obj->tzone = NULL; - goto err_free_obj; - } + obj->int340x_zone = int340x_thermal_zone_add(priv->adev, NULL); + if (IS_ERR(obj->int340x_zone)) + return PTR_ERR(obj->int340x_zone); result = acpi_install_notify_handler(priv->adev->handle, ACPI_DEVICE_NOTIFY, int3403_notify, @@ -293,7 +104,7 @@ static int int3403_sensor_add(struct int3403_priv *priv) return 0; err_free_obj: - thermal_zone_device_unregister(obj->tzone); + int340x_thermal_zone_remove(obj->int340x_zone); return result; } @@ -303,7 +114,8 @@ static int int3403_sensor_remove(struct int3403_priv *priv) acpi_remove_notify_handler(priv->adev->handle, ACPI_DEVICE_NOTIFY, int3403_notify); - thermal_zone_device_unregister(obj->tzone); + int340x_thermal_zone_remove(obj->int340x_zone); + return 0; } diff --git a/drivers/thermal/int340x_thermal/int340x_thermal_zone.c b/drivers/thermal/int340x_thermal/int340x_thermal_zone.c new file mode 100644 index 000000000000..f88b08877025 --- /dev/null +++ b/drivers/thermal/int340x_thermal/int340x_thermal_zone.c @@ -0,0 +1,276 @@ +/* + * int340x_thermal_zone.c + * Copyright (c) 2015, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/acpi.h> +#include <linux/thermal.h> +#include "int340x_thermal_zone.h" + +static int int340x_thermal_get_zone_temp(struct thermal_zone_device *zone, + unsigned long *temp) +{ + struct int34x_thermal_zone *d = zone->devdata; + unsigned long long tmp; + acpi_status status; + + if (d->override_ops && d->override_ops->get_temp) + return d->override_ops->get_temp(zone, temp); + + status = acpi_evaluate_integer(d->adev->handle, "_TMP", NULL, &tmp); + if (ACPI_FAILURE(status)) + return -EIO; + + if (d->lpat_table) { + int conv_temp; + + conv_temp = acpi_lpat_raw_to_temp(d->lpat_table, (int)tmp); + if (conv_temp < 0) + return conv_temp; + + *temp = (unsigned long)conv_temp * 10; + } else + /* _TMP returns the temperature in tenths of degrees Kelvin */ + *temp = DECI_KELVIN_TO_MILLICELSIUS(tmp); + + return 0; +} + +static int int340x_thermal_get_trip_temp(struct thermal_zone_device *zone, + int trip, unsigned long *temp) +{ + struct int34x_thermal_zone *d = zone->devdata; + int i; + + if (d->override_ops && d->override_ops->get_trip_temp) + return d->override_ops->get_trip_temp(zone, trip, temp); + + if (trip < d->aux_trip_nr) + *temp = d->aux_trips[trip]; + else if (trip == d->crt_trip_id) + *temp = d->crt_temp; + else if (trip == d->psv_trip_id) + *temp = d->psv_temp; + else if (trip == d->hot_trip_id) + *temp = d->hot_temp; + else { + for (i = 0; i < INT340X_THERMAL_MAX_ACT_TRIP_COUNT; i++) { + if (d->act_trips[i].valid && + d->act_trips[i].id == trip) { + *temp = d->act_trips[i].temp; + break; + } + } + if (i == INT340X_THERMAL_MAX_ACT_TRIP_COUNT) + return -EINVAL; + } + + return 0; +} + +static int int340x_thermal_get_trip_type(struct thermal_zone_device *zone, + int trip, + enum thermal_trip_type *type) +{ + struct int34x_thermal_zone *d = zone->devdata; + int i; + + if (d->override_ops && d->override_ops->get_trip_type) + return d->override_ops->get_trip_type(zone, trip, type); + + if (trip < d->aux_trip_nr) + *type = THERMAL_TRIP_PASSIVE; + else if (trip == d->crt_trip_id) + *type = THERMAL_TRIP_CRITICAL; + else if (trip == d->hot_trip_id) + *type = THERMAL_TRIP_HOT; + else if (trip == d->psv_trip_id) + *type = THERMAL_TRIP_PASSIVE; + else { + for (i = 0; i < INT340X_THERMAL_MAX_ACT_TRIP_COUNT; i++) { + if (d->act_trips[i].valid && + d->act_trips[i].id == trip) { + *type = THERMAL_TRIP_ACTIVE; + break; + } + } + if (i == INT340X_THERMAL_MAX_ACT_TRIP_COUNT) + return -EINVAL; + } + + return 0; +} + +static int int340x_thermal_set_trip_temp(struct thermal_zone_device *zone, + int trip, unsigned long temp) +{ + struct int34x_thermal_zone *d = zone->devdata; + acpi_status status; + char name[10]; + + if (d->override_ops && d->override_ops->set_trip_temp) + return d->override_ops->set_trip_temp(zone, trip, temp); + + snprintf(name, sizeof(name), "PAT%d", trip); + status = acpi_execute_simple_method(d->adev->handle, name, + MILLICELSIUS_TO_DECI_KELVIN(temp)); + if (ACPI_FAILURE(status)) + return -EIO; + + d->aux_trips[trip] = temp; + + return 0; +} + + +static int int340x_thermal_get_trip_hyst(struct thermal_zone_device *zone, + int trip, unsigned long *temp) +{ + struct int34x_thermal_zone *d = zone->devdata; + acpi_status status; + unsigned long long hyst; + + if (d->override_ops && d->override_ops->get_trip_hyst) + return d->override_ops->get_trip_hyst(zone, trip, temp); + + status = acpi_evaluate_integer(d->adev->handle, "GTSH", NULL, &hyst); + if (ACPI_FAILURE(status)) + return -EIO; + + *temp = hyst * 100; + + return 0; +} + +static struct thermal_zone_device_ops int340x_thermal_zone_ops = { + .get_temp = int340x_thermal_get_zone_temp, + .get_trip_temp = int340x_thermal_get_trip_temp, + .get_trip_type = int340x_thermal_get_trip_type, + .set_trip_temp = int340x_thermal_set_trip_temp, + .get_trip_hyst = int340x_thermal_get_trip_hyst, +}; + +static int int340x_thermal_get_trip_config(acpi_handle handle, char *name, + unsigned long *temp) +{ + unsigned long long r; + acpi_status status; + + status = acpi_evaluate_integer(handle, name, NULL, &r); + if (ACPI_FAILURE(status)) + return -EIO; + + *temp = DECI_KELVIN_TO_MILLICELSIUS(r); + + return 0; +} + +static struct thermal_zone_params int340x_thermal_params = { + .governor_name = "user_space", + .no_hwmon = true, +}; + +struct int34x_thermal_zone *int340x_thermal_zone_add(struct acpi_device *adev, + struct thermal_zone_device_ops *override_ops) +{ + struct int34x_thermal_zone *int34x_thermal_zone; + acpi_status status; + unsigned long long trip_cnt; + int trip_mask = 0, i; + int ret; + + int34x_thermal_zone = kzalloc(sizeof(*int34x_thermal_zone), + GFP_KERNEL); + if (!int34x_thermal_zone) + return ERR_PTR(-ENOMEM); + + int34x_thermal_zone->adev = adev; + int34x_thermal_zone->override_ops = override_ops; + + status = acpi_evaluate_integer(adev->handle, "PATC", NULL, &trip_cnt); + if (ACPI_FAILURE(status)) + trip_cnt = 0; + else { + int34x_thermal_zone->aux_trips = kzalloc( + sizeof(*int34x_thermal_zone->aux_trips) * + trip_cnt, GFP_KERNEL); + if (!int34x_thermal_zone->aux_trips) { + ret = -ENOMEM; + goto free_mem; + } + trip_mask = BIT(trip_cnt) - 1; + int34x_thermal_zone->aux_trip_nr = trip_cnt; + } + + int34x_thermal_zone->crt_trip_id = -1; + if (!int340x_thermal_get_trip_config(adev->handle, "_CRT", + &int34x_thermal_zone->crt_temp)) + int34x_thermal_zone->crt_trip_id = trip_cnt++; + int34x_thermal_zone->hot_trip_id = -1; + if (!int340x_thermal_get_trip_config(adev->handle, "_HOT", + &int34x_thermal_zone->hot_temp)) + int34x_thermal_zone->hot_trip_id = trip_cnt++; + int34x_thermal_zone->psv_trip_id = -1; + if (!int340x_thermal_get_trip_config(adev->handle, "_PSV", + &int34x_thermal_zone->psv_temp)) + int34x_thermal_zone->psv_trip_id = trip_cnt++; + for (i = 0; i < INT340X_THERMAL_MAX_ACT_TRIP_COUNT; i++) { + char name[5] = { '_', 'A', 'C', '0' + i, '\0' }; + + if (int340x_thermal_get_trip_config(adev->handle, name, + &int34x_thermal_zone->act_trips[i].temp)) + break; + + int34x_thermal_zone->act_trips[i].id = trip_cnt++; + int34x_thermal_zone->act_trips[i].valid = true; + } + int34x_thermal_zone->lpat_table = acpi_lpat_get_conversion_table( + adev->handle); + + int34x_thermal_zone->zone = thermal_zone_device_register( + acpi_device_bid(adev), + trip_cnt, + trip_mask, int34x_thermal_zone, + &int340x_thermal_zone_ops, + &int340x_thermal_params, + 0, 0); + if (IS_ERR(int34x_thermal_zone->zone)) { + ret = PTR_ERR(int34x_thermal_zone->zone); + goto free_lpat; + } + + return int34x_thermal_zone; + +free_lpat: + acpi_lpat_free_conversion_table(int34x_thermal_zone->lpat_table); +free_mem: + kfree(int34x_thermal_zone); + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(int340x_thermal_zone_add); + +void int340x_thermal_zone_remove(struct int34x_thermal_zone + *int34x_thermal_zone) +{ + thermal_zone_device_unregister(int34x_thermal_zone->zone); + acpi_lpat_free_conversion_table(int34x_thermal_zone->lpat_table); + kfree(int34x_thermal_zone); +} +EXPORT_SYMBOL_GPL(int340x_thermal_zone_remove); + +MODULE_AUTHOR("Aaron Lu <aaron.lu@intel.com>"); +MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>"); +MODULE_DESCRIPTION("Intel INT340x common thermal zone handler"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/thermal/int340x_thermal/int340x_thermal_zone.h b/drivers/thermal/int340x_thermal/int340x_thermal_zone.h new file mode 100644 index 000000000000..9f38ab72c4bf --- /dev/null +++ b/drivers/thermal/int340x_thermal/int340x_thermal_zone.h @@ -0,0 +1,68 @@ +/* + * int340x_thermal_zone.h + * Copyright (c) 2015, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#ifndef __INT340X_THERMAL_ZONE_H__ +#define __INT340X_THERMAL_ZONE_H__ + +#include <acpi/acpi_lpat.h> + +#define INT340X_THERMAL_MAX_ACT_TRIP_COUNT 10 + +struct active_trip { + unsigned long temp; + int id; + bool valid; +}; + +struct int34x_thermal_zone { + struct acpi_device *adev; + struct active_trip act_trips[INT340X_THERMAL_MAX_ACT_TRIP_COUNT]; + unsigned long *aux_trips; + int aux_trip_nr; + unsigned long psv_temp; + int psv_trip_id; + unsigned long crt_temp; + int crt_trip_id; + unsigned long hot_temp; + int hot_trip_id; + struct thermal_zone_device *zone; + struct thermal_zone_device_ops *override_ops; + void *priv_data; + struct acpi_lpat_conversion_table *lpat_table; +}; + +struct int34x_thermal_zone *int340x_thermal_zone_add(struct acpi_device *, + struct thermal_zone_device_ops *override_ops); +void int340x_thermal_zone_remove(struct int34x_thermal_zone *); + +static inline void int340x_thermal_zone_set_priv_data( + struct int34x_thermal_zone *tzone, void *priv_data) +{ + tzone->priv_data = priv_data; +} + +static inline void *int340x_thermal_zone_get_priv_data( + struct int34x_thermal_zone *tzone) +{ + return tzone->priv_data; +} + +static inline void int340x_thermal_zone_device_update( + struct int34x_thermal_zone *tzone) +{ + thermal_zone_device_update(tzone->zone); +} + +#endif diff --git a/drivers/thermal/int340x_thermal/processor_thermal_device.c b/drivers/thermal/int340x_thermal/processor_thermal_device.c index 0fe5dbbea968..5e8d8e91ea6d 100644 --- a/drivers/thermal/int340x_thermal/processor_thermal_device.c +++ b/drivers/thermal/int340x_thermal/processor_thermal_device.c @@ -18,6 +18,8 @@ #include <linux/pci.h> #include <linux/platform_device.h> #include <linux/acpi.h> +#include <linux/thermal.h> +#include "int340x_thermal_zone.h" /* Broadwell-U/HSB thermal reporting device */ #define PCI_DEVICE_ID_PROC_BDW_THERMAL 0x1603 @@ -39,6 +41,7 @@ struct proc_thermal_device { struct device *dev; struct acpi_device *adev; struct power_config power_limits[2]; + struct int34x_thermal_zone *int340x_zone; }; enum proc_thermal_emum_mode_type { @@ -117,6 +120,72 @@ static struct attribute_group power_limit_attribute_group = { .name = "power_limits" }; +static int stored_tjmax; /* since it is fixed, we can have local storage */ + +static int get_tjmax(void) +{ + u32 eax, edx; + u32 val; + int err; + + err = rdmsr_safe(MSR_IA32_TEMPERATURE_TARGET, &eax, &edx); + if (err) + return err; + + val = (eax >> 16) & 0xff; + if (val) + return val; + + return -EINVAL; +} + +static int read_temp_msr(unsigned long *temp) +{ + int cpu; + u32 eax, edx; + int err; + unsigned long curr_temp_off = 0; + + *temp = 0; + + for_each_online_cpu(cpu) { + err = rdmsr_safe_on_cpu(cpu, MSR_IA32_THERM_STATUS, &eax, + &edx); + if (err) + goto err_ret; + else { + if (eax & 0x80000000) { + curr_temp_off = (eax >> 16) & 0x7f; + if (!*temp || curr_temp_off < *temp) + *temp = curr_temp_off; + } else { + err = -EINVAL; + goto err_ret; + } + } + } + + return 0; +err_ret: + return err; +} + +static int proc_thermal_get_zone_temp(struct thermal_zone_device *zone, + unsigned long *temp) +{ + int ret; + + ret = read_temp_msr(temp); + if (!ret) + *temp = (stored_tjmax - *temp) * 1000; + + return ret; +} + +static struct thermal_zone_device_ops proc_thermal_local_ops = { + .get_temp = proc_thermal_get_zone_temp, +}; + static int proc_thermal_add(struct device *dev, struct proc_thermal_device **priv) { @@ -126,6 +195,8 @@ static int proc_thermal_add(struct device *dev, struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL }; union acpi_object *elements, *ppcc; union acpi_object *p; + unsigned long long tmp; + struct thermal_zone_device_ops *ops = NULL; int i; int ret; @@ -178,6 +249,24 @@ static int proc_thermal_add(struct device *dev, ret = sysfs_create_group(&dev->kobj, &power_limit_attribute_group); + if (ret) + goto free_buffer; + + status = acpi_evaluate_integer(adev->handle, "_TMP", NULL, &tmp); + if (ACPI_FAILURE(status)) { + /* there is no _TMP method, add local method */ + stored_tjmax = get_tjmax(); + if (stored_tjmax > 0) + ops = &proc_thermal_local_ops; + } + + proc_priv->int340x_zone = int340x_thermal_zone_add(adev, ops); + if (IS_ERR(proc_priv->int340x_zone)) { + sysfs_remove_group(&proc_priv->dev->kobj, |