aboutsummaryrefslogtreecommitdiff
path: root/drivers/thermal
diff options
context:
space:
mode:
authorZhang Rui <rui.zhang@intel.com>2013-04-15 09:26:58 +0800
committerZhang Rui <rui.zhang@intel.com>2013-04-15 09:26:58 +0800
commitd13cb03aef0c062dcdd16b411bd4c02c1574ff08 (patch)
tree0ee1d535ddf0ca49cd88484f042d12996f689e1c /drivers/thermal
parent2fd1db8819fbf73b5f74b4b4a205ab7be0957944 (diff)
parentbbf7fc88c78f7317e2cdcf77e974c8a9a8312641 (diff)
Merge branch 'thermal' of git://git.kernel.org/pub/scm/linux/kernel/git/rzhang/linux into next
Conflicts: drivers/thermal/cpu_cooling.c
Diffstat (limited to 'drivers/thermal')
-rw-r--r--drivers/thermal/Kconfig23
-rw-r--r--drivers/thermal/Makefile10
-rw-r--r--drivers/thermal/armada_thermal.c232
-rw-r--r--drivers/thermal/cpu_cooling.c154
-rw-r--r--drivers/thermal/exynos_thermal.c184
-rw-r--r--drivers/thermal/fair_share.c15
-rw-r--r--drivers/thermal/rcar_thermal.c34
-rw-r--r--drivers/thermal/step_wise.c26
-rw-r--r--drivers/thermal/thermal_core.c (renamed from drivers/thermal/thermal_sys.c)59
-rw-r--r--drivers/thermal/thermal_core.h27
-rw-r--r--drivers/thermal/user_space.c15
11 files changed, 536 insertions, 243 deletions
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index a764f165b58..d1c2160492a 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -67,7 +67,7 @@ config THERMAL_GOV_USER_SPACE
Enable this to let the user space manage the platform thermals.
config CPU_THERMAL
- tristate "generic cpu cooling support"
+ bool "generic cpu cooling support"
depends on CPU_FREQ
select CPU_FREQ_TABLE
help
@@ -86,6 +86,10 @@ config THERMAL_EMULATION
user can manually input temperature and test the different trip
threshold behaviour for simulation purpose.
+ WARNING: Be careful while enabling this option on production systems,
+ because userland can easily disable the thermal policy by simply
+ flooding this sysfs node with low temperature values.
+
config SPEAR_THERMAL
bool "SPEAr thermal sensor driver"
depends on PLAT_SPEAR
@@ -117,15 +121,6 @@ config EXYNOS_THERMAL
If you say yes here you get support for TMU (Thermal Management
Unit) on SAMSUNG EXYNOS series of SoC.
-config EXYNOS_THERMAL_EMUL
- bool "EXYNOS TMU emulation mode support"
- depends on EXYNOS_THERMAL
- help
- Exynos 4412 and 4414 and 5 series has emulation mode on TMU.
- Enable this option will be make sysfs node in exynos thermal platform
- device directory to support emulation mode. With emulation mode sysfs
- node, you can manually input temperature to TMU for simulation purpose.
-
config DOVE_THERMAL
tristate "Temperature sensor on Marvell Dove SoCs"
depends on ARCH_DOVE
@@ -144,6 +139,14 @@ config DB8500_THERMAL
created. Cooling devices can be bound to the trip points to cool this
thermal zone if trip points reached.
+config ARMADA_THERMAL
+ tristate "Armada 370/XP thermal management"
+ depends on ARCH_MVEBU
+ depends on OF
+ help
+ Enable this option if you want to have support for thermal management
+ controller present in Armada 370 and Armada XP SoC.
+
config DB8500_CPUFREQ_COOLING
tristate "DB8500 cpufreq cooling"
depends on ARCH_U8500
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
index d3a2b38c31e..c054d410ac3 100644
--- a/drivers/thermal/Makefile
+++ b/drivers/thermal/Makefile
@@ -3,14 +3,15 @@
#
obj-$(CONFIG_THERMAL) += thermal_sys.o
+thermal_sys-y += thermal_core.o
# governors
-obj-$(CONFIG_THERMAL_GOV_FAIR_SHARE) += fair_share.o
-obj-$(CONFIG_THERMAL_GOV_STEP_WISE) += step_wise.o
-obj-$(CONFIG_THERMAL_GOV_USER_SPACE) += user_space.o
+thermal_sys-$(CONFIG_THERMAL_GOV_FAIR_SHARE) += fair_share.o
+thermal_sys-$(CONFIG_THERMAL_GOV_STEP_WISE) += step_wise.o
+thermal_sys-$(CONFIG_THERMAL_GOV_USER_SPACE) += user_space.o
# cpufreq cooling
-obj-$(CONFIG_CPU_THERMAL) += cpu_cooling.o
+thermal_sys-$(CONFIG_CPU_THERMAL) += cpu_cooling.o
# platform thermal drivers
obj-$(CONFIG_SPEAR_THERMAL) += spear_thermal.o
@@ -19,6 +20,7 @@ obj-$(CONFIG_KIRKWOOD_THERMAL) += kirkwood_thermal.o
obj-$(CONFIG_EXYNOS_THERMAL) += exynos_thermal.o
obj-$(CONFIG_DOVE_THERMAL) += dove_thermal.o
obj-$(CONFIG_DB8500_THERMAL) += db8500_thermal.o
+obj-$(CONFIG_ARMADA_THERMAL) += armada_thermal.o
obj-$(CONFIG_DB8500_CPUFREQ_COOLING) += db8500_cpufreq_cooling.o
obj-$(CONFIG_INTEL_POWERCLAMP) += intel_powerclamp.o
diff --git a/drivers/thermal/armada_thermal.c b/drivers/thermal/armada_thermal.c
new file mode 100644
index 00000000000..5b4d75fd7b4
--- /dev/null
+++ b/drivers/thermal/armada_thermal.c
@@ -0,0 +1,232 @@
+/*
+ * Marvell Armada 370/XP thermal sensor driver
+ *
+ * Copyright (C) 2013 Marvell
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/of_device.h>
+#include <linux/thermal.h>
+
+#define THERMAL_VALID_OFFSET 9
+#define THERMAL_VALID_MASK 0x1
+#define THERMAL_TEMP_OFFSET 10
+#define THERMAL_TEMP_MASK 0x1ff
+
+/* Thermal Manager Control and Status Register */
+#define PMU_TDC0_SW_RST_MASK (0x1 << 1)
+#define PMU_TM_DISABLE_OFFS 0
+#define PMU_TM_DISABLE_MASK (0x1 << PMU_TM_DISABLE_OFFS)
+#define PMU_TDC0_REF_CAL_CNT_OFFS 11
+#define PMU_TDC0_REF_CAL_CNT_MASK (0x1ff << PMU_TDC0_REF_CAL_CNT_OFFS)
+#define PMU_TDC0_OTF_CAL_MASK (0x1 << 30)
+#define PMU_TDC0_START_CAL_MASK (0x1 << 25)
+
+struct armada_thermal_ops;
+
+/* Marvell EBU Thermal Sensor Dev Structure */
+struct armada_thermal_priv {
+ void __iomem *sensor;
+ void __iomem *control;
+ struct armada_thermal_ops *ops;
+};
+
+struct armada_thermal_ops {
+ /* Initialize the sensor */
+ void (*init_sensor)(struct armada_thermal_priv *);
+
+ /* Test for a valid sensor value (optional) */
+ bool (*is_valid)(struct armada_thermal_priv *);
+};
+
+static void armadaxp_init_sensor(struct armada_thermal_priv *priv)
+{
+ unsigned long reg;
+
+ reg = readl_relaxed(priv->control);
+ reg |= PMU_TDC0_OTF_CAL_MASK;
+ writel(reg, priv->control);
+
+ /* Reference calibration value */
+ reg &= ~PMU_TDC0_REF_CAL_CNT_MASK;
+ reg |= (0xf1 << PMU_TDC0_REF_CAL_CNT_OFFS);
+ writel(reg, priv->control);
+
+ /* Reset the sensor */
+ reg = readl_relaxed(priv->control);
+ writel((reg | PMU_TDC0_SW_RST_MASK), priv->control);
+
+ writel(reg, priv->control);
+
+ /* Enable the sensor */
+ reg = readl_relaxed(priv->sensor);
+ reg &= ~PMU_TM_DISABLE_MASK;
+ writel(reg, priv->sensor);
+}
+
+static void armada370_init_sensor(struct armada_thermal_priv *priv)
+{
+ unsigned long reg;
+
+ reg = readl_relaxed(priv->control);
+ reg |= PMU_TDC0_OTF_CAL_MASK;
+ writel(reg, priv->control);
+
+ /* Reference calibration value */
+ reg &= ~PMU_TDC0_REF_CAL_CNT_MASK;
+ reg |= (0xf1 << PMU_TDC0_REF_CAL_CNT_OFFS);
+ writel(reg, priv->control);
+
+ reg &= ~PMU_TDC0_START_CAL_MASK;
+ writel(reg, priv->control);
+
+ mdelay(10);
+}
+
+static bool armada_is_valid(struct armada_thermal_priv *priv)
+{
+ unsigned long reg = readl_relaxed(priv->sensor);
+
+ return (reg >> THERMAL_VALID_OFFSET) & THERMAL_VALID_MASK;
+}
+
+static int armada_get_temp(struct thermal_zone_device *thermal,
+ unsigned long *temp)
+{
+ struct armada_thermal_priv *priv = thermal->devdata;
+ unsigned long reg;
+
+ /* Valid check */
+ if (priv->ops->is_valid && !priv->ops->is_valid(priv)) {
+ dev_err(&thermal->device,
+ "Temperature sensor reading not valid\n");
+ return -EIO;
+ }
+
+ reg = readl_relaxed(priv->sensor);
+ reg = (reg >> THERMAL_TEMP_OFFSET) & THERMAL_TEMP_MASK;
+ *temp = (3153000000UL - (10000000UL*reg)) / 13825;
+ return 0;
+}
+
+static struct thermal_zone_device_ops ops = {
+ .get_temp = armada_get_temp,
+};
+
+static const struct armada_thermal_ops armadaxp_ops = {
+ .init_sensor = armadaxp_init_sensor,
+};
+
+static const struct armada_thermal_ops armada370_ops = {
+ .is_valid = armada_is_valid,
+ .init_sensor = armada370_init_sensor,
+};
+
+static const struct of_device_id armada_thermal_id_table[] = {
+ {
+ .compatible = "marvell,armadaxp-thermal",
+ .data = &armadaxp_ops,
+ },
+ {
+ .compatible = "marvell,armada370-thermal",
+ .data = &armada370_ops,
+ },
+ {
+ /* sentinel */
+ },
+};
+MODULE_DEVICE_TABLE(of, armada_thermal_id_table);
+
+static int armada_thermal_probe(struct platform_device *pdev)
+{
+ struct thermal_zone_device *thermal;
+ const struct of_device_id *match;
+ struct armada_thermal_priv *priv;
+ struct resource *res;
+
+ match = of_match_device(armada_thermal_id_table, &pdev->dev);
+ if (!match)
+ return -ENODEV;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "Failed to get platform resource\n");
+ return -ENODEV;
+ }
+
+ priv->sensor = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(priv->sensor))
+ return PTR_ERR(priv->sensor);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!res) {
+ dev_err(&pdev->dev, "Failed to get platform resource\n");
+ return -ENODEV;
+ }
+
+ priv->control = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(priv->control))
+ return PTR_ERR(priv->control);
+
+ priv->ops = (struct armada_thermal_ops *)match->data;
+ priv->ops->init_sensor(priv);
+
+ thermal = thermal_zone_device_register("armada_thermal", 0, 0,
+ priv, &ops, NULL, 0, 0);
+ if (IS_ERR(thermal)) {
+ dev_err(&pdev->dev,
+ "Failed to register thermal zone device\n");
+ return PTR_ERR(thermal);
+ }
+
+ platform_set_drvdata(pdev, thermal);
+
+ return 0;
+}
+
+static int armada_thermal_exit(struct platform_device *pdev)
+{
+ struct thermal_zone_device *armada_thermal =
+ platform_get_drvdata(pdev);
+
+ thermal_zone_device_unregister(armada_thermal);
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static struct platform_driver armada_thermal_driver = {
+ .probe = armada_thermal_probe,
+ .remove = armada_thermal_exit,
+ .driver = {
+ .name = "armada_thermal",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(armada_thermal_id_table),
+ },
+};
+
+module_platform_driver(armada_thermal_driver);
+
+MODULE_AUTHOR("Ezequiel Garcia <ezequiel.garcia@free-electrons.com>");
+MODULE_DESCRIPTION("Armada 370/XP thermal driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c
index be2e6b0e534..5f5c780bcd9 100644
--- a/drivers/thermal/cpu_cooling.c
+++ b/drivers/thermal/cpu_cooling.c
@@ -108,54 +108,120 @@ static int is_cpufreq_valid(int cpu)
return !cpufreq_get_policy(&policy, cpu);
}
-/**
- * get_cpu_frequency - get the absolute value of frequency from level.
- * @cpu: cpu for which frequency is fetched.
- * @level: level of frequency, equals cooling state of cpu cooling device
- * e.g level=0 --> 1st MAX FREQ, level=1 ---> 2nd MAX FREQ, .... etc
- */
-static unsigned int get_cpu_frequency(unsigned int cpu, unsigned long level)
+enum cpufreq_cooling_property {
+ GET_LEVEL,
+ GET_FREQ,
+ GET_MAXL,
+};
+
+/*
+ * this is the common function to
+ * 1. get maximum cpu cooling states
+ * 2. translate frequency to cooling state
+ * 3. translate cooling state to frequency
+ * Note that the code may be not in good shape
+ * but it is written in this way in order to:
+ * a) reduce duplicate code as most of the code can be shared.
+ * b) make sure the logic is consistent when translating between
+ * cooling states and frequencies.
+*/
+static int get_property(unsigned int cpu, unsigned long input,
+ unsigned int* output, enum cpufreq_cooling_property property)
{
- int ret = 0, i = 0;
- unsigned long level_index;
- bool descend = false;
+ int i, j;
+ unsigned long max_level = 0, level;
+ unsigned int freq = CPUFREQ_ENTRY_INVALID;
+ int descend = -1;
struct cpufreq_frequency_table *table =
cpufreq_frequency_get_table(cpu);
+
+ if (!output)
+ return -EINVAL;
+
if (!table)
- return ret;
+ return -EINVAL;
- while (table[i].frequency != CPUFREQ_TABLE_END) {
+
+ for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) {
+ /* ignore invalid entries */
if (table[i].frequency == CPUFREQ_ENTRY_INVALID)
continue;
- /*check if table in ascending or descending order*/
- if ((table[i + 1].frequency != CPUFREQ_TABLE_END) &&
- (table[i + 1].frequency < table[i].frequency)
- && !descend) {
- descend = true;
- }
+ /* ignore duplicate entry */
+ if (freq == table[i].frequency)
+ continue;
+
+ /* get the frequency order */
+ if (freq != CPUFREQ_ENTRY_INVALID && descend != -1)
+ descend = !!(freq > table[i].frequency);
- /*return if level matched and table in descending order*/
- if (descend && i == level)
- return table[i].frequency;
- i++;
+ freq = table[i].frequency;
+ max_level++;
}
- i--;
- if (level > i || descend)
- return ret;
- level_index = i - level;
+ /* get max level */
+ if (property == GET_MAXL) {
+ *output = (unsigned int)max_level;
+ return 0;
+ }
+
+ if (property == GET_FREQ)
+ level = descend ? input : (max_level - input -1);
+
- /*Scan the table in reverse order and match the level*/
- while (i >= 0) {
+ for (i = 0, j = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) {
+ /* ignore invalid entry */
if (table[i].frequency == CPUFREQ_ENTRY_INVALID)
continue;
- /*return if level matched*/
- if (i == level_index)
- return table[i].frequency;
- i--;
+
+ /* ignore duplicate entry */
+ if (freq == table[i].frequency)
+ continue;
+
+ /* now we have a valid frequency entry */
+ freq = table[i].frequency;
+
+ if (property == GET_LEVEL && (unsigned int)input == freq) {
+ /* get level by frequency */
+ *output = descend ? j : (max_level - j - 1);
+ return 0;
+ }
+ if (property == GET_FREQ && level == j) {
+ /* get frequency by level */
+ *output = freq;
+ return 0;
+ }
+ j++;
}
- return ret;
+ return -EINVAL;
+}
+
+unsigned long cpufreq_cooling_get_level(unsigned int cpu, unsigned int freq)
+{
+ unsigned int val;
+
+ if (get_property(cpu, (unsigned long)freq, &val, GET_LEVEL))
+ return THERMAL_CSTATE_INVALID;
+ return (unsigned long)val;
+}
+
+EXPORT_SYMBOL(cpufreq_cooling_get_level);
+
+/**
+ * get_cpu_frequency - get the absolute value of frequency from level.
+ * @cpu: cpu for which frequency is fetched.
+ * @level: level of frequency, equals cooling state of cpu cooling device
+ * e.g level=0 --> 1st MAX FREQ, level=1 ---> 2nd MAX FREQ, .... etc
+ */
+static unsigned int get_cpu_frequency(unsigned int cpu, unsigned long level)
+{
+ int ret = 0;
+ unsigned int freq;
+
+ ret = get_property(cpu, level, &freq, GET_FREQ);
+ if (ret)
+ return 0;
+ return freq;
}
/**
@@ -237,29 +303,17 @@ static int cpufreq_get_max_state(struct thermal_cooling_device *cdev,
struct cpufreq_cooling_device *cpufreq_device = cdev->devdata;
struct cpumask *mask = &cpufreq_device->allowed_cpus;
unsigned int cpu;
- struct cpufreq_frequency_table *table;
unsigned long count = 0;
- int i = 0;
+ int ret;
cpu = cpumask_any(mask);
- table = cpufreq_frequency_get_table(cpu);
- if (!table) {
- *state = 0;
- return 0;
- }
- for (i = 0; (table[i].frequency != CPUFREQ_TABLE_END); i++) {
- if (table[i].frequency == CPUFREQ_ENTRY_INVALID)
- continue;
- count++;
- }
+ ret = get_property(cpu, 0, (unsigned int *)&count, GET_MAXL);
- if (count > 0) {
- *state = --count;
- return 0;
- }
+ if (count > 0)
+ *state = count;
- return -EINVAL;
+ return ret;
}
/**
diff --git a/drivers/thermal/exynos_thermal.c b/drivers/thermal/exynos_thermal.c
index 46568c078de..e34d842cc67 100644
--- a/drivers/thermal/exynos_thermal.c
+++ b/drivers/thermal/exynos_thermal.c
@@ -39,8 +39,6 @@
#include <linux/cpu_cooling.h>
#include <linux/of.h>
-#include <plat/cpu.h>
-
/* Exynos generic registers */
#define EXYNOS_TMU_REG_TRIMINFO 0x0
#define EXYNOS_TMU_REG_CONTROL 0x20
@@ -100,13 +98,13 @@
#define IDLE_INTERVAL 10000
#define MCELSIUS 1000
-#ifdef CONFIG_EXYNOS_THERMAL_EMUL
+#ifdef CONFIG_THERMAL_EMULATION
#define EXYNOS_EMUL_TIME 0x57F0
#define EXYNOS_EMUL_TIME_SHIFT 16
#define EXYNOS_EMUL_DATA_SHIFT 8
#define EXYNOS_EMUL_DATA_MASK 0xFF
#define EXYNOS_EMUL_ENABLE 0x1
-#endif /* CONFIG_EXYNOS_THERMAL_EMUL */
+#endif /* CONFIG_THERMAL_EMULATION */
/* CPU Zone information */
#define PANIC_ZONE 4
@@ -145,6 +143,7 @@ struct thermal_cooling_conf {
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 *private_data;
@@ -242,26 +241,6 @@ static int exynos_get_crit_temp(struct thermal_zone_device *thermal,
return ret;
}
-static int exynos_get_frequency_level(unsigned int cpu, unsigned int freq)
-{
- int i = 0, ret = -EINVAL;
- struct cpufreq_frequency_table *table = NULL;
-#ifdef CONFIG_CPU_FREQ
- table = cpufreq_frequency_get_table(cpu);
-#endif
- if (!table)
- return ret;
-
- while (table[i].frequency != CPUFREQ_TABLE_END) {
- if (table[i].frequency == CPUFREQ_ENTRY_INVALID)
- continue;
- if (table[i].frequency == freq)
- return i;
- i++;
- }
- return ret;
-}
-
/* Bind callback functions for thermal zone */
static int exynos_bind(struct thermal_zone_device *thermal,
struct thermal_cooling_device *cdev)
@@ -288,8 +267,8 @@ static int exynos_bind(struct thermal_zone_device *thermal,
/* 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 = exynos_get_frequency_level(0, clip_data->freq_clip_max);
- if (level < 0)
+ 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:
@@ -369,6 +348,23 @@ static int exynos_get_temp(struct thermal_zone_device *thermal,
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;
+
+ if (!th_zone->sensor_conf) {
+ pr_info("Temperature sensor not initialised\n");
+ return -EINVAL;
+ }
+ data = th_zone->sensor_conf->private_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)
@@ -392,6 +388,7 @@ static struct thermal_zone_device_ops const 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,
@@ -714,6 +711,47 @@ static int exynos_tmu_read(struct exynos_tmu_data *data)
return temp;
}
+#ifdef CONFIG_THERMAL_EMULATION
+static int exynos_tmu_set_emulation(void *drv_data, unsigned long temp)
+{
+ struct exynos_tmu_data *data = drv_data;
+ unsigned int reg;
+ int ret = -EINVAL;
+
+ if (data->soc == SOC_ARCH_EXYNOS4210)
+ goto out;
+
+ if (temp && temp < MCELSIUS)
+ goto out;
+
+ mutex_lock(&data->lock);
+ clk_enable(data->clk);
+
+ reg = readl(data->base + EXYNOS_EMUL_CON);
+
+ if (temp) {
+ temp /= MCELSIUS;
+
+ reg = (EXYNOS_EMUL_TIME << EXYNOS_EMUL_TIME_SHIFT) |
+ (temp_to_code(data, temp)
+ << EXYNOS_EMUL_DATA_SHIFT) | EXYNOS_EMUL_ENABLE;
+ } else {
+ reg &= ~EXYNOS_EMUL_ENABLE;
+ }
+
+ writel(reg, data->base + EXYNOS_EMUL_CON);
+
+ clk_disable(data->clk);
+ mutex_unlock(&data->lock);
+ return 0;
+out:
+ return ret;
+}
+#else
+static int exynos_tmu_set_emulation(void *drv_data, unsigned long temp)
+ { return -EINVAL; }
+#endif/*CONFIG_THERMAL_EMULATION*/
+
static void exynos_tmu_work(struct work_struct *work)
{
struct exynos_tmu_data *data = container_of(work,
@@ -747,6 +785,7 @@ static irqreturn_t exynos_tmu_irq(int irq, void *id)
static struct thermal_sensor_conf exynos_sensor_conf = {
.name = "exynos-therm",
.read_temperature = (int (*)(void *))exynos_tmu_read,
+ .write_emul_temp = exynos_tmu_set_emulation,
};
#if defined(CONFIG_CPU_EXYNOS4210)
@@ -853,93 +892,6 @@ static inline struct exynos_tmu_platform_data *exynos_get_driver_data(
platform_get_device_id(pdev)->driver_data;
}
-#ifdef CONFIG_EXYNOS_THERMAL_EMUL
-static ssize_t exynos_tmu_emulation_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- struct platform_device *pdev = container_of(dev,
- struct platform_device, dev);
- struct exynos_tmu_data *data = platform_get_drvdata(pdev);
- unsigned int reg;
- u8 temp_code;
- int temp = 0;
-
- if (data->soc == SOC_ARCH_EXYNOS4210)
- goto out;
-
- mutex_lock(&data->lock);
- clk_enable(data->clk);
- reg = readl(data->base + EXYNOS_EMUL_CON);
- clk_disable(data->clk);
- mutex_unlock(&data->lock);
-
- if (reg & EXYNOS_EMUL_ENABLE) {
- reg >>= EXYNOS_EMUL_DATA_SHIFT;
- temp_code = reg & EXYNOS_EMUL_DATA_MASK;
- temp = code_to_temp(data, temp_code);
- }
-out:
- return sprintf(buf, "%d\n", temp * MCELSIUS);
-}
-
-static ssize_t exynos_tmu_emulation_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct platform_device *pdev = container_of(dev,
- struct platform_device, dev);
- struct exynos_tmu_data *data = platform_get_drvdata(pdev);
- unsigned int reg;
- int temp;
-
- if (data->soc == SOC_ARCH_EXYNOS4210)
- goto out;
-
- if (!sscanf(buf, "%d\n", &temp) || temp < 0)
- return -EINVAL;
-
- mutex_lock(&data->lock);
- clk_enable(data->clk);
-
- reg = readl(data->base + EXYNOS_EMUL_CON);
-
- if (temp) {
- /* Both CELSIUS and MCELSIUS type are available for input */
- if (temp > MCELSIUS)
- temp /= MCELSIUS;
-
- reg = (EXYNOS_EMUL_TIME << EXYNOS_EMUL_TIME_SHIFT) |
- (temp_to_code(data, (temp / MCELSIUS))
- << EXYNOS_EMUL_DATA_SHIFT) | EXYNOS_EMUL_ENABLE;
- } else {
- reg &= ~EXYNOS_EMUL_ENABLE;
- }
-
- writel(reg, data->base + EXYNOS_EMUL_CON);
-
- clk_disable(data->clk);
- mutex_unlock(&data->lock);
-
-out:
- return count;
-}
-
-static DEVICE_ATTR(emulation, 0644, exynos_tmu_emulation_show,
- exynos_tmu_emulation_store);
-static int create_emulation_sysfs(struct device *dev)
-{
- return device_create_file(dev, &dev_attr_emulation);
-}
-static void remove_emulation_sysfs(struct device *dev)
-{
- device_remove_file(dev, &dev_attr_emulation);
-}
-#else
-static inline int create_emulation_sysfs(struct device *dev) { return 0; }
-static inline void remove_emulation_sysfs(struct device *dev) {}
-#endif
-
static int exynos_tmu_probe(struct platform_device *pdev)
{
struct exynos_tmu_data *data;
@@ -1039,10 +991,6 @@ static int exynos_tmu_probe(struct platform_device *pdev)
goto err_clk;
}
- ret = create_emulation_sysfs(&pdev->dev);
- if (ret)
- dev_err(&pdev->dev, "Failed to create emulation mode sysfs node\n");
-
return 0;
err_clk:
platform_set_drvdata(pdev, NULL);
@@ -1054,8 +1002,6 @@ static int exynos_tmu_remove(struct platform_device *pdev)
{
struct exynos_tmu_data *data = platform_get_drvdata(pdev);
- remove_emulation_sysfs(&pdev->dev);
-
exynos_tmu_control(pdev, false);
exynos_unregister_thermal();
diff --git a/drivers/thermal/fair_share.c b/drivers/thermal/fair_share.c
index 792479f2b64..944ba2f340c 100644
--- a/drivers/thermal/fair_share.c
+++ b/drivers/thermal/fair_share.c
@@ -22,9 +22,6 @@
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/module.h>
#include <linux/thermal.h>
#include "thermal_core.h"
@@ -111,23 +108,15 @@ static int fair_share_throttle(struct thermal_zone_device *tz, int trip)
static struct thermal_governor thermal_gov_fair_share = {
.name = "fair_share",
.throttle = fair_share_throttle,
- .owner = THIS_MODULE,
};
-static int __init thermal_gov_fair_share_init(void)
+int thermal_gov_fair_share_register(void)
{
return thermal_register_governor(&thermal_gov_fair_share);
}
-static void __exit thermal_gov_fair_share_exit(void)
+void thermal_gov_fair_share_unregister(void)
{
thermal_unregister_governor(&thermal_gov_fair_share);
}
-/* This should load after thermal framework */
-fs_initcall(thermal_gov_fair_share_init);
-module_exit(thermal_gov_fair_share_exit);
-
-MODULE_AUTHOR("Durgadoss R");
-MODULE_DESCRIPTION("A simple weight based thermal throttling governor");
-MODULE_LICENSE("GPL");
diff --git a/drivers/thermal/rcar_thermal.c b/drivers/thermal/rcar_thermal.c
index 2cc5b6115e3..8d7edd4c822 100644
--- a/drivers/thermal/rcar_thermal.c
+++ b/drivers/thermal/rcar_thermal.c
@@ -24,6 +24,7 @@
#include <linux/io.h>
#include <linux/module.h>
#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
#include <linux/reboot.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
@@ -377,6 +378,9 @@ static int rcar_thermal_probe(struct platform_device *pdev)
spin_lock_init(&common->lock);
common->dev = dev;
+ pm_runtime_enable(dev);
+ pm_runtime_get_sync(dev);
+
irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (irq) {
int ret;
@@ -419,12 +423,15 @@ static int rcar_thermal_probe(struct platform_device *pdev)
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv) {
dev_err(dev, "Could not allocate priv\n");
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto error_unregister;
}
priv->base = devm_ioremap_resource(dev, res);
- if (IS_ERR(priv->base))
- return PTR_ERR(priv->base);
+ if (IS_ERR(priv->base)) {
+ ret = PTR_ERR(priv->base);
+ goto error_unregister;
+ }
priv->common = common;
priv->id = i;
@@ -443,10 +450,10 @@ static int rcar_thermal_probe(struct platform_device *pdev)
goto error_unregister;
}
- list_move_tail(&priv->list, &common->head);
-
if (rcar_has_irq_support(priv))
rcar_thermal_irq_enable(priv);
+
+ list_move_tail(&priv->list, &common->head);
}
platform_set_drvdata(pdev, common);
@@ -456,8 +463,14 @@ static int rcar_thermal_probe(struct platform_device *pdev)
return 0;
error_unregister:
- rcar_thermal_for_each_priv(priv, common)
+ rcar_thermal_for_each_priv(priv, common) {
thermal_zone_device_unregister(priv->zone);
+ if (rcar_has_irq_support(priv))
+ rcar_thermal_irq_disable(priv);
+ }
+
+ pm_runtime_put_sync(dev);
+ pm_runtime_disable(dev);
return ret;
}
@@ -465,13 +478,20 @@ error_unregister:
static int rcar_thermal_remove(struct platform_device *pdev)
{
struct rcar_thermal_common *common = platform_get_drvdata(pdev);
+ struct device *dev = &pdev->dev;
struct rcar_thermal_priv *priv;
- rcar_thermal_for_each_priv(priv, common)
+ rcar_thermal_for_each_priv(priv, common) {
thermal_zone_device_unregister(priv->zone);
+ if (rcar_has_irq_support(priv))
+ rcar_thermal_irq_disable(priv);
+ }
platform_set_drvdata(pdev, NULL);
+ pm_runtime_put_sync(dev);
+ pm_runtime_disable(dev);
+
return 0;
}
diff --git a/drivers/thermal/step_wise.c b/drivers/thermal/step_wise.c
index 407cde3211c..4d4ddae1a99 100644
--- a/drivers/thermal/step_wise.c
+++ b/drivers/thermal/step_wise.c
@@ -22,9 +22,6 @@
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/module.h>
#include <linux/thermal.h>
#include "thermal_core.h"
@@ -59,9 +56,12 @@ static unsigned long get_target_state(struct thermal_instance *instance,
switch (trend) {
case THERMAL_TREND_RAISING:
- if (throttle)
+ if (throttle) {
cur_state = cur_state < instance->upper ?
(cur_state + 1) : instance->upper;
+ if (cur_state < instance->lower)
+ cur_state = instance->lower;
+ }
break;
case THERMAL_TREND_RAISE_FULL:
if (throttle)
@@ -71,8 +71,11 @@ static unsigned long get_target_state(struct thermal_instance *instance,
if (cur_state == instance->lower) {
if (!throttle)
cur_state = -1;
- } else
+ } else {
cur_state -= 1;
+ if (cur_state > instance->upper)
+ cur_state = instance->upper;
+ }
break;
case THERMAL_TREND_DROP_FULL:
if (cur_state == instance->lower) {
@@ -180,23 +183,14 @@ static int step_wise_throttle(struct thermal_zone_device *tz, int trip)
static struct thermal_governor thermal_gov_step_wise = {
.name = "step_wise",
.throttle = step_wise_throttle,
- .owner = THIS_MODULE,
};
-static int __init thermal_gov_step_wise_init(void)
+int thermal_gov_step_wise_register(void)
{
return thermal_register_governor(&thermal_gov_step_wise);
}
-static void __exit thermal_gov_step_wise_exit(void)
+void thermal_gov_step_wise_unregister(void)
{
thermal_unregister_governor(&thermal_gov_step_wise);
}
-
-/* This should load after thermal framework */
-fs_initcall(thermal_gov_step_wise_init);
-module_exit(thermal_gov_step_wise_exit);
-
-MODULE_AUTHOR("Durgadoss R");
-MODULE_DESCRIPTION("A step-by-step thermal throttling governor");
-MODULE_LICENSE("GPL");
diff --git a/drivers/thermal/thermal_sys.c b/drivers/thermal/thermal_core.c
index 5b7863a03f9..4cdc3e32722 100644
--- a/drivers/thermal/thermal_sys.c
+++ b/drivers/thermal/thermal_core.c
@@ -99,7 +99,6 @@ int thermal_register_governor(struct thermal_governor *governor)
return err;
}
-EXPORT_SYMBOL_GPL(thermal_register_governor);
void thermal_unregister_governor(struct thermal_governor *governor)
{
@@ -127,7 +126,6 @@ exit:
mutex_unlock(&thermal_governor_lock);
return;
}
-EXPORT_SYMBOL_GPL(thermal_unregister_governor);
static int get_idr(struct idr *idr, struct mutex *lock, int *id)
{
@@ -1858,30 +1856,69 @@ static inline int genetlink_init(void) { return 0; }
static inline void genetlink_exit(void) {}
#endif /* !CONFIG_NET */
+static int __init thermal_register_governors(void)
+{
+ int result;
+
+ result = thermal_gov_step_wise_register();
+ if (result)
+ return result;
+
+ result = thermal_gov_fair_share_register();
+ if (result)
+ return result;
+
+ return thermal_gov_user_space_register();
+}
+
+static void thermal_unregister_governors(void)
+{
+ thermal_gov_step_wise_unregister();
+ thermal_gov_fair_share_unregister();
+ thermal_gov_user_space_unregister();
+}
+
static int __init thermal_init(void)
{
- int result = 0;
+ int result;
+
+ result = thermal_register_governors();
+ if (result)
+ goto error;
result = class_register(&thermal_class);
- if (result) {
- idr_destroy(&thermal_tz_idr);
- idr_destroy(&thermal_cdev_idr);
- mutex_destroy(&thermal_idr_lock);
- mutex_destroy(&thermal_list_lock);
- return result;
- }
+ if (result)
+ goto unregister_governors;
+
result = genetlink_init();
+ if (result)
+ goto unregister_class;
+
+ return 0;
+
+unregister_governors:
+ thermal_unregister_governors();
+unregister_class:
+ class_unregister(&thermal_class);
+error:
+ idr_destroy(&thermal_tz_idr);
+ idr_destroy(&thermal_cdev_idr);
+ mutex_destroy(&thermal_idr_lock);
+ mutex_destroy(&thermal_list_lock);
+ mutex_destroy(&thermal_governor_lock);
return result;
}
static void __exit thermal_exit(void)
{
+ genetlink_exit();
class_unregister(&thermal_class);
+ thermal_unregister_governors();
idr_destroy(&thermal_tz_idr);
idr_destroy(&thermal_cdev_idr);
mutex_destroy(&thermal_idr_lock);
mutex_destroy(&thermal_list_lock);
- genetlink_exit();
+ mutex_destroy(&thermal_governor_lock);
}
fs_initcall(thermal_init);
diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h
index 0d3205a1811..7cf2f662625 100644
--- a/drivers/thermal/thermal_core.h
+++ b/drivers/thermal/thermal_core.h
@@ -50,4 +50,31 @@ struct thermal_instance {
struct list_head cdev_node; /* node in cdev->thermal_instances */
};
+int thermal_register_governor(struct thermal_governor *);
+void thermal_unregister_governor(struct thermal_governor *);
+
+#ifdef CONFIG_THERMAL_GOV_STEP_WISE
+int thermal_gov_step_wise_register(void);
+void thermal_gov_step_wise_unregister(void);
+#else
+static inline int thermal_gov_step_wise_register(void) { return 0; }
+static inline void thermal_gov_step_wise_unregister(void) {}
+#endif /* CONFIG_THERMAL_GOV_STEP_WISE */
+
+#ifdef CONFIG_THERMAL_GOV_FAIR_SHARE
+int thermal_gov_fair_share_register(void);
+void thermal_gov_fair_share_unregister(void);
+#else
+static inline int thermal_gov_fair_share_register(void) { return 0; }
+static inline void thermal_gov_fair_share_unregister(void) {}
+#endif /* CONFIG_THERMAL_GOV_FAIR_SHARE */
+
+#ifdef CONFIG_THERMAL_GOV_USER_SPACE
+int thermal_gov_user_space_register(void);
+void thermal_gov_user_space_unregister(void);
+#else
+static inline int thermal_gov_user_space_register(void) { return 0; }
+static inline void thermal_gov_user_space_unregister(void) {}
+#endif /* CONFIG_THERMAL_GOV_USER_SPACE */
+
#endif /* __THERMAL_CORE_H__ */
diff --git a/drivers/thermal/user_space.c b/drivers/thermal/user_space.c
index 6bbb380b6d1..10adcddc882 100644
--- a/drivers/thermal/user_space.c
+++ b/drivers/thermal/user_space.c
@@ -22,9 +22,6 @@
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/module.h>
#include <linux/thermal.h>
#include "thermal_core.h"
@@ -46,23 +43,15 @@ static int notify_user_space(struct thermal_zone_device *tz, int trip)
static struct thermal_governor thermal_gov_user_space = {
.name = "user_space",
.throttle = notify_user_space,
- .owner = THIS_MODULE,
};
-static int __init thermal_gov_user_space_init(void)
+int thermal_gov_user_space_register(void)
{
return thermal_register_governor(&thermal_gov_user_space);
}
-static void __exit thermal_gov_user_space_exit(void)
+void thermal_gov_user_space_unregister(void)
{
thermal_unregister_governor(&thermal_gov_user_space);
}
-/* This should load after thermal framework */
-fs_initcall(thermal_gov_user_space_init);
-module_exit(thermal_gov_user_space_exit);
-
-MODULE_AUTHOR("Durgadoss R");
-MODULE_DESCRIPTION("A user space Thermal notifier");
-MODULE_LICENSE("GPL");