aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/thermal/db8500-thermal.txt44
-rw-r--r--Documentation/thermal/sysfs-api.txt64
-rw-r--r--arch/arm/boot/dts/dbx5x0.dtsi14
-rw-r--r--arch/arm/boot/dts/snowball.dts31
-rw-r--r--arch/arm/configs/u8500_defconfig2
-rw-r--r--arch/arm/mach-ux500/board-mop500.c64
-rw-r--r--drivers/acpi/thermal.c6
-rw-r--r--drivers/platform/x86/acerhdf.c2
-rw-r--r--drivers/platform/x86/intel_mid_thermal.c2
-rw-r--r--drivers/power/power_supply_core.c2
-rw-r--r--drivers/staging/omap-thermal/omap-thermal-common.c2
-rw-r--r--drivers/thermal/Kconfig82
-rw-r--r--drivers/thermal/Makefile17
-rw-r--r--drivers/thermal/cpu_cooling.c107
-rw-r--r--drivers/thermal/db8500_cpufreq_cooling.c108
-rw-r--r--drivers/thermal/db8500_thermal.c531
-rw-r--r--drivers/thermal/exynos_thermal.c2
-rw-r--r--drivers/thermal/fair_share.c133
-rw-r--r--drivers/thermal/rcar_thermal.c27
-rw-r--r--drivers/thermal/spear_thermal.c2
-rw-r--r--drivers/thermal/step_wise.c194
-rw-r--r--drivers/thermal/thermal_core.h53
-rw-r--r--drivers/thermal/thermal_sys.c701
-rw-r--r--drivers/thermal/user_space.c68
-rw-r--r--include/linux/cpu_cooling.h6
-rw-r--r--include/linux/platform_data/db8500_thermal.h38
-rw-r--r--include/linux/thermal.h134
27 files changed, 2038 insertions, 398 deletions
diff --git a/Documentation/devicetree/bindings/thermal/db8500-thermal.txt b/Documentation/devicetree/bindings/thermal/db8500-thermal.txt
new file mode 100644
index 00000000000..2e1c06fad81
--- /dev/null
+++ b/Documentation/devicetree/bindings/thermal/db8500-thermal.txt
@@ -0,0 +1,44 @@
+* ST-Ericsson DB8500 Thermal
+
+** Thermal node properties:
+
+- compatible : "stericsson,db8500-thermal";
+- reg : address range of the thermal sensor registers;
+- interrupts : interrupts generated from PRCMU;
+- interrupt-names : "IRQ_HOTMON_LOW" and "IRQ_HOTMON_HIGH";
+- num-trips : number of total trip points, this is required, set it 0 if none,
+ if greater than 0, the following properties must be defined;
+- tripN-temp : temperature of trip point N, should be in ascending order;
+- tripN-type : type of trip point N, should be one of "active" "passive" "hot"
+ "critical";
+- tripN-cdev-num : number of the cooling devices which can be bound to trip
+ point N, this is required if trip point N is defined, set it 0 if none,
+ otherwise the following cooling device names must be defined;
+- tripN-cdev-nameM : name of the No. M cooling device of trip point N;
+
+Usually the num-trips and tripN-*** are separated in board related dts files.
+
+Example:
+thermal@801573c0 {
+ compatible = "stericsson,db8500-thermal";
+ reg = <0x801573c0 0x40>;
+ interrupts = <21 0x4>, <22 0x4>;
+ interrupt-names = "IRQ_HOTMON_LOW", "IRQ_HOTMON_HIGH";
+
+ num-trips = <3>;
+
+ trip0-temp = <75000>;
+ trip0-type = "active";
+ trip0-cdev-num = <1>;
+ trip0-cdev-name0 = "thermal-cpufreq-0";
+
+ trip1-temp = <80000>;
+ trip1-type = "active";
+ trip1-cdev-num = <2>;
+ trip1-cdev-name0 = "thermal-cpufreq-0";
+ trip1-cdev-name1 = "thermal-fan";
+
+ trip2-temp = <85000>;
+ trip2-type = "critical";
+ trip2-cdev-num = <0>;
+}
diff --git a/Documentation/thermal/sysfs-api.txt b/Documentation/thermal/sysfs-api.txt
index ca1a1a34970..88c02334e35 100644
--- a/Documentation/thermal/sysfs-api.txt
+++ b/Documentation/thermal/sysfs-api.txt
@@ -112,6 +112,29 @@ temperature) and throttle appropriate devices.
trip: indicates which trip point the cooling devices is associated with
in this thermal zone.
+1.4 Thermal Zone Parameters
+1.4.1 struct thermal_bind_params
+ This structure defines the following parameters that are used to bind
+ a zone with a cooling device for a particular trip point.
+ .cdev: The cooling device pointer
+ .weight: The 'influence' of a particular cooling device on this zone.
+ This is on a percentage scale. The sum of all these weights
+ (for a particular zone) cannot exceed 100.
+ .trip_mask:This is a bit mask that gives the binding relation between
+ this thermal zone and cdev, for a particular trip point.
+ If nth bit is set, then the cdev and thermal zone are bound
+ for trip point n.
+ .match: This call back returns success(0) if the 'tz and cdev' need to
+ be bound, as per platform data.
+1.4.2 struct thermal_zone_params
+ This structure defines the platform level parameters for a thermal zone.
+ This data, for each thermal zone should come from the platform layer.
+ This is an optional feature where some platforms can choose not to
+ provide this data.
+ .governor_name: Name of the thermal governor used for this zone
+ .num_tbps: Number of thermal_bind_params entries for this zone
+ .tbp: thermal_bind_params entries
+
2. sysfs attributes structure
RO read only value
@@ -126,6 +149,7 @@ Thermal zone device sys I/F, created once it's registered:
|---type: Type of the thermal zone
|---temp: Current temperature
|---mode: Working mode of the thermal zone
+ |---policy: Thermal governor used for this zone
|---trip_point_[0-*]_temp: Trip point temperature
|---trip_point_[0-*]_type: Trip point type
|---trip_point_[0-*]_hyst: Hysteresis value for this trip point
@@ -187,6 +211,10 @@ mode
charge of the thermal management.
RW, Optional
+policy
+ One of the various thermal governors used for a particular zone.
+ RW, Required
+
trip_point_[0-*]_temp
The temperature above which trip point will be fired.
Unit: millidegree Celsius
@@ -264,6 +292,7 @@ method, the sys I/F structure will be built like this:
|---type: acpitz
|---temp: 37000
|---mode: enabled
+ |---policy: step_wise
|---trip_point_0_temp: 100000
|---trip_point_0_type: critical
|---trip_point_1_temp: 80000
@@ -305,3 +334,38 @@ to a thermal_zone_device when it registers itself with the framework. The
event will be one of:{THERMAL_AUX0, THERMAL_AUX1, THERMAL_CRITICAL,
THERMAL_DEV_FAULT}. Notification can be sent when the current temperature
crosses any of the configured thresholds.
+
+5. Export Symbol APIs:
+
+5.1: get_tz_trend:
+This function returns the trend of a thermal zone, i.e the rate of change
+of temperature of the thermal zone. Ideally, the thermal sensor drivers
+are supposed to implement the callback. If they don't, the thermal
+framework calculated the trend by comparing the previous and the current
+temperature values.
+
+5.2:get_thermal_instance:
+This function returns the thermal_instance corresponding to a given
+{thermal_zone, cooling_device, trip_point} combination. Returns NULL
+if such an instance does not exist.
+
+5.3:notify_thermal_framework:
+This function handles the trip events from sensor drivers. It starts
+throttling the cooling devices according to the policy configured.
+For CRITICAL and HOT trip points, this notifies the respective drivers,
+and does actual throttling for other trip points i.e ACTIVE and PASSIVE.
+The throttling policy is based on the configured platform data; if no
+platform data is provided, this uses the step_wise throttling policy.
+
+5.4:thermal_cdev_update:
+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
+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.
diff --git a/arch/arm/boot/dts/dbx5x0.dtsi b/arch/arm/boot/dts/dbx5x0.dtsi
index 4b0e0ca08f4..731086b2fca 100644
--- a/arch/arm/boot/dts/dbx5x0.dtsi
+++ b/arch/arm/boot/dts/dbx5x0.dtsi
@@ -203,6 +203,14 @@
reg = <0x80157450 0xC>;
};
+ thermal@801573c0 {
+ compatible = "stericsson,db8500-thermal";
+ reg = <0x801573c0 0x40>;
+ interrupts = <21 0x4>, <22 0x4>;
+ interrupt-names = "IRQ_HOTMON_LOW", "IRQ_HOTMON_HIGH";
+ status = "disabled";
+ };
+
db8500-prcmu-regulators {
compatible = "stericsson,db8500-prcmu-regulator";
@@ -660,5 +668,11 @@
ranges = <0 0x50000000 0x4000000>;
status = "disabled";
};
+
+ cpufreq-cooling {
+ compatible = "stericsson,db8500-cpufreq-cooling";
+ status = "disabled";
+ };
+
};
};
diff --git a/arch/arm/boot/dts/snowball.dts b/arch/arm/boot/dts/snowball.dts
index 702c0baa600..c6f85f0bc53 100644
--- a/arch/arm/boot/dts/snowball.dts
+++ b/arch/arm/boot/dts/snowball.dts
@@ -99,6 +99,33 @@
status = "okay";
};
+ prcmu@80157000 {
+ thermal@801573c0 {
+ num-trips = <4>;
+
+ trip0-temp = <70000>;
+ trip0-type = "active";
+ trip0-cdev-num = <1>;
+ trip0-cdev-name0 = "thermal-cpufreq-0";
+
+ trip1-temp = <75000>;
+ trip1-type = "active";
+ trip1-cdev-num = <1>;
+ trip1-cdev-name0 = "thermal-cpufreq-0";
+
+ trip2-temp = <80000>;
+ trip2-type = "active";
+ trip2-cdev-num = <1>;
+ trip2-cdev-name0 = "thermal-cpufreq-0";
+
+ trip3-temp = <85000>;
+ trip3-type = "critical";
+ trip3-cdev-num = <0>;
+
+ status = "okay";
+ };
+ };
+
external-bus@50000000 {
status = "okay";
@@ -183,5 +210,9 @@
reg = <0x33>;
};
};
+
+ cpufreq-cooling {
+ status = "okay";
+ };
};
};
diff --git a/arch/arm/configs/u8500_defconfig b/arch/arm/configs/u8500_defconfig
index da6845493ca..250625d5223 100644
--- a/arch/arm/configs/u8500_defconfig
+++ b/arch/arm/configs/u8500_defconfig
@@ -69,6 +69,8 @@ CONFIG_GPIO_TC3589X=y
CONFIG_POWER_SUPPLY=y
CONFIG_AB8500_BM=y
CONFIG_AB8500_BATTERY_THERM_ON_BATCTRL=y
+CONFIG_THERMAL=y
+CONFIG_CPU_THERMAL=y
CONFIG_MFD_STMPE=y
CONFIG_MFD_TC3589X=y
CONFIG_AB5500_CORE=y
diff --git a/arch/arm/mach-ux500/board-mop500.c b/arch/arm/mach-ux500/board-mop500.c
index 0a3dd601a40..2d16b1dd5fe 100644
--- a/arch/arm/mach-ux500/board-mop500.c
+++ b/arch/arm/mach-ux500/board-mop500.c
@@ -16,6 +16,7 @@
#include <linux/io.h>
#include <linux/i2c.h>
#include <linux/platform_data/i2c-nomadik.h>
+#include <linux/platform_data/db8500_thermal.h>
#include <linux/gpio.h>
#include <linux/amba/bus.h>
#include <linux/amba/pl022.h>
@@ -229,6 +230,67 @@ static struct ab8500_platform_data ab8500_platdata = {
};
/*
+ * Thermal Sensor
+ */
+
+static struct resource db8500_thsens_resources[] = {
+ {
+ .name = "IRQ_HOTMON_LOW",
+ .start = IRQ_PRCMU_HOTMON_LOW,
+ .end = IRQ_PRCMU_HOTMON_LOW,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "IRQ_HOTMON_HIGH",
+ .start = IRQ_PRCMU_HOTMON_HIGH,
+ .end = IRQ_PRCMU_HOTMON_HIGH,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct db8500_thsens_platform_data db8500_thsens_data = {
+ .trip_points[0] = {
+ .temp = 70000,
+ .type = THERMAL_TRIP_ACTIVE,
+ .cdev_name = {
+ [0] = "thermal-cpufreq-0",
+ },
+ },
+ .trip_points[1] = {
+ .temp = 75000,
+ .type = THERMAL_TRIP_ACTIVE,
+ .cdev_name = {
+ [0] = "thermal-cpufreq-0",
+ },
+ },
+ .trip_points[2] = {
+ .temp = 80000,
+ .type = THERMAL_TRIP_ACTIVE,
+ .cdev_name = {
+ [0] = "thermal-cpufreq-0",
+ },
+ },
+ .trip_points[3] = {
+ .temp = 85000,
+ .type = THERMAL_TRIP_CRITICAL,
+ },
+ .num_trips = 4,
+};
+
+static struct platform_device u8500_thsens_device = {
+ .name = "db8500-thermal",
+ .resource = db8500_thsens_resources,
+ .num_resources = ARRAY_SIZE(db8500_thsens_resources),
+ .dev = {
+ .platform_data = &db8500_thsens_data,
+ },
+};
+
+static struct platform_device u8500_cpufreq_cooling_device = {
+ .name = "db8500-cpufreq-cooling",
+};
+
+/*
* TPS61052
*/
@@ -583,6 +645,8 @@ static struct platform_device *snowball_platform_devs[] __initdata = {
&snowball_key_dev,
&snowball_sbnet_dev,
&snowball_gpio_en_3v3_regulator_dev,
+ &u8500_thsens_device,
+ &u8500_cpufreq_cooling_device,
};
static void __init mop500_init_machine(void)
diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c
index 6e8cc16b54c..506fbd4b573 100644
--- a/drivers/acpi/thermal.c
+++ b/drivers/acpi/thermal.c
@@ -900,14 +900,14 @@ static int acpi_thermal_register_thermal_zone(struct acpi_thermal *tz)
if (tz->trips.passive.flags.valid)
tz->thermal_zone =
thermal_zone_device_register("acpitz", trips, 0, tz,
- &acpi_thermal_zone_ops,
+ &acpi_thermal_zone_ops, NULL,
tz->trips.passive.tsp*100,
tz->polling_frequency*100);
else
tz->thermal_zone =
thermal_zone_device_register("acpitz", trips, 0, tz,
- &acpi_thermal_zone_ops, 0,
- tz->polling_frequency*100);
+ &acpi_thermal_zone_ops, NULL,
+ 0, tz->polling_frequency*100);
if (IS_ERR(tz->thermal_zone))
return -ENODEV;
diff --git a/drivers/platform/x86/acerhdf.c b/drivers/platform/x86/acerhdf.c
index 84c56881ba8..c2e3e63d2c1 100644
--- a/drivers/platform/x86/acerhdf.c
+++ b/drivers/platform/x86/acerhdf.c
@@ -662,7 +662,7 @@ static int acerhdf_register_thermal(void)
return -EINVAL;
thz_dev = thermal_zone_device_register("acerhdf", 1, 0, NULL,
- &acerhdf_dev_ops, 0,
+ &acerhdf_dev_ops, NULL, 0,
(kernelmode) ? interval*1000 : 0);
if (IS_ERR(thz_dev))
return -EINVAL;
diff --git a/drivers/platform/x86/intel_mid_thermal.c b/drivers/platform/x86/intel_mid_thermal.c
index c8097616dd6..93de09019d1 100644
--- a/drivers/platform/x86/intel_mid_thermal.c
+++ b/drivers/platform/x86/intel_mid_thermal.c
@@ -502,7 +502,7 @@ static int mid_thermal_probe(struct platform_device *pdev)
goto err;
}
pinfo->tzd[i] = thermal_zone_device_register(name[i],
- 0, 0, td_info, &tzd_ops, 0, 0);
+ 0, 0, td_info, &tzd_ops, NULL, 0, 0);
if (IS_ERR(pinfo->tzd[i])) {
kfree(td_info);
ret = PTR_ERR(pinfo->tzd[i]);
diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c
index 2436f135001..f77a41272e5 100644
--- a/drivers/power/power_supply_core.c
+++ b/drivers/power/power_supply_core.c
@@ -201,7 +201,7 @@ static int psy_register_thermal(struct power_supply *psy)
for (i = 0; i < psy->num_properties; i++) {
if (psy->properties[i] == POWER_SUPPLY_PROP_TEMP) {
psy->tzd = thermal_zone_device_register(psy->name, 0, 0,
- psy, &psy_tzd_ops, 0, 0);
+ psy, &psy_tzd_ops, NULL, 0, 0);
if (IS_ERR(psy->tzd))
return PTR_ERR(psy->tzd);
break;
diff --git a/drivers/staging/omap-thermal/omap-thermal-common.c b/drivers/staging/omap-thermal/omap-thermal-common.c
index 15e9723ba4d..61f1070c666 100644
--- a/drivers/staging/omap-thermal/omap-thermal-common.c
+++ b/drivers/staging/omap-thermal/omap-thermal-common.c
@@ -270,7 +270,7 @@ int omap_thermal_expose_sensor(struct omap_bandgap *bg_ptr, int id,
/* Create thermal zone */
data->omap_thermal = thermal_zone_device_register(domain,
OMAP_TRIP_NUMBER, 0, data, &omap_thermal_ops,
- FAST_TEMP_MONITORING_RATE,
+ NULL, FAST_TEMP_MONITORING_RATE,
FAST_TEMP_MONITORING_RATE);
if (IS_ERR_OR_NULL(data->omap_thermal)) {
dev_err(bg_ptr->dev, "thermal zone device is NULL\n");
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index e1cb6bd75f6..8636fae1f7e 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -13,15 +13,62 @@ menuconfig THERMAL
All platforms with ACPI thermal support can use this driver.
If you want this support, you should say Y or M here.
+if THERMAL
+
config THERMAL_HWMON
bool
- depends on THERMAL
depends on HWMON=y || HWMON=THERMAL
default y
+choice
+ prompt "Default Thermal governor"
+ default THERMAL_DEFAULT_GOV_STEP_WISE
+ help
+ This option sets which thermal governor shall be loaded at
+ startup. If in doubt, select 'step_wise'.
+
+config THERMAL_DEFAULT_GOV_STEP_WISE
+ bool "step_wise"
+ select STEP_WISE
+ help
+ Use the step_wise governor as default. This throttles the
+ devices one step at a time.
+
+config THERMAL_DEFAULT_GOV_FAIR_SHARE
+ bool "fair_share"
+ select FAIR_SHARE
+ help
+ Use the fair_share governor as default. This throttles the
+ devices based on their 'contribution' to a zone. The
+ contribution should be provided through platform data.
+
+config THERMAL_DEFAULT_GOV_USER_SPACE
+ bool "user_space"
+ select USER_SPACE
+ help
+ Select this if you want to let the user space manage the
+ lpatform thermals.
+
+endchoice
+
+config FAIR_SHARE
+ bool "Fair-share thermal governor"
+ help
+ Enable this to manage platform thermals using fair-share governor.
+
+config STEP_WISE
+ bool "Step_wise thermal governor"
+ help
+ Enable this to manage platform thermals using a simple linear
+
+config USER_SPACE
+ bool "User_space thermal governor"
+ help
+ Enable this to let the user space manage the platform thermals.
+
config CPU_THERMAL
- bool "generic cpu cooling support"
- depends on THERMAL && CPU_FREQ
+ tristate "generic cpu cooling support"
+ depends on CPU_FREQ
select CPU_FREQ_TABLE
help
This implements the generic cpu cooling mechanism through frequency
@@ -33,7 +80,6 @@ config CPU_THERMAL
config SPEAR_THERMAL
bool "SPEAr thermal sensor driver"
- depends on THERMAL
depends on PLAT_SPEAR
depends on OF
help
@@ -42,7 +88,6 @@ config SPEAR_THERMAL
config RCAR_THERMAL
tristate "Renesas R-Car thermal driver"
- depends on THERMAL
depends on ARCH_SHMOBILE
help
Enable this to plug the R-Car thermal sensor driver into the Linux
@@ -50,8 +95,31 @@ config RCAR_THERMAL
config EXYNOS_THERMAL
tristate "Temperature sensor on Samsung EXYNOS"
- depends on (ARCH_EXYNOS4 || ARCH_EXYNOS5) && THERMAL
- select CPU_FREQ_TABLE
+ depends on (ARCH_EXYNOS4 || ARCH_EXYNOS5)
+ depends on CPU_THERMAL
help
If you say yes here you get support for TMU (Thermal Managment
Unit) on SAMSUNG EXYNOS series of SoC.
+
+config DB8500_THERMAL
+ bool "DB8500 thermal management"
+ depends on ARCH_U8500
+ default y
+ help
+ Adds DB8500 thermal management implementation according to the thermal
+ management framework. A thermal zone with several trip points will be
+ created. Cooling devices can be bound to the trip points to cool this
+ thermal zone if trip points reached.
+
+config DB8500_CPUFREQ_COOLING
+ tristate "DB8500 cpufreq cooling"
+ depends on ARCH_U8500
+ depends on CPU_THERMAL
+ default y
+ help
+ Adds DB8500 cpufreq cooling devices, and these cooling devices can be
+ bound to thermal zone trip points. When a trip point reached, the
+ bound cpufreq cooling device turns active to set CPU frequency low to
+ cool down the CPU.
+
+endif
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
index 885550dc64b..d8da683245f 100644
--- a/drivers/thermal/Makefile
+++ b/drivers/thermal/Makefile
@@ -3,7 +3,18 @@
#
obj-$(CONFIG_THERMAL) += thermal_sys.o
-obj-$(CONFIG_CPU_THERMAL) += cpu_cooling.o
-obj-$(CONFIG_SPEAR_THERMAL) += spear_thermal.o
+
+# governors
+obj-$(CONFIG_FAIR_SHARE) += fair_share.o
+obj-$(CONFIG_STEP_WISE) += step_wise.o
+obj-$(CONFIG_USER_SPACE) += user_space.o
+
+# cpufreq cooling
+obj-$(CONFIG_CPU_THERMAL) += cpu_cooling.o
+
+# platform thermal drivers
+obj-$(CONFIG_SPEAR_THERMAL) += spear_thermal.o
obj-$(CONFIG_RCAR_THERMAL) += rcar_thermal.o
-obj-$(CONFIG_EXYNOS_THERMAL) += exynos_thermal.o
+obj-$(CONFIG_EXYNOS_THERMAL) += exynos_thermal.o
+obj-$(CONFIG_DB8500_THERMAL) += db8500_thermal.o
+obj-$(CONFIG_DB8500_CPUFREQ_COOLING) += db8500_cpufreq_cooling.o
diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c
index cc1c930a90e..836828e29a8 100644
--- a/drivers/thermal/cpu_cooling.c
+++ b/drivers/thermal/cpu_cooling.c
@@ -58,12 +58,13 @@ struct cpufreq_cooling_device {
};
static LIST_HEAD(cooling_cpufreq_list);
static DEFINE_IDR(cpufreq_idr);
+static DEFINE_MUTEX(cooling_cpufreq_lock);
-static struct mutex cooling_cpufreq_lock;
+static unsigned int cpufreq_dev_count;
/* notify_table passes value to the CPUFREQ_ADJUST callback function. */
#define NOTIFY_INVALID NULL
-struct cpufreq_cooling_device *notify_device;
+static struct cpufreq_cooling_device *notify_device;
/**
* get_idr - function to get a unique id.
@@ -240,42 +241,32 @@ static int cpufreq_thermal_notifier(struct notifier_block *nb,
static int cpufreq_get_max_state(struct thermal_cooling_device *cdev,
unsigned long *state)
{
- int ret = -EINVAL, i = 0;
- struct cpufreq_cooling_device *cpufreq_device;
- struct cpumask *maskPtr;
+ struct cpufreq_cooling_device *cpufreq_device = cdev->devdata;
+ struct cpumask *maskPtr = &cpufreq_device->allowed_cpus;
unsigned int cpu;
struct cpufreq_frequency_table *table;
+ unsigned long count = 0;
+ int i = 0;
- mutex_lock(&cooling_cpufreq_lock);
- list_for_each_entry(cpufreq_device, &cooling_cpufreq_list, node) {
- if (cpufreq_device && cpufreq_device->cool_dev == cdev)
- break;
- }
- if (cpufreq_device == NULL)
- goto return_get_max_state;
-
- maskPtr = &cpufreq_device->allowed_cpus;
cpu = cpumask_any(maskPtr);
table = cpufreq_frequency_get_table(cpu);
if (!table) {
*state = 0;
- ret = 0;
- goto return_get_max_state;
+ return 0;
}
- while (table[i].frequency != CPUFREQ_TABLE_END) {
+ for (i = 0; (table[i].frequency != CPUFREQ_TABLE_END); i++) {
if (table[i].frequency == CPUFREQ_ENTRY_INVALID)
continue;
- i++;
+ count++;
}
- if (i > 0) {
- *state = --i;
- ret = 0;
+
+ if (count > 0) {
+ *state = --count;
+ return 0;
}
-return_get_max_state:
- mutex_unlock(&cooling_cpufreq_lock);
- return ret;
+ return -EINVAL;
}
/**
@@ -286,20 +277,10 @@ return_get_max_state:
static int cpufreq_get_cur_state(struct thermal_cooling_device *cdev,
unsigned long *state)
{
- int ret = -EINVAL;
- struct cpufreq_cooling_device *cpufreq_device;
+ struct cpufreq_cooling_device *cpufreq_device = cdev->devdata;
- mutex_lock(&cooling_cpufreq_lock);
- list_for_each_entry(cpufreq_device, &cooling_cpufreq_list, node) {
- if (cpufreq_device && cpufreq_device->cool_dev == cdev) {
- *state = cpufreq_device->cpufreq_state;
- ret = 0;
- break;
- }
- }
- mutex_unlock(&cooling_cpufreq_lock);
-
- return ret;
+ *state = cpufreq_device->cpufreq_state;
+ return 0;
}
/**
@@ -310,22 +291,9 @@ static int cpufreq_get_cur_state(struct thermal_cooling_device *cdev,
static int cpufreq_set_cur_state(struct thermal_cooling_device *cdev,
unsigned long state)
{
- int ret = -EINVAL;
- struct cpufreq_cooling_device *cpufreq_device;
+ struct cpufreq_cooling_device *cpufreq_device = cdev->devdata;
- mutex_lock(&cooling_cpufreq_lock);
- list_for_each_entry(cpufreq_device, &cooling_cpufreq_list, node) {
- if (cpufreq_device && cpufreq_device->cool_dev == cdev) {
- ret = 0;
- break;
- }
- }
- if (!ret)
- ret = cpufreq_apply_cooling(cpufreq_device, state);
-
- mutex_unlock(&cooling_cpufreq_lock);
-
- return ret;
+ return cpufreq_apply_cooling(cpufreq_device, state);
}
/* Bind cpufreq callbacks to thermal cooling device ops */
@@ -345,18 +313,15 @@ static struct notifier_block thermal_cpufreq_notifier_block = {
* @clip_cpus: cpumask of cpus where the frequency constraints will happen.
*/
struct thermal_cooling_device *cpufreq_cooling_register(
- struct cpumask *clip_cpus)
+ const struct cpumask *clip_cpus)
{
struct thermal_cooling_device *cool_dev;
struct cpufreq_cooling_device *cpufreq_dev = NULL;
- unsigned int cpufreq_dev_count = 0, min = 0, max = 0;
+ unsigned int min = 0, max = 0;
char dev_name[THERMAL_NAME_LENGTH];
int ret = 0, i;
struct cpufreq_policy policy;
- list_for_each_entry(cpufreq_dev, &cooling_cpufreq_list, node)
- cpufreq_dev_count++;
-
/*Verify that all the clip cpus have same freq_min, freq_max limit*/
for_each_cpu(i, clip_cpus) {
/*continue if cpufreq policy not found and not return error*/
@@ -369,7 +334,7 @@ struct thermal_cooling_device *cpufreq_cooling_register(
if (min != policy.cpuinfo.min_freq ||
max != policy.cpuinfo.max_freq)
return ERR_PTR(-EINVAL);
-}
+ }
}
cpufreq_dev = kzalloc(sizeof(struct cpufreq_cooling_device),
GFP_KERNEL);
@@ -378,9 +343,6 @@ struct thermal_cooling_device *cpufreq_cooling_register(
cpumask_copy(&cpufreq_dev->allowed_cpus, clip_cpus);
- if (cpufreq_dev_count == 0)
- mutex_init(&cooling_cpufreq_lock);
-
ret = get_idr(&cpufreq_idr, &cpufreq_dev->id);
if (ret) {
kfree(cpufreq_dev);
@@ -399,12 +361,12 @@ struct thermal_cooling_device *cpufreq_cooling_register(
cpufreq_dev->cool_dev = cool_dev;
cpufreq_dev->cpufreq_state = 0;
mutex_lock(&cooling_cpufreq_lock);
- list_add_tail(&cpufreq_dev->node, &cooling_cpufreq_list);
/* Register the notifier for first cpufreq cooling device */
if (cpufreq_dev_count == 0)
cpufreq_register_notifier(&thermal_cpufreq_notifier_block,
CPUFREQ_POLICY_NOTIFIER);
+ cpufreq_dev_count++;
mutex_unlock(&cooling_cpufreq_lock);
return cool_dev;
@@ -417,33 +379,20 @@ EXPORT_SYMBOL(cpufreq_cooling_register);
*/
void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev)
{
- struct cpufreq_cooling_device *cpufreq_dev = NULL;
- unsigned int cpufreq_dev_count = 0;
+ struct cpufreq_cooling_device *cpufreq_dev = cdev->devdata;
mutex_lock(&cooling_cpufreq_lock);
- list_for_each_entry(cpufreq_dev, &cooling_cpufreq_list, node) {
- if (cpufreq_dev && cpufreq_dev->cool_dev == cdev)
- break;
- cpufreq_dev_count++;
- }
-
- if (!cpufreq_dev || cpufreq_dev->cool_dev != cdev) {
- mutex_unlock(&cooling_cpufreq_lock);
- return;
- }
-
- list_del(&cpufreq_dev->node);
+ cpufreq_dev_count--;
/* Unregister the notifier for the last cpufreq cooling device */
- if (cpufreq_dev_count == 1) {
+ if (cpufreq_dev_count == 0) {
cpufreq_unregister_notifier(&thermal_cpufreq_notifier_block,
CPUFREQ_POLICY_NOTIFIER);
}
mutex_unlock(&cooling_cpufreq_lock);
+
thermal_cooling_device_unregister(cpufreq_dev->cool_dev);
release_idr(&cpufreq_idr, cpufreq_dev->id);
- if (cpufreq_dev_count == 1)
- mutex_destroy(&cooling_cpufreq_lock);
kfree(cpufreq_dev);
}
EXPORT_SYMBOL(cpufreq_cooling_unregister);
diff --git a/drivers/thermal/db8500_cpufreq_cooling.c b/drivers/thermal/db8500_cpufreq_cooling.c
new file mode 100644
index 00000000000..4cf8e72af90
--- /dev/null
+++ b/drivers/thermal/db8500_cpufreq_cooling.c
@@ -0,0 +1,108 @@
+/*
+ * db8500_cpufreq_cooling.c - DB8500 cpufreq works as cooling device.
+ *
+ * Copyright (C) 2012 ST-Ericsson
+ * Copyright (C) 2012 Linaro Ltd.
+ *
+ * Author: Hongbo Zhang <hongbo.zhang@linaro.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.
+ */
+
+#include <linux/cpu_cooling.h>
+#include <linux/cpufreq.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+static int db8500_cpufreq_cooling_probe(struct platform_device *pdev)
+{
+ struct thermal_cooling_device *cdev;
+ struct cpumask mask_val;
+
+ /* make sure cpufreq driver has been initialized */
+ if (!cpufreq_frequency_get_table(0))
+ return -EPROBE_DEFER;
+
+ cpumask_set_cpu(0, &mask_val);
+ cdev = cpufreq_cooling_register(&mask_val);
+
+ if (IS_ERR_OR_NULL(cdev)) {
+ dev_err(&pdev->dev, "Failed to register cooling device\n");
+ return PTR_ERR(cdev);
+ }
+
+ platform_set_drvdata(pdev, cdev);
+
+ dev_info(&pdev->dev, "Cooling device registered: %s\n", cdev->type);
+
+ return 0;
+}
+
+static int db8500_cpufreq_cooling_remove(struct platform_device *pdev)
+{
+ struct thermal_cooling_device *cdev = platform_get_drvdata(pdev);
+
+ cpufreq_cooling_unregister(cdev);
+
+ return 0;
+}
+
+static int db8500_cpufreq_cooling_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ return -ENOSYS;
+}
+
+static int db8500_cpufreq_cooling_resume(struct platform_device *pdev)
+{
+ return -ENOSYS;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id db8500_cpufreq_cooling_match[] = {
+ { .compatible = "stericsson,db8500-cpufreq-cooling" },
+ {},
+};
+#else
+#define db8500_cpufreq_cooling_match NULL
+#endif
+
+static struct platform_driver db8500_cpufreq_cooling_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "db8500-cpufreq-cooling",
+ .of_match_table = db8500_cpufreq_cooling_match,
+ },
+ .probe = db8500_cpufreq