mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2025-01-22 13:54:57 +08:00
Merge branch 'thermal' of git://git.kernel.org/pub/scm/linux/kernel/git/rzhang/linux into next
Conflicts: drivers/thermal/cpu_cooling.c
This commit is contained in:
commit
d13cb03aef
22
Documentation/devicetree/bindings/thermal/armada-thermal.txt
Normal file
22
Documentation/devicetree/bindings/thermal/armada-thermal.txt
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
* Marvell Armada 370/XP thermal management
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
|
||||||
|
- compatible: Should be set to one of the following:
|
||||||
|
marvell,armada370-thermal
|
||||||
|
marvell,armadaxp-thermal
|
||||||
|
|
||||||
|
- reg: Device's register space.
|
||||||
|
Two entries are expected, see the examples below.
|
||||||
|
The first one is required for the sensor register;
|
||||||
|
the second one is required for the control register
|
||||||
|
to be used for sensor initialization (a.k.a. calibration).
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
thermal@d0018300 {
|
||||||
|
compatible = "marvell,armada370-thermal";
|
||||||
|
reg = <0xd0018300 0x4
|
||||||
|
0xd0018304 0x4>;
|
||||||
|
status = "okay";
|
||||||
|
};
|
@ -13,11 +13,11 @@ Thermal emulation mode supports software debug for TMU's operation. User can set
|
|||||||
manually with software code and TMU will read current temperature from user value not from
|
manually with software code and TMU will read current temperature from user value not from
|
||||||
sensor's value.
|
sensor's value.
|
||||||
|
|
||||||
Enabling CONFIG_EXYNOS_THERMAL_EMUL option will make this support in available.
|
Enabling CONFIG_THERMAL_EMULATION option will make this support available.
|
||||||
When it's enabled, sysfs node will be created under
|
When it's enabled, sysfs node will be created as
|
||||||
/sys/bus/platform/devices/'exynos device name'/ with name of 'emulation'.
|
/sys/devices/virtual/thermal/thermal_zone'zone id'/emul_temp.
|
||||||
|
|
||||||
The sysfs node, 'emulation', will contain value 0 for the initial state. When you input any
|
The sysfs node, 'emul_node', will contain value 0 for the initial state. When you input any
|
||||||
temperature you want to update to sysfs node, it automatically enable emulation mode and
|
temperature you want to update to sysfs node, it automatically enable emulation mode and
|
||||||
current temperature will be changed into it.
|
current temperature will be changed into it.
|
||||||
(Exynos also supports user changable delay time which would be used to delay of
|
(Exynos also supports user changable delay time which would be used to delay of
|
||||||
|
@ -265,6 +265,10 @@ emul_temp
|
|||||||
Unit: millidegree Celsius
|
Unit: millidegree Celsius
|
||||||
WO, Optional
|
WO, Optional
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
*****************************
|
*****************************
|
||||||
* Cooling device attributes *
|
* Cooling device attributes *
|
||||||
*****************************
|
*****************************
|
||||||
@ -375,11 +379,3 @@ platform data is provided, this uses the step_wise throttling policy.
|
|||||||
This function serves as an arbitrator to set the state of a cooling
|
This function serves as an arbitrator to set the state of a cooling
|
||||||
device. It sets the cooling device to the deepest cooling state if
|
device. It sets the cooling device to the deepest cooling state if
|
||||||
possible.
|
possible.
|
||||||
|
|
||||||
5.5:thermal_register_governor:
|
|
||||||
This function lets the various thermal governors to register themselves
|
|
||||||
with the Thermal framework. At run time, depending on a zone's platform
|
|
||||||
data, a particular governor is used for throttling.
|
|
||||||
|
|
||||||
5.6:thermal_unregister_governor:
|
|
||||||
This function unregisters a governor from the thermal framework.
|
|
||||||
|
@ -67,7 +67,7 @@ config THERMAL_GOV_USER_SPACE
|
|||||||
Enable this to let the user space manage the platform thermals.
|
Enable this to let the user space manage the platform thermals.
|
||||||
|
|
||||||
config CPU_THERMAL
|
config CPU_THERMAL
|
||||||
tristate "generic cpu cooling support"
|
bool "generic cpu cooling support"
|
||||||
depends on CPU_FREQ
|
depends on CPU_FREQ
|
||||||
select CPU_FREQ_TABLE
|
select CPU_FREQ_TABLE
|
||||||
help
|
help
|
||||||
@ -86,6 +86,10 @@ config THERMAL_EMULATION
|
|||||||
user can manually input temperature and test the different trip
|
user can manually input temperature and test the different trip
|
||||||
threshold behaviour for simulation purpose.
|
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
|
config SPEAR_THERMAL
|
||||||
bool "SPEAr thermal sensor driver"
|
bool "SPEAr thermal sensor driver"
|
||||||
depends on PLAT_SPEAR
|
depends on PLAT_SPEAR
|
||||||
@ -117,15 +121,6 @@ config EXYNOS_THERMAL
|
|||||||
If you say yes here you get support for TMU (Thermal Management
|
If you say yes here you get support for TMU (Thermal Management
|
||||||
Unit) on SAMSUNG EXYNOS series of SoC.
|
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
|
config DOVE_THERMAL
|
||||||
tristate "Temperature sensor on Marvell Dove SoCs"
|
tristate "Temperature sensor on Marvell Dove SoCs"
|
||||||
depends on ARCH_DOVE
|
depends on ARCH_DOVE
|
||||||
@ -144,6 +139,14 @@ config DB8500_THERMAL
|
|||||||
created. Cooling devices can be bound to the trip points to cool this
|
created. Cooling devices can be bound to the trip points to cool this
|
||||||
thermal zone if trip points reached.
|
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
|
config DB8500_CPUFREQ_COOLING
|
||||||
tristate "DB8500 cpufreq cooling"
|
tristate "DB8500 cpufreq cooling"
|
||||||
depends on ARCH_U8500
|
depends on ARCH_U8500
|
||||||
|
@ -3,14 +3,15 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
obj-$(CONFIG_THERMAL) += thermal_sys.o
|
obj-$(CONFIG_THERMAL) += thermal_sys.o
|
||||||
|
thermal_sys-y += thermal_core.o
|
||||||
|
|
||||||
# governors
|
# governors
|
||||||
obj-$(CONFIG_THERMAL_GOV_FAIR_SHARE) += fair_share.o
|
thermal_sys-$(CONFIG_THERMAL_GOV_FAIR_SHARE) += fair_share.o
|
||||||
obj-$(CONFIG_THERMAL_GOV_STEP_WISE) += step_wise.o
|
thermal_sys-$(CONFIG_THERMAL_GOV_STEP_WISE) += step_wise.o
|
||||||
obj-$(CONFIG_THERMAL_GOV_USER_SPACE) += user_space.o
|
thermal_sys-$(CONFIG_THERMAL_GOV_USER_SPACE) += user_space.o
|
||||||
|
|
||||||
# cpufreq cooling
|
# cpufreq cooling
|
||||||
obj-$(CONFIG_CPU_THERMAL) += cpu_cooling.o
|
thermal_sys-$(CONFIG_CPU_THERMAL) += cpu_cooling.o
|
||||||
|
|
||||||
# platform thermal drivers
|
# platform thermal drivers
|
||||||
obj-$(CONFIG_SPEAR_THERMAL) += spear_thermal.o
|
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_EXYNOS_THERMAL) += exynos_thermal.o
|
||||||
obj-$(CONFIG_DOVE_THERMAL) += dove_thermal.o
|
obj-$(CONFIG_DOVE_THERMAL) += dove_thermal.o
|
||||||
obj-$(CONFIG_DB8500_THERMAL) += db8500_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_DB8500_CPUFREQ_COOLING) += db8500_cpufreq_cooling.o
|
||||||
obj-$(CONFIG_INTEL_POWERCLAMP) += intel_powerclamp.o
|
obj-$(CONFIG_INTEL_POWERCLAMP) += intel_powerclamp.o
|
||||||
|
|
||||||
|
232
drivers/thermal/armada_thermal.c
Normal file
232
drivers/thermal/armada_thermal.c
Normal file
@ -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");
|
@ -108,6 +108,105 @@ static int is_cpufreq_valid(int cpu)
|
|||||||
return !cpufreq_get_policy(&policy, cpu);
|
return !cpufreq_get_policy(&policy, cpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 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 -EINVAL;
|
||||||
|
|
||||||
|
|
||||||
|
for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) {
|
||||||
|
/* ignore invalid entries */
|
||||||
|
if (table[i].frequency == CPUFREQ_ENTRY_INVALID)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* 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);
|
||||||
|
|
||||||
|
freq = table[i].frequency;
|
||||||
|
max_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);
|
||||||
|
|
||||||
|
|
||||||
|
for (i = 0, j = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) {
|
||||||
|
/* ignore invalid entry */
|
||||||
|
if (table[i].frequency == CPUFREQ_ENTRY_INVALID)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* 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 -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.
|
* get_cpu_frequency - get the absolute value of frequency from level.
|
||||||
* @cpu: cpu for which frequency is fetched.
|
* @cpu: cpu for which frequency is fetched.
|
||||||
@ -116,46 +215,13 @@ static int is_cpufreq_valid(int cpu)
|
|||||||
*/
|
*/
|
||||||
static unsigned int get_cpu_frequency(unsigned int cpu, unsigned long level)
|
static unsigned int get_cpu_frequency(unsigned int cpu, unsigned long level)
|
||||||
{
|
{
|
||||||
int ret = 0, i = 0;
|
int ret = 0;
|
||||||
unsigned long level_index;
|
unsigned int freq;
|
||||||
bool descend = false;
|
|
||||||
struct cpufreq_frequency_table *table =
|
|
||||||
cpufreq_frequency_get_table(cpu);
|
|
||||||
if (!table)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
while (table[i].frequency != CPUFREQ_TABLE_END) {
|
ret = get_property(cpu, level, &freq, GET_FREQ);
|
||||||
if (table[i].frequency == CPUFREQ_ENTRY_INVALID)
|
if (ret)
|
||||||
continue;
|
return 0;
|
||||||
|
return freq;
|
||||||
/*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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*return if level matched and table in descending order*/
|
|
||||||
if (descend && i == level)
|
|
||||||
return table[i].frequency;
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
i--;
|
|
||||||
|
|
||||||
if (level > i || descend)
|
|
||||||
return ret;
|
|
||||||
level_index = i - level;
|
|
||||||
|
|
||||||
/*Scan the table in reverse order and match the level*/
|
|
||||||
while (i >= 0) {
|
|
||||||
if (table[i].frequency == CPUFREQ_ENTRY_INVALID)
|
|
||||||
continue;
|
|
||||||
/*return if level matched*/
|
|
||||||
if (i == level_index)
|
|
||||||
return table[i].frequency;
|
|
||||||
i--;
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -237,29 +303,17 @@ static int cpufreq_get_max_state(struct thermal_cooling_device *cdev,
|
|||||||
struct cpufreq_cooling_device *cpufreq_device = cdev->devdata;
|
struct cpufreq_cooling_device *cpufreq_device = cdev->devdata;
|
||||||
struct cpumask *mask = &cpufreq_device->allowed_cpus;
|
struct cpumask *mask = &cpufreq_device->allowed_cpus;
|
||||||
unsigned int cpu;
|
unsigned int cpu;
|
||||||
struct cpufreq_frequency_table *table;
|
|
||||||
unsigned long count = 0;
|
unsigned long count = 0;
|
||||||
int i = 0;
|
int ret;
|
||||||
|
|
||||||
cpu = cpumask_any(mask);
|
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++) {
|
ret = get_property(cpu, 0, (unsigned int *)&count, GET_MAXL);
|
||||||
if (table[i].frequency == CPUFREQ_ENTRY_INVALID)
|
|
||||||
continue;
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count > 0) {
|
if (count > 0)
|
||||||
*state = --count;
|
*state = count;
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return -EINVAL;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -39,8 +39,6 @@
|
|||||||
#include <linux/cpu_cooling.h>
|
#include <linux/cpu_cooling.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
|
|
||||||
#include <plat/cpu.h>
|
|
||||||
|
|
||||||
/* Exynos generic registers */
|
/* Exynos generic registers */
|
||||||
#define EXYNOS_TMU_REG_TRIMINFO 0x0
|
#define EXYNOS_TMU_REG_TRIMINFO 0x0
|
||||||
#define EXYNOS_TMU_REG_CONTROL 0x20
|
#define EXYNOS_TMU_REG_CONTROL 0x20
|
||||||
@ -100,13 +98,13 @@
|
|||||||
#define IDLE_INTERVAL 10000
|
#define IDLE_INTERVAL 10000
|
||||||
#define MCELSIUS 1000
|
#define MCELSIUS 1000
|
||||||
|
|
||||||
#ifdef CONFIG_EXYNOS_THERMAL_EMUL
|
#ifdef CONFIG_THERMAL_EMULATION
|
||||||
#define EXYNOS_EMUL_TIME 0x57F0
|
#define EXYNOS_EMUL_TIME 0x57F0
|
||||||
#define EXYNOS_EMUL_TIME_SHIFT 16
|
#define EXYNOS_EMUL_TIME_SHIFT 16
|
||||||
#define EXYNOS_EMUL_DATA_SHIFT 8
|
#define EXYNOS_EMUL_DATA_SHIFT 8
|
||||||
#define EXYNOS_EMUL_DATA_MASK 0xFF
|
#define EXYNOS_EMUL_DATA_MASK 0xFF
|
||||||
#define EXYNOS_EMUL_ENABLE 0x1
|
#define EXYNOS_EMUL_ENABLE 0x1
|
||||||
#endif /* CONFIG_EXYNOS_THERMAL_EMUL */
|
#endif /* CONFIG_THERMAL_EMULATION */
|
||||||
|
|
||||||
/* CPU Zone information */
|
/* CPU Zone information */
|
||||||
#define PANIC_ZONE 4
|
#define PANIC_ZONE 4
|
||||||
@ -145,6 +143,7 @@ struct thermal_cooling_conf {
|
|||||||
struct thermal_sensor_conf {
|
struct thermal_sensor_conf {
|
||||||
char name[SENSOR_NAME_LEN];
|
char name[SENSOR_NAME_LEN];
|
||||||
int (*read_temperature)(void *data);
|
int (*read_temperature)(void *data);
|
||||||
|
int (*write_emul_temp)(void *drv_data, unsigned long temp);
|
||||||
struct thermal_trip_point_conf trip_data;
|
struct thermal_trip_point_conf trip_data;
|
||||||
struct thermal_cooling_conf cooling_data;
|
struct thermal_cooling_conf cooling_data;
|
||||||
void *private_data;
|
void *private_data;
|
||||||
@ -242,26 +241,6 @@ static int exynos_get_crit_temp(struct thermal_zone_device *thermal,
|
|||||||
return ret;
|
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 */
|
/* Bind callback functions for thermal zone */
|
||||||
static int exynos_bind(struct thermal_zone_device *thermal,
|
static int exynos_bind(struct thermal_zone_device *thermal,
|
||||||
struct thermal_cooling_device *cdev)
|
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 */
|
/* Bind the thermal zone to the cpufreq cooling device */
|
||||||
for (i = 0; i < tab_size; i++) {
|
for (i = 0; i < tab_size; i++) {
|
||||||
clip_data = (struct freq_clip_table *)&(tab_ptr[i]);
|
clip_data = (struct freq_clip_table *)&(tab_ptr[i]);
|
||||||
level = exynos_get_frequency_level(0, clip_data->freq_clip_max);
|
level = cpufreq_cooling_get_level(0, clip_data->freq_clip_max);
|
||||||
if (level < 0)
|
if (level == THERMAL_CSTATE_INVALID)
|
||||||
return 0;
|
return 0;
|
||||||
switch (GET_ZONE(i)) {
|
switch (GET_ZONE(i)) {
|
||||||
case MONITOR_ZONE:
|
case MONITOR_ZONE:
|
||||||
@ -369,6 +348,23 @@ static int exynos_get_temp(struct thermal_zone_device *thermal,
|
|||||||
return 0;
|
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 */
|
/* Get the temperature trend */
|
||||||
static int exynos_get_trend(struct thermal_zone_device *thermal,
|
static int exynos_get_trend(struct thermal_zone_device *thermal,
|
||||||
int trip, enum thermal_trend *trend)
|
int trip, enum thermal_trend *trend)
|
||||||
@ -392,6 +388,7 @@ static struct thermal_zone_device_ops const exynos_dev_ops = {
|
|||||||
.bind = exynos_bind,
|
.bind = exynos_bind,
|
||||||
.unbind = exynos_unbind,
|
.unbind = exynos_unbind,
|
||||||
.get_temp = exynos_get_temp,
|
.get_temp = exynos_get_temp,
|
||||||
|
.set_emul_temp = exynos_set_emul_temp,
|
||||||
.get_trend = exynos_get_trend,
|
.get_trend = exynos_get_trend,
|
||||||
.get_mode = exynos_get_mode,
|
.get_mode = exynos_get_mode,
|
||||||
.set_mode = exynos_set_mode,
|
.set_mode = exynos_set_mode,
|
||||||
@ -714,6 +711,47 @@ static int exynos_tmu_read(struct exynos_tmu_data *data)
|
|||||||
return temp;
|
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)
|
static void exynos_tmu_work(struct work_struct *work)
|
||||||
{
|
{
|
||||||
struct exynos_tmu_data *data = container_of(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 = {
|
static struct thermal_sensor_conf exynos_sensor_conf = {
|
||||||
.name = "exynos-therm",
|
.name = "exynos-therm",
|
||||||
.read_temperature = (int (*)(void *))exynos_tmu_read,
|
.read_temperature = (int (*)(void *))exynos_tmu_read,
|
||||||
|
.write_emul_temp = exynos_tmu_set_emulation,
|
||||||
};
|
};
|
||||||
|
|
||||||
#if defined(CONFIG_CPU_EXYNOS4210)
|
#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;
|
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)
|
static int exynos_tmu_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct exynos_tmu_data *data;
|
struct exynos_tmu_data *data;
|
||||||
@ -1039,10 +991,6 @@ static int exynos_tmu_probe(struct platform_device *pdev)
|
|||||||
goto err_clk;
|
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;
|
return 0;
|
||||||
err_clk:
|
err_clk:
|
||||||
platform_set_drvdata(pdev, NULL);
|
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);
|
struct exynos_tmu_data *data = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
remove_emulation_sysfs(&pdev->dev);
|
|
||||||
|
|
||||||
exynos_tmu_control(pdev, false);
|
exynos_tmu_control(pdev, false);
|
||||||
|
|
||||||
exynos_unregister_thermal();
|
exynos_unregister_thermal();
|
||||||
|
@ -22,9 +22,6 @@
|
|||||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
||||||
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/thermal.h>
|
#include <linux/thermal.h>
|
||||||
|
|
||||||
#include "thermal_core.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 = {
|
static struct thermal_governor thermal_gov_fair_share = {
|
||||||
.name = "fair_share",
|
.name = "fair_share",
|
||||||
.throttle = fair_share_throttle,
|
.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);
|
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);
|
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");
|
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/pm_runtime.h>
|
||||||
#include <linux/reboot.h>
|
#include <linux/reboot.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/spinlock.h>
|
#include <linux/spinlock.h>
|
||||||
@ -377,6 +378,9 @@ static int rcar_thermal_probe(struct platform_device *pdev)
|
|||||||
spin_lock_init(&common->lock);
|
spin_lock_init(&common->lock);
|
||||||
common->dev = dev;
|
common->dev = dev;
|
||||||
|
|
||||||
|
pm_runtime_enable(dev);
|
||||||
|
pm_runtime_get_sync(dev);
|
||||||
|
|
||||||
irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
||||||
if (irq) {
|
if (irq) {
|
||||||
int ret;
|
int ret;
|
||||||
@ -419,12 +423,15 @@ static int rcar_thermal_probe(struct platform_device *pdev)
|
|||||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||||
if (!priv) {
|
if (!priv) {
|
||||||
dev_err(dev, "Could not allocate priv\n");
|
dev_err(dev, "Could not allocate priv\n");
|
||||||
return -ENOMEM;
|
ret = -ENOMEM;
|
||||||
|
goto error_unregister;
|
||||||
}
|
}
|
||||||
|
|
||||||
priv->base = devm_ioremap_resource(dev, res);
|
priv->base = devm_ioremap_resource(dev, res);
|
||||||
if (IS_ERR(priv->base))
|
if (IS_ERR(priv->base)) {
|
||||||
return PTR_ERR(priv->base);
|
ret = PTR_ERR(priv->base);
|
||||||
|
goto error_unregister;
|
||||||
|
}
|
||||||
|
|
||||||
priv->common = common;
|
priv->common = common;
|
||||||
priv->id = i;
|
priv->id = i;
|
||||||
@ -443,10 +450,10 @@ static int rcar_thermal_probe(struct platform_device *pdev)
|
|||||||
goto error_unregister;
|
goto error_unregister;
|
||||||
}
|
}
|
||||||
|
|
||||||
list_move_tail(&priv->list, &common->head);
|
|
||||||
|
|
||||||
if (rcar_has_irq_support(priv))
|
if (rcar_has_irq_support(priv))
|
||||||
rcar_thermal_irq_enable(priv);
|
rcar_thermal_irq_enable(priv);
|
||||||
|
|
||||||
|
list_move_tail(&priv->list, &common->head);
|
||||||
}
|
}
|
||||||
|
|
||||||
platform_set_drvdata(pdev, common);
|
platform_set_drvdata(pdev, common);
|
||||||
@ -456,8 +463,14 @@ static int rcar_thermal_probe(struct platform_device *pdev)
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
error_unregister:
|
error_unregister:
|
||||||
rcar_thermal_for_each_priv(priv, common)
|
rcar_thermal_for_each_priv(priv, common) {
|
||||||
thermal_zone_device_unregister(priv->zone);
|
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;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -465,13 +478,20 @@ error_unregister:
|
|||||||
static int rcar_thermal_remove(struct platform_device *pdev)
|
static int rcar_thermal_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct rcar_thermal_common *common = platform_get_drvdata(pdev);
|
struct rcar_thermal_common *common = platform_get_drvdata(pdev);
|
||||||
|
struct device *dev = &pdev->dev;
|
||||||
struct rcar_thermal_priv *priv;
|
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);
|
thermal_zone_device_unregister(priv->zone);
|
||||||
|
if (rcar_has_irq_support(priv))
|
||||||
|
rcar_thermal_irq_disable(priv);
|
||||||
|
}
|
||||||
|
|
||||||
platform_set_drvdata(pdev, NULL);
|
platform_set_drvdata(pdev, NULL);
|
||||||
|
|
||||||
|
pm_runtime_put_sync(dev);
|
||||||
|
pm_runtime_disable(dev);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,9 +22,6 @@
|
|||||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
||||||
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/thermal.h>
|
#include <linux/thermal.h>
|
||||||
|
|
||||||
#include "thermal_core.h"
|
#include "thermal_core.h"
|
||||||
@ -59,9 +56,12 @@ static unsigned long get_target_state(struct thermal_instance *instance,
|
|||||||
|
|
||||||
switch (trend) {
|
switch (trend) {
|
||||||
case THERMAL_TREND_RAISING:
|
case THERMAL_TREND_RAISING:
|
||||||
if (throttle)
|
if (throttle) {
|
||||||
cur_state = cur_state < instance->upper ?
|
cur_state = cur_state < instance->upper ?
|
||||||
(cur_state + 1) : instance->upper;
|
(cur_state + 1) : instance->upper;
|
||||||
|
if (cur_state < instance->lower)
|
||||||
|
cur_state = instance->lower;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case THERMAL_TREND_RAISE_FULL:
|
case THERMAL_TREND_RAISE_FULL:
|
||||||
if (throttle)
|
if (throttle)
|
||||||
@ -71,8 +71,11 @@ static unsigned long get_target_state(struct thermal_instance *instance,
|
|||||||
if (cur_state == instance->lower) {
|
if (cur_state == instance->lower) {
|
||||||
if (!throttle)
|
if (!throttle)
|
||||||
cur_state = -1;
|
cur_state = -1;
|
||||||
} else
|
} else {
|
||||||
cur_state -= 1;
|
cur_state -= 1;
|
||||||
|
if (cur_state > instance->upper)
|
||||||
|
cur_state = instance->upper;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case THERMAL_TREND_DROP_FULL:
|
case THERMAL_TREND_DROP_FULL:
|
||||||
if (cur_state == instance->lower) {
|
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 = {
|
static struct thermal_governor thermal_gov_step_wise = {
|
||||||
.name = "step_wise",
|
.name = "step_wise",
|
||||||
.throttle = step_wise_throttle,
|
.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);
|
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);
|
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");
|
|
||||||
|
@ -99,7 +99,6 @@ int thermal_register_governor(struct thermal_governor *governor)
|
|||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(thermal_register_governor);
|
|
||||||
|
|
||||||
void thermal_unregister_governor(struct thermal_governor *governor)
|
void thermal_unregister_governor(struct thermal_governor *governor)
|
||||||
{
|
{
|
||||||
@ -127,7 +126,6 @@ exit:
|
|||||||
mutex_unlock(&thermal_governor_lock);
|
mutex_unlock(&thermal_governor_lock);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(thermal_unregister_governor);
|
|
||||||
|
|
||||||
static int get_idr(struct idr *idr, struct mutex *lock, int *id)
|
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) {}
|
static inline void genetlink_exit(void) {}
|
||||||
#endif /* !CONFIG_NET */
|
#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)
|
static int __init thermal_init(void)
|
||||||
{
|
{
|
||||||
int result = 0;
|
int result;
|
||||||
|
|
||||||
|
result = thermal_register_governors();
|
||||||
|
if (result)
|
||||||
|
goto error;
|
||||||
|
|
||||||
result = class_register(&thermal_class);
|
result = class_register(&thermal_class);
|
||||||
if (result) {
|
if (result)
|
||||||
idr_destroy(&thermal_tz_idr);
|
goto unregister_governors;
|
||||||
idr_destroy(&thermal_cdev_idr);
|
|
||||||
mutex_destroy(&thermal_idr_lock);
|
|
||||||
mutex_destroy(&thermal_list_lock);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
result = genetlink_init();
|
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;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __exit thermal_exit(void)
|
static void __exit thermal_exit(void)
|
||||||
{
|
{
|
||||||
|
genetlink_exit();
|
||||||
class_unregister(&thermal_class);
|
class_unregister(&thermal_class);
|
||||||
|
thermal_unregister_governors();
|
||||||
idr_destroy(&thermal_tz_idr);
|
idr_destroy(&thermal_tz_idr);
|
||||||
idr_destroy(&thermal_cdev_idr);
|
idr_destroy(&thermal_cdev_idr);
|
||||||
mutex_destroy(&thermal_idr_lock);
|
mutex_destroy(&thermal_idr_lock);
|
||||||
mutex_destroy(&thermal_list_lock);
|
mutex_destroy(&thermal_list_lock);
|
||||||
genetlink_exit();
|
mutex_destroy(&thermal_governor_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
fs_initcall(thermal_init);
|
fs_initcall(thermal_init);
|
@ -50,4 +50,31 @@ struct thermal_instance {
|
|||||||
struct list_head cdev_node; /* node in cdev->thermal_instances */
|
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__ */
|
#endif /* __THERMAL_CORE_H__ */
|
||||||
|
@ -22,9 +22,6 @@
|
|||||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
||||||
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/thermal.h>
|
#include <linux/thermal.h>
|
||||||
|
|
||||||
#include "thermal_core.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 = {
|
static struct thermal_governor thermal_gov_user_space = {
|
||||||
.name = "user_space",
|
.name = "user_space",
|
||||||
.throttle = notify_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);
|
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);
|
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");
|
|
||||||
|
@ -29,7 +29,7 @@
|
|||||||
#define CPUFREQ_COOLING_START 0
|
#define CPUFREQ_COOLING_START 0
|
||||||
#define CPUFREQ_COOLING_STOP 1
|
#define CPUFREQ_COOLING_STOP 1
|
||||||
|
|
||||||
#if defined(CONFIG_CPU_THERMAL) || defined(CONFIG_CPU_THERMAL_MODULE)
|
#ifdef CONFIG_CPU_THERMAL
|
||||||
/**
|
/**
|
||||||
* cpufreq_cooling_register - function to create cpufreq cooling device.
|
* cpufreq_cooling_register - function to create cpufreq cooling device.
|
||||||
* @clip_cpus: cpumask of cpus where the frequency constraints will happen
|
* @clip_cpus: cpumask of cpus where the frequency constraints will happen
|
||||||
@ -42,6 +42,8 @@ struct thermal_cooling_device *cpufreq_cooling_register(
|
|||||||
* @cdev: thermal cooling device pointer.
|
* @cdev: thermal cooling device pointer.
|
||||||
*/
|
*/
|
||||||
void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev);
|
void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev);
|
||||||
|
|
||||||
|
unsigned long cpufreq_cooling_get_level(unsigned int, unsigned int);
|
||||||
#else /* !CONFIG_CPU_THERMAL */
|
#else /* !CONFIG_CPU_THERMAL */
|
||||||
static inline struct thermal_cooling_device *cpufreq_cooling_register(
|
static inline struct thermal_cooling_device *cpufreq_cooling_register(
|
||||||
const struct cpumask *clip_cpus)
|
const struct cpumask *clip_cpus)
|
||||||
@ -53,6 +55,11 @@ static inline void cpufreq_cooling_unregister(
|
|||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
static inline unsigned long cpufreq_cooling_get_level(unsigned int,
|
||||||
|
unsigned int)
|
||||||
|
{
|
||||||
|
return THERMAL_CSTATE_INVALID;
|
||||||
|
}
|
||||||
#endif /* CONFIG_CPU_THERMAL */
|
#endif /* CONFIG_CPU_THERMAL */
|
||||||
|
|
||||||
#endif /* __CPU_COOLING_H__ */
|
#endif /* __CPU_COOLING_H__ */
|
||||||
|
@ -33,8 +33,11 @@
|
|||||||
#define THERMAL_MAX_TRIPS 12
|
#define THERMAL_MAX_TRIPS 12
|
||||||
#define THERMAL_NAME_LENGTH 20
|
#define THERMAL_NAME_LENGTH 20
|
||||||
|
|
||||||
|
/* invalid cooling state */
|
||||||
|
#define THERMAL_CSTATE_INVALID -1UL
|
||||||
|
|
||||||
/* No upper/lower limit requirement */
|
/* No upper/lower limit requirement */
|
||||||
#define THERMAL_NO_LIMIT -1UL
|
#define THERMAL_NO_LIMIT THERMAL_CSTATE_INVALID
|
||||||
|
|
||||||
/* Unit conversion macros */
|
/* Unit conversion macros */
|
||||||
#define KELVIN_TO_CELSIUS(t) (long)(((long)t-2732 >= 0) ? \
|
#define KELVIN_TO_CELSIUS(t) (long)(((long)t-2732 >= 0) ? \
|
||||||
@ -184,7 +187,6 @@ struct thermal_governor {
|
|||||||
char name[THERMAL_NAME_LENGTH];
|
char name[THERMAL_NAME_LENGTH];
|
||||||
int (*throttle)(struct thermal_zone_device *tz, int trip);
|
int (*throttle)(struct thermal_zone_device *tz, int trip);
|
||||||
struct list_head governor_list;
|
struct list_head governor_list;
|
||||||
struct module *owner;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Structure that holds binding parameters for a zone */
|
/* Structure that holds binding parameters for a zone */
|
||||||
@ -244,9 +246,6 @@ struct thermal_instance *get_thermal_instance(struct thermal_zone_device *,
|
|||||||
void thermal_cdev_update(struct thermal_cooling_device *);
|
void thermal_cdev_update(struct thermal_cooling_device *);
|
||||||
void notify_thermal_framework(struct thermal_zone_device *, int);
|
void notify_thermal_framework(struct thermal_zone_device *, int);
|
||||||
|
|
||||||
int thermal_register_governor(struct thermal_governor *);
|
|
||||||
void thermal_unregister_governor(struct thermal_governor *);
|
|
||||||
|
|
||||||
#ifdef CONFIG_NET
|
#ifdef CONFIG_NET
|
||||||
extern int thermal_generate_netlink_event(struct thermal_zone_device *tz,
|
extern int thermal_generate_netlink_event(struct thermal_zone_device *tz,
|
||||||
enum events event);
|
enum events event);
|
||||||
|
Loading…
Reference in New Issue
Block a user