summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorZhang Rui <rui.zhang@intel.com>2015-01-30 16:16:25 +0800
committerZhang Rui <rui.zhang@intel.com>2015-01-30 16:16:25 +0800
commit8cb68501e1e9c2093e0997eaf8dd45a8bceeefc0 (patch)
tree0cf883c0ef19facc11413276f5bf6c7c5093d53d /drivers
parent6c355fafeb2cde3fa6bf317777ae3018b7f254e6 (diff)
parent317d9dda0522c18eb230367c88601c2e46cda4f8 (diff)
downloadlinux-8cb68501e1e9c2093e0997eaf8dd45a8bceeefc0.tar.gz
linux-8cb68501e1e9c2093e0997eaf8dd45a8bceeefc0.tar.xz
Merge branches 'intel-dts-soc-thermal' and 'int340x-enhancement' of .git into next
Diffstat (limited to 'drivers')
-rw-r--r--drivers/acpi/Makefile1
-rw-r--r--drivers/acpi/acpi_lpat.c161
-rw-r--r--drivers/acpi/pmic/intel_pmic.c133
-rw-r--r--drivers/thermal/int340x_thermal/Makefile1
-rw-r--r--drivers/thermal/int340x_thermal/int3402_thermal.c208
-rw-r--r--drivers/thermal/int340x_thermal/int3403_thermal.c208
-rw-r--r--drivers/thermal/int340x_thermal/int340x_thermal_zone.c276
-rw-r--r--drivers/thermal/int340x_thermal/int340x_thermal_zone.h68
-rw-r--r--drivers/thermal/int340x_thermal/processor_thermal_device.c90
9 files changed, 662 insertions, 484 deletions
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index f74317cc1ca9..d22253d5b9f8 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -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
diff --git a/drivers/acpi/acpi_lpat.c b/drivers/acpi/acpi_lpat.c
new file mode 100644
index 000000000000..feb61c1630eb
--- /dev/null
+++ b/drivers/acpi/acpi_lpat.c
@@ -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");
diff --git a/drivers/acpi/pmic/intel_pmic.c b/drivers/acpi/pmic/intel_pmic.c
index a732e5d7e322..bd772cd56494 100644
--- a/drivers/acpi/pmic/intel_pmic.c
+++ b/drivers/acpi/pmic/intel_pmic.c
@@ -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);
diff --git a/drivers/thermal/int340x_thermal/Makefile b/drivers/thermal/int340x_thermal/Makefile
index d4413698a85f..ba77a34f659f 100644
--- a/drivers/thermal/int340x_thermal/Makefile
+++ b/drivers/thermal/int340x_thermal/Makefile
@@ -1,4 +1,5 @@
obj-$(CONFIG_INT340X_THERMAL) += int3400_thermal.o
+obj-$(CONFIG_INT340X_THERMAL) += int340x_thermal_zone.o
obj-$(CONFIG_INT340X_THERMAL) += int3402_thermal.o
obj-$(CONFIG_INT340X_THERMAL) += int3403_thermal.o
obj-$(CONFIG_INT340X_THERMAL) += processor_thermal_device.o
diff --git a/drivers/thermal/int340x_thermal/int3402_thermal.c b/drivers/thermal/int340x_thermal/int3402_thermal.c
index c5cbc3af3a05..69df3d960303 100644
--- a/drivers/thermal/int340x_thermal/int3402_thermal.c
+++ b/drivers/thermal/int340x_thermal/int3402_thermal.c
@@ -14,152 +14,39 @@
#include <linux/platform_device.h>
#include <linux/acpi.h>
#include <linux/thermal.h>
+#include "int340x_thermal_zone.h"
-#define ACPI_ACTIVE_COOLING_MAX_NR 10
-
-struct active_trip {
- unsigned long temp;
- int id;
- bool valid;
-};
+#define INT3402_PERF_CHANGED_EVENT 0x80
+#define INT3402_THERMAL_EVENT 0x90
struct int3402_thermal_data {
- unsigned long *aux_trips;
- int aux_trip_nr;
- unsigned long psv_temp;
- int psv_trip_id;
- unsigned long crt_temp;
- int crt_trip_id;
- unsigned long hot_temp;
- int hot_trip_id;
- struct active_trip act_trips[ACPI_ACTIVE_COOLING_MAX_NR];
acpi_handle *handle;
+ struct int34x_thermal_zone *int340x_zone;
};
-static int int3402_thermal_get_zone_temp(struct thermal_zone_device *zone,
- unsigned long *temp)
-{
- struct int3402_thermal_data *d = zone->devdata;
- unsigned long long tmp;
- acpi_status status;
-
- status = acpi_evaluate_integer(d->handle, "_TMP", NULL, &tmp);
- if (ACPI_FAILURE(status))
- return -ENODEV;
-
- /* _TMP returns the temperature in tenths of degrees Kelvin */
- *temp = DECI_KELVIN_TO_MILLICELSIUS(tmp);
-
- return 0;
-}
-
-static int int3402_thermal_get_trip_temp(struct thermal_zone_device *zone,
- int trip, unsigned long *temp)
+static void int3402_notify(acpi_handle handle, u32 event, void *data)
{
- struct int3402_thermal_data *d = zone->devdata;
- int i;
-
- if (trip < d->aux_trip_nr)
- *temp = d->aux_trips[trip];
- else if (trip == d->crt_trip_id)
- *temp = d->crt_temp;
- else if (trip == d->psv_trip_id)
- *temp = d->psv_temp;
- else if (trip == d->hot_trip_id)
- *temp = d->hot_temp;
- else {
- for (i = 0; i < ACPI_ACTIVE_COOLING_MAX_NR; i++) {
- if (d->act_trips[i].valid &&
- d->act_trips[i].id == trip) {
- *temp = d->act_trips[i].temp;
- break;
- }
- }
- if (i == ACPI_ACTIVE_COOLING_MAX_NR)
- return -EINVAL;
+ struct int3402_thermal_data *priv = data;
+
+ if (!priv)
+ return;
+
+ switch (event) {
+ case INT3402_PERF_CHANGED_EVENT:
+ break;
+ case INT3402_THERMAL_EVENT:
+ int340x_thermal_zone_device_update(priv->int340x_zone);
+ break;
+ default:
+ break;
}
- return 0;
-}
-
-static int int3402_thermal_get_trip_type(struct thermal_zone_device *zone,
- int trip, enum thermal_trip_type *type)
-{
- struct int3402_thermal_data *d = zone->devdata;
- int i;
-
- if (trip < d->aux_trip_nr)
- *type = THERMAL_TRIP_PASSIVE;
- else if (trip == d->crt_trip_id)
- *type = THERMAL_TRIP_CRITICAL;
- else if (trip == d->hot_trip_id)
- *type = THERMAL_TRIP_HOT;
- else if (trip == d->psv_trip_id)
- *type = THERMAL_TRIP_PASSIVE;
- else {
- for (i = 0; i < ACPI_ACTIVE_COOLING_MAX_NR; i++) {
- if (d->act_trips[i].valid &&
- d->act_trips[i].id == trip) {
- *type = THERMAL_TRIP_ACTIVE;
- break;
- }
- }
- if (i == ACPI_ACTIVE_COOLING_MAX_NR)
- return -EINVAL;
- }
- return 0;
-}
-
-static int int3402_thermal_set_trip_temp(struct thermal_zone_device *zone, int trip,
- unsigned long temp)
-{
- struct int3402_thermal_data *d = zone->devdata;
- acpi_status status;
- char name[10];
-
- snprintf(name, sizeof(name), "PAT%d", trip);
- status = acpi_execute_simple_method(d->handle, name,
- MILLICELSIUS_TO_DECI_KELVIN(temp));
- if (ACPI_FAILURE(status))
- return -EIO;
-
- d->aux_trips[trip] = temp;
- return 0;
-}
-
-static struct thermal_zone_device_ops int3402_thermal_zone_ops = {
- .get_temp = int3402_thermal_get_zone_temp,
- .get_trip_temp = int3402_thermal_get_trip_temp,
- .get_trip_type = int3402_thermal_get_trip_type,
- .set_trip_temp = int3402_thermal_set_trip_temp,
-};
-
-static struct thermal_zone_params int3402_thermal_params = {
- .governor_name = "user_space",
- .no_hwmon = true,
-};
-
-static int int3402_thermal_get_temp(acpi_handle handle, char *name,
- unsigned long *temp)
-{
- unsigned long long r;
- acpi_status status;
-
- status = acpi_evaluate_integer(handle, name, NULL, &r);
- if (ACPI_FAILURE(status))
- return -EIO;
-
- *temp = DECI_KELVIN_TO_MILLICELSIUS(r);
- return 0;
}
static int int3402_thermal_probe(struct platform_device *pdev)
{
struct acpi_device *adev = ACPI_COMPANION(&pdev->dev);
struct int3402_thermal_data *d;
- struct thermal_zone_device *zone;
- acpi_status status;
- unsigned long long trip_cnt;
- int trip_mask = 0, i;
+ int ret;
if (!acpi_has_method(adev->handle, "_TMP"))
return -ENODEV;
@@ -168,54 +55,33 @@ static int int3402_thermal_probe(struct platform_device *pdev)
if (!d)
return -ENOMEM;
- status = acpi_evaluate_integer(adev->handle, "PATC", NULL, &trip_cnt);
- if (ACPI_FAILURE(status))
- trip_cnt = 0;
- else {
- d->aux_trips = devm_kzalloc(&pdev->dev,
- sizeof(*d->aux_trips) * trip_cnt, GFP_KERNEL);
- if (!d->aux_trips)
- return -ENOMEM;
- trip_mask = trip_cnt - 1;
- d->handle = adev->handle;
- d->aux_trip_nr = trip_cnt;
- }
-
- d->crt_trip_id = -1;
- if (!int3402_thermal_get_temp(adev->handle, "_CRT", &d->crt_temp))
- d->crt_trip_id = trip_cnt++;
- d->hot_trip_id = -1;
- if (!int3402_thermal_get_temp(adev->handle, "_HOT", &d->hot_temp))
- d->hot_trip_id = trip_cnt++;
- d->psv_trip_id = -1;
- if (!int3402_thermal_get_temp(adev->handle, "_PSV", &d->psv_temp))
- d->psv_trip_id = trip_cnt++;
- for (i = 0; i < ACPI_ACTIVE_COOLING_MAX_NR; i++) {
- char name[5] = { '_', 'A', 'C', '0' + i, '\0' };
- if (int3402_thermal_get_temp(adev->handle, name,
- &d->act_trips[i].temp))
- break;
- d->act_trips[i].id = trip_cnt++;
- d->act_trips[i].valid = true;
+ d->int340x_zone = int340x_thermal_zone_add(adev, NULL);
+ if (IS_ERR(d->int340x_zone))
+ return PTR_ERR(d->int340x_zone);
+
+ ret = acpi_install_notify_handler(adev->handle,
+ ACPI_DEVICE_NOTIFY,
+ int3402_notify,
+ d);
+ if (ret) {
+ int340x_thermal_zone_remove(d->int340x_zone);
+ return ret;
}
- zone = thermal_zone_device_register(acpi_device_bid(adev), trip_cnt,
- trip_mask, d,
- &int3402_thermal_zone_ops,
- &int3402_thermal_params,
- 0, 0);
- if (IS_ERR(zone))
- return PTR_ERR(zone);
- platform_set_drvdata(pdev, zone);
+ d->handle = adev->handle;
+ platform_set_drvdata(pdev, d);
return 0;
}
static int int3402_thermal_remove(struct platform_device *pdev)
{
- struct thermal_zone_device *zone = platform_get_drvdata(pdev);
+ struct int3402_thermal_data *d = platform_get_drvdata(pdev);
+
+ acpi_remove_notify_handler(d->handle,
+ ACPI_DEVICE_NOTIFY, int3402_notify);
+ int340x_thermal_zone_remove(d->int340x_zone);
- thermal_zone_device_unregister(zone);
return 0;
}
diff --git a/drivers/thermal/int340x_thermal/int3403_thermal.c b/drivers/thermal/int340x_thermal/int3403_thermal.c
index 0faf500d8a77..50a7a08e3a15 100644
--- a/drivers/thermal/int340x_thermal/int3403_thermal.c
+++ b/drivers/thermal/int340x_thermal/int3403_thermal.c
@@ -19,6 +19,7 @@
#include <linux/acpi.h>
#include <linux/thermal.h>
#include <linux/platform_device.h>
+#include "int340x_thermal_zone.h"
#define INT3403_TYPE_SENSOR 0x03
#define INT3403_TYPE_CHARGER 0x0B
@@ -26,18 +27,9 @@
#define INT3403_PERF_CHANGED_EVENT 0x80
#define INT3403_THERMAL_EVENT 0x90
-#define DECI_KELVIN_TO_MILLI_CELSIUS(t, off) (((t) - (off)) * 100)
-#define KELVIN_OFFSET 2732
-#define MILLI_CELSIUS_TO_DECI_KELVIN(t, off) (((t) / 100) + (off))
-
+/* Preserved structure for future expandbility */
struct int3403_sensor {
- struct thermal_zone_device *tzone;
- unsigned long *thresholds;
- unsigned long crit_temp;
- int crit_trip_id;
- unsigned long psv_temp;
- int psv_trip_id;
-
+ struct int34x_thermal_zone *int340x_zone;
};
struct int3403_performance_state {
@@ -63,126 +55,6 @@ struct int3403_priv {
void *priv;
};
-static int sys_get_curr_temp(struct thermal_zone_device *tzone,
- unsigned long *temp)
-{
- struct int3403_priv *priv = tzone->devdata;
- struct acpi_device *device = priv->adev;
- unsigned long long tmp;
- acpi_status status;
-
- status = acpi_evaluate_integer(device->handle, "_TMP", NULL, &tmp);
- if (ACPI_FAILURE(status))
- return -EIO;
-
- *temp = DECI_KELVIN_TO_MILLI_CELSIUS(tmp, KELVIN_OFFSET);
-
- return 0;
-}
-
-static int sys_get_trip_hyst(struct thermal_zone_device *tzone,
- int trip, unsigned long *temp)
-{
- struct int3403_priv *priv = tzone->devdata;
- struct acpi_device *device = priv->adev;
- unsigned long long hyst;
- acpi_status status;
-
- status = acpi_evaluate_integer(device->handle, "GTSH", NULL, &hyst);
- if (ACPI_FAILURE(status))
- return -EIO;
-
- /*
- * Thermal hysteresis represents a temperature difference.
- * Kelvin and Celsius have same degree size. So the
- * conversion here between tenths of degree Kelvin unit
- * and Milli-Celsius unit is just to multiply 100.
- */
- *temp = hyst * 100;
-
- return 0;
-}
-
-static int sys_get_trip_temp(struct thermal_zone_device *tzone,
- int trip, unsigned long *temp)
-{
- struct int3403_priv *priv = tzone->devdata;
- struct int3403_sensor *obj = priv->priv;
-
- if (priv->type != INT3403_TYPE_SENSOR || !obj)
- return -EINVAL;
-
- if (trip == obj->crit_trip_id)
- *temp = obj->crit_temp;
- else if (trip == obj->psv_trip_id)
- *temp = obj->psv_temp;
- else {
- /*
- * get_trip_temp is a mandatory callback but
- * PATx method doesn't return any value, so return
- * cached value, which was last set from user space
- */
- *temp = obj->thresholds[trip];
- }
-
- return 0;
-}
-
-static int sys_get_trip_type(struct thermal_zone_device *thermal,
- int trip, enum thermal_trip_type *type)
-{
- struct int3403_priv *priv = thermal->devdata;
- struct int3403_sensor *obj = priv->priv;
-
- /* Mandatory callback, may not mean much here */
- if (trip == obj->crit_trip_id)
- *type = THERMAL_TRIP_CRITICAL;
- else
- *type = THERMAL_TRIP_PASSIVE;
-
- return 0;
-}
-
-int sys_set_trip_temp(struct thermal_zone_device *tzone, int trip,
- unsigned long temp)
-{
- struct int3403_priv *priv = tzone->devdata;
- struct acpi_device *device = priv->adev;
- struct int3403_sensor *obj = priv->priv;
- acpi_status status;
- char name[10];
- int ret = 0;
-
- snprintf(name, sizeof(name), "PAT%d", trip);
- if (acpi_has_method(device->handle, name)) {
- status = acpi_execute_simple_method(device->handle, name,
- MILLI_CELSIUS_TO_DECI_KELVIN(temp,
- KELVIN_OFFSET));
- if (ACPI_FAILURE(status))
- ret = -EIO;
- else
- obj->thresholds[trip] = temp;
- } else {
- ret = -EIO;
- dev_err(&device->dev, "sys_set_trip_temp: method not found\n");
- }
-
- return ret;
-}
-
-static struct thermal_zone_device_ops tzone_ops = {
- .get_temp = sys_get_curr_temp,
- .get_trip_temp = sys_get_trip_temp,
- .get_trip_type = sys_get_trip_type,
- .set_trip_temp = sys_set_trip_temp,
- .get_trip_hyst = sys_get_trip_hyst,
-};
-
-static struct thermal_zone_params int3403_thermal_params = {
- .governor_name = "user_space",
- .no_hwmon = true,
-};
-
static void int3403_notify(acpi_handle handle,
u32 event, void *data)
{
@@ -200,7 +72,7 @@ static void int3403_notify(acpi_handle handle,
case INT3403_PERF_CHANGED_EVENT:
break;
case INT3403_THERMAL_EVENT:
- thermal_zone_device_update(obj->tzone);
+ int340x_thermal_zone_device_update(obj->int340x_zone);
break;
default:
dev_err(&priv->pdev->dev, "Unsupported event [0x%x]\n", event);
@@ -208,41 +80,10 @@ static void int3403_notify(acpi_handle handle,
}
}
-static int sys_get_trip_crt(struct acpi_device *device, unsigned long *temp)
-{
- unsigned long long crt;
- acpi_status status;
-
- status = acpi_evaluate_integer(device->handle, "_CRT", NULL, &crt);
- if (ACPI_FAILURE(status))
- return -EIO;
-
- *temp = DECI_KELVIN_TO_MILLI_CELSIUS(crt, KELVIN_OFFSET);
-
- return 0;
-}
-
-static int sys_get_trip_psv(struct acpi_device *device, unsigned long *temp)
-{
- unsigned long long psv;
- acpi_status status;
-
- status = acpi_evaluate_integer(device->handle, "_PSV", NULL, &psv);
- if (ACPI_FAILURE(status))
- return -EIO;
-
- *temp = DECI_KELVIN_TO_MILLI_CELSIUS(psv, KELVIN_OFFSET);
-
- return 0;
-}
-
static int int3403_sensor_add(struct int3403_priv *priv)
{
int result = 0;
- acpi_status status;
struct int3403_sensor *obj;
- unsigned long long trip_cnt;
- int trip_mask = 0;
obj = devm_kzalloc(&priv->pdev->dev, sizeof(*obj), GFP_KERNEL);
if (!obj)
@@ -250,39 +91,9 @@ static int int3403_sensor_add(struct int3403_priv *priv)
priv->priv = obj;
- status = acpi_evaluate_integer(priv->adev->handle, "PATC", NULL,
- &trip_cnt);
- if (ACPI_FAILURE(status))
- trip_cnt = 0;
-
- if (trip_cnt) {
- /* We have to cache, thresholds can't be readback */
- obj->thresholds = devm_kzalloc(&priv->pdev->dev,
- sizeof(*obj->thresholds) * trip_cnt,
- GFP_KERNEL);
- if (!obj->thresholds) {
- result = -ENOMEM;
- goto err_free_obj;
- }
- trip_mask = BIT(trip_cnt) - 1;
- }
-
- obj->psv_trip_id = -1;
- if (!sys_get_trip_psv(priv->adev, &obj->psv_temp))
- obj->psv_trip_id = trip_cnt++;
-
- obj->crit_trip_id = -1;
- if (!sys_get_trip_crt(priv->adev, &obj->crit_temp))
- obj->crit_trip_id = trip_cnt++;
-
- obj->tzone = thermal_zone_device_register(acpi_device_bid(priv->adev),
- trip_cnt, trip_mask, priv, &tzone_ops,
- &int3403_thermal_params, 0, 0);
- if (IS_ERR(obj->tzone)) {
- result = PTR_ERR(obj->tzone);
- obj->tzone = NULL;
- goto err_free_obj;
- }
+ obj->int340x_zone = int340x_thermal_zone_add(priv->adev, NULL);
+ if (IS_ERR(obj->int340x_zone))
+ return PTR_ERR(obj->int340x_zone);
result = acpi_install_notify_handler(priv->adev->handle,
ACPI_DEVICE_NOTIFY, int3403_notify,
@@ -293,7 +104,7 @@ static int int3403_sensor_add(struct int3403_priv *priv)
return 0;
err_free_obj:
- thermal_zone_device_unregister(obj->tzone);
+ int340x_thermal_zone_remove(obj->int340x_zone);
return result;
}
@@ -303,7 +114,8 @@ static int int3403_sensor_remove(struct int3403_priv *priv)
acpi_remove_notify_handler(priv->adev->handle,
ACPI_DEVICE_NOTIFY, int3403_notify);
- thermal_zone_device_unregister(obj->tzone);
+ int340x_thermal_zone_remove(obj->int340x_zone);
+
return 0;
}
diff --git a/drivers/thermal/int340x_thermal/int340x_thermal_zone.c b/drivers/thermal/int340x_thermal/int340x_thermal_zone.c
new file mode 100644
index 000000000000..f88b08877025
--- /dev/null
+++ b/drivers/thermal/int340x_thermal/int340x_thermal_zone.c
@@ -0,0 +1,276 @@
+/*
+ * int340x_thermal_zone.c
+ * Copyright (c) 2015, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/acpi.h>
+#include <linux/thermal.h>
+#include "int340x_thermal_zone.h"
+
+static int int340x_thermal_get_zone_temp(struct thermal_zone_device *zone,
+ unsigned long *temp)
+{
+ struct int34x_thermal_zone *d = zone->devdata;
+ unsigned long long tmp;
+ acpi_status status;
+
+ if (d->override_ops && d->override_ops->get_temp)
+ return d->override_ops->get_temp(zone, temp);
+
+ status = acpi_evaluate_integer(d->adev->handle, "_TMP", NULL, &tmp);
+ if (ACPI_FAILURE(status))
+ return -EIO;
+
+ if (d->lpat_table) {
+ int conv_temp;
+
+ conv_temp = acpi_lpat_raw_to_temp(d->lpat_table, (int)tmp);
+ if (conv_temp < 0)
+ return conv_temp;
+
+ *temp = (unsigned long)conv_temp * 10;
+ } else
+ /* _TMP returns the temperature in tenths of degrees Kelvin */
+ *temp = DECI_KELVIN_TO_MILLICELSIUS(tmp);
+
+ return 0;
+}
+
+static int int340x_thermal_get_trip_temp(struct thermal_zone_device *zone,
+ int trip, unsigned long *temp)
+{
+ struct int34x_thermal_zone *d = zone->devdata;
+ int i;
+
+ if (d->override_ops && d->override_ops->get_trip_temp)
+ return d->override_ops->get_trip_temp(zone, trip, temp);
+
+ if (trip < d->aux_trip_nr)
+ *temp = d->aux_trips[trip];
+ else if (trip == d->crt_trip_id)
+ *temp = d->crt_temp;
+ else if (trip == d->psv_trip_id)
+ *temp = d->psv_temp;
+ else if (trip == d->hot_trip_id)
+ *temp = d->hot_temp;
+ else {
+ for (i = 0; i < INT340X_THERMAL_MAX_ACT_TRIP_COUNT; i++) {
+ if (d->act_trips[i].valid &&
+ d->act_trips[i].id == trip) {
+ *temp = d->act_trips[i].temp;
+ break;
+ }
+ }
+ if (i == INT340X_THERMAL_MAX_ACT_TRIP_COUNT)
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int int340x_thermal_get_trip_type(struct thermal_zone_device *zone,
+ int trip,
+ enum thermal_trip_type *type)
+{
+ struct int34x_thermal_zone *d = zone->devdata;
+ int i;
+
+ if (d->override_ops && d->override_ops->get_trip_type)
+ return d->override_ops->get_trip_type(zone, trip, type);
+
+ if (trip < d->aux_trip_nr)
+ *type = THERMAL_TRIP_PASSIVE;
+ else if (trip == d->crt_trip_id)
+ *type = THERMAL_TRIP_CRITICAL;
+ else if (trip == d->hot_trip_id)
+ *type = THERMAL_TRIP_HOT;
+ else if (trip == d->psv_trip_id)
+ *type = THERMAL_TRIP_PASSIVE;
+ else {
+ for (i = 0; i < INT340X_THERMAL_MAX_ACT_TRIP_COUNT; i++) {
+ if (d->act_trips[i].valid &&
+ d->act_trips[i].id == trip) {
+ *type = THERMAL_TRIP_ACTIVE;
+ break;
+ }
+ }
+ if (i == INT340X_THERMAL_MAX_ACT_TRIP_COUNT)
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int int340x_thermal_set_trip_temp(struct thermal_zone_device *zone,
+ int trip, unsigned long temp)
+{
+ struct int34x_thermal_zone *d = zone->devdata;
+ acpi_status status;
+ char name[10];
+
+ if (d->override_ops && d->override_ops->set_trip_temp)
+ return d->override_ops->set_trip_temp(zone, trip, temp);
+
+ snprintf(name, sizeof(name), "PAT%d", trip);
+ status = acpi_execute_simple_method(d->adev->handle, name,
+ MILLICELSIUS_TO_DECI_KELVIN(temp));
+ if (ACPI_FAILURE(status))
+ return -EIO;
+
+ d->aux_trips[trip] = temp;
+
+ return 0;
+}
+
+
+static int int340x_thermal_get_trip_hyst(struct thermal_zone_device *zone,
+ int trip, unsigned long *temp)
+{
+ struct int34x_thermal_zone *d = zone->devdata;
+ acpi_status status;
+ unsigned long long hyst;
+
+ if (d->override_ops && d->override_ops->get_trip_hyst)
+ return d->override_ops->get_trip_hyst(zone, trip, temp);
+
+ status = acpi_evaluate_integer(d->adev->handle, "GTSH", NULL, &hyst);
+ if (ACPI_FAILURE(status))
+ return -EIO;
+
+ *temp = hyst * 100;
+
+ return 0;
+}
+
+static struct thermal_zone_device_ops int340x_thermal_zone_ops = {
+ .get_temp = int340x_thermal_get_zone_temp,
+ .get_trip_temp = int340x_thermal_get_trip_temp,
+ .get_trip_type = int340x_thermal_get_trip_type,
+ .set_trip_temp = int340x_thermal_set_trip_temp,
+ .get_trip_hyst = int340x_thermal_get_trip_hyst,
+};
+
+static int int340x_thermal_get_trip_config(acpi_handle handle, char *name,
+ unsigned long *temp)
+{
+ unsigned long long r;
+ acpi_status status;
+
+ status = acpi_evaluate_integer(handle, name, NULL, &r);
+ if (ACPI_FAILURE(status))
+ return -EIO;
+
+ *temp = DECI_KELVIN_TO_MILLICELSIUS(r);
+
+ return 0;
+}
+
+static struct thermal_zone_params int340x_thermal_params = {
+ .governor_name = "user_space",
+ .no_hwmon = true,
+};
+
+struct int34x_thermal_zone *int340x_thermal_zone_add(struct acpi_device *adev,
+ struct thermal_zone_device_ops *override_ops)
+{
+ struct int34x_thermal_zone *int34x_thermal_zone;
+ acpi_status status;
+ unsigned long long trip_cnt;
+ int trip_mask = 0, i;
+ int ret;
+
+ int34x_thermal_zone = kzalloc(sizeof(*int34x_thermal_zone),
+ GFP_KERNEL);
+ if (!int34x_thermal_zone)
+ return ERR_PTR(-ENOMEM);
+
+ int34x_thermal_zone->adev = adev;
+ int34x_thermal_zone->override_ops = override_ops;
+
+ status = acpi_evaluate_integer(adev->handle, "PATC", NULL, &trip_cnt);
+ if (ACPI_FAILURE(status))
+ trip_cnt = 0;
+ else {
+ int34x_thermal_zone->aux_trips = kzalloc(
+ sizeof(*int34x_thermal_zone->aux_trips) *
+ trip_cnt, GFP_KERNEL);
+ if (!int34x_thermal_zone->aux_trips) {
+ ret = -ENOMEM;
+ goto free_mem;
+ }
+ trip_mask = BIT(trip_cnt) - 1;
+ int34x_thermal_zone->aux_trip_nr = trip_cnt;
+ }
+
+ int34x_thermal_zone->crt_trip_id = -1;
+ if (!int340x_thermal_get_trip_config(adev->handle, "_CRT",
+ &int34x_thermal_zone->crt_temp))
+ int34x_thermal_zone->crt_trip_id = trip_cnt++;
+ int34x_thermal_zone->hot_trip_id = -1;
+ if (!int340x_thermal_get_trip_config(adev->handle, "_HOT",
+ &int34x_thermal_zone->hot_temp))
+ int34x_thermal_zone->hot_trip_id = trip_cnt++;
+ int34x_thermal_zone->psv_trip_id = -1;
+ if (!int340x_thermal_get_trip_config(adev->handle, "_PSV",
+ &int34x_thermal_zone->psv_temp))
+ int34x_thermal_zone->psv_trip_id = trip_cnt++;
+ for (i = 0; i < INT340X_THERMAL_MAX_ACT_TRIP_COUNT; i++) {
+ char name[5] = { '_', 'A', 'C', '0' + i, '\0' };
+
+ if (int340x_thermal_get_trip_config(adev->handle, name,
+ &int34x_thermal_zone->act_trips[i].temp))
+ break;
+
+ int34x_thermal_zone->act_trips[i].id = trip_cnt++;
+ int34x_thermal_zone->act_trips[i].valid = true;
+ }
+ int34x_thermal_zone->lpat_table = acpi_lpat_get_conversion_table(
+ adev->handle);
+
+ int34x_thermal_zone->zone = thermal_zone_device_register(
+ acpi_device_bid(adev),
+ trip_cnt,
+ trip_mask, int34x_thermal_zone,
+ &int340x_thermal_zone_ops,
+ &int340x_thermal_params,
+ 0, 0);
+ if (IS_ERR(int34x_thermal_zone->zone)) {
+ ret = PTR_ERR(int34x_thermal_zone->zone);
+ goto free_lpat;
+ }
+
+ return int34x_thermal_zone;
+
+free_lpat:
+ acpi_lpat_free_conversion_table(int34x_thermal_zone->lpat_table);
+free_mem:
+ kfree(int34x_thermal_zone);
+ return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(int340x_thermal_zone_add);
+
+void int340x_thermal_zone_remove(struct int34x_thermal_zone
+ *int34x_thermal_zone)
+{
+ thermal_zone_device_unregister(int34x_thermal_zone->zone);
+ acpi_lpat_free_conversion_table(int34x_thermal_zone->lpat_table);
+ kfree(int34x_thermal_zone);
+}
+EXPORT_SYMBOL_GPL(int340x_thermal_zone_remove);
+
+MODULE_AUTHOR("Aaron Lu <aaron.lu@intel.com>");
+MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
+MODULE_DESCRIPTION("Intel INT340x common thermal zone handler");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/thermal/int340x_thermal/int340x_thermal_zone.h b/drivers/thermal/int340x_thermal/int340x_thermal_zone.h
new file mode 100644
index 000000000000..9f38ab72c4bf
--- /dev/null
+++ b/drivers/thermal/int340x_thermal/int340x_thermal_zone.h
@@ -0,0 +1,68 @@
+/*
+ * int340x_thermal_zone.h
+ * Copyright (c) 2015, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#ifndef __INT340X_THERMAL_ZONE_H__
+#define __INT340X_THERMAL_ZONE_H__
+
+#include <acpi/acpi_lpat.h>
+
+#define INT340X_THERMAL_MAX_ACT_TRIP_COUNT 10
+
+struct active_trip {
+ unsigned long temp;
+ int id;
+ bool valid;
+};
+
+struct int34x_thermal_zone {
+ struct acpi_device *adev;
+ struct active_trip act_trips[INT340X_THERMAL_MAX_ACT_TRIP_COUNT];
+ unsigned long *aux_trips;
+ int aux_trip_nr;
+ unsigned long psv_temp;
+ int psv_trip_id;
+ unsigned long crt_temp;
+ int crt_trip_id;
+ unsigned long hot_temp;
+ int hot_trip_id;
+ struct thermal_zone_device *zone;
+ struct thermal_zone_device_ops *override_ops;
+ void *priv_data;
+ struct acpi_lpat_conversion_table *lpat_table;
+};
+
+struct int34x_thermal_zone *int340x_thermal_zone_add(struct acpi_device *,
+ struct thermal_zone_device_ops *override_ops);
+void int340x_thermal_zone_remove(struct int34x_thermal_zone *);
+
+static inline void int340x_thermal_zone_set_priv_data(
+ struct int34x_thermal_zone *tzone, void *priv_data)
+{
+ tzone->priv_data = priv_data;
+}
+
+static inline void *int340x_thermal_zone_get_priv_data(
+ struct int34x_thermal_zone *tzone)
+{
+ return tzone->priv_data;
+}
+
+static inline void int340x_thermal_zone_device_update(
+ struct int34x_thermal_zone *tzone)
+{
+ thermal_zone_device_update(tzone->zone);
+}
+
+#endif
diff --git a/drivers/thermal/int340x_thermal/processor_thermal_device.c b/drivers/thermal/int340x_thermal/processor_thermal_device.c
index 0fe5dbbea968..7c3848da2df0 100644
--- a/drivers/thermal/int340x_thermal/processor_thermal_device.c
+++ b/drivers/thermal/int340x_thermal/processor_thermal_device.c
@@ -18,6 +18,8 @@
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <linux/acpi.h>
+#include <linux/thermal.h>
+#include "int340x_thermal_zone.h"
/* Broadwell-U/HSB thermal reporting device */
#define PCI_DEVICE_ID_PROC_BDW_THERMAL 0x1603
@@ -39,6 +41,7 @@ struct proc_thermal_device {
struct device *dev;
struct acpi_device *adev;
struct power_config power_limits[2];
+ struct int34x_thermal_zone *int340x_zone;
};
enum proc_thermal_emum_mode_type {
@@ -117,6 +120,72 @@ static struct attribute_group power_limit_attribute_group = {
.name = "power_limits"
};
+static int stored_tjmax; /* since it is fixed, we can have local storage */
+
+static int get_tjmax(void)
+{
+ u32 eax, edx;
+ u32 val;
+ int err;
+
+ err = rdmsr_safe(MSR_IA32_TEMPERATURE_TARGET, &eax, &edx);
+ if (err)
+ return err;
+
+ val = (eax >> 16) & 0xff;
+ if (val)
+ return val;
+
+ return -EINVAL;
+}
+
+static int read_temp_msr(unsigned long *temp)
+{
+ int cpu;
+ u32 eax, edx;
+ int err;
+ unsigned long curr_temp_off = 0;
+
+ *temp = 0;
+
+ for_each_online_cpu(cpu) {
+ err = rdmsr_safe_on_cpu(cpu, MSR_IA32_THERM_STATUS, &eax,
+ &edx);
+ if (err)
+ goto err_ret;
+ else {
+ if (eax & 0x80000000) {
+ curr_temp_off = (eax >> 16) & 0x7f;
+ if (!*temp || curr_temp_off < *temp)
+ *temp = curr_temp_off;
+ } else {
+ err = -EINVAL;
+ goto err_ret;
+ }
+ }
+ }
+
+ return 0;
+err_ret:
+ return err;
+}
+
+static int proc_thermal_get_zone_temp(struct thermal_zone_device *zone,
+ unsigned long *temp)
+{
+ int ret;
+
+ ret = read_temp_msr(temp);
+ if (!ret)
+ *temp = (stored_tjmax - *temp) * 1000;
+
+ return ret;
+}
+
+static struct thermal_zone_device_ops proc_thermal_local_ops = {
+ .get_temp = proc_thermal_get_zone_temp,
+};
+
static int proc_thermal_add(struct device *dev,
struct proc_thermal_device **priv)
{
@@ -126,6 +195,8 @@ static int proc_thermal_add(struct device *dev,
struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
union acpi_object *elements, *ppcc;
union acpi_object *p;
+ unsigned long long tmp;
+ struct thermal_zone_device_ops *ops = NULL;
int i;
int ret;
@@ -178,6 +249,24 @@ static int proc_thermal_add(struct device *dev,
ret = sysfs_create_group(&dev->kobj,
&power_limit_attribute_group);
+ if (ret)
+ goto free_buffer;
+
+ status = acpi_evaluate_integer(adev->handle, "_TMP", NULL, &tmp);
+ if (ACPI_FAILURE(status)) {
+ /* there is no _TMP method, add local method */
+ stored_tjmax = get_tjmax();
+ if (stored_tjmax > 0)
+ ops = &proc_thermal_local_ops;
+ }
+
+ proc_priv->int340x_zone = int340x_thermal_zone_add(adev, ops);
+ if (IS_ERR(proc_priv->int340x_zone)) {
+ sysfs_remove_group(&proc_priv->dev->kobj,
+ &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);
}