mirror of
https://github.com/torvalds/linux.git
synced 2024-11-23 12:42:02 +00:00
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:
commit
89d3fa45b4
@ -55,6 +55,7 @@ acpi-$(CONFIG_ACPI_PROCFS_POWER) += cm_sbs.o
|
||||
ifdef CONFIG_ACPI_VIDEO
|
||||
acpi-y += video_detect.o
|
||||
endif
|
||||
acpi-y += acpi_lpat.o
|
||||
|
||||
# These are (potentially) separate modules
|
||||
|
||||
|
161
drivers/acpi/acpi_lpat.c
Normal file
161
drivers/acpi/acpi_lpat.c
Normal file
@ -0,0 +1,161 @@
|
||||
/*
|
||||
* acpi_lpat.c - LPAT table processing functions
|
||||
*
|
||||
* Copyright (C) 2015 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version
|
||||
* 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that 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/module.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <acpi/acpi_lpat.h>
|
||||
|
||||
/**
|
||||
* acpi_lpat_raw_to_temp(): Return temperature from raw value through
|
||||
* LPAT conversion table
|
||||
*
|
||||
* @lpat_table: the temperature_raw mapping table structure
|
||||
* @raw: the raw value, used as a key to get the temerature from the
|
||||
* above mapping table
|
||||
*
|
||||
* A positive converted temperarure value will be returned on success,
|
||||
* a negative errno will be returned in error cases.
|
||||
*/
|
||||
int acpi_lpat_raw_to_temp(struct acpi_lpat_conversion_table *lpat_table,
|
||||
int raw)
|
||||
{
|
||||
int i, delta_temp, delta_raw, temp;
|
||||
struct acpi_lpat *lpat = lpat_table->lpat;
|
||||
|
||||
for (i = 0; i < lpat_table->lpat_count - 1; i++) {
|
||||
if ((raw >= lpat[i].raw && raw <= lpat[i+1].raw) ||
|
||||
(raw <= lpat[i].raw && raw >= lpat[i+1].raw))
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == lpat_table->lpat_count - 1)
|
||||
return -ENOENT;
|
||||
|
||||
delta_temp = lpat[i+1].temp - lpat[i].temp;
|
||||
delta_raw = lpat[i+1].raw - lpat[i].raw;
|
||||
temp = lpat[i].temp + (raw - lpat[i].raw) * delta_temp / delta_raw;
|
||||
|
||||
return temp;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(acpi_lpat_raw_to_temp);
|
||||
|
||||
/**
|
||||
* acpi_lpat_temp_to_raw(): Return raw value from temperature through
|
||||
* LPAT conversion table
|
||||
*
|
||||
* @lpat: the temperature_raw mapping table
|
||||
* @temp: the temperature, used as a key to get the raw value from the
|
||||
* above mapping table
|
||||
*
|
||||
* A positive converted temperature value will be returned on success,
|
||||
* a negative errno will be returned in error cases.
|
||||
*/
|
||||
int acpi_lpat_temp_to_raw(struct acpi_lpat_conversion_table *lpat_table,
|
||||
int temp)
|
||||
{
|
||||
int i, delta_temp, delta_raw, raw;
|
||||
struct acpi_lpat *lpat = lpat_table->lpat;
|
||||
|
||||
for (i = 0; i < lpat_table->lpat_count - 1; i++) {
|
||||
if (temp >= lpat[i].temp && temp <= lpat[i+1].temp)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == lpat_table->lpat_count - 1)
|
||||
return -ENOENT;
|
||||
|
||||
delta_temp = lpat[i+1].temp - lpat[i].temp;
|
||||
delta_raw = lpat[i+1].raw - lpat[i].raw;
|
||||
raw = lpat[i].raw + (temp - lpat[i].temp) * delta_raw / delta_temp;
|
||||
|
||||
return raw;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(acpi_lpat_temp_to_raw);
|
||||
|
||||
/**
|
||||
* acpi_lpat_get_conversion_table(): Parse ACPI LPAT table if present.
|
||||
*
|
||||
* @handle: Handle to acpi device
|
||||
*
|
||||
* Parse LPAT table to a struct of type acpi_lpat_table. On success
|
||||
* it returns a pointer to newly allocated table. This table must
|
||||
* be freed by the caller when finished processing, using a call to
|
||||
* acpi_lpat_free_conversion_table.
|
||||
*/
|
||||
struct acpi_lpat_conversion_table *acpi_lpat_get_conversion_table(acpi_handle
|
||||
handle)
|
||||
{
|
||||
struct acpi_lpat_conversion_table *lpat_table = NULL;
|
||||
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
union acpi_object *obj_p, *obj_e;
|
||||
int *lpat, i;
|
||||
acpi_status status;
|
||||
|
||||
status = acpi_evaluate_object(handle, "LPAT", NULL, &buffer);
|
||||
if (ACPI_FAILURE(status))
|
||||
return NULL;
|
||||
|
||||
obj_p = (union acpi_object *)buffer.pointer;
|
||||
if (!obj_p || (obj_p->type != ACPI_TYPE_PACKAGE) ||
|
||||
(obj_p->package.count % 2) || (obj_p->package.count < 4))
|
||||
goto out;
|
||||
|
||||
lpat = kcalloc(obj_p->package.count, sizeof(int), GFP_KERNEL);
|
||||
if (!lpat)
|
||||
goto out;
|
||||
|
||||
for (i = 0; i < obj_p->package.count; i++) {
|
||||
obj_e = &obj_p->package.elements[i];
|
||||
if (obj_e->type != ACPI_TYPE_INTEGER) {
|
||||
kfree(lpat);
|
||||
goto out;
|
||||
}
|
||||
lpat[i] = (s64)obj_e->integer.value;
|
||||
}
|
||||
|
||||
lpat_table = kzalloc(sizeof(*lpat_table), GFP_KERNEL);
|
||||
if (!lpat_table) {
|
||||
kfree(lpat);
|
||||
goto out;
|
||||
}
|
||||
|
||||
lpat_table->lpat = (struct acpi_lpat *)lpat;
|
||||
lpat_table->lpat_count = obj_p->package.count / 2;
|
||||
|
||||
out:
|
||||
kfree(buffer.pointer);
|
||||
return lpat_table;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(acpi_lpat_get_conversion_table);
|
||||
|
||||
/**
|
||||
* acpi_lpat_free_conversion_table(): Free LPAT table.
|
||||
*
|
||||
* @lpat_table: the temperature_raw mapping table structure
|
||||
*
|
||||
* Frees the LPAT table previously allocated by a call to
|
||||
* acpi_lpat_get_conversion_table.
|
||||
*/
|
||||
void acpi_lpat_free_conversion_table(struct acpi_lpat_conversion_table
|
||||
*lpat_table)
|
||||
{
|
||||
if (lpat_table) {
|
||||
kfree(lpat_table->lpat);
|
||||
kfree(lpat_table);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(acpi_lpat_free_conversion_table);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
@ -16,20 +16,15 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <acpi/acpi_lpat.h>
|
||||
#include "intel_pmic.h"
|
||||
|
||||
#define PMIC_POWER_OPREGION_ID 0x8d
|
||||
#define PMIC_THERMAL_OPREGION_ID 0x8c
|
||||
|
||||
struct acpi_lpat {
|
||||
int temp;
|
||||
int raw;
|
||||
};
|
||||
|
||||
struct intel_pmic_opregion {
|
||||
struct mutex lock;
|
||||
struct acpi_lpat *lpat;
|
||||
int lpat_count;
|
||||
struct acpi_lpat_conversion_table *lpat_table;
|
||||
struct regmap *regmap;
|
||||
struct intel_pmic_opregion_data *data;
|
||||
};
|
||||
@ -50,105 +45,6 @@ static int pmic_get_reg_bit(int address, struct pmic_table *table,
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
/**
|
||||
* raw_to_temp(): Return temperature from raw value through LPAT table
|
||||
*
|
||||
* @lpat: the temperature_raw mapping table
|
||||
* @count: the count of the above mapping table
|
||||
* @raw: the raw value, used as a key to get the temerature from the
|
||||
* above mapping table
|
||||
*
|
||||
* A positive value will be returned on success, a negative errno will
|
||||
* be returned in error cases.
|
||||
*/
|
||||
static int raw_to_temp(struct acpi_lpat *lpat, int count, int raw)
|
||||
{
|
||||
int i, delta_temp, delta_raw, temp;
|
||||
|
||||
for (i = 0; i < count - 1; i++) {
|
||||
if ((raw >= lpat[i].raw && raw <= lpat[i+1].raw) ||
|
||||
(raw <= lpat[i].raw && raw >= lpat[i+1].raw))
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == count - 1)
|
||||
return -ENOENT;
|
||||
|
||||
delta_temp = lpat[i+1].temp - lpat[i].temp;
|
||||
delta_raw = lpat[i+1].raw - lpat[i].raw;
|
||||
temp = lpat[i].temp + (raw - lpat[i].raw) * delta_temp / delta_raw;
|
||||
|
||||
return temp;
|
||||
}
|
||||
|
||||
/**
|
||||
* temp_to_raw(): Return raw value from temperature through LPAT table
|
||||
*
|
||||
* @lpat: the temperature_raw mapping table
|
||||
* @count: the count of the above mapping table
|
||||
* @temp: the temperature, used as a key to get the raw value from the
|
||||
* above mapping table
|
||||
*
|
||||
* A positive value will be returned on success, a negative errno will
|
||||
* be returned in error cases.
|
||||
*/
|
||||
static int temp_to_raw(struct acpi_lpat *lpat, int count, int temp)
|
||||
{
|
||||
int i, delta_temp, delta_raw, raw;
|
||||
|
||||
for (i = 0; i < count - 1; i++) {
|
||||
if (temp >= lpat[i].temp && temp <= lpat[i+1].temp)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == count - 1)
|
||||
return -ENOENT;
|
||||
|
||||
delta_temp = lpat[i+1].temp - lpat[i].temp;
|
||||
delta_raw = lpat[i+1].raw - lpat[i].raw;
|
||||
raw = lpat[i].raw + (temp - lpat[i].temp) * delta_raw / delta_temp;
|
||||
|
||||
return raw;
|
||||
}
|
||||
|
||||
static void pmic_thermal_lpat(struct intel_pmic_opregion *opregion,
|
||||
acpi_handle handle, struct device *dev)
|
||||
{
|
||||
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
union acpi_object *obj_p, *obj_e;
|
||||
int *lpat, i;
|
||||
acpi_status status;
|
||||
|
||||
status = acpi_evaluate_object(handle, "LPAT", NULL, &buffer);
|
||||
if (ACPI_FAILURE(status))
|
||||
return;
|
||||
|
||||
obj_p = (union acpi_object *)buffer.pointer;
|
||||
if (!obj_p || (obj_p->type != ACPI_TYPE_PACKAGE) ||
|
||||
(obj_p->package.count % 2) || (obj_p->package.count < 4))
|
||||
goto out;
|
||||
|
||||
lpat = devm_kmalloc(dev, sizeof(int) * obj_p->package.count,
|
||||
GFP_KERNEL);
|
||||
if (!lpat)
|
||||
goto out;
|
||||
|
||||
for (i = 0; i < obj_p->package.count; i++) {
|
||||
obj_e = &obj_p->package.elements[i];
|
||||
if (obj_e->type != ACPI_TYPE_INTEGER) {
|
||||
devm_kfree(dev, lpat);
|
||||
goto out;
|
||||
}
|
||||
lpat[i] = (s64)obj_e->integer.value;
|
||||
}
|
||||
|
||||
opregion->lpat = (struct acpi_lpat *)lpat;
|
||||
opregion->lpat_count = obj_p->package.count / 2;
|
||||
|
||||
out:
|
||||
kfree(buffer.pointer);
|
||||
}
|
||||
|
||||
static acpi_status intel_pmic_power_handler(u32 function,
|
||||
acpi_physical_address address, u32 bits, u64 *value64,
|
||||
void *handler_context, void *region_context)
|
||||
@ -192,12 +88,12 @@ static int pmic_read_temp(struct intel_pmic_opregion *opregion,
|
||||
if (raw_temp < 0)
|
||||
return raw_temp;
|
||||
|
||||
if (!opregion->lpat) {
|
||||
if (!opregion->lpat_table) {
|
||||
*value = raw_temp;
|
||||
return 0;
|
||||
}
|
||||
|
||||
temp = raw_to_temp(opregion->lpat, opregion->lpat_count, raw_temp);
|
||||
temp = acpi_lpat_raw_to_temp(opregion->lpat_table, raw_temp);
|
||||
if (temp < 0)
|
||||
return temp;
|
||||
|
||||
@ -223,9 +119,8 @@ static int pmic_thermal_aux(struct intel_pmic_opregion *opregion, int reg,
|
||||
if (!opregion->data->update_aux)
|
||||
return -ENXIO;
|
||||
|
||||
if (opregion->lpat) {
|
||||
raw_temp = temp_to_raw(opregion->lpat, opregion->lpat_count,
|
||||
*value);
|
||||
if (opregion->lpat_table) {
|
||||
raw_temp = acpi_lpat_temp_to_raw(opregion->lpat_table, *value);
|
||||
if (raw_temp < 0)
|
||||
return raw_temp;
|
||||
} else {
|
||||
@ -314,6 +209,7 @@ int intel_pmic_install_opregion_handler(struct device *dev, acpi_handle handle,
|
||||
{
|
||||
acpi_status status;
|
||||
struct intel_pmic_opregion *opregion;
|
||||
int ret;
|
||||
|
||||
if (!dev || !regmap || !d)
|
||||
return -EINVAL;
|
||||
@ -327,14 +223,16 @@ int intel_pmic_install_opregion_handler(struct device *dev, acpi_handle handle,
|
||||
|
||||
mutex_init(&opregion->lock);
|
||||
opregion->regmap = regmap;
|
||||
pmic_thermal_lpat(opregion, handle, dev);
|
||||
opregion->lpat_table = acpi_lpat_get_conversion_table(handle);
|
||||
|
||||
status = acpi_install_address_space_handler(handle,
|
||||
PMIC_POWER_OPREGION_ID,
|
||||
intel_pmic_power_handler,
|
||||
NULL, opregion);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -ENODEV;
|
||||
if (ACPI_FAILURE(status)) {
|
||||
ret = -ENODEV;
|
||||
goto out_error;
|
||||
}
|
||||
|
||||
status = acpi_install_address_space_handler(handle,
|
||||
PMIC_THERMAL_OPREGION_ID,
|
||||
@ -343,11 +241,16 @@ int intel_pmic_install_opregion_handler(struct device *dev, acpi_handle handle,
|
||||
if (ACPI_FAILURE(status)) {
|
||||
acpi_remove_address_space_handler(handle, PMIC_POWER_OPREGION_ID,
|
||||
intel_pmic_power_handler);
|
||||
return -ENODEV;
|
||||
ret = -ENODEV;
|
||||
goto out_error;
|
||||
}
|
||||
|
||||
opregion->data = d;
|
||||
return 0;
|
||||
|
||||
out_error:
|
||||
acpi_lpat_free_conversion_table(opregion->lpat_table);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(intel_pmic_install_opregion_handler);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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 <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");
|
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);
|
||||
@ -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);
|
||||
}
|
||||
|
@ -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]);
|
||||
|
@ -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
|
||||
|
65
include/acpi/acpi_lpat.h
Normal file
65
include/acpi/acpi_lpat.h
Normal file
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* acpi_lpat.h - LPAT table processing functions
|
||||
*
|
||||
* Copyright (C) 2015 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version
|
||||
* 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that 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 ACPI_LPAT_H
|
||||
#define ACPI_LPAT_H
|
||||
|
||||
struct acpi_lpat {
|
||||
int temp;
|
||||
int raw;
|
||||
};
|
||||
|
||||
struct acpi_lpat_conversion_table {
|
||||
struct acpi_lpat *lpat;
|
||||
int lpat_count;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
|
||||
int acpi_lpat_raw_to_temp(struct acpi_lpat_conversion_table *lpat_table,
|
||||
int raw);
|
||||
int acpi_lpat_temp_to_raw(struct acpi_lpat_conversion_table *lpat_table,
|
||||
int temp);
|
||||
struct acpi_lpat_conversion_table *acpi_lpat_get_conversion_table(acpi_handle
|
||||
handle);
|
||||
void acpi_lpat_free_conversion_table(struct acpi_lpat_conversion_table
|
||||
*lpat_table);
|
||||
|
||||
#else
|
||||
static int acpi_lpat_raw_to_temp(struct acpi_lpat_conversion_table *lpat_table,
|
||||
int raw)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acpi_lpat_temp_to_raw(struct acpi_lpat_conversion_table *lpat_table,
|
||||
int temp)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct acpi_lpat_conversion_table *acpi_lpat_get_conversion_table(
|
||||
acpi_handle handle)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void acpi_lpat_free_conversion_table(struct acpi_lpat_conversion_table
|
||||
*lpat_table)
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user