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
This commit is contained in:
Linus Torvalds 2015-02-19 11:28:36 -08:00
commit 89d3fa45b4
13 changed files with 760 additions and 507 deletions

View file

@ -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

View file

@ -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 {

View file

@ -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)
static void int3402_notify(acpi_handle handle, u32 event, void *data)
{
struct int3402_thermal_data *d = zone->devdata;
unsigned long long tmp;
acpi_status status;
struct int3402_thermal_data *priv = data;
status = acpi_evaluate_integer(d->handle, "_TMP", NULL, &tmp);
if (ACPI_FAILURE(status))
return -ENODEV;
if (!priv)
return;
/* _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)
{
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;
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->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;
}
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;
}
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;
}

View file

@ -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;
}

View file

@ -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 <[email protected]>");
MODULE_AUTHOR("Srinivas Pandruvada <[email protected]>");
MODULE_DESCRIPTION("Intel INT340x common thermal zone handler");
MODULE_LICENSE("GPL v2");

View file

@ -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

View file

@ -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,
&power_limit_attribute_group);
ret = PTR_ERR(proc_priv->int340x_zone);
} else
ret = 0;
free_buffer:
kfree(buf.pointer);
@ -185,8 +274,9 @@ free_buffer:
return ret;
}
void proc_thermal_remove(struct proc_thermal_device *proc_priv)
static void proc_thermal_remove(struct proc_thermal_device *proc_priv)
{
int340x_thermal_zone_remove(proc_priv->int340x_zone);
sysfs_remove_group(&proc_priv->dev->kobj,
&power_limit_attribute_group);
}

View file

@ -309,10 +309,13 @@ static int soc_dts_enable(int id)
return ret;
}
static struct soc_sensor_entry *alloc_soc_dts(int id, u32 tj_max)
static struct soc_sensor_entry *alloc_soc_dts(int id, u32 tj_max,
bool notification_support)
{
struct soc_sensor_entry *aux_entry;
char name[10];
int trip_count = 0;
int trip_mask = 0;
int err;
aux_entry = kzalloc(sizeof(*aux_entry), GFP_KERNEL);
@ -332,11 +335,16 @@ static struct soc_sensor_entry *alloc_soc_dts(int id, u32 tj_max)
aux_entry->tj_max = tj_max;
aux_entry->temp_mask = 0x00FF << (id * 8);
aux_entry->temp_shift = id * 8;
if (notification_support) {
trip_count = SOC_MAX_DTS_TRIPS;
trip_mask = 0x02;
}
snprintf(name, sizeof(name), "soc_dts%d", id);
aux_entry->tzone = thermal_zone_device_register(name,
SOC_MAX_DTS_TRIPS,
0x02,
aux_entry, &tzone_ops, NULL, 0, 0);
trip_count,
trip_mask,
aux_entry, &tzone_ops,
NULL, 0, 0);
if (IS_ERR(aux_entry->tzone)) {
err = PTR_ERR(aux_entry->tzone);
goto err_ret;
@ -402,6 +410,7 @@ static irqreturn_t soc_irq_thread_fn(int irq, void *dev_data)
static const struct x86_cpu_id soc_thermal_ids[] = {
{ X86_VENDOR_INTEL, X86_FAMILY_ANY, 0x37, 0, BYT_SOC_DTS_APIC_IRQ},
{ X86_VENDOR_INTEL, X86_FAMILY_ANY, 0x4c, 0, 0},
{}
};
MODULE_DEVICE_TABLE(x86cpu, soc_thermal_ids);
@ -420,8 +429,11 @@ static int __init intel_soc_thermal_init(void)
if (get_tj_max(&tj_max))
return -EINVAL;
soc_dts_thres_irq = (int)match_cpu->driver_data;
for (i = 0; i < SOC_MAX_DTS_SENSORS; ++i) {
soc_dts[i] = alloc_soc_dts(i, tj_max);
soc_dts[i] = alloc_soc_dts(i, tj_max,
soc_dts_thres_irq ? true : false);
if (IS_ERR(soc_dts[i])) {
err = PTR_ERR(soc_dts[i]);
goto err_free;
@ -430,15 +442,15 @@ static int __init intel_soc_thermal_init(void)
spin_lock_init(&intr_notify_lock);
soc_dts_thres_irq = (int)match_cpu->driver_data;
err = request_threaded_irq(soc_dts_thres_irq, NULL,
soc_irq_thread_fn,
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
"soc_dts", soc_dts);
if (err) {
pr_err("request_threaded_irq ret %d\n", err);
goto err_free;
if (soc_dts_thres_irq) {
err = request_threaded_irq(soc_dts_thres_irq, NULL,
soc_irq_thread_fn,
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
"soc_dts", soc_dts);
if (err) {
pr_err("request_threaded_irq ret %d\n", err);
goto err_free;
}
}
for (i = 0; i < SOC_MAX_DTS_SENSORS; ++i) {
@ -451,7 +463,8 @@ static int __init intel_soc_thermal_init(void)
err_trip_temp:
i = SOC_MAX_DTS_SENSORS;
free_irq(soc_dts_thres_irq, soc_dts);
if (soc_dts_thres_irq)
free_irq(soc_dts_thres_irq, soc_dts);
err_free:
while (--i >= 0)
free_soc_dts(soc_dts[i]);
@ -466,7 +479,8 @@ static void __exit intel_soc_thermal_exit(void)
for (i = 0; i < SOC_MAX_DTS_SENSORS; ++i)
update_trip_temp(soc_dts[i], 0, 0);
free_irq(soc_dts_thres_irq, soc_dts);
if (soc_dts_thres_irq)
free_irq(soc_dts_thres_irq, soc_dts);
for (i = 0; i < SOC_MAX_DTS_SENSORS; ++i)
free_soc_dts(soc_dts[i]);

View file

@ -45,7 +45,7 @@
* c. if the trend is THERMAL_TREND_RAISE_FULL, do nothing
* d. if the trend is THERMAL_TREND_DROP_FULL, use lower limit,
* if the cooling state already equals lower limit,
* deactive the thermal instance
* deactivate the thermal instance
*/
static unsigned long get_target_state(struct thermal_instance *instance,
enum thermal_trend trend, bool throttle)
@ -169,7 +169,7 @@ static void thermal_zone_trip_update(struct thermal_zone_device *tz, int trip)
}
/**
* step_wise_throttle - throttles devices asscciated with the given zone
* step_wise_throttle - throttles devices associated with the given zone
* @tz - thermal_zone_device
* @trip - the trip point
* @trip_type - type of the trip point