mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-29 03:01:39 -04:00
Merge branches 'intel-dts-soc-thermal' and 'int340x-enhancement' of .git into next
This commit is contained in:
commit
8cb68501e1
10 changed files with 727 additions and 484 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
276
drivers/thermal/int340x_thermal/int340x_thermal_zone.c
Normal file
276
drivers/thermal/int340x_thermal/int340x_thermal_zone.c
Normal 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");
|
||||
68
drivers/thermal/int340x_thermal/int340x_thermal_zone.h
Normal file
68
drivers/thermal/int340x_thermal/int340x_thermal_zone.h
Normal 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
|
||||
|
|
@ -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);
|
||||
|
|
@ -187,6 +276,7 @@ free_buffer:
|
|||
|
||||
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);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue