summaryrefslogtreecommitdiff
path: root/drivers/thermal
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/thermal')
-rw-r--r--drivers/thermal/int340x_thermal/Makefile1
-rw-r--r--drivers/thermal/int340x_thermal/int3400_thermal.c4
-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.c92
-rw-r--r--drivers/thermal/intel_soc_dts_thermal.c46
-rw-r--r--drivers/thermal/of-thermal.c3
-rw-r--r--drivers/thermal/rockchip_thermal.c36
-rw-r--r--drivers/thermal/samsung/Kconfig9
-rw-r--r--drivers/thermal/samsung/Makefile2
-rw-r--r--drivers/thermal/samsung/exynos_thermal_common.c427
-rw-r--r--drivers/thermal/samsung/exynos_thermal_common.h106
-rw-r--r--drivers/thermal/samsung/exynos_tmu.c553
-rw-r--r--drivers/thermal/samsung/exynos_tmu.h77
-rw-r--r--drivers/thermal/samsung/exynos_tmu_data.c264
-rw-r--r--drivers/thermal/step_wise.c4
-rw-r--r--drivers/thermal/thermal_core.c6
19 files changed, 963 insertions, 1427 deletions
diff --git a/drivers/thermal/int340x_thermal/Makefile b/drivers/thermal/int340x_thermal/Makefile
index d4413698a85f..ba77a34f659f 100644
--- a/drivers/thermal/int340x_thermal/Makefile
+++ b/drivers/thermal/int340x_thermal/Makefile
@@ -1,4 +1,5 @@
obj-$(CONFIG_INT340X_THERMAL) += int3400_thermal.o
+obj-$(CONFIG_INT340X_THERMAL) += int340x_thermal_zone.o
obj-$(CONFIG_INT340X_THERMAL) += int3402_thermal.o
obj-$(CONFIG_INT340X_THERMAL) += int3403_thermal.o
obj-$(CONFIG_INT340X_THERMAL) += processor_thermal_device.o
diff --git a/drivers/thermal/int340x_thermal/int3400_thermal.c b/drivers/thermal/int340x_thermal/int3400_thermal.c
index 65a98a97df07..25d244cbbe8f 100644
--- a/drivers/thermal/int340x_thermal/int3400_thermal.c
+++ b/drivers/thermal/int340x_thermal/int3400_thermal.c
@@ -18,19 +18,15 @@
enum int3400_thermal_uuid {
INT3400_THERMAL_PASSIVE_1,
- INT3400_THERMAL_PASSIVE_2,
INT3400_THERMAL_ACTIVE,
INT3400_THERMAL_CRITICAL,
- INT3400_THERMAL_COOLING_MODE,
INT3400_THERMAL_MAXIMUM_UUID,
};
static u8 *int3400_thermal_uuids[INT3400_THERMAL_MAXIMUM_UUID] = {
"42A441D6-AE6A-462b-A84B-4A8CE79027D3",
- "9E04115A-AE87-4D1C-9500-0F3E340BFE75",
"3A95C389-E4B8-4629-A526-C52C88626BAE",
"97C68AE7-15FA-499c-B8C9-5DA81D606E0A",
- "16CAF1B7-DD38-40ed-B1C1-1B8A1913D531",
};
struct int3400_thermal_priv {
diff --git a/drivers/thermal/int340x_thermal/int3402_thermal.c b/drivers/thermal/int340x_thermal/int3402_thermal.c
index c5cbc3af3a05..69df3d960303 100644
--- a/drivers/thermal/int340x_thermal/int3402_thermal.c
+++ b/drivers/thermal/int340x_thermal/int3402_thermal.c
@@ -14,152 +14,39 @@
#include <linux/platform_device.h>
#include <linux/acpi.h>
#include <linux/thermal.h>
+#include "int340x_thermal_zone.h"
-#define ACPI_ACTIVE_COOLING_MAX_NR 10
-
-struct active_trip {
- unsigned long temp;
- int id;
- bool valid;
-};
+#define INT3402_PERF_CHANGED_EVENT 0x80
+#define INT3402_THERMAL_EVENT 0x90
struct int3402_thermal_data {
- unsigned long *aux_trips;
- int aux_trip_nr;
- unsigned long psv_temp;
- int psv_trip_id;
- unsigned long crt_temp;
- int crt_trip_id;
- unsigned long hot_temp;
- int hot_trip_id;
- struct active_trip act_trips[ACPI_ACTIVE_COOLING_MAX_NR];
acpi_handle *handle;
+ struct int34x_thermal_zone *int340x_zone;
};
-static int int3402_thermal_get_zone_temp(struct thermal_zone_device *zone,
- unsigned long *temp)
-{
- struct int3402_thermal_data *d = zone->devdata;
- unsigned long long tmp;
- acpi_status status;
-
- status = acpi_evaluate_integer(d->handle, "_TMP", NULL, &tmp);
- if (ACPI_FAILURE(status))
- return -ENODEV;
-
- /* _TMP returns the temperature in tenths of degrees Kelvin */
- *temp = DECI_KELVIN_TO_MILLICELSIUS(tmp);
-
- return 0;
-}
-
-static int int3402_thermal_get_trip_temp(struct thermal_zone_device *zone,
- int trip, unsigned long *temp)
+static void int3402_notify(acpi_handle handle, u32 event, void *data)
{
- struct int3402_thermal_data *d = zone->devdata;
- int i;
-
- if (trip < d->aux_trip_nr)
- *temp = d->aux_trips[trip];
- else if (trip == d->crt_trip_id)
- *temp = d->crt_temp;
- else if (trip == d->psv_trip_id)
- *temp = d->psv_temp;
- else if (trip == d->hot_trip_id)
- *temp = d->hot_temp;
- else {
- for (i = 0; i < ACPI_ACTIVE_COOLING_MAX_NR; i++) {
- if (d->act_trips[i].valid &&
- d->act_trips[i].id == trip) {
- *temp = d->act_trips[i].temp;
- break;
- }
- }
- if (i == ACPI_ACTIVE_COOLING_MAX_NR)
- return -EINVAL;
+ struct int3402_thermal_data *priv = data;
+
+ if (!priv)
+ return;
+
+ switch (event) {
+ case INT3402_PERF_CHANGED_EVENT:
+ break;
+ case INT3402_THERMAL_EVENT:
+ int340x_thermal_zone_device_update(priv->int340x_zone);
+ break;
+ default:
+ break;
}
- return 0;
-}
-
-static int int3402_thermal_get_trip_type(struct thermal_zone_device *zone,
- int trip, enum thermal_trip_type *type)
-{
- struct int3402_thermal_data *d = zone->devdata;
- int i;
-
- if (trip < d->aux_trip_nr)
- *type = THERMAL_TRIP_PASSIVE;
- else if (trip == d->crt_trip_id)
- *type = THERMAL_TRIP_CRITICAL;
- else if (trip == d->hot_trip_id)
- *type = THERMAL_TRIP_HOT;
- else if (trip == d->psv_trip_id)
- *type = THERMAL_TRIP_PASSIVE;
- else {
- for (i = 0; i < ACPI_ACTIVE_COOLING_MAX_NR; i++) {
- if (d->act_trips[i].valid &&
- d->act_trips[i].id == trip) {
- *type = THERMAL_TRIP_ACTIVE;
- break;
- }
- }
- if (i == ACPI_ACTIVE_COOLING_MAX_NR)
- return -EINVAL;
- }
- return 0;
-}
-
-static int int3402_thermal_set_trip_temp(struct thermal_zone_device *zone, int trip,
- unsigned long temp)
-{
- struct int3402_thermal_data *d = zone->devdata;
- acpi_status status;
- char name[10];
-
- snprintf(name, sizeof(name), "PAT%d", trip);
- status = acpi_execute_simple_method(d->handle, name,
- MILLICELSIUS_TO_DECI_KELVIN(temp));
- if (ACPI_FAILURE(status))
- return -EIO;
-
- d->aux_trips[trip] = temp;
- return 0;
-}
-
-static struct thermal_zone_device_ops int3402_thermal_zone_ops = {
- .get_temp = int3402_thermal_get_zone_temp,
- .get_trip_temp = int3402_thermal_get_trip_temp,
- .get_trip_type = int3402_thermal_get_trip_type,
- .set_trip_temp = int3402_thermal_set_trip_temp,
-};
-
-static struct thermal_zone_params int3402_thermal_params = {
- .governor_name = "user_space",
- .no_hwmon = true,
-};
-
-static int int3402_thermal_get_temp(acpi_handle handle, char *name,
- unsigned long *temp)
-{
- unsigned long long r;
- acpi_status status;
-
- status = acpi_evaluate_integer(handle, name, NULL, &r);
- if (ACPI_FAILURE(status))
- return -EIO;
-
- *temp = DECI_KELVIN_TO_MILLICELSIUS(r);
- return 0;
}
static int int3402_thermal_probe(struct platform_device *pdev)
{
struct acpi_device *adev = ACPI_COMPANION(&pdev->dev);
struct int3402_thermal_data *d;
- struct thermal_zone_device *zone;
- acpi_status status;
- unsigned long long trip_cnt;
- int trip_mask = 0, i;
+ int ret;
if (!acpi_has_method(adev->handle, "_TMP"))
return -ENODEV;
@@ -168,54 +55,33 @@ static int int3402_thermal_probe(struct platform_device *pdev)
if (!d)
return -ENOMEM;
- status = acpi_evaluate_integer(adev->handle, "PATC", NULL, &trip_cnt);
- if (ACPI_FAILURE(status))
- trip_cnt = 0;
- else {
- d->aux_trips = devm_kzalloc(&pdev->dev,
- sizeof(*d->aux_trips) * trip_cnt, GFP_KERNEL);
- if (!d->aux_trips)
- return -ENOMEM;
- trip_mask = trip_cnt - 1;
- d->handle = adev->handle;
- d->aux_trip_nr = trip_cnt;
- }
-
- d->crt_trip_id = -1;
- if (!int3402_thermal_get_temp(adev->handle, "_CRT", &d->crt_temp))
- d->crt_trip_id = trip_cnt++;
- d->hot_trip_id = -1;
- if (!int3402_thermal_get_temp(adev->handle, "_HOT", &d->hot_temp))
- d->hot_trip_id = trip_cnt++;
- d->psv_trip_id = -1;
- if (!int3402_thermal_get_temp(adev->handle, "_PSV", &d->psv_temp))
- d->psv_trip_id = trip_cnt++;
- for (i = 0; i < ACPI_ACTIVE_COOLING_MAX_NR; i++) {
- char name[5] = { '_', 'A', 'C', '0' + i, '\0' };
- if (int3402_thermal_get_temp(adev->handle, name,
- &d->act_trips[i].temp))
- break;
- d->act_trips[i].id = trip_cnt++;
- d->act_trips[i].valid = true;
+ d->int340x_zone = int340x_thermal_zone_add(adev, NULL);
+ if (IS_ERR(d->int340x_zone))
+ return PTR_ERR(d->int340x_zone);
+
+ ret = acpi_install_notify_handler(adev->handle,
+ ACPI_DEVICE_NOTIFY,
+ int3402_notify,
+ d);
+ if (ret) {
+ int340x_thermal_zone_remove(d->int340x_zone);
+ return ret;
}
- zone = thermal_zone_device_register(acpi_device_bid(adev), trip_cnt,
- trip_mask, d,
- &int3402_thermal_zone_ops,
- &int3402_thermal_params,
- 0, 0);
- if (IS_ERR(zone))
- return PTR_ERR(zone);
- platform_set_drvdata(pdev, zone);
+ d->handle = adev->handle;
+ platform_set_drvdata(pdev, d);
return 0;
}
static int int3402_thermal_remove(struct platform_device *pdev)
{
- struct thermal_zone_device *zone = platform_get_drvdata(pdev);
+ struct int3402_thermal_data *d = platform_get_drvdata(pdev);
+
+ acpi_remove_notify_handler(d->handle,
+ ACPI_DEVICE_NOTIFY, int3402_notify);
+ int340x_thermal_zone_remove(d->int340x_zone);
- thermal_zone_device_unregister(zone);
return 0;
}
diff --git a/drivers/thermal/int340x_thermal/int3403_thermal.c b/drivers/thermal/int340x_thermal/int3403_thermal.c
index 0faf500d8a77..50a7a08e3a15 100644
--- a/drivers/thermal/int340x_thermal/int3403_thermal.c
+++ b/drivers/thermal/int340x_thermal/int3403_thermal.c
@@ -19,6 +19,7 @@
#include <linux/acpi.h>
#include <linux/thermal.h>
#include <linux/platform_device.h>
+#include "int340x_thermal_zone.h"
#define INT3403_TYPE_SENSOR 0x03
#define INT3403_TYPE_CHARGER 0x0B
@@ -26,18 +27,9 @@
#define INT3403_PERF_CHANGED_EVENT 0x80
#define INT3403_THERMAL_EVENT 0x90
-#define DECI_KELVIN_TO_MILLI_CELSIUS(t, off) (((t) - (off)) * 100)
-#define KELVIN_OFFSET 2732
-#define MILLI_CELSIUS_TO_DECI_KELVIN(t, off) (((t) / 100) + (off))
-
+/* Preserved structure for future expandbility */
struct int3403_sensor {
- struct thermal_zone_device *tzone;
- unsigned long *thresholds;
- unsigned long crit_temp;
- int crit_trip_id;
- unsigned long psv_temp;
- int psv_trip_id;
-
+ struct int34x_thermal_zone *int340x_zone;
};
struct int3403_performance_state {
@@ -63,126 +55,6 @@ struct int3403_priv {
void *priv;
};
-static int sys_get_curr_temp(struct thermal_zone_device *tzone,
- unsigned long *temp)
-{
- struct int3403_priv *priv = tzone->devdata;
- struct acpi_device *device = priv->adev;
- unsigned long long tmp;
- acpi_status status;
-
- status = acpi_evaluate_integer(device->handle, "_TMP", NULL, &tmp);
- if (ACPI_FAILURE(status))
- return -EIO;
-
- *temp = DECI_KELVIN_TO_MILLI_CELSIUS(tmp, KELVIN_OFFSET);
-
- return 0;
-}
-
-static int sys_get_trip_hyst(struct thermal_zone_device *tzone,
- int trip, unsigned long *temp)
-{
- struct int3403_priv *priv = tzone->devdata;
- struct acpi_device *device = priv->adev;
- unsigned long long hyst;
- acpi_status status;
-
- status = acpi_evaluate_integer(device->handle, "GTSH", NULL, &hyst);
- if (ACPI_FAILURE(status))
- return -EIO;
-
- /*
- * Thermal hysteresis represents a temperature difference.
- * Kelvin and Celsius have same degree size. So the
- * conversion here between tenths of degree Kelvin unit
- * and Milli-Celsius unit is just to multiply 100.
- */
- *temp = hyst * 100;
-
- return 0;
-}
-
-static int sys_get_trip_temp(struct thermal_zone_device *tzone,
- int trip, unsigned long *temp)
-{
- struct int3403_priv *priv = tzone->devdata;
- struct int3403_sensor *obj = priv->priv;
-
- if (priv->type != INT3403_TYPE_SENSOR || !obj)
- return -EINVAL;
-
- if (trip == obj->crit_trip_id)
- *temp = obj->crit_temp;
- else if (trip == obj->psv_trip_id)
- *temp = obj->psv_temp;
- else {
- /*
- * get_trip_temp is a mandatory callback but
- * PATx method doesn't return any value, so return
- * cached value, which was last set from user space
- */
- *temp = obj->thresholds[trip];
- }
-
- return 0;
-}
-
-static int sys_get_trip_type(struct thermal_zone_device *thermal,
- int trip, enum thermal_trip_type *type)
-{
- struct int3403_priv *priv = thermal->devdata;
- struct int3403_sensor *obj = priv->priv;
-
- /* Mandatory callback, may not mean much here */
- if (trip == obj->crit_trip_id)
- *type = THERMAL_TRIP_CRITICAL;
- else
- *type = THERMAL_TRIP_PASSIVE;
-
- return 0;
-}
-
-int sys_set_trip_temp(struct thermal_zone_device *tzone, int trip,
- unsigned long temp)
-{
- struct int3403_priv *priv = tzone->devdata;
- struct acpi_device *device = priv->adev;
- struct int3403_sensor *obj = priv->priv;
- acpi_status status;
- char name[10];
- int ret = 0;
-
- snprintf(name, sizeof(name), "PAT%d", trip);
- if (acpi_has_method(device->handle, name)) {
- status = acpi_execute_simple_method(device->handle, name,
- MILLI_CELSIUS_TO_DECI_KELVIN(temp,
- KELVIN_OFFSET));
- if (ACPI_FAILURE(status))
- ret = -EIO;
- else
- obj->thresholds[trip] = temp;
- } else {
- ret = -EIO;
- dev_err(&device->dev, "sys_set_trip_temp: method not found\n");
- }
-
- return ret;
-}
-
-static struct thermal_zone_device_ops tzone_ops = {
- .get_temp = sys_get_curr_temp,
- .get_trip_temp = sys_get_trip_temp,
- .get_trip_type = sys_get_trip_type,
- .set_trip_temp = sys_set_trip_temp,
- .get_trip_hyst = sys_get_trip_hyst,
-};
-
-static struct thermal_zone_params int3403_thermal_params = {
- .governor_name = "user_space",
- .no_hwmon = true,
-};
-
static void int3403_notify(acpi_handle handle,
u32 event, void *data)
{
@@ -200,7 +72,7 @@ static void int3403_notify(acpi_handle handle,
case INT3403_PERF_CHANGED_EVENT:
break;
case INT3403_THERMAL_EVENT:
- thermal_zone_device_update(obj->tzone);
+ int340x_thermal_zone_device_update(obj->int340x_zone);
break;
default:
dev_err(&priv->pdev->dev, "Unsupported event [0x%x]\n", event);
@@ -208,41 +80,10 @@ static void int3403_notify(acpi_handle handle,
}
}
-static int sys_get_trip_crt(struct acpi_device *device, unsigned long *temp)
-{
- unsigned long long crt;
- acpi_status status;
-
- status = acpi_evaluate_integer(device->handle, "_CRT", NULL, &crt);
- if (ACPI_FAILURE(status))
- return -EIO;
-
- *temp = DECI_KELVIN_TO_MILLI_CELSIUS(crt, KELVIN_OFFSET);
-
- return 0;
-}
-
-static int sys_get_trip_psv(struct acpi_device *device, unsigned long *temp)
-{
- unsigned long long psv;
- acpi_status status;
-
- status = acpi_evaluate_integer(device->handle, "_PSV", NULL, &psv);
- if (ACPI_FAILURE(status))
- return -EIO;
-
- *temp = DECI_KELVIN_TO_MILLI_CELSIUS(psv, KELVIN_OFFSET);
-
- return 0;
-}
-
static int int3403_sensor_add(struct int3403_priv *priv)
{
int result = 0;
- acpi_status status;
struct int3403_sensor *obj;
- unsigned long long trip_cnt;
- int trip_mask = 0;
obj = devm_kzalloc(&priv->pdev->dev, sizeof(*obj), GFP_KERNEL);
if (!obj)
@@ -250,39 +91,9 @@ static int int3403_sensor_add(struct int3403_priv *priv)
priv->priv = obj;
- status = acpi_evaluate_integer(priv->adev->handle, "PATC", NULL,
- &trip_cnt);
- if (ACPI_FAILURE(status))
- trip_cnt = 0;
-
- if (trip_cnt) {
- /* We have to cache, thresholds can't be readback */
- obj->thresholds = devm_kzalloc(&priv->pdev->dev,
- sizeof(*obj->thresholds) * trip_cnt,
- GFP_KERNEL);
- if (!obj->thresholds) {
- result = -ENOMEM;
- goto err_free_obj;
- }
- trip_mask = BIT(trip_cnt) - 1;
- }
-
- obj->psv_trip_id = -1;
- if (!sys_get_trip_psv(priv->adev, &obj->psv_temp))
- obj->psv_trip_id = trip_cnt++;
-
- obj->crit_trip_id = -1;
- if (!sys_get_trip_crt(priv->adev, &obj->crit_temp))
- obj->crit_trip_id = trip_cnt++;
-
- obj->tzone = thermal_zone_device_register(acpi_device_bid(priv->adev),
- trip_cnt, trip_mask, priv, &tzone_ops,
- &int3403_thermal_params, 0, 0);
- if (IS_ERR(obj->tzone)) {
- result = PTR_ERR(obj->tzone);
- obj->tzone = NULL;
- goto err_free_obj;
- }
+ obj->int340x_zone = int340x_thermal_zone_add(priv->adev, NULL);
+ if (IS_ERR(obj->int340x_zone))
+ return PTR_ERR(obj->int340x_zone);
result = acpi_install_notify_handler(priv->adev->handle,
ACPI_DEVICE_NOTIFY, int3403_notify,
@@ -293,7 +104,7 @@ static int int3403_sensor_add(struct int3403_priv *priv)
return 0;
err_free_obj:
- thermal_zone_device_unregister(obj->tzone);
+ int340x_thermal_zone_remove(obj->int340x_zone);
return result;
}
@@ -303,7 +114,8 @@ static int int3403_sensor_remove(struct int3403_priv *priv)
acpi_remove_notify_handler(priv->adev->handle,
ACPI_DEVICE_NOTIFY, int3403_notify);
- thermal_zone_device_unregister(obj->tzone);
+ int340x_thermal_zone_remove(obj->int340x_zone);
+
return 0;
}
diff --git a/drivers/thermal/int340x_thermal/int340x_thermal_zone.c b/drivers/thermal/int340x_thermal/int340x_thermal_zone.c
new file mode 100644
index 000000000000..f88b08877025
--- /dev/null
+++ b/drivers/thermal/int340x_thermal/int340x_thermal_zone.c
@@ -0,0 +1,276 @@
+/*
+ * int340x_thermal_zone.c
+ * Copyright (c) 2015, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/acpi.h>
+#include <linux/thermal.h>
+#include "int340x_thermal_zone.h"
+
+static int int340x_thermal_get_zone_temp(struct thermal_zone_device *zone,
+ unsigned long *temp)
+{
+ struct int34x_thermal_zone *d = zone->devdata;
+ unsigned long long tmp;
+ acpi_status status;
+
+ if (d->override_ops && d->override_ops->get_temp)
+ return d->override_ops->get_temp(zone, temp);
+
+ status = acpi_evaluate_integer(d->adev->handle, "_TMP", NULL, &tmp);
+ if (ACPI_FAILURE(status))
+ return -EIO;
+
+ if (d->lpat_table) {
+ int conv_temp;
+
+ conv_temp = acpi_lpat_raw_to_temp(d->lpat_table, (int)tmp);
+ if (conv_temp < 0)
+ return conv_temp;
+
+ *temp = (unsigned long)conv_temp * 10;
+ } else
+ /* _TMP returns the temperature in tenths of degrees Kelvin */
+ *temp = DECI_KELVIN_TO_MILLICELSIUS(tmp);
+
+ return 0;
+}
+
+static int int340x_thermal_get_trip_temp(struct thermal_zone_device *zone,
+ int trip, unsigned long *temp)
+{
+ struct int34x_thermal_zone *d = zone->devdata;
+ int i;
+
+ if (d->override_ops && d->override_ops->get_trip_temp)
+ return d->override_ops->get_trip_temp(zone, trip, temp);
+
+ if (trip < d->aux_trip_nr)
+ *temp = d->aux_trips[trip];
+ else if (trip == d->crt_trip_id)
+ *temp = d->crt_temp;
+ else if (trip == d->psv_trip_id)
+ *temp = d->psv_temp;
+ else if (trip == d->hot_trip_id)
+ *temp = d->hot_temp;
+ else {
+ for (i = 0; i < INT340X_THERMAL_MAX_ACT_TRIP_COUNT; i++) {
+ if (d->act_trips[i].valid &&
+ d->act_trips[i].id == trip) {
+ *temp = d->act_trips[i].temp;
+ break;
+ }
+ }
+ if (i == INT340X_THERMAL_MAX_ACT_TRIP_COUNT)
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int int340x_thermal_get_trip_type(struct thermal_zone_device *zone,
+ int trip,
+ enum thermal_trip_type *type)
+{
+ struct int34x_thermal_zone *d = zone->devdata;
+ int i;
+
+ if (d->override_ops && d->override_ops->get_trip_type)
+ return d->override_ops->get_trip_type(zone, trip, type);
+
+ if (trip < d->aux_trip_nr)
+ *type = THERMAL_TRIP_PASSIVE;
+ else if (trip == d->crt_trip_id)
+ *type = THERMAL_TRIP_CRITICAL;
+ else if (trip == d->hot_trip_id)
+ *type = THERMAL_TRIP_HOT;
+ else if (trip == d->psv_trip_id)
+ *type = THERMAL_TRIP_PASSIVE;
+ else {
+ for (i = 0; i < INT340X_THERMAL_MAX_ACT_TRIP_COUNT; i++) {
+ if (d->act_trips[i].valid &&
+ d->act_trips[i].id == trip) {
+ *type = THERMAL_TRIP_ACTIVE;
+ break;
+ }
+ }
+ if (i == INT340X_THERMAL_MAX_ACT_TRIP_COUNT)
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int int340x_thermal_set_trip_temp(struct thermal_zone_device *zone,
+ int trip, unsigned long temp)
+{
+ struct int34x_thermal_zone *d = zone->devdata;
+ acpi_status status;
+ char name[10];
+
+ if (d->override_ops && d->override_ops->set_trip_temp)
+ return d->override_ops->set_trip_temp(zone, trip, temp);
+
+ snprintf(name, sizeof(name), "PAT%d", trip);
+ status = acpi_execute_simple_method(d->adev->handle, name,
+ MILLICELSIUS_TO_DECI_KELVIN(temp));
+ if (ACPI_FAILURE(status))
+ return -EIO;
+
+ d->aux_trips[trip] = temp;
+
+ return 0;
+}
+
+
+static int int340x_thermal_get_trip_hyst(struct thermal_zone_device *zone,
+ int trip, unsigned long *temp)
+{
+ struct int34x_thermal_zone *d = zone->devdata;
+ acpi_status status;
+ unsigned long long hyst;
+
+ if (d->override_ops && d->override_ops->get_trip_hyst)
+ return d->override_ops->get_trip_hyst(zone, trip, temp);
+
+ status = acpi_evaluate_integer(d->adev->handle, "GTSH", NULL, &hyst);
+ if (ACPI_FAILURE(status))
+ return -EIO;
+
+ *temp = hyst * 100;
+
+ return 0;
+}
+
+static struct thermal_zone_device_ops int340x_thermal_zone_ops = {
+ .get_temp = int340x_thermal_get_zone_temp,
+ .get_trip_temp = int340x_thermal_get_trip_temp,
+ .get_trip_type = int340x_thermal_get_trip_type,
+ .set_trip_temp = int340x_thermal_set_trip_temp,
+ .get_trip_hyst = int340x_thermal_get_trip_hyst,
+};
+
+static int int340x_thermal_get_trip_config(acpi_handle handle, char *name,
+ unsigned long *temp)
+{
+ unsigned long long r;
+ acpi_status status;
+
+ status = acpi_evaluate_integer(handle, name, NULL, &r);
+ if (ACPI_FAILURE(status))
+ return -EIO;
+
+ *temp = DECI_KELVIN_TO_MILLICELSIUS(r);
+
+ return 0;
+}
+
+static struct thermal_zone_params int340x_thermal_params = {
+ .governor_name = "user_space",
+ .no_hwmon = true,
+};
+
+struct int34x_thermal_zone *int340x_thermal_zone_add(struct acpi_device *adev,
+ struct thermal_zone_device_ops *override_ops)
+{
+ struct int34x_thermal_zone *int34x_thermal_zone;
+ acpi_status status;
+ unsigned long long trip_cnt;
+ int trip_mask = 0, i;
+ int ret;
+
+ int34x_thermal_zone = kzalloc(sizeof(*int34x_thermal_zone),
+ GFP_KERNEL);
+ if (!int34x_thermal_zone)
+ return ERR_PTR(-ENOMEM);
+
+ int34x_thermal_zone->adev = adev;
+ int34x_thermal_zone->override_ops = override_ops;
+
+ status = acpi_evaluate_integer(adev->handle, "PATC", NULL, &trip_cnt);
+ if (ACPI_FAILURE(status))
+ trip_cnt = 0;
+ else {
+ int34x_thermal_zone->aux_trips = kzalloc(
+ sizeof(*int34x_thermal_zone->aux_trips) *
+ trip_cnt, GFP_KERNEL);
+ if (!int34x_thermal_zone->aux_trips) {
+ ret = -ENOMEM;
+ goto free_mem;
+ }
+ trip_mask = BIT(trip_cnt) - 1;
+ int34x_thermal_zone->aux_trip_nr = trip_cnt;
+ }
+
+ int34x_thermal_zone->crt_trip_id = -1;
+ if (!int340x_thermal_get_trip_config(adev->handle, "_CRT",
+ &int34x_thermal_zone->crt_temp))
+ int34x_thermal_zone->crt_trip_id = trip_cnt++;
+ int34x_thermal_zone->hot_trip_id = -1;
+ if (!int340x_thermal_get_trip_config(adev->handle, "_HOT",
+ &int34x_thermal_zone->hot_temp))
+ int34x_thermal_zone->hot_trip_id = trip_cnt++;
+ int34x_thermal_zone->psv_trip_id = -1;
+ if (!int340x_thermal_get_trip_config(adev->handle, "_PSV",
+ &int34x_thermal_zone->psv_temp))
+ int34x_thermal_zone->psv_trip_id = trip_cnt++;
+ for (i = 0; i < INT340X_THERMAL_MAX_ACT_TRIP_COUNT; i++) {
+ char name[5] = { '_', 'A', 'C', '0' + i, '\0' };
+
+ if (int340x_thermal_get_trip_config(adev->handle, name,
+ &int34x_thermal_zone->act_trips[i].temp))
+ break;
+
+ int34x_thermal_zone->act_trips[i].id = trip_cnt++;
+ int34x_thermal_zone->act_trips[i].valid = true;
+ }
+ int34x_thermal_zone->lpat_table = acpi_lpat_get_conversion_table(
+ adev->handle);
+
+ int34x_thermal_zone->zone = thermal_zone_device_register(
+ acpi_device_bid(adev),
+ trip_cnt,
+ trip_mask, int34x_thermal_zone,
+ &int340x_thermal_zone_ops,
+ &int340x_thermal_params,
+ 0, 0);
+ if (IS_ERR(int34x_thermal_zone->zone)) {
+ ret = PTR_ERR(int34x_thermal_zone->zone);
+ goto free_lpat;
+ }
+
+ return int34x_thermal_zone;
+
+free_lpat:
+ acpi_lpat_free_conversion_table(int34x_thermal_zone->lpat_table);
+free_mem:
+ kfree(int34x_thermal_zone);
+ return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(int340x_thermal_zone_add);
+
+void int340x_thermal_zone_remove(struct int34x_thermal_zone
+ *int34x_thermal_zone)
+{
+ thermal_zone_device_unregister(int34x_thermal_zone->zone);
+ acpi_lpat_free_conversion_table(int34x_thermal_zone->lpat_table);
+ kfree(int34x_thermal_zone);
+}
+EXPORT_SYMBOL_GPL(int340x_thermal_zone_remove);
+
+MODULE_AUTHOR("Aaron Lu <aaron.lu@intel.com>");
+MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
+MODULE_DESCRIPTION("Intel INT340x common thermal zone handler");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/thermal/int340x_thermal/int340x_thermal_zone.h b/drivers/thermal/int340x_thermal/int340x_thermal_zone.h
new file mode 100644
index 000000000000..9f38ab72c4bf
--- /dev/null
+++ b/drivers/thermal/int340x_thermal/int340x_thermal_zone.h
@@ -0,0 +1,68 @@
+/*
+ * int340x_thermal_zone.h
+ * Copyright (c) 2015, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#ifndef __INT340X_THERMAL_ZONE_H__
+#define __INT340X_THERMAL_ZONE_H__
+
+#include <acpi/acpi_lpat.h>
+
+#define INT340X_THERMAL_MAX_ACT_TRIP_COUNT 10
+
+struct active_trip {
+ unsigned long temp;
+ int id;
+ bool valid;
+};
+
+struct int34x_thermal_zone {
+ struct acpi_device *adev;
+ struct active_trip act_trips[INT340X_THERMAL_MAX_ACT_TRIP_COUNT];
+ unsigned long *aux_trips;
+ int aux_trip_nr;
+ unsigned long psv_temp;
+ int psv_trip_id;
+ unsigned long crt_temp;
+ int crt_trip_id;
+ unsigned long hot_temp;
+ int hot_trip_id;
+ struct thermal_zone_device *zone;
+ struct thermal_zone_device_ops *override_ops;
+ void *priv_data;
+ struct acpi_lpat_conversion_table *lpat_table;
+};
+
+struct int34x_thermal_zone *int340x_thermal_zone_add(struct acpi_device *,
+ struct thermal_zone_device_ops *override_ops);
+void int340x_thermal_zone_remove(struct int34x_thermal_zone *);
+
+static inline void int340x_thermal_zone_set_priv_data(
+ struct int34x_thermal_zone *tzone, void *priv_data)
+{
+ tzone->priv_data = priv_data;
+}
+
+static inline void *int340x_thermal_zone_get_priv_data(
+ struct int34x_thermal_zone *tzone)
+{
+ return tzone->priv_data;
+}
+
+static inline void int340x_thermal_zone_device_update(
+ struct int34x_thermal_zone *tzone)
+{
+ thermal_zone_device_update(tzone->zone);
+}
+
+#endif
diff --git a/drivers/thermal/int340x_thermal/processor_thermal_device.c b/drivers/thermal/int340x_thermal/processor_thermal_device.c
index 0fe5dbbea968..5e8d8e91ea6d 100644
--- a/drivers/thermal/int340x_thermal/processor_thermal_device.c
+++ b/drivers/thermal/int340x_thermal/processor_thermal_device.c
@@ -18,6 +18,8 @@
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <linux/acpi.h>
+#include <linux/thermal.h>
+#include "int340x_thermal_zone.h"
/* Broadwell-U/HSB thermal reporting device */
#define PCI_DEVICE_ID_PROC_BDW_THERMAL 0x1603
@@ -39,6 +41,7 @@ struct proc_thermal_device {
struct device *dev;
struct acpi_device *adev;
struct power_config power_limits[2];
+ struct int34x_thermal_zone *int340x_zone;
};
enum proc_thermal_emum_mode_type {
@@ -117,6 +120,72 @@ static struct attribute_group power_limit_attribute_group = {
.name = "power_limits"
};
+static int stored_tjmax; /* since it is fixed, we can have local storage */
+
+static int get_tjmax(void)
+{
+ u32 eax, edx;
+ u32 val;
+ int err;
+
+ err = rdmsr_safe(MSR_IA32_TEMPERATURE_TARGET, &eax, &edx);
+ if (err)
+ return err;
+
+ val = (eax >> 16) & 0xff;
+ if (val)
+ return val;
+
+ return -EINVAL;
+}
+
+static int read_temp_msr(unsigned long *temp)
+{
+ int cpu;
+ u32 eax, edx;
+ int err;
+ unsigned long curr_temp_off = 0;
+
+ *temp = 0;
+
+ for_each_online_cpu(cpu) {
+ err = rdmsr_safe_on_cpu(cpu, MSR_IA32_THERM_STATUS, &eax,
+ &edx);
+ if (err)
+ goto err_ret;
+ else {
+ if (eax & 0x80000000) {
+ curr_temp_off = (eax >> 16) & 0x7f;
+ if (!*temp || curr_temp_off < *temp)
+ *temp = curr_temp_off;
+ } else {
+ err = -EINVAL;
+ goto err_ret;
+ }
+ }
+ }
+
+ return 0;
+err_ret:
+ return err;
+}
+
+static int proc_thermal_get_zone_temp(struct thermal_zone_device *zone,
+ unsigned long *temp)
+{
+ int ret;
+
+ ret = read_temp_msr(temp);
+ if (!ret)
+ *temp = (stored_tjmax - *temp) * 1000;
+
+ return ret;
+}
+
+static struct thermal_zone_device_ops proc_thermal_local_ops = {
+ .get_temp = proc_thermal_get_zone_temp,
+};
+
static int proc_thermal_add(struct device *dev,
struct proc_thermal_device **priv)
{
@@ -126,6 +195,8 @@ static int proc_thermal_add(struct device *dev,
struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
union acpi_object *elements, *ppcc;
union acpi_object *p;
+ unsigned long long tmp;
+ struct thermal_zone_device_ops *ops = NULL;
int i;
int ret;
@@ -178,6 +249,24 @@ static int proc_thermal_add(struct device *dev,
ret = sysfs_create_group(&dev->kobj,
&power_limit_attribute_group);
+ if (ret)
+ goto free_buffer;
+
+ status = acpi_evaluate_integer(adev->handle, "_TMP", NULL, &tmp);
+ if (ACPI_FAILURE(status)) {
+ /* there is no _TMP method, add local method */
+ stored_tjmax = get_tjmax();
+ if (stored_tjmax > 0)
+ ops = &proc_thermal_local_ops;
+ }
+
+ proc_priv->int340x_zone = int340x_thermal_zone_add(adev, ops);
+ if (IS_ERR(proc_priv->int340x_zone)) {
+ sysfs_remove_group(&proc_priv->dev->kobj,
+ &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);
}
diff --git a/drivers/thermal/intel_soc_dts_thermal.c b/drivers/thermal/intel_soc_dts_thermal.c
index 5580f5b24eb9..9013505e43b7 100644
--- a/drivers/thermal/intel_soc_dts_thermal.c
+++ b/drivers/thermal/intel_soc_dts_thermal.c
@@ -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]);
diff --git a/drivers/thermal/of-thermal.c b/drivers/thermal/of-thermal.c
index d717f3dab6f1..668fb1bdea9e 100644
--- a/drivers/thermal/of-thermal.c
+++ b/drivers/thermal/of-thermal.c
@@ -497,6 +497,9 @@ thermal_zone_of_sensor_register(struct device *dev, int sensor_id, void *data,
if (sensor_specs.np == sensor_np && id == sensor_id) {
tzd = thermal_zone_of_add_sensor(child, sensor_np,
data, ops);
+ if (!IS_ERR(tzd))
+ tzd->ops->set_mode(tzd, THERMAL_DEVICE_ENABLED);
+
of_node_put(sensor_specs.np);
of_node_put(child);
goto exit;
diff --git a/drivers/thermal/rockchip_thermal.c b/drivers/thermal/rockchip_thermal.c
index 9c6ce548e363..3aa46ac7cdbc 100644
--- a/drivers/thermal/rockchip_thermal.c
+++ b/drivers/thermal/rockchip_thermal.c
@@ -193,19 +193,20 @@ static u32 rk_tsadcv2_temp_to_code(long temp)
static long rk_tsadcv2_code_to_temp(u32 code)
{
- int high, low, mid;
-
- low = 0;
- high = ARRAY_SIZE(v2_code_table) - 1;
- mid = (high + low) / 2;
-
- if (code > v2_code_table[low].code || code < v2_code_table[high].code)
- return 125000; /* No code available, return max temperature */
+ unsigned int low = 0;
+ unsigned int high = ARRAY_SIZE(v2_code_table) - 1;
+ unsigned int mid = (low + high) / 2;
+ unsigned int num;
+ unsigned long denom;
+
+ /* Invalid code, return -EAGAIN */
+ if (code > TSADCV2_DATA_MASK)
+ return -EAGAIN;
- while (low <= high) {
- if (code >= v2_code_table[mid].code && code <
- v2_code_table[mid - 1].code)
- return v2_code_table[mid].temp;
+ while (low <= high && mid) {
+ if (code >= v2_code_table[mid].code &&
+ code < v2_code_table[mid - 1].code)
+ break;
else if (code < v2_code_table[mid].code)
low = mid + 1;
else
@@ -213,7 +214,16 @@ static long rk_tsadcv2_code_to_temp(u32 code)
mid = (low + high) / 2;
}
- return 125000;
+ /*
+ * The 5C granularity provided by the table is too much. Let's
+ * assume that the relationship between sensor readings and
+ * temperature between 2 table entries is linear and interpolate
+ * to produce less granular result.
+ */
+ num = v2_code_table[mid].temp - v2_code_table[mid - 1].temp;
+ num *= v2_code_table[mid - 1].code - code;
+ denom = v2_code_table[mid - 1].code - v2_code_table[mid].code;
+ return v2_code_table[mid - 1].temp + (num / denom);
}
/**
diff --git a/drivers/thermal/samsung/Kconfig b/drivers/thermal/samsung/Kconfig
index c43306ecc0ab..c8e35c1a43dc 100644
--- a/drivers/thermal/samsung/Kconfig
+++ b/drivers/thermal/samsung/Kconfig
@@ -7,12 +7,3 @@ config EXYNOS_THERMAL
the TMU, reports temperature and handles cooling action if defined.
This driver uses the Exynos core thermal APIs and TMU configuration
data from the supported SoCs.
-
-config EXYNOS_THERMAL_CORE
- bool "Core thermal framework support for EXYNOS SOCs"
- depends on EXYNOS_THERMAL
- help
- If you say yes here you get support for EXYNOS TMU
- (Thermal Management Unit) common registration/unregistration
- functions to the core thermal layer and also to use the generic
- CPU cooling APIs.
diff --git a/drivers/thermal/samsung/Makefile b/drivers/thermal/samsung/Makefile
index c09d83095dc2..1e47d0d89ce0 100644
--- a/drivers/thermal/samsung/Makefile
+++ b/drivers/thermal/samsung/Makefile
@@ -3,5 +3,3 @@
#
obj-$(CONFIG_EXYNOS_THERMAL) += exynos_thermal.o
exynos_thermal-y := exynos_tmu.o
-exynos_thermal-y += exynos_tmu_data.o
-exynos_thermal-$(CONFIG_EXYNOS_THERMAL_CORE) += exynos_thermal_common.o
diff --git a/drivers/thermal/samsung/exynos_thermal_common.c b/drivers/thermal/samsung/exynos_thermal_common.c
deleted file mode 100644
index 6dc3815cc73f..000000000000
--- a/drivers/thermal/samsung/exynos_thermal_common.c
+++ /dev/null
@@ -1,427 +0,0 @@
-/*
- * exynos_thermal_common.c - Samsung EXYNOS common thermal file
- *
- * Copyright (C) 2013 Samsung Electronics
- * Amit Daniel Kachhap <amit.daniel@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- */
-
-#include <linux/cpu_cooling.h>
-#include <linux/err.h>
-#include <linux/slab.h>
-#include <linux/thermal.h>
-
-#include "exynos_thermal_common.h"
-
-struct exynos_thermal_zone {
- enum thermal_device_mode mode;
- struct thermal_zone_device *therm_dev;
- struct thermal_cooling_device *cool_dev[MAX_COOLING_DEVICE];
- unsigned int cool_dev_size;
- struct platform_device *exynos4_dev;
- struct thermal_sensor_conf *sensor_conf;
- bool bind;
-};
-
-/* Get mode callback functions for thermal zone */
-static int exynos_get_mode(struct thermal_zone_device *thermal,
- enum thermal_device_mode *mode)
-{
- struct exynos_thermal_zone *th_zone = thermal->devdata;
- if (th_zone)
- *mode = th_zone->mode;
- return 0;
-}
-
-/* Set mode callback functions for thermal zone */
-static int exynos_set_mode(struct thermal_zone_device *thermal,
- enum thermal_device_mode mode)
-{
- struct exynos_thermal_zone *th_zone = thermal->devdata;
- if (!th_zone) {
- dev_err(&thermal->device,
- "thermal zone not registered\n");
- return 0;
- }
-
- mutex_lock(&thermal->lock);
-
- if (mode == THERMAL_DEVICE_ENABLED &&
- !th_zone->sensor_conf->trip_data.trigger_falling)
- thermal->polling_delay = IDLE_INTERVAL;
- else
- thermal->polling_delay = 0;
-
- mutex_unlock(&thermal->lock);
-
- th_zone->mode = mode;
- thermal_zone_device_update(thermal);
- dev_dbg(th_zone->sensor_conf->dev,
- "thermal polling set for duration=%d msec\n",
- thermal->polling_delay);
- return 0;
-}
-
-
-/* Get trip type callback functions for thermal zone */
-static int exynos_get_trip_type(struct thermal_zone_device *thermal, int trip,
- enum thermal_trip_type *type)
-{
- struct exynos_thermal_zone *th_zone = thermal->devdata;
- int max_trip = th_zone->sensor_conf->trip_data.trip_count;
- int trip_type;
-
- if (trip < 0 || trip >= max_trip)
- return -EINVAL;
-
- trip_type = th_zone->sensor_conf->trip_data.trip_type[trip];
-
- if (trip_type == SW_TRIP)
- *type = THERMAL_TRIP_CRITICAL;
- else if (trip_type == THROTTLE_ACTIVE)
- *type = THERMAL_TRIP_ACTIVE;
- else if (trip_type == THROTTLE_PASSIVE)
- *type = THERMAL_TRIP_PASSIVE;
- else
- return -EINVAL;
-
- return 0;
-}
-
-/* Get trip temperature callback functions for thermal zone */
-static int exynos_get_trip_temp(struct thermal_zone_device *thermal, int trip,
- unsigned long *temp)
-{
- struct exynos_thermal_zone *th_zone = thermal->devdata;
- int max_trip = th_zone->sensor_conf->trip_data.trip_count;
-
- if (trip < 0 || trip >= max_trip)
- return -EINVAL;
-
- *temp = th_zone->sensor_conf->trip_data.trip_val[trip];
- /* convert the temperature into millicelsius */
- *temp = *temp * MCELSIUS;
-
- return 0;
-}
-
-/* Get critical temperature callback functions for thermal zone */
-static int exynos_get_crit_temp(struct thermal_zone_device *thermal,
- unsigned long *temp)
-{
- struct exynos_thermal_zone *th_zone = thermal->devdata;
- int max_trip = th_zone->sensor_conf->trip_data.trip_count;
- /* Get the temp of highest trip*/
- return exynos_get_trip_temp(thermal, max_trip - 1, temp);
-}
-
-/* Bind callback functions for thermal zone */
-static int exynos_bind(struct thermal_zone_device *thermal,
- struct thermal_cooling_device *cdev)
-{
- int ret = 0, i, tab_size, level;
- struct freq_clip_table *tab_ptr, *clip_data;
- struct exynos_thermal_zone *th_zone = thermal->devdata;
- struct thermal_sensor_conf *data = th_zone->sensor_conf;
-
- tab_ptr = (struct freq_clip_table *)data->cooling_data.freq_data;
- tab_size = data->cooling_data.freq_clip_count;
-
- if (tab_ptr == NULL || tab_size == 0)
- return 0;
-
- /* find the cooling device registered*/
- for (i = 0; i < th_zone->cool_dev_size; i++)
- if (cdev == th_zone->cool_dev[i])
- break;
-
- /* No matching cooling device */
- if (i == th_zone->cool_dev_size)
- return 0;
-
- /* Bind the thermal zone to the cpufreq cooling device */
- for (i = 0; i < tab_size; i++) {
- clip_data = (struct freq_clip_table *)&(tab_ptr[i]);
- level = cpufreq_cooling_get_level(0, clip_data->freq_clip_max);
- if (level == THERMAL_CSTATE_INVALID)
- return 0;
- switch (GET_ZONE(i)) {
- case MONITOR_ZONE:
- case WARN_ZONE:
- if (thermal_zone_bind_cooling_device(thermal, i, cdev,
- level, 0)) {
- dev_err(data->dev,
- "error unbinding cdev inst=%d\n", i);
- ret = -EINVAL;
- }
- th_zone->bind = true;
- break;
- default:
- ret = -EINVAL;
- }
- }
-
- return ret;
-}
-
-/* Unbind callback functions for thermal zone */
-static int exynos_unbind(struct thermal_zone_device *thermal,
- struct thermal_cooling_device *cdev)
-{
- int ret = 0, i, tab_size;
- struct exynos_thermal_zone *th_zone = thermal->devdata;
- struct thermal_sensor_conf *data = th_zone->sensor_conf;
-
- if (th_zone->bind == false)
- return 0;
-
- tab_size = data->cooling_data.freq_clip_count;
-
- if (tab_size == 0)
- return 0;
-
- /* find the cooling device registered*/
- for (i = 0; i < th_zone->cool_dev_size; i++)
- if (cdev == th_zone->cool_dev[i])
- break;
-
- /* No matching cooling device */
- if (i == th_zone->cool_dev_size)
- return 0;
-
- /* Bind the thermal zone to the cpufreq cooling device */
- for (i = 0; i < tab_size; i++) {
- switch (GET_ZONE(i)) {
- case MONITOR_ZONE:
- case WARN_ZONE:
- if (thermal_zone_unbind_cooling_device(thermal, i,
- cdev)) {
- dev_err(data->dev,
- "error unbinding cdev inst=%d\n", i);
- ret = -EINVAL;
- }
- th_zone->bind = false;
- break;
- default:
- ret = -EINVAL;
- }
- }
- return ret;
-}
-
-/* Get temperature callback functions for thermal zone */
-static int exynos_get_temp(struct thermal_zone_device *thermal,
- unsigned long *temp)
-{
- struct exynos_thermal_zone *th_zone = thermal->devdata;
- void *data;
-
- if (!th_zone->sensor_conf) {
- dev_err(&thermal->device,
- "Temperature sensor not initialised\n");
- return -EINVAL;
- }
- data = th_zone->sensor_conf->driver_data;
- *temp = th_zone->sensor_conf->read_temperature(data);
- /* convert the temperature into millicelsius */
- *temp = *temp * MCELSIUS;
- return 0;
-}
-
-/* Get temperature callback functions for thermal zone */
-static int exynos_set_emul_temp(struct thermal_zone_device *thermal,
- unsigned long temp)
-{
- void *data;
- int ret = -EINVAL;
- struct exynos_thermal_zone *th_zone = thermal->devdata;
-
- if (!th_zone->sensor_conf) {
- dev_err(&thermal->device,
- "Temperature sensor not initialised\n");
- return -EINVAL;
- }
- data = th_zone->sensor_conf->driver_data;
- if (th_zone->sensor_conf->write_emul_temp)
- ret = th_zone->sensor_conf->write_emul_temp(data, temp);
- return ret;
-}
-
-/* Get the temperature trend */
-static int exynos_get_trend(struct thermal_zone_device *thermal,
- int trip, enum thermal_trend *trend)
-{
- int ret;
- unsigned long trip_temp;
-
- ret = exynos_get_trip_temp(thermal, trip, &trip_temp);
- if (ret < 0)
- return ret;
-
- if (thermal->temperature >= trip_temp)
- *trend = THERMAL_TREND_RAISE_FULL;
- else
- *trend = THERMAL_TREND_DROP_FULL;
-
- return 0;
-}
-/* Operation callback functions for thermal zone */
-static struct thermal_zone_device_ops exynos_dev_ops = {
- .bind = exynos_bind,
- .unbind = exynos_unbind,
- .get_temp = exynos_get_temp,
- .set_emul_temp = exynos_set_emul_temp,
- .get_trend = exynos_get_trend,
- .get_mode = exynos_get_mode,
- .set_mode = exynos_set_mode,
- .get_trip_type = exynos_get_trip_type,
- .get_trip_temp = exynos_get_trip_temp,
- .get_crit_temp = exynos_get_crit_temp,
-};
-
-/*
- * This function may be called from interrupt based temperature sensor
- * when threshold is changed.
- */
-void exynos_report_trigger(struct thermal_sensor_conf *conf)
-{
- unsigned int i;
- char data[10];
- char *envp[] = { data, NULL };
- struct exynos_thermal_zone *th_zone;
-
- if (!conf || !conf->pzone_data) {
- pr_err("Invalid temperature sensor configuration data\n");
- return;
- }
-
- th_zone = conf->pzone_data;
-
- if (th_zone->bind == false) {
- for (i = 0; i < th_zone->cool_dev_size; i++) {
- if (!th_zone->cool_dev[i])
- continue;
- exynos_bind(th_zone->therm_dev,
- th_zone->cool_dev[i]);
- }
- }
-
- thermal_zone_device_update(th_zone->therm_dev);
-
- mutex_lock(&th_zone->therm_dev->lock);
- /* Find the level for which trip happened */
- for (i = 0; i < th_zone->sensor_conf->trip_data.trip_count; i++) {
- if (th_zone->therm_dev->last_temperature <
- th_zone->sensor_conf->trip_data.trip_val[i] * MCELSIUS)
- break;
- }
-
- if (th_zone->mode == THERMAL_DEVICE_ENABLED &&
- !th_zone->sensor_conf->trip_data.trigger_falling) {
- if (i > 0)
- th_zone->therm_dev->polling_delay = ACTIVE_INTERVAL;
- else
- th_zone->therm_dev->polling_delay = IDLE_INTERVAL;
- }
-
- snprintf(data, sizeof(data), "%u", i);
- kobject_uevent_env(&th_zone->therm_dev->device.kobj, KOBJ_CHANGE, envp);
- mutex_unlock(&th_zone->therm_dev->lock);
-}
-
-/* Register with the in-kernel thermal management */
-int exynos_register_thermal(struct thermal_sensor_conf *sensor_conf)
-{
- int ret;
- struct exynos_thermal_zone *th_zone;
-
- if (!sensor_conf || !sensor_conf->read_temperature) {
- pr_err("Temperature sensor not initialised\n");
- return -EINVAL;
- }
-
- th_zone = devm_kzalloc(sensor_conf->dev,
- sizeof(struct exynos_thermal_zone), GFP_KERNEL);
- if (!th_zone)
- return -ENOMEM;
-
- th_zone->sensor_conf = sensor_conf;
- /*
- * TODO: 1) Handle multiple cooling devices in a thermal zone
- * 2) Add a flag/name in cooling info to map to specific
- * sensor
- */
- if (sensor_conf->cooling_data.freq_clip_count > 0) {
- th_zone->cool_dev[th_zone->cool_dev_size] =
- cpufreq_cooling_register(cpu_present_mask);
- if (IS_ERR(th_zone->cool_dev[th_zone->cool_dev_size])) {
- ret = PTR_ERR(th_zone->cool_dev[th_zone->cool_dev_size]);
- if (ret != -EPROBE_DEFER)
- dev_err(sensor_conf->dev,
- "Failed to register cpufreq cooling device: %d\n",
- ret);
- goto err_unregister;
- }
- th_zone->cool_dev_size++;
- }
-
- th_zone->therm_dev = thermal_zone_device_register(
- sensor_conf->name, sensor_conf->trip_data.trip_count,
- 0, th_zone, &exynos_dev_ops, NULL, 0,
- sensor_conf->trip_data.trigger_falling ? 0 :
- IDLE_INTERVAL);
-
- if (IS_ERR(th_zone->therm_dev)) {
- dev_err(sensor_conf->dev,
- "Failed to register thermal zone device\n");
- ret = PTR_ERR(th_zone->therm_dev);
- goto err_unregister;
- }
- th_zone->mode = THERMAL_DEVICE_ENABLED;
- sensor_conf->pzone_data = th_zone;
-
- dev_info(sensor_conf->dev,
- "Exynos: Thermal zone(%s) registered\n", sensor_conf->name);
-
- return 0;
-
-err_unregister:
- exynos_unregister_thermal(sensor_conf);
- return ret;
-}
-
-/* Un-Register with the in-kernel thermal management */
-void exynos_unregister_thermal(struct thermal_sensor_conf *sensor_conf)
-{
- int i;
- struct exynos_thermal_zone *th_zone;
-
- if (!sensor_conf || !sensor_conf->pzone_data) {
- pr_err("Invalid temperature sensor configuration data\n");
- return;
- }
-
- th_zone = sensor_conf->pzone_data;
-
- thermal_zone_device_unregister(th_zone->therm_dev);
-
- for (i = 0; i < th_zone->cool_dev_size; ++i)
- cpufreq_cooling_unregister(th_zone->cool_dev[i]);
-
- dev_info(sensor_conf->dev,
- "Exynos: Kernel Thermal management unregistered\n");
-}
diff --git a/drivers/thermal/samsung/exynos_thermal_common.h b/drivers/thermal/samsung/exynos_thermal_common.h
deleted file mode 100644
index cd4471925cdd..000000000000
--- a/drivers/thermal/samsung/exynos_thermal_common.h
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * exynos_thermal_common.h - Samsung EXYNOS common header file
- *
- * Copyright (C) 2013 Samsung Electronics
- * Amit Daniel Kachhap <amit.daniel@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- */
-
-#ifndef _EXYNOS_THERMAL_COMMON_H
-#define _EXYNOS_THERMAL_COMMON_H
-
-/* In-kernel thermal framework related macros & definations */
-#define SENSOR_NAME_LEN 16
-#define MAX_TRIP_COUNT 8
-#define MAX_COOLING_DEVICE 4
-
-#define ACTIVE_INTERVAL 500
-#define IDLE_INTERVAL 10000
-#define MCELSIUS 1000
-
-/* CPU Zone information */
-#define PANIC_ZONE 4
-#define WARN_ZONE 3
-#define MONITOR_ZONE 2
-#define SAFE_ZONE 1
-
-#define GET_ZONE(trip) (trip + 2)
-#define GET_TRIP(zone) (zone - 2)
-
-enum trigger_type {
- THROTTLE_ACTIVE = 1,
- THROTTLE_PASSIVE,
- SW_TRIP,
- HW_TRIP,
-};
-
-/**
- * struct freq_clip_table
- * @freq_clip_max: maximum frequency allowed for this cooling state.
- * @temp_level: Temperature level at which the temperature clipping will
- * happen.
- * @mask_val: cpumask of the allowed cpu's where the clipping will take place.
- *
- * This structure is required to be filled and passed to the
- * cpufreq_cooling_unregister function.
- */
-struct freq_clip_table {
- unsigned int freq_clip_max;
- unsigned int temp_level;
- const struct cpumask *mask_val;
-};
-
-struct thermal_trip_point_conf {
- int trip_val[MAX_TRIP_COUNT];
- int trip_type[MAX_TRIP_COUNT];
- int trip_count;
- unsigned char trigger_falling;
-};
-
-struct thermal_cooling_conf {
- struct freq_clip_table freq_data[MAX_TRIP_COUNT];
- int freq_clip_count;
-};
-
-struct thermal_sensor_conf {
- char name[SENSOR_NAME_LEN];
- int (*read_temperature)(void *data);
- int (*write_emul_temp)(void *drv_data, unsigned long temp);
- struct thermal_trip_point_conf trip_data;
- struct thermal_cooling_conf cooling_data;
- void *driver_data;
- void *pzone_data;
- struct device *dev;
-};
-
-/*Functions used exynos based thermal sensor driver*/
-#ifdef CONFIG_EXYNOS_THERMAL_CORE
-void exynos_unregister_thermal(struct thermal_sensor_conf *sensor_conf);
-int exynos_register_thermal(struct thermal_sensor_conf *sensor_conf);
-void exynos_report_trigger(struct thermal_sensor_conf *sensor_conf);
-#else
-static inline void
-exynos_unregister_thermal(struct thermal_sensor_conf *sensor_conf) { return; }
-
-static inline int
-exynos_register_thermal(struct thermal_sensor_conf *sensor_conf) { return 0; }
-
-static inline void
-exynos_report_trigger(struct thermal_sensor_conf *sensor_conf) { return; }
-
-#endif /* CONFIG_EXYNOS_THERMAL_CORE */
-#endif /* _EXYNOS_THERMAL_COMMON_H */
diff --git a/drivers/thermal/samsung/exynos_tmu.c b/drivers/thermal/samsung/exynos_tmu.c
index d2f1e62a4232..933cd80a6bc5 100644
--- a/drivers/thermal/samsung/exynos_tmu.c
+++ b/drivers/thermal/samsung/exynos_tmu.c
@@ -1,6 +1,10 @@
/*
* exynos_tmu.c - Samsung EXYNOS TMU (Thermal Management Unit)
*
+ * Copyright (C) 2014 Samsung Electronics
+ * Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
+ * Lukasz Majewski <l.majewski@samsung.com>
+ *
* Copyright (C) 2011 Samsung Electronics
* Donggeun Kim <dg77.kim@samsung.com>
* Amit Daniel Kachhap <amit.kachhap@linaro.org>
@@ -31,8 +35,8 @@
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
-#include "exynos_thermal_common.h"
#include "exynos_tmu.h"
+#include "../thermal_core.h"
/* Exynos generic registers */
#define EXYNOS_TMU_REG_TRIMINFO 0x0
@@ -115,6 +119,27 @@
#define EXYNOS5440_TMU_TH_RISE4_SHIFT 24
#define EXYNOS5440_EFUSE_SWAP_OFFSET 8
+/* Exynos7 specific registers */
+#define EXYNOS7_THD_TEMP_RISE7_6 0x50
+#define EXYNOS7_THD_TEMP_FALL7_6 0x60
+#define EXYNOS7_TMU_REG_INTEN 0x110
+#define EXYNOS7_TMU_REG_INTPEND 0x118
+#define EXYNOS7_TMU_REG_EMUL_CON 0x160
+
+#define EXYNOS7_TMU_TEMP_MASK 0x1ff
+#define EXYNOS7_PD_DET_EN_SHIFT 23
+#define EXYNOS7_TMU_INTEN_RISE0_SHIFT 0
+#define EXYNOS7_TMU_INTEN_RISE1_SHIFT 1
+#define EXYNOS7_TMU_INTEN_RISE2_SHIFT 2
+#define EXYNOS7_TMU_INTEN_RISE3_SHIFT 3
+#define EXYNOS7_TMU_INTEN_RISE4_SHIFT 4
+#define EXYNOS7_TMU_INTEN_RISE5_SHIFT 5
+#define EXYNOS7_TMU_INTEN_RISE6_SHIFT 6
+#define EXYNOS7_TMU_INTEN_RISE7_SHIFT 7
+#define EXYNOS7_EMUL_DATA_SHIFT 7
+#define EXYNOS7_EMUL_DATA_MASK 0x1ff
+
+#define MCELSIUS 1000
/**
* struct exynos_tmu_data : A structure to hold the private data of the TMU
driver
@@ -128,6 +153,7 @@
* @lock: lock to implement synchronization.
* @clk: pointer to the clock structure.
* @clk_sec: pointer to the clock structure for accessing the base_second.
+ * @sclk: pointer to the clock structure for accessing the tmu special clk.
* @temp_error1: fused value of the first point trim.
* @temp_error2: fused value of the second point trim.
* @regulator: pointer to the TMU regulator structure.
@@ -147,10 +173,11 @@ struct exynos_tmu_data {
enum soc_type soc;
struct work_struct irq_work;
struct mutex lock;
- struct clk *clk, *clk_sec;
- u8 temp_error1, temp_error2;
+ struct clk *clk, *clk_sec, *sclk;
+ u16 temp_error1, temp_error2;
struct regulator *regulator;
- struct thermal_sensor_conf *reg_conf;
+ struct thermal_zone_device *tzd;
+
int (*tmu_initialize)(struct platform_device *pdev);
void (*tmu_control)(struct platform_device *pdev, bool on);
int (*tmu_read)(struct exynos_tmu_data *data);
@@ -159,6 +186,33 @@ struct exynos_tmu_data {
void (*tmu_clear_irqs)(struct exynos_tmu_data *data);
};
+static void exynos_report_trigger(struct exynos_tmu_data *p)
+{
+ char data[10], *envp[] = { data, NULL };
+ struct thermal_zone_device *tz = p->tzd;
+ unsigned long temp;
+ unsigned int i;
+
+ if (!tz) {
+ pr_err("No thermal zone device defined\n");
+ return;
+ }
+
+ thermal_zone_device_update(tz);
+
+ mutex_lock(&tz->lock);
+ /* Find the level for which trip happened */
+ for (i = 0; i < of_thermal_get_ntrips(tz); i++) {
+ tz->ops->get_trip_temp(tz, i, &temp);
+ if (tz->last_temperature < temp)
+ break;
+ }
+
+ snprintf(data, sizeof(data), "%u", i);
+ kobject_uevent_env(&tz->device.kobj, KOBJ_CHANGE, envp);
+ mutex_unlock(&tz->lock);
+}
+
/*
* TMU treats temperature as a mapped temperature code.
* The temperature is converted differently depending on the calibration type.
@@ -190,7 +244,7 @@ static int temp_to_code(struct exynos_tmu_data *data, u8 temp)
* Calculate a temperature value from a temperature code.
* The unit of the temperature is degree Celsius.
*/
-static int code_to_temp(struct exynos_tmu_data *data, u8 temp_code)
+static int code_to_temp(struct exynos_tmu_data *data, u16 temp_code)
{
struct exynos_tmu_platform_data *pdata = data->pdata;
int temp;
@@ -234,14 +288,25 @@ static void sanitize_temp_error(struct exynos_tmu_data *data, u32 trim_info)
static u32 get_th_reg(struct exynos_tmu_data *data, u32 threshold, bool falling)
{
- struct exynos_tmu_platform_data *pdata = data->pdata;
+ struct thermal_zone_device *tz = data->tzd;
+ const struct thermal_trip * const trips =
+ of_thermal_get_trip_points(tz);
+ unsigned long temp;
int i;
- for (i = 0; i < pdata->non_hw_trigger_levels; i++) {
- u8 temp = pdata->trigger_levels[i];
+ if (!trips) {
+ pr_err("%s: Cannot get trip points from of-thermal.c!\n",
+ __func__);
+ return 0;
+ }
+
+ for (i = 0; i < of_thermal_get_ntrips(tz); i++) {
+ if (trips[i].type == THERMAL_TRIP_CRITICAL)
+ continue;
+ temp = trips[i].temperature / MCELSIUS;
if (falling)
- temp -= pdata->threshold_falling;
+ temp -= (trips[i].hysteresis / MCELSIUS);
else
threshold &= ~(0xff << 8 * i);
@@ -305,9 +370,19 @@ static void exynos_tmu_control(struct platform_device *pdev, bool on)
static int exynos4210_tmu_initialize(struct platform_device *pdev)
{
struct exynos_tmu_data *data = platform_get_drvdata(pdev);
- struct exynos_tmu_platform_data *pdata = data->pdata;
- unsigned int status;
+ struct thermal_zone_device *tz = data->tzd;
+ const struct thermal_trip * const trips =
+ of_thermal_get_trip_points(tz);
int ret = 0, threshold_code, i;
+ unsigned long reference, temp;
+ unsigned int status;
+
+ if (!trips) {
+ pr_err("%s: Cannot get trip points from of-thermal.c!\n",
+ __func__);
+ ret = -ENODEV;
+ goto out;
+ }
status = readb(data->base + EXYNOS_TMU_REG_STATUS);
if (!status) {
@@ -318,12 +393,19 @@ static int exynos4210_tmu_initialize(struct platform_device *pdev)
sanitize_temp_error(data, readl(data->base + EXYNOS_TMU_REG_TRIMINFO));
/* Write temperature code for threshold */
- threshold_code = temp_to_code(data, pdata->threshold);
+ reference = trips[0].temperature / MCELSIUS;
+ threshold_code = temp_to_code(data, reference);
+ if (threshold_code < 0) {
+ ret = threshold_code;
+ goto out;
+ }
writeb(threshold_code, data->base + EXYNOS4210_TMU_REG_THRESHOLD_TEMP);
- for (i = 0; i < pdata->non_hw_trigger_levels; i++)
- writeb(pdata->trigger_levels[i], data->base +
+ for (i = 0; i < of_thermal_get_ntrips(tz); i++) {
+ temp = trips[i].temperature / MCELSIUS;
+ writeb(temp - reference, data->base +
EXYNOS4210_TMU_REG_TRIG_LEVEL0 + i * 4);
+ }
data->tmu_clear_irqs(data);
out:
@@ -333,9 +415,11 @@ out:
static int exynos4412_tmu_initialize(struct platform_device *pdev)
{
struct exynos_tmu_data *data = platform_get_drvdata(pdev);
- struct exynos_tmu_platform_data *pdata = data->pdata;
+ const struct thermal_trip * const trips =
+ of_thermal_get_trip_points(data->tzd);
unsigned int status, trim_info, con, ctrl, rising_threshold;
int ret = 0, threshold_code, i;
+ unsigned long crit_temp = 0;
status = readb(data->base + EXYNOS_TMU_REG_STATUS);
if (!status) {
@@ -373,17 +457,29 @@ static int exynos4412_tmu_initialize(struct platform_device *pdev)
data->tmu_clear_irqs(data);
/* if last threshold limit is also present */
- i = pdata->max_trigger_level - 1;
- if (pdata->trigger_levels[i] && pdata->trigger_type[i] == HW_TRIP) {
- threshold_code = temp_to_code(data, pdata->trigger_levels[i]);
- /* 1-4 level to be assigned in th0 reg */
- rising_threshold &= ~(0xff << 8 * i);
- rising_threshold |= threshold_code << 8 * i;
- writel(rising_threshold, data->base + EXYNOS_THD_TEMP_RISE);
- con = readl(data->base + EXYNOS_TMU_REG_CONTROL);
- con |= (1 << EXYNOS_TMU_THERM_TRIP_EN_SHIFT);
- writel(con, data->base + EXYNOS_TMU_REG_CONTROL);
+ for (i = 0; i < of_thermal_get_ntrips(data->tzd); i++) {
+ if (trips[i].type == THERMAL_TRIP_CRITICAL) {
+ crit_temp = trips[i].temperature;
+ break;
+ }
}
+
+ if (i == of_thermal_get_ntrips(data->tzd)) {
+ pr_err("%s: No CRITICAL trip point defined at of-thermal.c!\n",
+ __func__);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ threshold_code = temp_to_code(data, crit_temp / MCELSIUS);
+ /* 1-4 level to be assigned in th0 reg */
+ rising_threshold &= ~(0xff << 8 * i);
+ rising_threshold |= threshold_code << 8 * i;
+ writel(rising_threshold, data->base + EXYNOS_THD_TEMP_RISE);
+ con = readl(data->base + EXYNOS_TMU_REG_CONTROL);
+ con |= (1 << EXYNOS_TMU_THERM_TRIP_EN_SHIFT);
+ writel(con, data->base + EXYNOS_TMU_REG_CONTROL);
+
out:
return ret;
}
@@ -391,9 +487,9 @@ out:
static int exynos5440_tmu_initialize(struct platform_device *pdev)
{
struct exynos_tmu_data *data = platform_get_drvdata(pdev);
- struct exynos_tmu_platform_data *pdata = data->pdata;
unsigned int trim_info = 0, con, rising_threshold;
- int ret = 0, threshold_code, i;
+ int ret = 0, threshold_code;
+ unsigned long crit_temp = 0;
/*
* For exynos5440 soc triminfo value is swapped between TMU0 and
@@ -422,9 +518,8 @@ static int exynos5440_tmu_initialize(struct platform_device *pdev)
data->tmu_clear_irqs(data);
/* if last threshold limit is also present */
- i = pdata->max_trigger_level - 1;
- if (pdata->trigger_levels[i] && pdata->trigger_type[i] == HW_TRIP) {
- threshold_code = temp_to_code(data, pdata->trigger_levels[i]);
+ if (!data->tzd->ops->get_crit_temp(data->tzd, &crit_temp)) {
+ threshold_code = temp_to_code(data, crit_temp / MCELSIUS);
/* 5th level to be assigned in th2 reg */
rising_threshold =
threshold_code << EXYNOS5440_TMU_TH_RISE4_SHIFT;
@@ -439,10 +534,88 @@ static int exynos5440_tmu_initialize(struct platform_device *pdev)
return ret;
}
-static void exynos4210_tmu_control(struct platform_device *pdev, bool on)
+static int exynos7_tmu_initialize(struct platform_device *pdev)
{
struct exynos_tmu_data *data = platform_get_drvdata(pdev);
+ struct thermal_zone_device *tz = data->tzd;
struct exynos_tmu_platform_data *pdata = data->pdata;
+ unsigned int status, trim_info;
+ unsigned int rising_threshold = 0, falling_threshold = 0;
+ int ret = 0, threshold_code, i;
+ unsigned long temp, temp_hist;
+ unsigned int reg_off, bit_off;
+
+ status = readb(data->base + EXYNOS_TMU_REG_STATUS);
+ if (!status) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ trim_info = readl(data->base + EXYNOS_TMU_REG_TRIMINFO);
+
+ data->temp_error1 = trim_info & EXYNOS7_TMU_TEMP_MASK;
+ if (!data->temp_error1 ||
+ (pdata->min_efuse_value > data->temp_error1) ||
+ (data->temp_error1 > pdata->max_efuse_value))
+ data->temp_error1 = pdata->efuse_value & EXYNOS_TMU_TEMP_MASK;
+
+ /* Write temperature code for rising and falling threshold */
+ for (i = (of_thermal_get_ntrips(tz) - 1); i >= 0; i--) {
+ /*
+ * On exynos7 there are 4 rising and 4 falling threshold
+ * registers (0x50-0x5c and 0x60-0x6c respectively). Each
+ * register holds the value of two threshold levels (at bit
+ * offsets 0 and 16). Based on the fact that there are atmost
+ * eight possible trigger levels, calculate the register and
+ * bit offsets where the threshold levels are to be written.
+ *
+ * e.g. EXYNOS7_THD_TEMP_RISE7_6 (0x50)
+ * [24:16] - Threshold level 7
+ * [8:0] - Threshold level 6
+ * e.g. EXYNOS7_THD_TEMP_RISE5_4 (0x54)
+ * [24:16] - Threshold level 5
+ * [8:0] - Threshold level 4
+ *
+ * and similarly for falling thresholds.
+ *
+ * Based on the above, calculate the register and bit offsets
+ * for rising/falling threshold levels and populate them.
+ */
+ reg_off = ((7 - i) / 2) * 4;
+ bit_off = ((8 - i) % 2);
+
+ tz->ops->get_trip_temp(tz, i, &temp);
+ temp /= MCELSIUS;
+
+ tz->ops->get_trip_hyst(tz, i, &temp_hist);
+ temp_hist = temp - (temp_hist / MCELSIUS);
+
+ /* Set 9-bit temperature code for rising threshold levels */
+ threshold_code = temp_to_code(data, temp);
+ rising_threshold = readl(data->base +
+ EXYNOS7_THD_TEMP_RISE7_6 + reg_off);
+ rising_threshold &= ~(EXYNOS7_TMU_TEMP_MASK << (16 * bit_off));
+ rising_threshold |= threshold_code << (16 * bit_off);
+ writel(rising_threshold,
+ data->base + EXYNOS7_THD_TEMP_RISE7_6 + reg_off);
+
+ /* Set 9-bit temperature code for falling threshold levels */
+ threshold_code = temp_to_code(data, temp_hist);
+ falling_threshold &= ~(EXYNOS7_TMU_TEMP_MASK << (16 * bit_off));
+ falling_threshold |= threshold_code << (16 * bit_off);
+ writel(falling_threshold,
+ data->base + EXYNOS7_THD_TEMP_FALL7_6 + reg_off);
+ }
+
+ data->tmu_clear_irqs(data);
+out:
+ return ret;
+}
+
+static void exynos4210_tmu_control(struct platform_device *pdev, bool on)
+{
+ struct exynos_tmu_data *data = platform_get_drvdata(pdev);
+ struct thermal_zone_device *tz = data->tzd;
unsigned int con, interrupt_en;
con = get_con_reg(data, readl(data->base + EXYNOS_TMU_REG_CONTROL));
@@ -450,10 +623,15 @@ static void exynos4210_tmu_control(struct platform_device *pdev, bool on)
if (on) {
con |= (1 << EXYNOS_TMU_CORE_EN_SHIFT);
interrupt_en =
- pdata->trigger_enable[3] << EXYNOS_TMU_INTEN_RISE3_SHIFT |
- pdata->trigger_enable[2] << EXYNOS_TMU_INTEN_RISE2_SHIFT |
- pdata->trigger_enable[1] << EXYNOS_TMU_INTEN_RISE1_SHIFT |
- pdata->trigger_enable[0] << EXYNOS_TMU_INTEN_RISE0_SHIFT;
+ (of_thermal_is_trip_valid(tz, 3)
+ << EXYNOS_TMU_INTEN_RISE3_SHIFT) |
+ (of_thermal_is_trip_valid(tz, 2)
+ << EXYNOS_TMU_INTEN_RISE2_SHIFT) |
+ (of_thermal_is_trip_valid(tz, 1)
+ << EXYNOS_TMU_INTEN_RISE1_SHIFT) |
+ (of_thermal_is_trip_valid(tz, 0)
+ << EXYNOS_TMU_INTEN_RISE0_SHIFT);
+
if (data->soc != SOC_ARCH_EXYNOS4210)
interrupt_en |=
interrupt_en << EXYNOS_TMU_INTEN_FALL0_SHIFT;
@@ -468,7 +646,7 @@ static void exynos4210_tmu_control(struct platform_device *pdev, bool on)
static void exynos5440_tmu_control(struct platform_device *pdev, bool on)
{
struct exynos_tmu_data *data = platform_get_drvdata(pdev);
- struct exynos_tmu_platform_data *pdata = data->pdata;
+ struct thermal_zone_device *tz = data->tzd;
unsigned int con, interrupt_en;
con = get_con_reg(data, readl(data->base + EXYNOS5440_TMU_S0_7_CTRL));
@@ -476,11 +654,16 @@ static void exynos5440_tmu_control(struct platform_device *pdev, bool on)
if (on) {
con |= (1 << EXYNOS_TMU_CORE_EN_SHIFT);
interrupt_en =
- pdata->trigger_enable[3] << EXYNOS5440_TMU_INTEN_RISE3_SHIFT |
- pdata->trigger_enable[2] << EXYNOS5440_TMU_INTEN_RISE2_SHIFT |
- pdata->trigger_enable[1] << EXYNOS5440_TMU_INTEN_RISE1_SHIFT |
- pdata->trigger_enable[0] << EXYNOS5440_TMU_INTEN_RISE0_SHIFT;
- interrupt_en |= interrupt_en << EXYNOS5440_TMU_INTEN_FALL0_SHIFT;
+ (of_thermal_is_trip_valid(tz, 3)
+ << EXYNOS5440_TMU_INTEN_RISE3_SHIFT) |
+ (of_thermal_is_trip_valid(tz, 2)
+ << EXYNOS5440_TMU_INTEN_RISE2_SHIFT) |
+ (of_thermal_is_trip_valid(tz, 1)
+ << EXYNOS5440_TMU_INTEN_RISE1_SHIFT) |
+ (of_thermal_is_trip_valid(tz, 0)
+ << EXYNOS5440_TMU_INTEN_RISE0_SHIFT);
+ interrupt_en |=
+ interrupt_en << EXYNOS5440_TMU_INTEN_FALL0_SHIFT;
} else {
con &= ~(1 << EXYNOS_TMU_CORE_EN_SHIFT);
interrupt_en = 0; /* Disable all interrupts */
@@ -489,19 +672,62 @@ static void exynos5440_tmu_control(struct platform_device *pdev, bool on)
writel(con, data->base + EXYNOS5440_TMU_S0_7_CTRL);
}
-static int exynos_tmu_read(struct exynos_tmu_data *data)
+static void exynos7_tmu_control(struct platform_device *pdev, bool on)
{
- int ret;
+ struct exynos_tmu_data *data = platform_get_drvdata(pdev);
+ struct thermal_zone_device *tz = data->tzd;
+ unsigned int con, interrupt_en;
+
+ con = get_con_reg(data, readl(data->base + EXYNOS_TMU_REG_CONTROL));
+
+ if (on) {
+ con |= (1 << EXYNOS_TMU_CORE_EN_SHIFT);
+ interrupt_en =
+ (of_thermal_is_trip_valid(tz, 7)
+ << EXYNOS7_TMU_INTEN_RISE7_SHIFT) |
+ (of_thermal_is_trip_valid(tz, 6)
+ << EXYNOS7_TMU_INTEN_RISE6_SHIFT) |
+ (of_thermal_is_trip_valid(tz, 5)
+ << EXYNOS7_TMU_INTEN_RISE5_SHIFT) |
+ (of_thermal_is_trip_valid(tz, 4)
+ << EXYNOS7_TMU_INTEN_RISE4_SHIFT) |
+ (of_thermal_is_trip_valid(tz, 3)
+ << EXYNOS7_TMU_INTEN_RISE3_SHIFT) |
+ (of_thermal_is_trip_valid(tz, 2)
+ << EXYNOS7_TMU_INTEN_RISE2_SHIFT) |
+ (of_thermal_is_trip_valid(tz, 1)
+ << EXYNOS7_TMU_INTEN_RISE1_SHIFT) |
+ (of_thermal_is_trip_valid(tz, 0)
+ << EXYNOS7_TMU_INTEN_RISE0_SHIFT);
+
+ interrupt_en |=
+ interrupt_en << EXYNOS_TMU_INTEN_FALL0_SHIFT;
+ } else {
+ con &= ~(1 << EXYNOS_TMU_CORE_EN_SHIFT);
+ interrupt_en = 0; /* Disable all interrupts */
+ }
+ con |= 1 << EXYNOS7_PD_DET_EN_SHIFT;
+
+ writel(interrupt_en, data->base + EXYNOS7_TMU_REG_INTEN);
+ writel(con, data->base + EXYNOS_TMU_REG_CONTROL);
+}
+
+static int exynos_get_temp(void *p, long *temp)
+{
+ struct exynos_tmu_data *data = p;
+
+ if (!data || !data->tmu_read)
+ return -EINVAL;
mutex_lock(&data->lock);
clk_enable(data->clk);
- ret = data->tmu_read(data);
- if (ret >= 0)
- ret = code_to_temp(data, ret);
+
+ *temp = code_to_temp(data, data->tmu_read(data)) * MCELSIUS;
+
clk_disable(data->clk);
mutex_unlock(&data->lock);
- return ret;
+ return 0;
}
#ifdef CONFIG_THERMAL_EMULATION
@@ -515,9 +741,19 @@ static u32 get_emul_con_reg(struct exynos_tmu_data *data, unsigned int val,
val &= ~(EXYNOS_EMUL_TIME_MASK << EXYNOS_EMUL_TIME_SHIFT);
val |= (EXYNOS_EMUL_TIME << EXYNOS_EMUL_TIME_SHIFT);
}
- val &= ~(EXYNOS_EMUL_DATA_MASK << EXYNOS_EMUL_DATA_SHIFT);
- val |= (temp_to_code(data, temp) << EXYNOS_EMUL_DATA_SHIFT) |
- EXYNOS_EMUL_ENABLE;
+ if (data->soc == SOC_ARCH_EXYNOS7) {
+ val &= ~(EXYNOS7_EMUL_DATA_MASK <<
+ EXYNOS7_EMUL_DATA_SHIFT);
+ val |= (temp_to_code(data, temp) <<
+ EXYNOS7_EMUL_DATA_SHIFT) |
+ EXYNOS_EMUL_ENABLE;
+ } else {
+ val &= ~(EXYNOS_EMUL_DATA_MASK <<
+ EXYNOS_EMUL_DATA_SHIFT);
+ val |= (temp_to_code(data, temp) <<
+ EXYNOS_EMUL_DATA_SHIFT) |
+ EXYNOS_EMUL_ENABLE;
+ }
} else {
val &= ~EXYNOS_EMUL_ENABLE;
}
@@ -533,6 +769,8 @@ static void exynos4412_tmu_set_emulation(struct exynos_tmu_data *data,
if (data->soc == SOC_ARCH_EXYNOS5260)
emul_con = EXYNOS5260_EMUL_CON;
+ else if (data->soc == SOC_ARCH_EXYNOS7)
+ emul_con = EXYNOS7_TMU_REG_EMUL_CON;
else
emul_con = EXYNOS_EMUL_CON;
@@ -576,7 +814,7 @@ out:
#define exynos5440_tmu_set_emulation NULL
static int exynos_tmu_set_emulation(void *drv_data, unsigned long temp)
{ return -EINVAL; }
-#endif/*CONFIG_THERMAL_EMULATION*/
+#endif /* CONFIG_THERMAL_EMULATION */
static int exynos4210_tmu_read(struct exynos_tmu_data *data)
{
@@ -596,6 +834,12 @@ static int exynos5440_tmu_read(struct exynos_tmu_data *data)
return readb(data->base + EXYNOS5440_TMU_S0_7_TEMP);
}
+static int exynos7_tmu_read(struct exynos_tmu_data *data)
+{
+ return readw(data->base + EXYNOS_TMU_REG_CURRENT_TEMP) &
+ EXYNOS7_TMU_TEMP_MASK;
+}
+
static void exynos_tmu_work(struct work_struct *work)
{
struct exynos_tmu_data *data = container_of(work,
@@ -613,7 +857,7 @@ static void exynos_tmu_work(struct work_struct *work)
if (!IS_ERR(data->clk_sec))
clk_disable(data->clk_sec);
- exynos_report_trigger(data->reg_conf);
+ exynos_report_trigger(data);
mutex_lock(&data->lock);
clk_enable(data->clk);
@@ -634,6 +878,9 @@ static void exynos4210_tmu_clear_irqs(struct exynos_tmu_data *data)
if (data->soc == SOC_ARCH_EXYNOS5260) {
tmu_intstat = EXYNOS5260_TMU_REG_INTSTAT;
tmu_intclear = EXYNOS5260_TMU_REG_INTCLEAR;
+ } else if (data->soc == SOC_ARCH_EXYNOS7) {
+ tmu_intstat = EXYNOS7_TMU_REG_INTPEND;
+ tmu_intclear = EXYNOS7_TMU_REG_INTPEND;
} else {
tmu_intstat = EXYNOS_TMU_REG_INTSTAT;
tmu_intclear = EXYNOS_TMU_REG_INTCLEAR;
@@ -673,55 +920,94 @@ static irqreturn_t exynos_tmu_irq(int irq, void *id)
static const struct of_device_id exynos_tmu_match[] = {
{
.compatible = "samsung,exynos3250-tmu",
- .data = &exynos3250_default_tmu_data,
},
{
.compatible = "samsung,exynos4210-tmu",
- .data = &exynos4210_default_tmu_data,
},
{
.compatible = "samsung,exynos4412-tmu",
- .data = &exynos4412_default_tmu_data,
},
{
.compatible = "samsung,exynos5250-tmu",
- .data = &exynos5250_default_tmu_data,
},
{
.compatible = "samsung,exynos5260-tmu",
- .data = &exynos5260_default_tmu_data,
},
{
.compatible = "samsung,exynos5420-tmu",
- .data = &exynos5420_default_tmu_data,
},
{
.compatible = "samsung,exynos5420-tmu-ext-triminfo",
- .data = &exynos5420_default_tmu_data,
},
{
.compatible = "samsung,exynos5440-tmu",
- .data = &exynos5440_default_tmu_data,
+ },
+ {
+ .compatible = "samsung,exynos7-tmu",
},
{},
};
MODULE_DEVICE_TABLE(of, exynos_tmu_match);
-static inline struct exynos_tmu_platform_data *exynos_get_driver_data(
- struct platform_device *pdev, int id)
+static int exynos_of_get_soc_type(struct device_node *np)
+{
+ if (of_device_is_compatible(np, "samsung,exynos3250-tmu"))
+ return SOC_ARCH_EXYNOS3250;
+ else if (of_device_is_compatible(np, "samsung,exynos4210-tmu"))
+ return SOC_ARCH_EXYNOS4210;
+ else if (of_device_is_compatible(np, "samsung,exynos4412-tmu"))
+ return SOC_ARCH_EXYNOS4412;
+ else if (of_device_is_compatible(np, "samsung,exynos5250-tmu"))
+ return SOC_ARCH_EXYNOS5250;
+ else if (of_device_is_compatible(np, "samsung,exynos5260-tmu"))
+ return SOC_ARCH_EXYNOS5260;
+ else if (of_device_is_compatible(np, "samsung,exynos5420-tmu"))
+ return SOC_ARCH_EXYNOS5420;
+ else if (of_device_is_compatible(np,
+ "samsung,exynos5420-tmu-ext-triminfo"))
+ return SOC_ARCH_EXYNOS5420_TRIMINFO;
+ else if (of_device_is_compatible(np, "samsung,exynos5440-tmu"))
+ return SOC_ARCH_EXYNOS5440;
+ else if (of_device_is_compatible(np, "samsung,exynos7-tmu"))
+ return SOC_ARCH_EXYNOS7;
+
+ return -EINVAL;
+}
+
+static int exynos_of_sensor_conf(struct device_node *np,
+ struct exynos_tmu_platform_data *pdata)
{
- struct exynos_tmu_init_data *data_table;
- struct exynos_tmu_platform_data *tmu_data;
- const struct of_device_id *match;
+ u32 value;
+ int ret;
- match = of_match_node(exynos_tmu_match, pdev->dev.of_node);
- if (!match)
- return NULL;
- data_table = (struct exynos_tmu_init_data *) match->data;
- if (!data_table || id >= data_table->tmu_count)
- return NULL;
- tmu_data = data_table->tmu_data;
- return (struct exynos_tmu_platform_data *) (tmu_data + id);
+ of_node_get(np);
+
+ ret = of_property_read_u32(np, "samsung,tmu_gain", &value);
+ pdata->gain = (u8)value;
+ of_property_read_u32(np, "samsung,tmu_reference_voltage", &value);
+ pdata->reference_voltage = (u8)value;
+ of_property_read_u32(np, "samsung,tmu_noise_cancel_mode", &value);
+ pdata->noise_cancel_mode = (u8)value;
+
+ of_property_read_u32(np, "samsung,tmu_efuse_value",
+ &pdata->efuse_value);
+ of_property_read_u32(np, "samsung,tmu_min_efuse_value",
+ &pdata->min_efuse_value);
+ of_property_read_u32(np, "samsung,tmu_max_efuse_value",
+ &pdata->max_efuse_value);
+
+ of_property_read_u32(np, "samsung,tmu_first_point_trim", &value);
+ pdata->first_point_trim = (u8)value;
+ of_property_read_u32(np, "samsung,tmu_second_point_trim", &value);
+ pdata->second_point_trim = (u8)value;
+ of_property_read_u32(np, "samsung,tmu_default_temp_offset", &value);
+ pdata->default_temp_offset = (u8)value;
+
+ of_property_read_u32(np, "samsung,tmu_cal_type", &pdata->cal_type);
+ of_property_read_u32(np, "samsung,tmu_cal_mode", &pdata->cal_mode);
+
+ of_node_put(np);
+ return 0;
}
static int exynos_map_dt_data(struct platform_device *pdev)
@@ -771,14 +1057,15 @@ static int exynos_map_dt_data(struct platform_device *pdev)
return -EADDRNOTAVAIL;
}
- pdata = exynos_get_driver_data(pdev, data->id);
- if (!pdata) {
- dev_err(&pdev->dev, "No platform init data supplied.\n");
- return -ENODEV;
- }
+ pdata = devm_kzalloc(&pdev->dev,
+ sizeof(struct exynos_tmu_platform_data),
+ GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+ exynos_of_sensor_conf(pdev->dev.of_node, pdata);
data->pdata = pdata;
- data->soc = pdata->type;
+ data->soc = exynos_of_get_soc_type(pdev->dev.of_node);
switch (data->soc) {
case SOC_ARCH_EXYNOS4210:
@@ -806,6 +1093,13 @@ static int exynos_map_dt_data(struct platform_device *pdev)
data->tmu_set_emulation = exynos5440_tmu_set_emulation;
data->tmu_clear_irqs = exynos5440_tmu_clear_irqs;
break;
+ case SOC_ARCH_EXYNOS7:
+ data->tmu_initialize = exynos7_tmu_initialize;
+ data->tmu_control = exynos7_tmu_control;
+ data->tmu_read = exynos7_tmu_read;
+ data->tmu_set_emulation = exynos4412_tmu_set_emulation;
+ data->tmu_clear_irqs = exynos4210_tmu_clear_irqs;
+ break;
default:
dev_err(&pdev->dev, "Platform not supported\n");
return -EINVAL;
@@ -834,12 +1128,16 @@ static int exynos_map_dt_data(struct platform_device *pdev)
return 0;
}
+static struct thermal_zone_of_device_ops exynos_sensor_ops = {
+ .get_temp = exynos_get_temp,
+ .set_emul_temp = exynos_tmu_set_emulation,
+};
+
static int exynos_tmu_probe(struct platform_device *pdev)
{
- struct exynos_tmu_data *data;
struct exynos_tmu_platform_data *pdata;
- struct thermal_sensor_conf *sensor_conf;
- int ret, i;
+ struct exynos_tmu_data *data;
+ int ret;
data = devm_kzalloc(&pdev->dev, sizeof(struct exynos_tmu_data),
GFP_KERNEL);
@@ -849,9 +1147,15 @@ static int exynos_tmu_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, data);
mutex_init(&data->lock);
+ data->tzd = thermal_zone_of_sensor_register(&pdev->dev, 0, data,
+ &exynos_sensor_ops);
+ if (IS_ERR(data->tzd)) {
+ pr_err("thermal: tz: %p ERROR\n", data->tzd);
+ return PTR_ERR(data->tzd);
+ }
ret = exynos_map_dt_data(pdev);
if (ret)
- return ret;
+ goto err_sensor;
pdata = data->pdata;
@@ -860,20 +1164,22 @@ static int exynos_tmu_probe(struct platform_device *pdev)
data->clk = devm_clk_get(&pdev->dev, "tmu_apbif");
if (IS_ERR(data->clk)) {
dev_err(&pdev->dev, "Failed to get clock\n");
- return PTR_ERR(data->clk);
+ ret = PTR_ERR(data->clk);
+ goto err_sensor;
}
data->clk_sec = devm_clk_get(&pdev->dev, "tmu_triminfo_apbif");
if (IS_ERR(data->clk_sec)) {
if (data->soc == SOC_ARCH_EXYNOS5420_TRIMINFO) {
dev_err(&pdev->dev, "Failed to get triminfo clock\n");
- return PTR_ERR(data->clk_sec);
+ ret = PTR_ERR(data->clk_sec);
+ goto err_sensor;
}
} else {
ret = clk_prepare(data->clk_sec);
if (ret) {
dev_err(&pdev->dev, "Failed to get clock\n");
- return ret;
+ goto err_sensor;
}
}
@@ -883,82 +1189,57 @@ static int exynos_tmu_probe(struct platform_device *pdev)
goto err_clk_sec;
}
- ret = exynos_tmu_initialize(pdev);
- if (ret) {
- dev_err(&pdev->dev, "Failed to initialize TMU\n");
- goto err_clk;
+ if (data->soc == SOC_ARCH_EXYNOS7) {
+ data->sclk = devm_clk_get(&pdev->dev, "tmu_sclk");
+ if (IS_ERR(data->sclk)) {
+ dev_err(&pdev->dev, "Failed to get sclk\n");
+ goto err_clk;
+ } else {
+ ret = clk_prepare_enable(data->sclk);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to enable sclk\n");
+ goto err_clk;
+ }
+ }
}
- exynos_tmu_control(pdev, true);
-
- /* Allocate a structure to register with the exynos core thermal */
- sensor_conf = devm_kzalloc(&pdev->dev,
- sizeof(struct thermal_sensor_conf), GFP_KERNEL);
- if (!sensor_conf) {
- ret = -ENOMEM;
- goto err_clk;
- }
- sprintf(sensor_conf->name, "therm_zone%d", data->id);
- sensor_conf->read_temperature = (int (*)(void *))exynos_tmu_read;
- sensor_conf->write_emul_temp =
- (int (*)(void *, unsigned long))exynos_tmu_set_emulation;
- sensor_conf->driver_data = data;
- sensor_conf->trip_data.trip_count = pdata->trigger_enable[0] +
- pdata->trigger_enable[1] + pdata->trigger_enable[2]+
- pdata->trigger_enable[3];
-
- for (i = 0; i < sensor_conf->trip_data.trip_count; i++) {
- sensor_conf->trip_data.trip_val[i] =
- pdata->threshold + pdata->trigger_levels[i];
- sensor_conf->trip_data.trip_type[i] =
- pdata->trigger_type[i];
- }
-
- sensor_conf->trip_data.trigger_falling = pdata->threshold_falling;
-
- sensor_conf->cooling_data.freq_clip_count = pdata->freq_tab_count;
- for (i = 0; i < pdata->freq_tab_count; i++) {
- sensor_conf->cooling_data.freq_data[i].freq_clip_max =
- pdata->freq_tab[i].freq_clip_max;
- sensor_conf->cooling_data.freq_data[i].temp_level =
- pdata->freq_tab[i].temp_level;
- }
- sensor_conf->dev = &pdev->dev;
- /* Register the sensor with thermal management interface */
- ret = exynos_register_thermal(sensor_conf);
+ ret = exynos_tmu_initialize(pdev);
if (ret) {
- if (ret != -EPROBE_DEFER)
- dev_err(&pdev->dev,
- "Failed to register thermal interface: %d\n",
- ret);
- goto err_clk;
+ dev_err(&pdev->dev, "Failed to initialize TMU\n");
+ goto err_sclk;
}
- data->reg_conf = sensor_conf;
ret = devm_request_irq(&pdev->dev, data->irq, exynos_tmu_irq,
IRQF_TRIGGER_RISING | IRQF_SHARED, dev_name(&pdev->dev), data);
if (ret) {
dev_err(&pdev->dev, "Failed to request irq: %d\n", data->irq);
- goto err_clk;
+ goto err_sclk;
}
+ exynos_tmu_control(pdev, true);
return 0;
+err_sclk:
+ clk_disable_unprepare(data->sclk);
err_clk:
clk_unprepare(data->clk);
err_clk_sec:
if (!IS_ERR(data->clk_sec))
clk_unprepare(data->clk_sec);
+err_sensor:
+ thermal_zone_of_sensor_unregister(&pdev->dev, data->tzd);
+
return ret;
}
static int exynos_tmu_remove(struct platform_device *pdev)
{
struct exynos_tmu_data *data = platform_get_drvdata(pdev);
+ struct thermal_zone_device *tzd = data->tzd;
- exynos_unregister_thermal(data->reg_conf);
-
+ thermal_zone_of_sensor_unregister(&pdev->dev, tzd);
exynos_tmu_control(pdev, false);
+ clk_disable_unprepare(data->sclk);
clk_unprepare(data->clk);
if (!IS_ERR(data->clk_sec))
clk_unprepare(data->clk_sec);
diff --git a/drivers/thermal/samsung/exynos_tmu.h b/drivers/thermal/samsung/exynos_tmu.h
index da3009bff6c4..4d71ec6c9aa0 100644
--- a/drivers/thermal/samsung/exynos_tmu.h
+++ b/drivers/thermal/samsung/exynos_tmu.h
@@ -23,16 +23,7 @@
#ifndef _EXYNOS_TMU_H
#define _EXYNOS_TMU_H
#include <linux/cpu_cooling.h>
-
-#include "exynos_thermal_common.h"
-
-enum calibration_type {
- TYPE_ONE_POINT_TRIMMING,
- TYPE_ONE_POINT_TRIMMING_25,
- TYPE_ONE_POINT_TRIMMING_85,
- TYPE_TWO_POINT_TRIMMING,
- TYPE_NONE,
-};
+#include <dt-bindings/thermal/thermal_exynos.h>
enum soc_type {
SOC_ARCH_EXYNOS3250 = 1,
@@ -43,38 +34,11 @@ enum soc_type {
SOC_ARCH_EXYNOS5420,
SOC_ARCH_EXYNOS5420_TRIMINFO,
SOC_ARCH_EXYNOS5440,
+ SOC_ARCH_EXYNOS7,
};
/**
* struct exynos_tmu_platform_data
- * @threshold: basic temperature for generating interrupt
- * 25 <= threshold <= 125 [unit: degree Celsius]
- * @threshold_falling: differntial value for setting threshold
- * of temperature falling interrupt.
- * @trigger_levels: array for each interrupt levels
- * [unit: degree Celsius]
- * 0: temperature for trigger_level0 interrupt
- * condition for trigger_level0 interrupt:
- * current temperature > threshold + trigger_levels[0]
- * 1: temperature for trigger_level1 interrupt
- * condition for trigger_level1 interrupt:
- * current temperature > threshold + trigger_levels[1]
- * 2: temperature for trigger_level2 interrupt
- * condition for trigger_level2 interrupt:
- * current temperature > threshold + trigger_levels[2]
- * 3: temperature for trigger_level3 interrupt
- * condition for trigger_level3 interrupt:
- * current temperature > threshold + trigger_levels[3]
- * @trigger_type: defines the type of trigger. Possible values are,
- * THROTTLE_ACTIVE trigger type
- * THROTTLE_PASSIVE trigger type
- * SW_TRIP trigger type
- * HW_TRIP
- * @trigger_enable[]: array to denote which trigger levels are enabled.
- * 1 = enable trigger_level[] interrupt,
- * 0 = disable trigger_level[] interrupt
- * @max_trigger_level: max trigger level supported by the TMU
- * @non_hw_trigger_levels: number of defined non-hardware trigger levels
* @gain: gain of amplifier in the positive-TC generator block
* 0 < gain <= 15
* @reference_voltage: reference voltage of amplifier
@@ -86,24 +50,12 @@ enum soc_type {
* @efuse_value: platform defined fuse value
* @min_efuse_value: minimum valid trimming data
* @max_efuse_value: maximum valid trimming data
- * @first_point_trim: temp value of the first point trimming
- * @second_point_trim: temp value of the second point trimming
* @default_temp_offset: default temperature offset in case of no trimming
* @cal_type: calibration type for temperature
- * @freq_clip_table: Table representing frequency reduction percentage.
- * @freq_tab_count: Count of the above table as frequency reduction may
- * applicable to only some of the trigger levels.
*
* This structure is required for configuration of exynos_tmu driver.
*/
struct exynos_tmu_platform_data {
- u8 threshold;
- u8 threshold_falling;
- u8 trigger_levels[MAX_TRIP_COUNT];
- enum trigger_type trigger_type[MAX_TRIP_COUNT];
- bool trigger_enable[MAX_TRIP_COUNT];
- u8 max_trigger_level;
- u8 non_hw_trigger_levels;
u8 gain;
u8 reference_voltage;
u8 noise_cancel_mode;
@@ -115,30 +67,9 @@ struct exynos_tmu_platform_data {
u8 second_point_trim;
u8 default_temp_offset;
- enum calibration_type cal_type;
enum soc_type type;
- struct freq_clip_table freq_tab[4];
- unsigned int freq_tab_count;
-};
-
-/**
- * struct exynos_tmu_init_data
- * @tmu_count: number of TMU instances.
- * @tmu_data: platform data of all TMU instances.
- * This structure is required to store data for multi-instance exynos tmu
- * driver.
- */
-struct exynos_tmu_init_data {
- int tmu_count;
- struct exynos_tmu_platform_data tmu_data[];
+ u32 cal_type;
+ u32 cal_mode;
};
-extern struct exynos_tmu_init_data const exynos3250_default_tmu_data;
-extern struct exynos_tmu_init_data const exynos4210_default_tmu_data;
-extern struct exynos_tmu_init_data const exynos4412_default_tmu_data;
-extern struct exynos_tmu_init_data const exynos5250_default_tmu_data;
-extern struct exynos_tmu_init_data const exynos5260_default_tmu_data;
-extern struct exynos_tmu_init_data const exynos5420_default_tmu_data;
-extern struct exynos_tmu_init_data const exynos5440_default_tmu_data;
-
#endif /* _EXYNOS_TMU_H */
diff --git a/drivers/thermal/samsung/exynos_tmu_data.c b/drivers/thermal/samsung/exynos_tmu_data.c
deleted file mode 100644
index b23910069f68..000000000000
--- a/drivers/thermal/samsung/exynos_tmu_data.c
+++ /dev/null
@@ -1,264 +0,0 @@
-/*
- * exynos_tmu_data.c - Samsung EXYNOS tmu data file
- *
- * Copyright (C) 2013 Samsung Electronics
- * Amit Daniel Kachhap <amit.daniel@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- */
-
-#include "exynos_thermal_common.h"
-#include "exynos_tmu.h"
-
-struct exynos_tmu_init_data const exynos4210_default_tmu_data = {
- .tmu_data = {
- {
- .threshold = 80,
- .trigger_levels[0] = 5,
- .trigger_levels[1] = 20,
- .trigger_levels[2] = 30,
- .trigger_enable[0] = true,
- .trigger_enable[1] = true,
- .trigger_enable[2] = true,
- .trigger_enable[3] = false,
- .trigger_type[0] = THROTTLE_ACTIVE,
- .trigger_type[1] = THROTTLE_ACTIVE,
- .trigger_type[2] = SW_TRIP,
- .max_trigger_level = 4,
- .non_hw_trigger_levels = 3,
- .gain = 15,
- .reference_voltage = 7,
- .cal_type = TYPE_ONE_POINT_TRIMMING,
- .min_efuse_value = 40,
- .max_efuse_value = 100,
- .first_point_trim = 25,
- .second_point_trim = 85,
- .default_temp_offset = 50,
- .freq_tab[0] = {
- .freq_clip_max = 800 * 1000,
- .temp_level = 85,
- },
- .freq_tab[1] = {
- .freq_clip_max = 200 * 1000,
- .temp_level = 100,
- },
- .freq_tab_count = 2,
- .type = SOC_ARCH_EXYNOS4210,
- },
- },
- .tmu_count = 1,
-};
-
-#define EXYNOS3250_TMU_DATA \
- .threshold_falling = 10, \
- .trigger_levels[0] = 70, \
- .trigger_levels[1] = 95, \
- .trigger_levels[2] = 110, \
- .trigger_levels[3] = 120, \
- .trigger_enable[0] = true, \
- .trigger_enable[1] = true, \
- .trigger_enable[2] = true, \
- .trigger_enable[3] = false, \
- .trigger_type[0] = THROTTLE_ACTIVE, \
- .trigger_type[1] = THROTTLE_ACTIVE, \
- .trigger_type[2] = SW_TRIP, \
- .trigger_type[3] = HW_TRIP, \
- .max_trigger_level = 4, \
- .non_hw_trigger_levels = 3, \
- .gain = 8, \
- .reference_voltage = 16, \
- .noise_cancel_mode = 4, \
- .cal_type = TYPE_TWO_POINT_TRIMMING, \
- .efuse_value = 55, \
- .min_efuse_value = 40, \
- .max_efuse_value = 100, \
- .first_point_trim = 25, \
- .second_point_trim = 85, \
- .default_temp_offset = 50, \
- .freq_tab[0] = { \
- .freq_clip_max = 800 * 1000, \
- .temp_level = 70, \
- }, \
- .freq_tab[1] = { \
- .freq_clip_max = 400 * 1000, \
- .temp_level = 95, \
- }, \
- .freq_tab_count = 2
-
-struct exynos_tmu_init_data const exynos3250_default_tmu_data = {
- .tmu_data = {
- {
- EXYNOS3250_TMU_DATA,
- .type = SOC_ARCH_EXYNOS3250,
- },
- },
- .tmu_count = 1,
-};
-
-#define EXYNOS4412_TMU_DATA \
- .threshold_falling = 10, \
- .trigger_levels[0] = 70, \
- .trigger_levels[1] = 95, \
- .trigger_levels[2] = 110, \
- .trigger_levels[3] = 120, \
- .trigger_enable[0] = true, \
- .trigger_enable[1] = true, \
- .trigger_enable[2] = true, \
- .trigger_enable[3] = false, \
- .trigger_type[0] = THROTTLE_ACTIVE, \
- .trigger_type[1] = THROTTLE_ACTIVE, \
- .trigger_type[2] = SW_TRIP, \
- .trigger_type[3] = HW_TRIP, \
- .max_trigger_level = 4, \
- .non_hw_trigger_levels = 3, \
- .gain = 8, \
- .reference_voltage = 16, \
- .noise_cancel_mode = 4, \
- .cal_type = TYPE_ONE_POINT_TRIMMING, \
- .efuse_value = 55, \
- .min_efuse_value = 40, \
- .max_efuse_value = 100, \
- .first_point_trim = 25, \
- .second_point_trim = 85, \
- .default_temp_offset = 50, \
- .freq_tab[0] = { \
- .freq_clip_max = 1400 * 1000, \
- .temp_level = 70, \
- }, \
- .freq_tab[1] = { \
- .freq_clip_max = 400 * 1000, \
- .temp_level = 95, \
- }, \
- .freq_tab_count = 2
-
-struct exynos_tmu_init_data const exynos4412_default_tmu_data = {
- .tmu_data = {
- {
- EXYNOS4412_TMU_DATA,
- .type = SOC_ARCH_EXYNOS4412,
- },
- },
- .tmu_count = 1,
-};
-
-struct exynos_tmu_init_data const exynos5250_default_tmu_data = {
- .tmu_data = {
- {
- EXYNOS4412_TMU_DATA,
- .type = SOC_ARCH_EXYNOS5250,
- },
- },
- .tmu_count = 1,
-};
-
-#define __EXYNOS5260_TMU_DATA \
- .threshold_falling = 10, \
- .trigger_levels[0] = 85, \
- .trigger_levels[1] = 103, \
- .trigger_levels[2] = 110, \
- .trigger_levels[3] = 120, \
- .trigger_enable[0] = true, \
- .trigger_enable[1] = true, \
- .trigger_enable[2] = true, \
- .trigger_enable[3] = false, \
- .trigger_type[0] = THROTTLE_ACTIVE, \
- .trigger_type[1] = THROTTLE_ACTIVE, \
- .trigger_type[2] = SW_TRIP, \
- .trigger_type[3] = HW_TRIP, \
- .max_trigger_level = 4, \
- .non_hw_trigger_levels = 3, \
- .gain = 8, \
- .reference_voltage = 16, \
- .noise_cancel_mode = 4, \
- .cal_type = TYPE_ONE_POINT_TRIMMING, \
- .efuse_value = 55, \
- .min_efuse_value = 40, \
- .max_efuse_value = 100, \
- .first_point_trim = 25, \
- .second_point_trim = 85, \
- .default_temp_offset = 50, \
- .freq_tab[0] = { \
- .freq_clip_max = 800 * 1000, \
- .temp_level = 85, \
- }, \
- .freq_tab[1] = { \
- .freq_clip_max = 200 * 1000, \
- .temp_level = 103, \
- }, \
- .freq_tab_count = 2, \
-
-#define EXYNOS5260_TMU_DATA \
- __EXYNOS5260_TMU_DATA \
- .type = SOC_ARCH_EXYNOS5260
-
-struct exynos_tmu_init_data const exynos5260_default_tmu_data = {
- .tmu_data = {
- { EXYNOS5260_TMU_DATA },
- { EXYNOS5260_TMU_DATA },
- { EXYNOS5260_TMU_DATA },
- { EXYNOS5260_TMU_DATA },
- { EXYNOS5260_TMU_DATA },
- },
- .tmu_count = 5,
-};
-
-#define EXYNOS5420_TMU_DATA \
- __EXYNOS5260_TMU_DATA \
- .type = SOC_ARCH_EXYNOS5420
-
-#define EXYNOS5420_TMU_DATA_SHARED \
- __EXYNOS5260_TMU_DATA \
- .type = SOC_ARCH_EXYNOS5420_TRIMINFO
-
-struct exynos_tmu_init_data const exynos5420_default_tmu_data = {
- .tmu_data = {
- { EXYNOS5420_TMU_DATA },
- { EXYNOS5420_TMU_DATA },
- { EXYNOS5420_TMU_DATA_SHARED },
- { EXYNOS5420_TMU_DATA_SHARED },
- { EXYNOS5420_TMU_DATA_SHARED },
- },
- .tmu_count = 5,
-};
-
-#define EXYNOS5440_TMU_DATA \
- .trigger_levels[0] = 100, \
- .trigger_levels[4] = 105, \
- .trigger_enable[0] = 1, \
- .trigger_type[0] = SW_TRIP, \
- .trigger_type[4] = HW_TRIP, \
- .max_trigger_level = 5, \
- .non_hw_trigger_levels = 1, \
- .gain = 5, \
- .reference_voltage = 16, \
- .noise_cancel_mode = 4, \
- .cal_type = TYPE_ONE_POINT_TRIMMING, \
- .efuse_value = 0x5b2d, \
- .min_efuse_value = 16, \
- .max_efuse_value = 76, \
- .first_point_trim = 25, \
- .second_point_trim = 70, \
- .default_temp_offset = 25, \
- .type = SOC_ARCH_EXYNOS5440
-
-struct exynos_tmu_init_data const exynos5440_default_tmu_data = {
- .tmu_data = {
- { EXYNOS5440_TMU_DATA } ,
- { EXYNOS5440_TMU_DATA } ,
- { EXYNOS5440_TMU_DATA } ,
- },
- .tmu_count = 3,
-};
diff --git a/drivers/thermal/step_wise.c b/drivers/thermal/step_wise.c
index fdd1f523a1ed..5a0f12d08e8b 100644
--- a/drivers/thermal/step_wise.c
+++ b/drivers/thermal/step_wise.c
@@ -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
diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c
index 87e0b0782023..48491d1a81d6 100644
--- a/drivers/thermal/thermal_core.c
+++ b/drivers/thermal/thermal_core.c
@@ -1759,11 +1759,7 @@ int thermal_generate_netlink_event(struct thermal_zone_device *tz,
thermal_event->event = event;
/* send multicast genetlink message */
- result = genlmsg_end(skb, msg_header);
- if (result < 0) {
- nlmsg_free(skb);
- return result;
- }
+ genlmsg_end(skb, msg_header);
result = genlmsg_multicast(&thermal_event_genl_family, skb, 0,
0, GFP_ATOMIC);