Commit 1dc3217dad3e771f2bd12703d6daed6cb818fdd8
Exists in
master
and in
16 other branches
Merge branch 'for-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/rzhang/linux
Pull thermal fixes from Zhang Rui: "Specifics: - Update the help text of INT3403 Thermal driver, which was not friendly to users. From Zhang Rui. - The "type" sysfs attribute of x86_pkg_temp_thermal registered thermal zones includes an instance number, which makes the thermal-to-hwmon bridge fails to group them all in a single hwmon device. Fixed by Jean Delvare. - The hwmon device registered by x86_pkg_temp_thermal driver is redundant because the temperature value reported by x86_pkg_temp_thermal is already reported by the coretemp driver. Fixed by Jean Delvare. - Fix a problem that the cooling device can not be updated properly if it is initialized at max cooling state. From Ni Wade. - Fix a problem that OF registered thermal zones are running without thermal governors. From Zhang Rui. - Commit beeb5a1e0ef7 ("thermal: rcar-thermal: Enable driver compilation with COMPILE_TEST") broke build on archs wihout io memory. Thus make it depend on HAS_IOMEM to bypass build failures. Fixed by Richard Weinberger" * 'for-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/rzhang/linux: Thermal: thermal zone governor fix Thermal: Allow first update of cooling device state thermal,rcar_thermal: Add dependency on HAS_IOMEM x86_pkg_temp_thermal: Fix the thermal zone type x86_pkg_temp_thermal: Do not expose as a hwmon device Thermal: update INT3404 thermal driver help text
Showing 3 changed files Inline Diff
drivers/thermal/Kconfig
1 | # | 1 | # |
2 | # Generic thermal sysfs drivers configuration | 2 | # Generic thermal sysfs drivers configuration |
3 | # | 3 | # |
4 | 4 | ||
5 | menuconfig THERMAL | 5 | menuconfig THERMAL |
6 | tristate "Generic Thermal sysfs driver" | 6 | tristate "Generic Thermal sysfs driver" |
7 | help | 7 | help |
8 | Generic Thermal Sysfs driver offers a generic mechanism for | 8 | Generic Thermal Sysfs driver offers a generic mechanism for |
9 | thermal management. Usually it's made up of one or more thermal | 9 | thermal management. Usually it's made up of one or more thermal |
10 | zone and cooling device. | 10 | zone and cooling device. |
11 | Each thermal zone contains its own temperature, trip points, | 11 | Each thermal zone contains its own temperature, trip points, |
12 | cooling devices. | 12 | cooling devices. |
13 | All platforms with ACPI thermal support can use this driver. | 13 | All platforms with ACPI thermal support can use this driver. |
14 | If you want this support, you should say Y or M here. | 14 | If you want this support, you should say Y or M here. |
15 | 15 | ||
16 | if THERMAL | 16 | if THERMAL |
17 | 17 | ||
18 | config THERMAL_HWMON | 18 | config THERMAL_HWMON |
19 | bool | 19 | bool |
20 | prompt "Expose thermal sensors as hwmon device" | 20 | prompt "Expose thermal sensors as hwmon device" |
21 | depends on HWMON=y || HWMON=THERMAL | 21 | depends on HWMON=y || HWMON=THERMAL |
22 | default y | 22 | default y |
23 | help | 23 | help |
24 | In case a sensor is registered with the thermal | 24 | In case a sensor is registered with the thermal |
25 | framework, this option will also register it | 25 | framework, this option will also register it |
26 | as a hwmon. The sensor will then have the common | 26 | as a hwmon. The sensor will then have the common |
27 | hwmon sysfs interface. | 27 | hwmon sysfs interface. |
28 | 28 | ||
29 | Say 'Y' here if you want all thermal sensors to | 29 | Say 'Y' here if you want all thermal sensors to |
30 | have hwmon sysfs interface too. | 30 | have hwmon sysfs interface too. |
31 | 31 | ||
32 | config THERMAL_OF | 32 | config THERMAL_OF |
33 | bool | 33 | bool |
34 | prompt "APIs to parse thermal data out of device tree" | 34 | prompt "APIs to parse thermal data out of device tree" |
35 | depends on OF | 35 | depends on OF |
36 | default y | 36 | default y |
37 | help | 37 | help |
38 | This options provides helpers to add the support to | 38 | This options provides helpers to add the support to |
39 | read and parse thermal data definitions out of the | 39 | read and parse thermal data definitions out of the |
40 | device tree blob. | 40 | device tree blob. |
41 | 41 | ||
42 | Say 'Y' here if you need to build thermal infrastructure | 42 | Say 'Y' here if you need to build thermal infrastructure |
43 | based on device tree. | 43 | based on device tree. |
44 | 44 | ||
45 | choice | 45 | choice |
46 | prompt "Default Thermal governor" | 46 | prompt "Default Thermal governor" |
47 | default THERMAL_DEFAULT_GOV_STEP_WISE | 47 | default THERMAL_DEFAULT_GOV_STEP_WISE |
48 | help | 48 | help |
49 | This option sets which thermal governor shall be loaded at | 49 | This option sets which thermal governor shall be loaded at |
50 | startup. If in doubt, select 'step_wise'. | 50 | startup. If in doubt, select 'step_wise'. |
51 | 51 | ||
52 | config THERMAL_DEFAULT_GOV_STEP_WISE | 52 | config THERMAL_DEFAULT_GOV_STEP_WISE |
53 | bool "step_wise" | 53 | bool "step_wise" |
54 | select THERMAL_GOV_STEP_WISE | 54 | select THERMAL_GOV_STEP_WISE |
55 | help | 55 | help |
56 | Use the step_wise governor as default. This throttles the | 56 | Use the step_wise governor as default. This throttles the |
57 | devices one step at a time. | 57 | devices one step at a time. |
58 | 58 | ||
59 | config THERMAL_DEFAULT_GOV_FAIR_SHARE | 59 | config THERMAL_DEFAULT_GOV_FAIR_SHARE |
60 | bool "fair_share" | 60 | bool "fair_share" |
61 | select THERMAL_GOV_FAIR_SHARE | 61 | select THERMAL_GOV_FAIR_SHARE |
62 | help | 62 | help |
63 | Use the fair_share governor as default. This throttles the | 63 | Use the fair_share governor as default. This throttles the |
64 | devices based on their 'contribution' to a zone. The | 64 | devices based on their 'contribution' to a zone. The |
65 | contribution should be provided through platform data. | 65 | contribution should be provided through platform data. |
66 | 66 | ||
67 | config THERMAL_DEFAULT_GOV_USER_SPACE | 67 | config THERMAL_DEFAULT_GOV_USER_SPACE |
68 | bool "user_space" | 68 | bool "user_space" |
69 | select THERMAL_GOV_USER_SPACE | 69 | select THERMAL_GOV_USER_SPACE |
70 | help | 70 | help |
71 | Select this if you want to let the user space manage the | 71 | Select this if you want to let the user space manage the |
72 | platform thermals. | 72 | platform thermals. |
73 | 73 | ||
74 | endchoice | 74 | endchoice |
75 | 75 | ||
76 | config THERMAL_GOV_FAIR_SHARE | 76 | config THERMAL_GOV_FAIR_SHARE |
77 | bool "Fair-share thermal governor" | 77 | bool "Fair-share thermal governor" |
78 | help | 78 | help |
79 | Enable this to manage platform thermals using fair-share governor. | 79 | Enable this to manage platform thermals using fair-share governor. |
80 | 80 | ||
81 | config THERMAL_GOV_STEP_WISE | 81 | config THERMAL_GOV_STEP_WISE |
82 | bool "Step_wise thermal governor" | 82 | bool "Step_wise thermal governor" |
83 | help | 83 | help |
84 | Enable this to manage platform thermals using a simple linear | 84 | Enable this to manage platform thermals using a simple linear |
85 | governor. | 85 | governor. |
86 | 86 | ||
87 | config THERMAL_GOV_USER_SPACE | 87 | config THERMAL_GOV_USER_SPACE |
88 | bool "User_space thermal governor" | 88 | bool "User_space thermal governor" |
89 | help | 89 | help |
90 | Enable this to let the user space manage the platform thermals. | 90 | Enable this to let the user space manage the platform thermals. |
91 | 91 | ||
92 | config CPU_THERMAL | 92 | config CPU_THERMAL |
93 | bool "generic cpu cooling support" | 93 | bool "generic cpu cooling support" |
94 | depends on CPU_FREQ | 94 | depends on CPU_FREQ |
95 | depends on THERMAL_OF | 95 | depends on THERMAL_OF |
96 | help | 96 | help |
97 | This implements the generic cpu cooling mechanism through frequency | 97 | This implements the generic cpu cooling mechanism through frequency |
98 | reduction. An ACPI version of this already exists | 98 | reduction. An ACPI version of this already exists |
99 | (drivers/acpi/processor_thermal.c). | 99 | (drivers/acpi/processor_thermal.c). |
100 | This will be useful for platforms using the generic thermal interface | 100 | This will be useful for platforms using the generic thermal interface |
101 | and not the ACPI interface. | 101 | and not the ACPI interface. |
102 | 102 | ||
103 | If you want this support, you should say Y here. | 103 | If you want this support, you should say Y here. |
104 | 104 | ||
105 | config THERMAL_EMULATION | 105 | config THERMAL_EMULATION |
106 | bool "Thermal emulation mode support" | 106 | bool "Thermal emulation mode support" |
107 | help | 107 | help |
108 | Enable this option to make a emul_temp sysfs node in thermal zone | 108 | Enable this option to make a emul_temp sysfs node in thermal zone |
109 | directory to support temperature emulation. With emulation sysfs node, | 109 | directory to support temperature emulation. With emulation sysfs node, |
110 | user can manually input temperature and test the different trip | 110 | user can manually input temperature and test the different trip |
111 | threshold behaviour for simulation purpose. | 111 | threshold behaviour for simulation purpose. |
112 | 112 | ||
113 | WARNING: Be careful while enabling this option on production systems, | 113 | WARNING: Be careful while enabling this option on production systems, |
114 | because userland can easily disable the thermal policy by simply | 114 | because userland can easily disable the thermal policy by simply |
115 | flooding this sysfs node with low temperature values. | 115 | flooding this sysfs node with low temperature values. |
116 | 116 | ||
117 | config IMX_THERMAL | 117 | config IMX_THERMAL |
118 | tristate "Temperature sensor driver for Freescale i.MX SoCs" | 118 | tristate "Temperature sensor driver for Freescale i.MX SoCs" |
119 | depends on CPU_THERMAL | 119 | depends on CPU_THERMAL |
120 | depends on MFD_SYSCON | 120 | depends on MFD_SYSCON |
121 | depends on OF | 121 | depends on OF |
122 | help | 122 | help |
123 | Support for Temperature Monitor (TEMPMON) found on Freescale i.MX SoCs. | 123 | Support for Temperature Monitor (TEMPMON) found on Freescale i.MX SoCs. |
124 | It supports one critical trip point and one passive trip point. The | 124 | It supports one critical trip point and one passive trip point. The |
125 | cpufreq is used as the cooling device to throttle CPUs when the | 125 | cpufreq is used as the cooling device to throttle CPUs when the |
126 | passive trip is crossed. | 126 | passive trip is crossed. |
127 | 127 | ||
128 | config SPEAR_THERMAL | 128 | config SPEAR_THERMAL |
129 | bool "SPEAr thermal sensor driver" | 129 | bool "SPEAr thermal sensor driver" |
130 | depends on PLAT_SPEAR | 130 | depends on PLAT_SPEAR |
131 | depends on OF | 131 | depends on OF |
132 | help | 132 | help |
133 | Enable this to plug the SPEAr thermal sensor driver into the Linux | 133 | Enable this to plug the SPEAr thermal sensor driver into the Linux |
134 | thermal framework. | 134 | thermal framework. |
135 | 135 | ||
136 | config RCAR_THERMAL | 136 | config RCAR_THERMAL |
137 | tristate "Renesas R-Car thermal driver" | 137 | tristate "Renesas R-Car thermal driver" |
138 | depends on ARCH_SHMOBILE || COMPILE_TEST | 138 | depends on ARCH_SHMOBILE || COMPILE_TEST |
139 | depends on HAS_IOMEM | ||
139 | help | 140 | help |
140 | Enable this to plug the R-Car thermal sensor driver into the Linux | 141 | Enable this to plug the R-Car thermal sensor driver into the Linux |
141 | thermal framework. | 142 | thermal framework. |
142 | 143 | ||
143 | config KIRKWOOD_THERMAL | 144 | config KIRKWOOD_THERMAL |
144 | tristate "Temperature sensor on Marvell Kirkwood SoCs" | 145 | tristate "Temperature sensor on Marvell Kirkwood SoCs" |
145 | depends on ARCH_KIRKWOOD | 146 | depends on ARCH_KIRKWOOD |
146 | depends on OF | 147 | depends on OF |
147 | help | 148 | help |
148 | Support for the Kirkwood thermal sensor driver into the Linux thermal | 149 | Support for the Kirkwood thermal sensor driver into the Linux thermal |
149 | framework. Only kirkwood 88F6282 and 88F6283 have this sensor. | 150 | framework. Only kirkwood 88F6282 and 88F6283 have this sensor. |
150 | 151 | ||
151 | config DOVE_THERMAL | 152 | config DOVE_THERMAL |
152 | tristate "Temperature sensor on Marvell Dove SoCs" | 153 | tristate "Temperature sensor on Marvell Dove SoCs" |
153 | depends on ARCH_DOVE | 154 | depends on ARCH_DOVE |
154 | depends on OF | 155 | depends on OF |
155 | help | 156 | help |
156 | Support for the Dove thermal sensor driver in the Linux thermal | 157 | Support for the Dove thermal sensor driver in the Linux thermal |
157 | framework. | 158 | framework. |
158 | 159 | ||
159 | config DB8500_THERMAL | 160 | config DB8500_THERMAL |
160 | bool "DB8500 thermal management" | 161 | bool "DB8500 thermal management" |
161 | depends on ARCH_U8500 | 162 | depends on ARCH_U8500 |
162 | default y | 163 | default y |
163 | help | 164 | help |
164 | Adds DB8500 thermal management implementation according to the thermal | 165 | Adds DB8500 thermal management implementation according to the thermal |
165 | management framework. A thermal zone with several trip points will be | 166 | management framework. A thermal zone with several trip points will be |
166 | created. Cooling devices can be bound to the trip points to cool this | 167 | created. Cooling devices can be bound to the trip points to cool this |
167 | thermal zone if trip points reached. | 168 | thermal zone if trip points reached. |
168 | 169 | ||
169 | config ARMADA_THERMAL | 170 | config ARMADA_THERMAL |
170 | tristate "Armada 370/XP thermal management" | 171 | tristate "Armada 370/XP thermal management" |
171 | depends on ARCH_MVEBU | 172 | depends on ARCH_MVEBU |
172 | depends on OF | 173 | depends on OF |
173 | help | 174 | help |
174 | Enable this option if you want to have support for thermal management | 175 | Enable this option if you want to have support for thermal management |
175 | controller present in Armada 370 and Armada XP SoC. | 176 | controller present in Armada 370 and Armada XP SoC. |
176 | 177 | ||
177 | config DB8500_CPUFREQ_COOLING | 178 | config DB8500_CPUFREQ_COOLING |
178 | tristate "DB8500 cpufreq cooling" | 179 | tristate "DB8500 cpufreq cooling" |
179 | depends on ARCH_U8500 | 180 | depends on ARCH_U8500 |
180 | depends on CPU_THERMAL | 181 | depends on CPU_THERMAL |
181 | default y | 182 | default y |
182 | help | 183 | help |
183 | Adds DB8500 cpufreq cooling devices, and these cooling devices can be | 184 | Adds DB8500 cpufreq cooling devices, and these cooling devices can be |
184 | bound to thermal zone trip points. When a trip point reached, the | 185 | bound to thermal zone trip points. When a trip point reached, the |
185 | bound cpufreq cooling device turns active to set CPU frequency low to | 186 | bound cpufreq cooling device turns active to set CPU frequency low to |
186 | cool down the CPU. | 187 | cool down the CPU. |
187 | 188 | ||
188 | config INTEL_POWERCLAMP | 189 | config INTEL_POWERCLAMP |
189 | tristate "Intel PowerClamp idle injection driver" | 190 | tristate "Intel PowerClamp idle injection driver" |
190 | depends on THERMAL | 191 | depends on THERMAL |
191 | depends on X86 | 192 | depends on X86 |
192 | depends on CPU_SUP_INTEL | 193 | depends on CPU_SUP_INTEL |
193 | help | 194 | help |
194 | Enable this to enable Intel PowerClamp idle injection driver. This | 195 | Enable this to enable Intel PowerClamp idle injection driver. This |
195 | enforce idle time which results in more package C-state residency. The | 196 | enforce idle time which results in more package C-state residency. The |
196 | user interface is exposed via generic thermal framework. | 197 | user interface is exposed via generic thermal framework. |
197 | 198 | ||
198 | config X86_PKG_TEMP_THERMAL | 199 | config X86_PKG_TEMP_THERMAL |
199 | tristate "X86 package temperature thermal driver" | 200 | tristate "X86 package temperature thermal driver" |
200 | depends on X86_THERMAL_VECTOR | 201 | depends on X86_THERMAL_VECTOR |
201 | select THERMAL_GOV_USER_SPACE | 202 | select THERMAL_GOV_USER_SPACE |
202 | default m | 203 | default m |
203 | help | 204 | help |
204 | Enable this to register CPU digital sensor for package temperature as | 205 | Enable this to register CPU digital sensor for package temperature as |
205 | thermal zone. Each package will have its own thermal zone. There are | 206 | thermal zone. Each package will have its own thermal zone. There are |
206 | two trip points which can be set by user to get notifications via thermal | 207 | two trip points which can be set by user to get notifications via thermal |
207 | notification methods. | 208 | notification methods. |
208 | 209 | ||
209 | config ACPI_INT3403_THERMAL | 210 | config ACPI_INT3403_THERMAL |
210 | tristate "ACPI INT3403 thermal driver" | 211 | tristate "ACPI INT3403 thermal driver" |
211 | depends on X86 && ACPI | 212 | depends on X86 && ACPI |
212 | help | 213 | help |
213 | This driver uses ACPI INT3403 device objects. If present, it will | 214 | Newer laptops and tablets that use ACPI may have thermal sensors |
214 | register each INT3403 thermal sensor as a thermal zone. | 215 | outside the core CPU/SOC for thermal safety reasons. These |
216 | temperature sensors are also exposed for the OS to use via the so | ||
217 | called INT3403 ACPI object. This driver will, on devices that have | ||
218 | such sensors, expose the temperature information from these sensors | ||
219 | to userspace via the normal thermal framework. This means that a wide | ||
220 | range of applications and GUI widgets can show this information to | ||
221 | the user or use this information for making decisions. For example, | ||
222 | the Intel Thermal Daemon can use this information to allow the user | ||
223 | to select his laptop to run without turning on the fans. | ||
215 | 224 | ||
216 | menu "Texas Instruments thermal drivers" | 225 | menu "Texas Instruments thermal drivers" |
217 | source "drivers/thermal/ti-soc-thermal/Kconfig" | 226 | source "drivers/thermal/ti-soc-thermal/Kconfig" |
218 | endmenu | 227 | endmenu |
219 | 228 | ||
220 | menu "Samsung thermal drivers" | 229 | menu "Samsung thermal drivers" |
221 | depends on PLAT_SAMSUNG | 230 | depends on PLAT_SAMSUNG |
222 | source "drivers/thermal/samsung/Kconfig" | 231 | source "drivers/thermal/samsung/Kconfig" |
223 | endmenu | 232 | endmenu |
224 | 233 | ||
225 | endif | 234 | endif |
226 | 235 |
drivers/thermal/thermal_core.c
1 | /* | 1 | /* |
2 | * thermal.c - Generic Thermal Management Sysfs support. | 2 | * thermal.c - Generic Thermal Management Sysfs support. |
3 | * | 3 | * |
4 | * Copyright (C) 2008 Intel Corp | 4 | * Copyright (C) 2008 Intel Corp |
5 | * Copyright (C) 2008 Zhang Rui <rui.zhang@intel.com> | 5 | * Copyright (C) 2008 Zhang Rui <rui.zhang@intel.com> |
6 | * Copyright (C) 2008 Sujith Thomas <sujith.thomas@intel.com> | 6 | * Copyright (C) 2008 Sujith Thomas <sujith.thomas@intel.com> |
7 | * | 7 | * |
8 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | 8 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
9 | * | 9 | * |
10 | * This program is free software; you can redistribute it and/or modify | 10 | * This program is free software; you can redistribute it and/or modify |
11 | * it under the terms of the GNU General Public License as published by | 11 | * it under the terms of the GNU General Public License as published by |
12 | * the Free Software Foundation; version 2 of the License. | 12 | * the Free Software Foundation; version 2 of the License. |
13 | * | 13 | * |
14 | * This program is distributed in the hope that it will be useful, but | 14 | * This program is distributed in the hope that it will be useful, but |
15 | * WITHOUT ANY WARRANTY; without even the implied warranty of | 15 | * WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
17 | * General Public License for more details. | 17 | * General Public License for more details. |
18 | * | 18 | * |
19 | * You should have received a copy of the GNU General Public License along | 19 | * You should have received a copy of the GNU General Public License along |
20 | * with this program; if not, write to the Free Software Foundation, Inc., | 20 | * with this program; if not, write to the Free Software Foundation, Inc., |
21 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. | 21 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. |
22 | * | 22 | * |
23 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | 23 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
24 | */ | 24 | */ |
25 | 25 | ||
26 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | 26 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
27 | 27 | ||
28 | #include <linux/module.h> | 28 | #include <linux/module.h> |
29 | #include <linux/device.h> | 29 | #include <linux/device.h> |
30 | #include <linux/err.h> | 30 | #include <linux/err.h> |
31 | #include <linux/slab.h> | 31 | #include <linux/slab.h> |
32 | #include <linux/kdev_t.h> | 32 | #include <linux/kdev_t.h> |
33 | #include <linux/idr.h> | 33 | #include <linux/idr.h> |
34 | #include <linux/thermal.h> | 34 | #include <linux/thermal.h> |
35 | #include <linux/reboot.h> | 35 | #include <linux/reboot.h> |
36 | #include <linux/string.h> | 36 | #include <linux/string.h> |
37 | #include <linux/of.h> | 37 | #include <linux/of.h> |
38 | #include <net/netlink.h> | 38 | #include <net/netlink.h> |
39 | #include <net/genetlink.h> | 39 | #include <net/genetlink.h> |
40 | 40 | ||
41 | #include "thermal_core.h" | 41 | #include "thermal_core.h" |
42 | #include "thermal_hwmon.h" | 42 | #include "thermal_hwmon.h" |
43 | 43 | ||
44 | MODULE_AUTHOR("Zhang Rui"); | 44 | MODULE_AUTHOR("Zhang Rui"); |
45 | MODULE_DESCRIPTION("Generic thermal management sysfs support"); | 45 | MODULE_DESCRIPTION("Generic thermal management sysfs support"); |
46 | MODULE_LICENSE("GPL v2"); | 46 | MODULE_LICENSE("GPL v2"); |
47 | 47 | ||
48 | static DEFINE_IDR(thermal_tz_idr); | 48 | static DEFINE_IDR(thermal_tz_idr); |
49 | static DEFINE_IDR(thermal_cdev_idr); | 49 | static DEFINE_IDR(thermal_cdev_idr); |
50 | static DEFINE_MUTEX(thermal_idr_lock); | 50 | static DEFINE_MUTEX(thermal_idr_lock); |
51 | 51 | ||
52 | static LIST_HEAD(thermal_tz_list); | 52 | static LIST_HEAD(thermal_tz_list); |
53 | static LIST_HEAD(thermal_cdev_list); | 53 | static LIST_HEAD(thermal_cdev_list); |
54 | static LIST_HEAD(thermal_governor_list); | 54 | static LIST_HEAD(thermal_governor_list); |
55 | 55 | ||
56 | static DEFINE_MUTEX(thermal_list_lock); | 56 | static DEFINE_MUTEX(thermal_list_lock); |
57 | static DEFINE_MUTEX(thermal_governor_lock); | 57 | static DEFINE_MUTEX(thermal_governor_lock); |
58 | 58 | ||
59 | static struct thermal_governor *def_governor; | ||
60 | |||
59 | static struct thermal_governor *__find_governor(const char *name) | 61 | static struct thermal_governor *__find_governor(const char *name) |
60 | { | 62 | { |
61 | struct thermal_governor *pos; | 63 | struct thermal_governor *pos; |
62 | 64 | ||
65 | if (!name || !name[0]) | ||
66 | return def_governor; | ||
67 | |||
63 | list_for_each_entry(pos, &thermal_governor_list, governor_list) | 68 | list_for_each_entry(pos, &thermal_governor_list, governor_list) |
64 | if (!strnicmp(name, pos->name, THERMAL_NAME_LENGTH)) | 69 | if (!strnicmp(name, pos->name, THERMAL_NAME_LENGTH)) |
65 | return pos; | 70 | return pos; |
66 | 71 | ||
67 | return NULL; | 72 | return NULL; |
68 | } | 73 | } |
69 | 74 | ||
70 | int thermal_register_governor(struct thermal_governor *governor) | 75 | int thermal_register_governor(struct thermal_governor *governor) |
71 | { | 76 | { |
72 | int err; | 77 | int err; |
73 | const char *name; | 78 | const char *name; |
74 | struct thermal_zone_device *pos; | 79 | struct thermal_zone_device *pos; |
75 | 80 | ||
76 | if (!governor) | 81 | if (!governor) |
77 | return -EINVAL; | 82 | return -EINVAL; |
78 | 83 | ||
79 | mutex_lock(&thermal_governor_lock); | 84 | mutex_lock(&thermal_governor_lock); |
80 | 85 | ||
81 | err = -EBUSY; | 86 | err = -EBUSY; |
82 | if (__find_governor(governor->name) == NULL) { | 87 | if (__find_governor(governor->name) == NULL) { |
83 | err = 0; | 88 | err = 0; |
84 | list_add(&governor->governor_list, &thermal_governor_list); | 89 | list_add(&governor->governor_list, &thermal_governor_list); |
90 | if (!def_governor && !strncmp(governor->name, | ||
91 | DEFAULT_THERMAL_GOVERNOR, THERMAL_NAME_LENGTH)) | ||
92 | def_governor = governor; | ||
85 | } | 93 | } |
86 | 94 | ||
87 | mutex_lock(&thermal_list_lock); | 95 | mutex_lock(&thermal_list_lock); |
88 | 96 | ||
89 | list_for_each_entry(pos, &thermal_tz_list, node) { | 97 | list_for_each_entry(pos, &thermal_tz_list, node) { |
98 | /* | ||
99 | * only thermal zones with specified tz->tzp->governor_name | ||
100 | * may run with tz->govenor unset | ||
101 | */ | ||
90 | if (pos->governor) | 102 | if (pos->governor) |
91 | continue; | 103 | continue; |
92 | if (pos->tzp) | 104 | |
93 | name = pos->tzp->governor_name; | 105 | name = pos->tzp->governor_name; |
94 | else | 106 | |
95 | name = DEFAULT_THERMAL_GOVERNOR; | ||
96 | if (!strnicmp(name, governor->name, THERMAL_NAME_LENGTH)) | 107 | if (!strnicmp(name, governor->name, THERMAL_NAME_LENGTH)) |
97 | pos->governor = governor; | 108 | pos->governor = governor; |
98 | } | 109 | } |
99 | 110 | ||
100 | mutex_unlock(&thermal_list_lock); | 111 | mutex_unlock(&thermal_list_lock); |
101 | mutex_unlock(&thermal_governor_lock); | 112 | mutex_unlock(&thermal_governor_lock); |
102 | 113 | ||
103 | return err; | 114 | return err; |
104 | } | 115 | } |
105 | 116 | ||
106 | void thermal_unregister_governor(struct thermal_governor *governor) | 117 | void thermal_unregister_governor(struct thermal_governor *governor) |
107 | { | 118 | { |
108 | struct thermal_zone_device *pos; | 119 | struct thermal_zone_device *pos; |
109 | 120 | ||
110 | if (!governor) | 121 | if (!governor) |
111 | return; | 122 | return; |
112 | 123 | ||
113 | mutex_lock(&thermal_governor_lock); | 124 | mutex_lock(&thermal_governor_lock); |
114 | 125 | ||
115 | if (__find_governor(governor->name) == NULL) | 126 | if (__find_governor(governor->name) == NULL) |
116 | goto exit; | 127 | goto exit; |
117 | 128 | ||
118 | mutex_lock(&thermal_list_lock); | 129 | mutex_lock(&thermal_list_lock); |
119 | 130 | ||
120 | list_for_each_entry(pos, &thermal_tz_list, node) { | 131 | list_for_each_entry(pos, &thermal_tz_list, node) { |
121 | if (!strnicmp(pos->governor->name, governor->name, | 132 | if (!strnicmp(pos->governor->name, governor->name, |
122 | THERMAL_NAME_LENGTH)) | 133 | THERMAL_NAME_LENGTH)) |
123 | pos->governor = NULL; | 134 | pos->governor = NULL; |
124 | } | 135 | } |
125 | 136 | ||
126 | mutex_unlock(&thermal_list_lock); | 137 | mutex_unlock(&thermal_list_lock); |
127 | list_del(&governor->governor_list); | 138 | list_del(&governor->governor_list); |
128 | exit: | 139 | exit: |
129 | mutex_unlock(&thermal_governor_lock); | 140 | mutex_unlock(&thermal_governor_lock); |
130 | return; | 141 | return; |
131 | } | 142 | } |
132 | 143 | ||
133 | static int get_idr(struct idr *idr, struct mutex *lock, int *id) | 144 | static int get_idr(struct idr *idr, struct mutex *lock, int *id) |
134 | { | 145 | { |
135 | int ret; | 146 | int ret; |
136 | 147 | ||
137 | if (lock) | 148 | if (lock) |
138 | mutex_lock(lock); | 149 | mutex_lock(lock); |
139 | ret = idr_alloc(idr, NULL, 0, 0, GFP_KERNEL); | 150 | ret = idr_alloc(idr, NULL, 0, 0, GFP_KERNEL); |
140 | if (lock) | 151 | if (lock) |
141 | mutex_unlock(lock); | 152 | mutex_unlock(lock); |
142 | if (unlikely(ret < 0)) | 153 | if (unlikely(ret < 0)) |
143 | return ret; | 154 | return ret; |
144 | *id = ret; | 155 | *id = ret; |
145 | return 0; | 156 | return 0; |
146 | } | 157 | } |
147 | 158 | ||
148 | static void release_idr(struct idr *idr, struct mutex *lock, int id) | 159 | static void release_idr(struct idr *idr, struct mutex *lock, int id) |
149 | { | 160 | { |
150 | if (lock) | 161 | if (lock) |
151 | mutex_lock(lock); | 162 | mutex_lock(lock); |
152 | idr_remove(idr, id); | 163 | idr_remove(idr, id); |
153 | if (lock) | 164 | if (lock) |
154 | mutex_unlock(lock); | 165 | mutex_unlock(lock); |
155 | } | 166 | } |
156 | 167 | ||
157 | int get_tz_trend(struct thermal_zone_device *tz, int trip) | 168 | int get_tz_trend(struct thermal_zone_device *tz, int trip) |
158 | { | 169 | { |
159 | enum thermal_trend trend; | 170 | enum thermal_trend trend; |
160 | 171 | ||
161 | if (tz->emul_temperature || !tz->ops->get_trend || | 172 | if (tz->emul_temperature || !tz->ops->get_trend || |
162 | tz->ops->get_trend(tz, trip, &trend)) { | 173 | tz->ops->get_trend(tz, trip, &trend)) { |
163 | if (tz->temperature > tz->last_temperature) | 174 | if (tz->temperature > tz->last_temperature) |
164 | trend = THERMAL_TREND_RAISING; | 175 | trend = THERMAL_TREND_RAISING; |
165 | else if (tz->temperature < tz->last_temperature) | 176 | else if (tz->temperature < tz->last_temperature) |
166 | trend = THERMAL_TREND_DROPPING; | 177 | trend = THERMAL_TREND_DROPPING; |
167 | else | 178 | else |
168 | trend = THERMAL_TREND_STABLE; | 179 | trend = THERMAL_TREND_STABLE; |
169 | } | 180 | } |
170 | 181 | ||
171 | return trend; | 182 | return trend; |
172 | } | 183 | } |
173 | EXPORT_SYMBOL(get_tz_trend); | 184 | EXPORT_SYMBOL(get_tz_trend); |
174 | 185 | ||
175 | struct thermal_instance *get_thermal_instance(struct thermal_zone_device *tz, | 186 | struct thermal_instance *get_thermal_instance(struct thermal_zone_device *tz, |
176 | struct thermal_cooling_device *cdev, int trip) | 187 | struct thermal_cooling_device *cdev, int trip) |
177 | { | 188 | { |
178 | struct thermal_instance *pos = NULL; | 189 | struct thermal_instance *pos = NULL; |
179 | struct thermal_instance *target_instance = NULL; | 190 | struct thermal_instance *target_instance = NULL; |
180 | 191 | ||
181 | mutex_lock(&tz->lock); | 192 | mutex_lock(&tz->lock); |
182 | mutex_lock(&cdev->lock); | 193 | mutex_lock(&cdev->lock); |
183 | 194 | ||
184 | list_for_each_entry(pos, &tz->thermal_instances, tz_node) { | 195 | list_for_each_entry(pos, &tz->thermal_instances, tz_node) { |
185 | if (pos->tz == tz && pos->trip == trip && pos->cdev == cdev) { | 196 | if (pos->tz == tz && pos->trip == trip && pos->cdev == cdev) { |
186 | target_instance = pos; | 197 | target_instance = pos; |
187 | break; | 198 | break; |
188 | } | 199 | } |
189 | } | 200 | } |
190 | 201 | ||
191 | mutex_unlock(&cdev->lock); | 202 | mutex_unlock(&cdev->lock); |
192 | mutex_unlock(&tz->lock); | 203 | mutex_unlock(&tz->lock); |
193 | 204 | ||
194 | return target_instance; | 205 | return target_instance; |
195 | } | 206 | } |
196 | EXPORT_SYMBOL(get_thermal_instance); | 207 | EXPORT_SYMBOL(get_thermal_instance); |
197 | 208 | ||
198 | static void print_bind_err_msg(struct thermal_zone_device *tz, | 209 | static void print_bind_err_msg(struct thermal_zone_device *tz, |
199 | struct thermal_cooling_device *cdev, int ret) | 210 | struct thermal_cooling_device *cdev, int ret) |
200 | { | 211 | { |
201 | dev_err(&tz->device, "binding zone %s with cdev %s failed:%d\n", | 212 | dev_err(&tz->device, "binding zone %s with cdev %s failed:%d\n", |
202 | tz->type, cdev->type, ret); | 213 | tz->type, cdev->type, ret); |
203 | } | 214 | } |
204 | 215 | ||
205 | static void __bind(struct thermal_zone_device *tz, int mask, | 216 | static void __bind(struct thermal_zone_device *tz, int mask, |
206 | struct thermal_cooling_device *cdev, | 217 | struct thermal_cooling_device *cdev, |
207 | unsigned long *limits) | 218 | unsigned long *limits) |
208 | { | 219 | { |
209 | int i, ret; | 220 | int i, ret; |
210 | 221 | ||
211 | for (i = 0; i < tz->trips; i++) { | 222 | for (i = 0; i < tz->trips; i++) { |
212 | if (mask & (1 << i)) { | 223 | if (mask & (1 << i)) { |
213 | unsigned long upper, lower; | 224 | unsigned long upper, lower; |
214 | 225 | ||
215 | upper = THERMAL_NO_LIMIT; | 226 | upper = THERMAL_NO_LIMIT; |
216 | lower = THERMAL_NO_LIMIT; | 227 | lower = THERMAL_NO_LIMIT; |
217 | if (limits) { | 228 | if (limits) { |
218 | lower = limits[i * 2]; | 229 | lower = limits[i * 2]; |
219 | upper = limits[i * 2 + 1]; | 230 | upper = limits[i * 2 + 1]; |
220 | } | 231 | } |
221 | ret = thermal_zone_bind_cooling_device(tz, i, cdev, | 232 | ret = thermal_zone_bind_cooling_device(tz, i, cdev, |
222 | upper, lower); | 233 | upper, lower); |
223 | if (ret) | 234 | if (ret) |
224 | print_bind_err_msg(tz, cdev, ret); | 235 | print_bind_err_msg(tz, cdev, ret); |
225 | } | 236 | } |
226 | } | 237 | } |
227 | } | 238 | } |
228 | 239 | ||
229 | static void __unbind(struct thermal_zone_device *tz, int mask, | 240 | static void __unbind(struct thermal_zone_device *tz, int mask, |
230 | struct thermal_cooling_device *cdev) | 241 | struct thermal_cooling_device *cdev) |
231 | { | 242 | { |
232 | int i; | 243 | int i; |
233 | 244 | ||
234 | for (i = 0; i < tz->trips; i++) | 245 | for (i = 0; i < tz->trips; i++) |
235 | if (mask & (1 << i)) | 246 | if (mask & (1 << i)) |
236 | thermal_zone_unbind_cooling_device(tz, i, cdev); | 247 | thermal_zone_unbind_cooling_device(tz, i, cdev); |
237 | } | 248 | } |
238 | 249 | ||
239 | static void bind_cdev(struct thermal_cooling_device *cdev) | 250 | static void bind_cdev(struct thermal_cooling_device *cdev) |
240 | { | 251 | { |
241 | int i, ret; | 252 | int i, ret; |
242 | const struct thermal_zone_params *tzp; | 253 | const struct thermal_zone_params *tzp; |
243 | struct thermal_zone_device *pos = NULL; | 254 | struct thermal_zone_device *pos = NULL; |
244 | 255 | ||
245 | mutex_lock(&thermal_list_lock); | 256 | mutex_lock(&thermal_list_lock); |
246 | 257 | ||
247 | list_for_each_entry(pos, &thermal_tz_list, node) { | 258 | list_for_each_entry(pos, &thermal_tz_list, node) { |
248 | if (!pos->tzp && !pos->ops->bind) | 259 | if (!pos->tzp && !pos->ops->bind) |
249 | continue; | 260 | continue; |
250 | 261 | ||
251 | if (pos->ops->bind) { | 262 | if (pos->ops->bind) { |
252 | ret = pos->ops->bind(pos, cdev); | 263 | ret = pos->ops->bind(pos, cdev); |
253 | if (ret) | 264 | if (ret) |
254 | print_bind_err_msg(pos, cdev, ret); | 265 | print_bind_err_msg(pos, cdev, ret); |
255 | continue; | 266 | continue; |
256 | } | 267 | } |
257 | 268 | ||
258 | tzp = pos->tzp; | 269 | tzp = pos->tzp; |
259 | if (!tzp || !tzp->tbp) | 270 | if (!tzp || !tzp->tbp) |
260 | continue; | 271 | continue; |
261 | 272 | ||
262 | for (i = 0; i < tzp->num_tbps; i++) { | 273 | for (i = 0; i < tzp->num_tbps; i++) { |
263 | if (tzp->tbp[i].cdev || !tzp->tbp[i].match) | 274 | if (tzp->tbp[i].cdev || !tzp->tbp[i].match) |
264 | continue; | 275 | continue; |
265 | if (tzp->tbp[i].match(pos, cdev)) | 276 | if (tzp->tbp[i].match(pos, cdev)) |
266 | continue; | 277 | continue; |
267 | tzp->tbp[i].cdev = cdev; | 278 | tzp->tbp[i].cdev = cdev; |
268 | __bind(pos, tzp->tbp[i].trip_mask, cdev, | 279 | __bind(pos, tzp->tbp[i].trip_mask, cdev, |
269 | tzp->tbp[i].binding_limits); | 280 | tzp->tbp[i].binding_limits); |
270 | } | 281 | } |
271 | } | 282 | } |
272 | 283 | ||
273 | mutex_unlock(&thermal_list_lock); | 284 | mutex_unlock(&thermal_list_lock); |
274 | } | 285 | } |
275 | 286 | ||
276 | static void bind_tz(struct thermal_zone_device *tz) | 287 | static void bind_tz(struct thermal_zone_device *tz) |
277 | { | 288 | { |
278 | int i, ret; | 289 | int i, ret; |
279 | struct thermal_cooling_device *pos = NULL; | 290 | struct thermal_cooling_device *pos = NULL; |
280 | const struct thermal_zone_params *tzp = tz->tzp; | 291 | const struct thermal_zone_params *tzp = tz->tzp; |
281 | 292 | ||
282 | if (!tzp && !tz->ops->bind) | 293 | if (!tzp && !tz->ops->bind) |
283 | return; | 294 | return; |
284 | 295 | ||
285 | mutex_lock(&thermal_list_lock); | 296 | mutex_lock(&thermal_list_lock); |
286 | 297 | ||
287 | /* If there is ops->bind, try to use ops->bind */ | 298 | /* If there is ops->bind, try to use ops->bind */ |
288 | if (tz->ops->bind) { | 299 | if (tz->ops->bind) { |
289 | list_for_each_entry(pos, &thermal_cdev_list, node) { | 300 | list_for_each_entry(pos, &thermal_cdev_list, node) { |
290 | ret = tz->ops->bind(tz, pos); | 301 | ret = tz->ops->bind(tz, pos); |
291 | if (ret) | 302 | if (ret) |
292 | print_bind_err_msg(tz, pos, ret); | 303 | print_bind_err_msg(tz, pos, ret); |
293 | } | 304 | } |
294 | goto exit; | 305 | goto exit; |
295 | } | 306 | } |
296 | 307 | ||
297 | if (!tzp || !tzp->tbp) | 308 | if (!tzp || !tzp->tbp) |
298 | goto exit; | 309 | goto exit; |
299 | 310 | ||
300 | list_for_each_entry(pos, &thermal_cdev_list, node) { | 311 | list_for_each_entry(pos, &thermal_cdev_list, node) { |
301 | for (i = 0; i < tzp->num_tbps; i++) { | 312 | for (i = 0; i < tzp->num_tbps; i++) { |
302 | if (tzp->tbp[i].cdev || !tzp->tbp[i].match) | 313 | if (tzp->tbp[i].cdev || !tzp->tbp[i].match) |
303 | continue; | 314 | continue; |
304 | if (tzp->tbp[i].match(tz, pos)) | 315 | if (tzp->tbp[i].match(tz, pos)) |
305 | continue; | 316 | continue; |
306 | tzp->tbp[i].cdev = pos; | 317 | tzp->tbp[i].cdev = pos; |
307 | __bind(tz, tzp->tbp[i].trip_mask, pos, | 318 | __bind(tz, tzp->tbp[i].trip_mask, pos, |
308 | tzp->tbp[i].binding_limits); | 319 | tzp->tbp[i].binding_limits); |
309 | } | 320 | } |
310 | } | 321 | } |
311 | exit: | 322 | exit: |
312 | mutex_unlock(&thermal_list_lock); | 323 | mutex_unlock(&thermal_list_lock); |
313 | } | 324 | } |
314 | 325 | ||
315 | static void thermal_zone_device_set_polling(struct thermal_zone_device *tz, | 326 | static void thermal_zone_device_set_polling(struct thermal_zone_device *tz, |
316 | int delay) | 327 | int delay) |
317 | { | 328 | { |
318 | if (delay > 1000) | 329 | if (delay > 1000) |
319 | mod_delayed_work(system_freezable_wq, &tz->poll_queue, | 330 | mod_delayed_work(system_freezable_wq, &tz->poll_queue, |
320 | round_jiffies(msecs_to_jiffies(delay))); | 331 | round_jiffies(msecs_to_jiffies(delay))); |
321 | else if (delay) | 332 | else if (delay) |
322 | mod_delayed_work(system_freezable_wq, &tz->poll_queue, | 333 | mod_delayed_work(system_freezable_wq, &tz->poll_queue, |
323 | msecs_to_jiffies(delay)); | 334 | msecs_to_jiffies(delay)); |
324 | else | 335 | else |
325 | cancel_delayed_work(&tz->poll_queue); | 336 | cancel_delayed_work(&tz->poll_queue); |
326 | } | 337 | } |
327 | 338 | ||
328 | static void monitor_thermal_zone(struct thermal_zone_device *tz) | 339 | static void monitor_thermal_zone(struct thermal_zone_device *tz) |
329 | { | 340 | { |
330 | mutex_lock(&tz->lock); | 341 | mutex_lock(&tz->lock); |
331 | 342 | ||
332 | if (tz->passive) | 343 | if (tz->passive) |
333 | thermal_zone_device_set_polling(tz, tz->passive_delay); | 344 | thermal_zone_device_set_polling(tz, tz->passive_delay); |
334 | else if (tz->polling_delay) | 345 | else if (tz->polling_delay) |
335 | thermal_zone_device_set_polling(tz, tz->polling_delay); | 346 | thermal_zone_device_set_polling(tz, tz->polling_delay); |
336 | else | 347 | else |
337 | thermal_zone_device_set_polling(tz, 0); | 348 | thermal_zone_device_set_polling(tz, 0); |
338 | 349 | ||
339 | mutex_unlock(&tz->lock); | 350 | mutex_unlock(&tz->lock); |
340 | } | 351 | } |
341 | 352 | ||
342 | static void handle_non_critical_trips(struct thermal_zone_device *tz, | 353 | static void handle_non_critical_trips(struct thermal_zone_device *tz, |
343 | int trip, enum thermal_trip_type trip_type) | 354 | int trip, enum thermal_trip_type trip_type) |
344 | { | 355 | { |
345 | if (tz->governor) | 356 | tz->governor ? tz->governor->throttle(tz, trip) : |
346 | tz->governor->throttle(tz, trip); | 357 | def_governor->throttle(tz, trip); |
347 | } | 358 | } |
348 | 359 | ||
349 | static void handle_critical_trips(struct thermal_zone_device *tz, | 360 | static void handle_critical_trips(struct thermal_zone_device *tz, |
350 | int trip, enum thermal_trip_type trip_type) | 361 | int trip, enum thermal_trip_type trip_type) |
351 | { | 362 | { |
352 | long trip_temp; | 363 | long trip_temp; |
353 | 364 | ||
354 | tz->ops->get_trip_temp(tz, trip, &trip_temp); | 365 | tz->ops->get_trip_temp(tz, trip, &trip_temp); |
355 | 366 | ||
356 | /* If we have not crossed the trip_temp, we do not care. */ | 367 | /* If we have not crossed the trip_temp, we do not care. */ |
357 | if (tz->temperature < trip_temp) | 368 | if (tz->temperature < trip_temp) |
358 | return; | 369 | return; |
359 | 370 | ||
360 | if (tz->ops->notify) | 371 | if (tz->ops->notify) |
361 | tz->ops->notify(tz, trip, trip_type); | 372 | tz->ops->notify(tz, trip, trip_type); |
362 | 373 | ||
363 | if (trip_type == THERMAL_TRIP_CRITICAL) { | 374 | if (trip_type == THERMAL_TRIP_CRITICAL) { |
364 | dev_emerg(&tz->device, | 375 | dev_emerg(&tz->device, |
365 | "critical temperature reached(%d C),shutting down\n", | 376 | "critical temperature reached(%d C),shutting down\n", |
366 | tz->temperature / 1000); | 377 | tz->temperature / 1000); |
367 | orderly_poweroff(true); | 378 | orderly_poweroff(true); |
368 | } | 379 | } |
369 | } | 380 | } |
370 | 381 | ||
371 | static void handle_thermal_trip(struct thermal_zone_device *tz, int trip) | 382 | static void handle_thermal_trip(struct thermal_zone_device *tz, int trip) |
372 | { | 383 | { |
373 | enum thermal_trip_type type; | 384 | enum thermal_trip_type type; |
374 | 385 | ||
375 | tz->ops->get_trip_type(tz, trip, &type); | 386 | tz->ops->get_trip_type(tz, trip, &type); |
376 | 387 | ||
377 | if (type == THERMAL_TRIP_CRITICAL || type == THERMAL_TRIP_HOT) | 388 | if (type == THERMAL_TRIP_CRITICAL || type == THERMAL_TRIP_HOT) |
378 | handle_critical_trips(tz, trip, type); | 389 | handle_critical_trips(tz, trip, type); |
379 | else | 390 | else |
380 | handle_non_critical_trips(tz, trip, type); | 391 | handle_non_critical_trips(tz, trip, type); |
381 | /* | 392 | /* |
382 | * Alright, we handled this trip successfully. | 393 | * Alright, we handled this trip successfully. |
383 | * So, start monitoring again. | 394 | * So, start monitoring again. |
384 | */ | 395 | */ |
385 | monitor_thermal_zone(tz); | 396 | monitor_thermal_zone(tz); |
386 | } | 397 | } |
387 | 398 | ||
388 | /** | 399 | /** |
389 | * thermal_zone_get_temp() - returns its the temperature of thermal zone | 400 | * thermal_zone_get_temp() - returns its the temperature of thermal zone |
390 | * @tz: a valid pointer to a struct thermal_zone_device | 401 | * @tz: a valid pointer to a struct thermal_zone_device |
391 | * @temp: a valid pointer to where to store the resulting temperature. | 402 | * @temp: a valid pointer to where to store the resulting temperature. |
392 | * | 403 | * |
393 | * When a valid thermal zone reference is passed, it will fetch its | 404 | * When a valid thermal zone reference is passed, it will fetch its |
394 | * temperature and fill @temp. | 405 | * temperature and fill @temp. |
395 | * | 406 | * |
396 | * Return: On success returns 0, an error code otherwise | 407 | * Return: On success returns 0, an error code otherwise |
397 | */ | 408 | */ |
398 | int thermal_zone_get_temp(struct thermal_zone_device *tz, unsigned long *temp) | 409 | int thermal_zone_get_temp(struct thermal_zone_device *tz, unsigned long *temp) |
399 | { | 410 | { |
400 | int ret = -EINVAL; | 411 | int ret = -EINVAL; |
401 | #ifdef CONFIG_THERMAL_EMULATION | 412 | #ifdef CONFIG_THERMAL_EMULATION |
402 | int count; | 413 | int count; |
403 | unsigned long crit_temp = -1UL; | 414 | unsigned long crit_temp = -1UL; |
404 | enum thermal_trip_type type; | 415 | enum thermal_trip_type type; |
405 | #endif | 416 | #endif |
406 | 417 | ||
407 | if (!tz || IS_ERR(tz) || !tz->ops->get_temp) | 418 | if (!tz || IS_ERR(tz) || !tz->ops->get_temp) |
408 | goto exit; | 419 | goto exit; |
409 | 420 | ||
410 | mutex_lock(&tz->lock); | 421 | mutex_lock(&tz->lock); |
411 | 422 | ||
412 | ret = tz->ops->get_temp(tz, temp); | 423 | ret = tz->ops->get_temp(tz, temp); |
413 | #ifdef CONFIG_THERMAL_EMULATION | 424 | #ifdef CONFIG_THERMAL_EMULATION |
414 | if (!tz->emul_temperature) | 425 | if (!tz->emul_temperature) |
415 | goto skip_emul; | 426 | goto skip_emul; |
416 | 427 | ||
417 | for (count = 0; count < tz->trips; count++) { | 428 | for (count = 0; count < tz->trips; count++) { |
418 | ret = tz->ops->get_trip_type(tz, count, &type); | 429 | ret = tz->ops->get_trip_type(tz, count, &type); |
419 | if (!ret && type == THERMAL_TRIP_CRITICAL) { | 430 | if (!ret && type == THERMAL_TRIP_CRITICAL) { |
420 | ret = tz->ops->get_trip_temp(tz, count, &crit_temp); | 431 | ret = tz->ops->get_trip_temp(tz, count, &crit_temp); |
421 | break; | 432 | break; |
422 | } | 433 | } |
423 | } | 434 | } |
424 | 435 | ||
425 | if (ret) | 436 | if (ret) |
426 | goto skip_emul; | 437 | goto skip_emul; |
427 | 438 | ||
428 | if (*temp < crit_temp) | 439 | if (*temp < crit_temp) |
429 | *temp = tz->emul_temperature; | 440 | *temp = tz->emul_temperature; |
430 | skip_emul: | 441 | skip_emul: |
431 | #endif | 442 | #endif |
432 | mutex_unlock(&tz->lock); | 443 | mutex_unlock(&tz->lock); |
433 | exit: | 444 | exit: |
434 | return ret; | 445 | return ret; |
435 | } | 446 | } |
436 | EXPORT_SYMBOL_GPL(thermal_zone_get_temp); | 447 | EXPORT_SYMBOL_GPL(thermal_zone_get_temp); |
437 | 448 | ||
438 | static void update_temperature(struct thermal_zone_device *tz) | 449 | static void update_temperature(struct thermal_zone_device *tz) |
439 | { | 450 | { |
440 | long temp; | 451 | long temp; |
441 | int ret; | 452 | int ret; |
442 | 453 | ||
443 | ret = thermal_zone_get_temp(tz, &temp); | 454 | ret = thermal_zone_get_temp(tz, &temp); |
444 | if (ret) { | 455 | if (ret) { |
445 | dev_warn(&tz->device, "failed to read out thermal zone %d\n", | 456 | dev_warn(&tz->device, "failed to read out thermal zone %d\n", |
446 | tz->id); | 457 | tz->id); |
447 | return; | 458 | return; |
448 | } | 459 | } |
449 | 460 | ||
450 | mutex_lock(&tz->lock); | 461 | mutex_lock(&tz->lock); |
451 | tz->last_temperature = tz->temperature; | 462 | tz->last_temperature = tz->temperature; |
452 | tz->temperature = temp; | 463 | tz->temperature = temp; |
453 | mutex_unlock(&tz->lock); | 464 | mutex_unlock(&tz->lock); |
454 | 465 | ||
455 | dev_dbg(&tz->device, "last_temperature=%d, current_temperature=%d\n", | 466 | dev_dbg(&tz->device, "last_temperature=%d, current_temperature=%d\n", |
456 | tz->last_temperature, tz->temperature); | 467 | tz->last_temperature, tz->temperature); |
457 | } | 468 | } |
458 | 469 | ||
459 | void thermal_zone_device_update(struct thermal_zone_device *tz) | 470 | void thermal_zone_device_update(struct thermal_zone_device *tz) |
460 | { | 471 | { |
461 | int count; | 472 | int count; |
462 | 473 | ||
463 | if (!tz->ops->get_temp) | 474 | if (!tz->ops->get_temp) |
464 | return; | 475 | return; |
465 | 476 | ||
466 | update_temperature(tz); | 477 | update_temperature(tz); |
467 | 478 | ||
468 | for (count = 0; count < tz->trips; count++) | 479 | for (count = 0; count < tz->trips; count++) |
469 | handle_thermal_trip(tz, count); | 480 | handle_thermal_trip(tz, count); |
470 | } | 481 | } |
471 | EXPORT_SYMBOL_GPL(thermal_zone_device_update); | 482 | EXPORT_SYMBOL_GPL(thermal_zone_device_update); |
472 | 483 | ||
473 | static void thermal_zone_device_check(struct work_struct *work) | 484 | static void thermal_zone_device_check(struct work_struct *work) |
474 | { | 485 | { |
475 | struct thermal_zone_device *tz = container_of(work, struct | 486 | struct thermal_zone_device *tz = container_of(work, struct |
476 | thermal_zone_device, | 487 | thermal_zone_device, |
477 | poll_queue.work); | 488 | poll_queue.work); |
478 | thermal_zone_device_update(tz); | 489 | thermal_zone_device_update(tz); |
479 | } | 490 | } |
480 | 491 | ||
481 | /* sys I/F for thermal zone */ | 492 | /* sys I/F for thermal zone */ |
482 | 493 | ||
483 | #define to_thermal_zone(_dev) \ | 494 | #define to_thermal_zone(_dev) \ |
484 | container_of(_dev, struct thermal_zone_device, device) | 495 | container_of(_dev, struct thermal_zone_device, device) |
485 | 496 | ||
486 | static ssize_t | 497 | static ssize_t |
487 | type_show(struct device *dev, struct device_attribute *attr, char *buf) | 498 | type_show(struct device *dev, struct device_attribute *attr, char *buf) |
488 | { | 499 | { |
489 | struct thermal_zone_device *tz = to_thermal_zone(dev); | 500 | struct thermal_zone_device *tz = to_thermal_zone(dev); |
490 | 501 | ||
491 | return sprintf(buf, "%s\n", tz->type); | 502 | return sprintf(buf, "%s\n", tz->type); |
492 | } | 503 | } |
493 | 504 | ||
494 | static ssize_t | 505 | static ssize_t |
495 | temp_show(struct device *dev, struct device_attribute *attr, char *buf) | 506 | temp_show(struct device *dev, struct device_attribute *attr, char *buf) |
496 | { | 507 | { |
497 | struct thermal_zone_device *tz = to_thermal_zone(dev); | 508 | struct thermal_zone_device *tz = to_thermal_zone(dev); |
498 | long temperature; | 509 | long temperature; |
499 | int ret; | 510 | int ret; |
500 | 511 | ||
501 | ret = thermal_zone_get_temp(tz, &temperature); | 512 | ret = thermal_zone_get_temp(tz, &temperature); |
502 | 513 | ||
503 | if (ret) | 514 | if (ret) |
504 | return ret; | 515 | return ret; |
505 | 516 | ||
506 | return sprintf(buf, "%ld\n", temperature); | 517 | return sprintf(buf, "%ld\n", temperature); |
507 | } | 518 | } |
508 | 519 | ||
509 | static ssize_t | 520 | static ssize_t |
510 | mode_show(struct device *dev, struct device_attribute *attr, char *buf) | 521 | mode_show(struct device *dev, struct device_attribute *attr, char *buf) |
511 | { | 522 | { |
512 | struct thermal_zone_device *tz = to_thermal_zone(dev); | 523 | struct thermal_zone_device *tz = to_thermal_zone(dev); |
513 | enum thermal_device_mode mode; | 524 | enum thermal_device_mode mode; |
514 | int result; | 525 | int result; |
515 | 526 | ||
516 | if (!tz->ops->get_mode) | 527 | if (!tz->ops->get_mode) |
517 | return -EPERM; | 528 | return -EPERM; |
518 | 529 | ||
519 | result = tz->ops->get_mode(tz, &mode); | 530 | result = tz->ops->get_mode(tz, &mode); |
520 | if (result) | 531 | if (result) |
521 | return result; | 532 | return result; |
522 | 533 | ||
523 | return sprintf(buf, "%s\n", mode == THERMAL_DEVICE_ENABLED ? "enabled" | 534 | return sprintf(buf, "%s\n", mode == THERMAL_DEVICE_ENABLED ? "enabled" |
524 | : "disabled"); | 535 | : "disabled"); |
525 | } | 536 | } |
526 | 537 | ||
527 | static ssize_t | 538 | static ssize_t |
528 | mode_store(struct device *dev, struct device_attribute *attr, | 539 | mode_store(struct device *dev, struct device_attribute *attr, |
529 | const char *buf, size_t count) | 540 | const char *buf, size_t count) |
530 | { | 541 | { |
531 | struct thermal_zone_device *tz = to_thermal_zone(dev); | 542 | struct thermal_zone_device *tz = to_thermal_zone(dev); |
532 | int result; | 543 | int result; |
533 | 544 | ||
534 | if (!tz->ops->set_mode) | 545 | if (!tz->ops->set_mode) |
535 | return -EPERM; | 546 | return -EPERM; |
536 | 547 | ||
537 | if (!strncmp(buf, "enabled", sizeof("enabled") - 1)) | 548 | if (!strncmp(buf, "enabled", sizeof("enabled") - 1)) |
538 | result = tz->ops->set_mode(tz, THERMAL_DEVICE_ENABLED); | 549 | result = tz->ops->set_mode(tz, THERMAL_DEVICE_ENABLED); |
539 | else if (!strncmp(buf, "disabled", sizeof("disabled") - 1)) | 550 | else if (!strncmp(buf, "disabled", sizeof("disabled") - 1)) |
540 | result = tz->ops->set_mode(tz, THERMAL_DEVICE_DISABLED); | 551 | result = tz->ops->set_mode(tz, THERMAL_DEVICE_DISABLED); |
541 | else | 552 | else |
542 | result = -EINVAL; | 553 | result = -EINVAL; |
543 | 554 | ||
544 | if (result) | 555 | if (result) |
545 | return result; | 556 | return result; |
546 | 557 | ||
547 | return count; | 558 | return count; |
548 | } | 559 | } |
549 | 560 | ||
550 | static ssize_t | 561 | static ssize_t |
551 | trip_point_type_show(struct device *dev, struct device_attribute *attr, | 562 | trip_point_type_show(struct device *dev, struct device_attribute *attr, |
552 | char *buf) | 563 | char *buf) |
553 | { | 564 | { |
554 | struct thermal_zone_device *tz = to_thermal_zone(dev); | 565 | struct thermal_zone_device *tz = to_thermal_zone(dev); |
555 | enum thermal_trip_type type; | 566 | enum thermal_trip_type type; |
556 | int trip, result; | 567 | int trip, result; |
557 | 568 | ||
558 | if (!tz->ops->get_trip_type) | 569 | if (!tz->ops->get_trip_type) |
559 | return -EPERM; | 570 | return -EPERM; |
560 | 571 | ||
561 | if (!sscanf(attr->attr.name, "trip_point_%d_type", &trip)) | 572 | if (!sscanf(attr->attr.name, "trip_point_%d_type", &trip)) |
562 | return -EINVAL; | 573 | return -EINVAL; |
563 | 574 | ||
564 | result = tz->ops->get_trip_type(tz, trip, &type); | 575 | result = tz->ops->get_trip_type(tz, trip, &type); |
565 | if (result) | 576 | if (result) |
566 | return result; | 577 | return result; |
567 | 578 | ||
568 | switch (type) { | 579 | switch (type) { |
569 | case THERMAL_TRIP_CRITICAL: | 580 | case THERMAL_TRIP_CRITICAL: |
570 | return sprintf(buf, "critical\n"); | 581 | return sprintf(buf, "critical\n"); |
571 | case THERMAL_TRIP_HOT: | 582 | case THERMAL_TRIP_HOT: |
572 | return sprintf(buf, "hot\n"); | 583 | return sprintf(buf, "hot\n"); |
573 | case THERMAL_TRIP_PASSIVE: | 584 | case THERMAL_TRIP_PASSIVE: |
574 | return sprintf(buf, "passive\n"); | 585 | return sprintf(buf, "passive\n"); |
575 | case THERMAL_TRIP_ACTIVE: | 586 | case THERMAL_TRIP_ACTIVE: |
576 | return sprintf(buf, "active\n"); | 587 | return sprintf(buf, "active\n"); |
577 | default: | 588 | default: |
578 | return sprintf(buf, "unknown\n"); | 589 | return sprintf(buf, "unknown\n"); |
579 | } | 590 | } |
580 | } | 591 | } |
581 | 592 | ||
582 | static ssize_t | 593 | static ssize_t |
583 | trip_point_temp_store(struct device *dev, struct device_attribute *attr, | 594 | trip_point_temp_store(struct device *dev, struct device_attribute *attr, |
584 | const char *buf, size_t count) | 595 | const char *buf, size_t count) |
585 | { | 596 | { |
586 | struct thermal_zone_device *tz = to_thermal_zone(dev); | 597 | struct thermal_zone_device *tz = to_thermal_zone(dev); |
587 | int trip, ret; | 598 | int trip, ret; |
588 | unsigned long temperature; | 599 | unsigned long temperature; |
589 | 600 | ||
590 | if (!tz->ops->set_trip_temp) | 601 | if (!tz->ops->set_trip_temp) |
591 | return -EPERM; | 602 | return -EPERM; |
592 | 603 | ||
593 | if (!sscanf(attr->attr.name, "trip_point_%d_temp", &trip)) | 604 | if (!sscanf(attr->attr.name, "trip_point_%d_temp", &trip)) |
594 | return -EINVAL; | 605 | return -EINVAL; |
595 | 606 | ||
596 | if (kstrtoul(buf, 10, &temperature)) | 607 | if (kstrtoul(buf, 10, &temperature)) |
597 | return -EINVAL; | 608 | return -EINVAL; |
598 | 609 | ||
599 | ret = tz->ops->set_trip_temp(tz, trip, temperature); | 610 | ret = tz->ops->set_trip_temp(tz, trip, temperature); |
600 | 611 | ||
601 | return ret ? ret : count; | 612 | return ret ? ret : count; |
602 | } | 613 | } |
603 | 614 | ||
604 | static ssize_t | 615 | static ssize_t |
605 | trip_point_temp_show(struct device *dev, struct device_attribute *attr, | 616 | trip_point_temp_show(struct device *dev, struct device_attribute *attr, |
606 | char *buf) | 617 | char *buf) |
607 | { | 618 | { |
608 | struct thermal_zone_device *tz = to_thermal_zone(dev); | 619 | struct thermal_zone_device *tz = to_thermal_zone(dev); |
609 | int trip, ret; | 620 | int trip, ret; |
610 | long temperature; | 621 | long temperature; |
611 | 622 | ||
612 | if (!tz->ops->get_trip_temp) | 623 | if (!tz->ops->get_trip_temp) |
613 | return -EPERM; | 624 | return -EPERM; |
614 | 625 | ||
615 | if (!sscanf(attr->attr.name, "trip_point_%d_temp", &trip)) | 626 | if (!sscanf(attr->attr.name, "trip_point_%d_temp", &trip)) |
616 | return -EINVAL; | 627 | return -EINVAL; |
617 | 628 | ||
618 | ret = tz->ops->get_trip_temp(tz, trip, &temperature); | 629 | ret = tz->ops->get_trip_temp(tz, trip, &temperature); |
619 | 630 | ||
620 | if (ret) | 631 | if (ret) |
621 | return ret; | 632 | return ret; |
622 | 633 | ||
623 | return sprintf(buf, "%ld\n", temperature); | 634 | return sprintf(buf, "%ld\n", temperature); |
624 | } | 635 | } |
625 | 636 | ||
626 | static ssize_t | 637 | static ssize_t |
627 | trip_point_hyst_store(struct device *dev, struct device_attribute *attr, | 638 | trip_point_hyst_store(struct device *dev, struct device_attribute *attr, |
628 | const char *buf, size_t count) | 639 | const char *buf, size_t count) |
629 | { | 640 | { |
630 | struct thermal_zone_device *tz = to_thermal_zone(dev); | 641 | struct thermal_zone_device *tz = to_thermal_zone(dev); |
631 | int trip, ret; | 642 | int trip, ret; |
632 | unsigned long temperature; | 643 | unsigned long temperature; |
633 | 644 | ||
634 | if (!tz->ops->set_trip_hyst) | 645 | if (!tz->ops->set_trip_hyst) |
635 | return -EPERM; | 646 | return -EPERM; |
636 | 647 | ||
637 | if (!sscanf(attr->attr.name, "trip_point_%d_hyst", &trip)) | 648 | if (!sscanf(attr->attr.name, "trip_point_%d_hyst", &trip)) |
638 | return -EINVAL; | 649 | return -EINVAL; |
639 | 650 | ||
640 | if (kstrtoul(buf, 10, &temperature)) | 651 | if (kstrtoul(buf, 10, &temperature)) |
641 | return -EINVAL; | 652 | return -EINVAL; |
642 | 653 | ||
643 | /* | 654 | /* |
644 | * We are not doing any check on the 'temperature' value | 655 | * We are not doing any check on the 'temperature' value |
645 | * here. The driver implementing 'set_trip_hyst' has to | 656 | * here. The driver implementing 'set_trip_hyst' has to |
646 | * take care of this. | 657 | * take care of this. |
647 | */ | 658 | */ |
648 | ret = tz->ops->set_trip_hyst(tz, trip, temperature); | 659 | ret = tz->ops->set_trip_hyst(tz, trip, temperature); |
649 | 660 | ||
650 | return ret ? ret : count; | 661 | return ret ? ret : count; |
651 | } | 662 | } |
652 | 663 | ||
653 | static ssize_t | 664 | static ssize_t |
654 | trip_point_hyst_show(struct device *dev, struct device_attribute *attr, | 665 | trip_point_hyst_show(struct device *dev, struct device_attribute *attr, |
655 | char *buf) | 666 | char *buf) |
656 | { | 667 | { |
657 | struct thermal_zone_device *tz = to_thermal_zone(dev); | 668 | struct thermal_zone_device *tz = to_thermal_zone(dev); |
658 | int trip, ret; | 669 | int trip, ret; |
659 | unsigned long temperature; | 670 | unsigned long temperature; |
660 | 671 | ||
661 | if (!tz->ops->get_trip_hyst) | 672 | if (!tz->ops->get_trip_hyst) |
662 | return -EPERM; | 673 | return -EPERM; |
663 | 674 | ||
664 | if (!sscanf(attr->attr.name, "trip_point_%d_hyst", &trip)) | 675 | if (!sscanf(attr->attr.name, "trip_point_%d_hyst", &trip)) |
665 | return -EINVAL; | 676 | return -EINVAL; |
666 | 677 | ||
667 | ret = tz->ops->get_trip_hyst(tz, trip, &temperature); | 678 | ret = tz->ops->get_trip_hyst(tz, trip, &temperature); |
668 | 679 | ||
669 | return ret ? ret : sprintf(buf, "%ld\n", temperature); | 680 | return ret ? ret : sprintf(buf, "%ld\n", temperature); |
670 | } | 681 | } |
671 | 682 | ||
672 | static ssize_t | 683 | static ssize_t |
673 | passive_store(struct device *dev, struct device_attribute *attr, | 684 | passive_store(struct device *dev, struct device_attribute *attr, |
674 | const char *buf, size_t count) | 685 | const char *buf, size_t count) |
675 | { | 686 | { |
676 | struct thermal_zone_device *tz = to_thermal_zone(dev); | 687 | struct thermal_zone_device *tz = to_thermal_zone(dev); |
677 | struct thermal_cooling_device *cdev = NULL; | 688 | struct thermal_cooling_device *cdev = NULL; |
678 | int state; | 689 | int state; |
679 | 690 | ||
680 | if (!sscanf(buf, "%d\n", &state)) | 691 | if (!sscanf(buf, "%d\n", &state)) |
681 | return -EINVAL; | 692 | return -EINVAL; |
682 | 693 | ||
683 | /* sanity check: values below 1000 millicelcius don't make sense | 694 | /* sanity check: values below 1000 millicelcius don't make sense |
684 | * and can cause the system to go into a thermal heart attack | 695 | * and can cause the system to go into a thermal heart attack |
685 | */ | 696 | */ |
686 | if (state && state < 1000) | 697 | if (state && state < 1000) |
687 | return -EINVAL; | 698 | return -EINVAL; |
688 | 699 | ||
689 | if (state && !tz->forced_passive) { | 700 | if (state && !tz->forced_passive) { |
690 | mutex_lock(&thermal_list_lock); | 701 | mutex_lock(&thermal_list_lock); |
691 | list_for_each_entry(cdev, &thermal_cdev_list, node) { | 702 | list_for_each_entry(cdev, &thermal_cdev_list, node) { |
692 | if (!strncmp("Processor", cdev->type, | 703 | if (!strncmp("Processor", cdev->type, |
693 | sizeof("Processor"))) | 704 | sizeof("Processor"))) |
694 | thermal_zone_bind_cooling_device(tz, | 705 | thermal_zone_bind_cooling_device(tz, |
695 | THERMAL_TRIPS_NONE, cdev, | 706 | THERMAL_TRIPS_NONE, cdev, |
696 | THERMAL_NO_LIMIT, | 707 | THERMAL_NO_LIMIT, |
697 | THERMAL_NO_LIMIT); | 708 | THERMAL_NO_LIMIT); |
698 | } | 709 | } |
699 | mutex_unlock(&thermal_list_lock); | 710 | mutex_unlock(&thermal_list_lock); |
700 | if (!tz->passive_delay) | 711 | if (!tz->passive_delay) |
701 | tz->passive_delay = 1000; | 712 | tz->passive_delay = 1000; |
702 | } else if (!state && tz->forced_passive) { | 713 | } else if (!state && tz->forced_passive) { |
703 | mutex_lock(&thermal_list_lock); | 714 | mutex_lock(&thermal_list_lock); |
704 | list_for_each_entry(cdev, &thermal_cdev_list, node) { | 715 | list_for_each_entry(cdev, &thermal_cdev_list, node) { |
705 | if (!strncmp("Processor", cdev->type, | 716 | if (!strncmp("Processor", cdev->type, |
706 | sizeof("Processor"))) | 717 | sizeof("Processor"))) |
707 | thermal_zone_unbind_cooling_device(tz, | 718 | thermal_zone_unbind_cooling_device(tz, |
708 | THERMAL_TRIPS_NONE, | 719 | THERMAL_TRIPS_NONE, |
709 | cdev); | 720 | cdev); |
710 | } | 721 | } |
711 | mutex_unlock(&thermal_list_lock); | 722 | mutex_unlock(&thermal_list_lock); |
712 | tz->passive_delay = 0; | 723 | tz->passive_delay = 0; |
713 | } | 724 | } |
714 | 725 | ||
715 | tz->forced_passive = state; | 726 | tz->forced_passive = state; |
716 | 727 | ||
717 | thermal_zone_device_update(tz); | 728 | thermal_zone_device_update(tz); |
718 | 729 | ||
719 | return count; | 730 | return count; |
720 | } | 731 | } |
721 | 732 | ||
722 | static ssize_t | 733 | static ssize_t |
723 | passive_show(struct device *dev, struct device_attribute *attr, | 734 | passive_show(struct device *dev, struct device_attribute *attr, |
724 | char *buf) | 735 | char *buf) |
725 | { | 736 | { |
726 | struct thermal_zone_device *tz = to_thermal_zone(dev); | 737 | struct thermal_zone_device *tz = to_thermal_zone(dev); |
727 | 738 | ||
728 | return sprintf(buf, "%d\n", tz->forced_passive); | 739 | return sprintf(buf, "%d\n", tz->forced_passive); |
729 | } | 740 | } |
730 | 741 | ||
731 | static ssize_t | 742 | static ssize_t |
732 | policy_store(struct device *dev, struct device_attribute *attr, | 743 | policy_store(struct device *dev, struct device_attribute *attr, |
733 | const char *buf, size_t count) | 744 | const char *buf, size_t count) |
734 | { | 745 | { |
735 | int ret = -EINVAL; | 746 | int ret = -EINVAL; |
736 | struct thermal_zone_device *tz = to_thermal_zone(dev); | 747 | struct thermal_zone_device *tz = to_thermal_zone(dev); |
737 | struct thermal_governor *gov; | 748 | struct thermal_governor *gov; |
738 | char name[THERMAL_NAME_LENGTH]; | 749 | char name[THERMAL_NAME_LENGTH]; |
739 | 750 | ||
740 | snprintf(name, sizeof(name), "%s", buf); | 751 | snprintf(name, sizeof(name), "%s", buf); |
741 | 752 | ||
742 | mutex_lock(&thermal_governor_lock); | 753 | mutex_lock(&thermal_governor_lock); |
743 | 754 | ||
744 | gov = __find_governor(strim(name)); | 755 | gov = __find_governor(strim(name)); |
745 | if (!gov) | 756 | if (!gov) |
746 | goto exit; | 757 | goto exit; |
747 | 758 | ||
748 | tz->governor = gov; | 759 | tz->governor = gov; |
749 | ret = count; | 760 | ret = count; |
750 | 761 | ||
751 | exit: | 762 | exit: |
752 | mutex_unlock(&thermal_governor_lock); | 763 | mutex_unlock(&thermal_governor_lock); |
753 | return ret; | 764 | return ret; |
754 | } | 765 | } |
755 | 766 | ||
756 | static ssize_t | 767 | static ssize_t |
757 | policy_show(struct device *dev, struct device_attribute *devattr, char *buf) | 768 | policy_show(struct device *dev, struct device_attribute *devattr, char *buf) |
758 | { | 769 | { |
759 | struct thermal_zone_device *tz = to_thermal_zone(dev); | 770 | struct thermal_zone_device *tz = to_thermal_zone(dev); |
760 | 771 | ||
761 | return sprintf(buf, "%s\n", tz->governor->name); | 772 | return sprintf(buf, "%s\n", tz->governor->name); |
762 | } | 773 | } |
763 | 774 | ||
764 | #ifdef CONFIG_THERMAL_EMULATION | 775 | #ifdef CONFIG_THERMAL_EMULATION |
765 | static ssize_t | 776 | static ssize_t |
766 | emul_temp_store(struct device *dev, struct device_attribute *attr, | 777 | emul_temp_store(struct device *dev, struct device_attribute *attr, |
767 | const char *buf, size_t count) | 778 | const char *buf, size_t count) |
768 | { | 779 | { |
769 | struct thermal_zone_device *tz = to_thermal_zone(dev); | 780 | struct thermal_zone_device *tz = to_thermal_zone(dev); |
770 | int ret = 0; | 781 | int ret = 0; |
771 | unsigned long temperature; | 782 | unsigned long temperature; |
772 | 783 | ||
773 | if (kstrtoul(buf, 10, &temperature)) | 784 | if (kstrtoul(buf, 10, &temperature)) |
774 | return -EINVAL; | 785 | return -EINVAL; |
775 | 786 | ||
776 | if (!tz->ops->set_emul_temp) { | 787 | if (!tz->ops->set_emul_temp) { |
777 | mutex_lock(&tz->lock); | 788 | mutex_lock(&tz->lock); |
778 | tz->emul_temperature = temperature; | 789 | tz->emul_temperature = temperature; |
779 | mutex_unlock(&tz->lock); | 790 | mutex_unlock(&tz->lock); |
780 | } else { | 791 | } else { |
781 | ret = tz->ops->set_emul_temp(tz, temperature); | 792 | ret = tz->ops->set_emul_temp(tz, temperature); |
782 | } | 793 | } |
783 | 794 | ||
784 | if (!ret) | 795 | if (!ret) |
785 | thermal_zone_device_update(tz); | 796 | thermal_zone_device_update(tz); |
786 | 797 | ||
787 | return ret ? ret : count; | 798 | return ret ? ret : count; |
788 | } | 799 | } |
789 | static DEVICE_ATTR(emul_temp, S_IWUSR, NULL, emul_temp_store); | 800 | static DEVICE_ATTR(emul_temp, S_IWUSR, NULL, emul_temp_store); |
790 | #endif/*CONFIG_THERMAL_EMULATION*/ | 801 | #endif/*CONFIG_THERMAL_EMULATION*/ |
791 | 802 | ||
792 | static DEVICE_ATTR(type, 0444, type_show, NULL); | 803 | static DEVICE_ATTR(type, 0444, type_show, NULL); |
793 | static DEVICE_ATTR(temp, 0444, temp_show, NULL); | 804 | static DEVICE_ATTR(temp, 0444, temp_show, NULL); |
794 | static DEVICE_ATTR(mode, 0644, mode_show, mode_store); | 805 | static DEVICE_ATTR(mode, 0644, mode_show, mode_store); |
795 | static DEVICE_ATTR(passive, S_IRUGO | S_IWUSR, passive_show, passive_store); | 806 | static DEVICE_ATTR(passive, S_IRUGO | S_IWUSR, passive_show, passive_store); |
796 | static DEVICE_ATTR(policy, S_IRUGO | S_IWUSR, policy_show, policy_store); | 807 | static DEVICE_ATTR(policy, S_IRUGO | S_IWUSR, policy_show, policy_store); |
797 | 808 | ||
798 | /* sys I/F for cooling device */ | 809 | /* sys I/F for cooling device */ |
799 | #define to_cooling_device(_dev) \ | 810 | #define to_cooling_device(_dev) \ |
800 | container_of(_dev, struct thermal_cooling_device, device) | 811 | container_of(_dev, struct thermal_cooling_device, device) |
801 | 812 | ||
802 | static ssize_t | 813 | static ssize_t |
803 | thermal_cooling_device_type_show(struct device *dev, | 814 | thermal_cooling_device_type_show(struct device *dev, |
804 | struct device_attribute *attr, char *buf) | 815 | struct device_attribute *attr, char *buf) |
805 | { | 816 | { |
806 | struct thermal_cooling_device *cdev = to_cooling_device(dev); | 817 | struct thermal_cooling_device *cdev = to_cooling_device(dev); |
807 | 818 | ||
808 | return sprintf(buf, "%s\n", cdev->type); | 819 | return sprintf(buf, "%s\n", cdev->type); |
809 | } | 820 | } |
810 | 821 | ||
811 | static ssize_t | 822 | static ssize_t |
812 | thermal_cooling_device_max_state_show(struct device *dev, | 823 | thermal_cooling_device_max_state_show(struct device *dev, |
813 | struct device_attribute *attr, char *buf) | 824 | struct device_attribute *attr, char *buf) |
814 | { | 825 | { |
815 | struct thermal_cooling_device *cdev = to_cooling_device(dev); | 826 | struct thermal_cooling_device *cdev = to_cooling_device(dev); |
816 | unsigned long state; | 827 | unsigned long state; |
817 | int ret; | 828 | int ret; |
818 | 829 | ||
819 | ret = cdev->ops->get_max_state(cdev, &state); | 830 | ret = cdev->ops->get_max_state(cdev, &state); |
820 | if (ret) | 831 | if (ret) |
821 | return ret; | 832 | return ret; |
822 | return sprintf(buf, "%ld\n", state); | 833 | return sprintf(buf, "%ld\n", state); |
823 | } | 834 | } |
824 | 835 | ||
825 | static ssize_t | 836 | static ssize_t |
826 | thermal_cooling_device_cur_state_show(struct device *dev, | 837 | thermal_cooling_device_cur_state_show(struct device *dev, |
827 | struct device_attribute *attr, char *buf) | 838 | struct device_attribute *attr, char *buf) |
828 | { | 839 | { |
829 | struct thermal_cooling_device *cdev = to_cooling_device(dev); | 840 | struct thermal_cooling_device *cdev = to_cooling_device(dev); |
830 | unsigned long state; | 841 | unsigned long state; |
831 | int ret; | 842 | int ret; |
832 | 843 | ||
833 | ret = cdev->ops->get_cur_state(cdev, &state); | 844 | ret = cdev->ops->get_cur_state(cdev, &state); |
834 | if (ret) | 845 | if (ret) |
835 | return ret; | 846 | return ret; |
836 | return sprintf(buf, "%ld\n", state); | 847 | return sprintf(buf, "%ld\n", state); |
837 | } | 848 | } |
838 | 849 | ||
839 | static ssize_t | 850 | static ssize_t |
840 | thermal_cooling_device_cur_state_store(struct device *dev, | 851 | thermal_cooling_device_cur_state_store(struct device *dev, |
841 | struct device_attribute *attr, | 852 | struct device_attribute *attr, |
842 | const char *buf, size_t count) | 853 | const char *buf, size_t count) |
843 | { | 854 | { |
844 | struct thermal_cooling_device *cdev = to_cooling_device(dev); | 855 | struct thermal_cooling_device *cdev = to_cooling_device(dev); |
845 | unsigned long state; | 856 | unsigned long state; |
846 | int result; | 857 | int result; |
847 | 858 | ||
848 | if (!sscanf(buf, "%ld\n", &state)) | 859 | if (!sscanf(buf, "%ld\n", &state)) |
849 | return -EINVAL; | 860 | return -EINVAL; |
850 | 861 | ||
851 | if ((long)state < 0) | 862 | if ((long)state < 0) |
852 | return -EINVAL; | 863 | return -EINVAL; |
853 | 864 | ||
854 | result = cdev->ops->set_cur_state(cdev, state); | 865 | result = cdev->ops->set_cur_state(cdev, state); |
855 | if (result) | 866 | if (result) |
856 | return result; | 867 | return result; |
857 | return count; | 868 | return count; |
858 | } | 869 | } |
859 | 870 | ||
860 | static struct device_attribute dev_attr_cdev_type = | 871 | static struct device_attribute dev_attr_cdev_type = |
861 | __ATTR(type, 0444, thermal_cooling_device_type_show, NULL); | 872 | __ATTR(type, 0444, thermal_cooling_device_type_show, NULL); |
862 | static DEVICE_ATTR(max_state, 0444, | 873 | static DEVICE_ATTR(max_state, 0444, |
863 | thermal_cooling_device_max_state_show, NULL); | 874 | thermal_cooling_device_max_state_show, NULL); |
864 | static DEVICE_ATTR(cur_state, 0644, | 875 | static DEVICE_ATTR(cur_state, 0644, |
865 | thermal_cooling_device_cur_state_show, | 876 | thermal_cooling_device_cur_state_show, |
866 | thermal_cooling_device_cur_state_store); | 877 | thermal_cooling_device_cur_state_store); |
867 | 878 | ||
868 | static ssize_t | 879 | static ssize_t |
869 | thermal_cooling_device_trip_point_show(struct device *dev, | 880 | thermal_cooling_device_trip_point_show(struct device *dev, |
870 | struct device_attribute *attr, char *buf) | 881 | struct device_attribute *attr, char *buf) |
871 | { | 882 | { |
872 | struct thermal_instance *instance; | 883 | struct thermal_instance *instance; |
873 | 884 | ||
874 | instance = | 885 | instance = |
875 | container_of(attr, struct thermal_instance, attr); | 886 | container_of(attr, struct thermal_instance, attr); |
876 | 887 | ||
877 | if (instance->trip == THERMAL_TRIPS_NONE) | 888 | if (instance->trip == THERMAL_TRIPS_NONE) |
878 | return sprintf(buf, "-1\n"); | 889 | return sprintf(buf, "-1\n"); |
879 | else | 890 | else |
880 | return sprintf(buf, "%d\n", instance->trip); | 891 | return sprintf(buf, "%d\n", instance->trip); |
881 | } | 892 | } |
882 | 893 | ||
883 | /* Device management */ | 894 | /* Device management */ |
884 | 895 | ||
885 | /** | 896 | /** |
886 | * thermal_zone_bind_cooling_device() - bind a cooling device to a thermal zone | 897 | * thermal_zone_bind_cooling_device() - bind a cooling device to a thermal zone |
887 | * @tz: pointer to struct thermal_zone_device | 898 | * @tz: pointer to struct thermal_zone_device |
888 | * @trip: indicates which trip point the cooling devices is | 899 | * @trip: indicates which trip point the cooling devices is |
889 | * associated with in this thermal zone. | 900 | * associated with in this thermal zone. |
890 | * @cdev: pointer to struct thermal_cooling_device | 901 | * @cdev: pointer to struct thermal_cooling_device |
891 | * @upper: the Maximum cooling state for this trip point. | 902 | * @upper: the Maximum cooling state for this trip point. |
892 | * THERMAL_NO_LIMIT means no upper limit, | 903 | * THERMAL_NO_LIMIT means no upper limit, |
893 | * and the cooling device can be in max_state. | 904 | * and the cooling device can be in max_state. |
894 | * @lower: the Minimum cooling state can be used for this trip point. | 905 | * @lower: the Minimum cooling state can be used for this trip point. |
895 | * THERMAL_NO_LIMIT means no lower limit, | 906 | * THERMAL_NO_LIMIT means no lower limit, |
896 | * and the cooling device can be in cooling state 0. | 907 | * and the cooling device can be in cooling state 0. |
897 | * | 908 | * |
898 | * This interface function bind a thermal cooling device to the certain trip | 909 | * This interface function bind a thermal cooling device to the certain trip |
899 | * point of a thermal zone device. | 910 | * point of a thermal zone device. |
900 | * This function is usually called in the thermal zone device .bind callback. | 911 | * This function is usually called in the thermal zone device .bind callback. |
901 | * | 912 | * |
902 | * Return: 0 on success, the proper error value otherwise. | 913 | * Return: 0 on success, the proper error value otherwise. |
903 | */ | 914 | */ |
904 | int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz, | 915 | int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz, |
905 | int trip, | 916 | int trip, |
906 | struct thermal_cooling_device *cdev, | 917 | struct thermal_cooling_device *cdev, |
907 | unsigned long upper, unsigned long lower) | 918 | unsigned long upper, unsigned long lower) |
908 | { | 919 | { |
909 | struct thermal_instance *dev; | 920 | struct thermal_instance *dev; |
910 | struct thermal_instance *pos; | 921 | struct thermal_instance *pos; |
911 | struct thermal_zone_device *pos1; | 922 | struct thermal_zone_device *pos1; |
912 | struct thermal_cooling_device *pos2; | 923 | struct thermal_cooling_device *pos2; |
913 | unsigned long max_state; | 924 | unsigned long max_state; |
914 | int result; | 925 | int result; |
915 | 926 | ||
916 | if (trip >= tz->trips || (trip < 0 && trip != THERMAL_TRIPS_NONE)) | 927 | if (trip >= tz->trips || (trip < 0 && trip != THERMAL_TRIPS_NONE)) |
917 | return -EINVAL; | 928 | return -EINVAL; |
918 | 929 | ||
919 | list_for_each_entry(pos1, &thermal_tz_list, node) { | 930 | list_for_each_entry(pos1, &thermal_tz_list, node) { |
920 | if (pos1 == tz) | 931 | if (pos1 == tz) |
921 | break; | 932 | break; |
922 | } | 933 | } |
923 | list_for_each_entry(pos2, &thermal_cdev_list, node) { | 934 | list_for_each_entry(pos2, &thermal_cdev_list, node) { |
924 | if (pos2 == cdev) | 935 | if (pos2 == cdev) |
925 | break; | 936 | break; |
926 | } | 937 | } |
927 | 938 | ||
928 | if (tz != pos1 || cdev != pos2) | 939 | if (tz != pos1 || cdev != pos2) |
929 | return -EINVAL; | 940 | return -EINVAL; |
930 | 941 | ||
931 | cdev->ops->get_max_state(cdev, &max_state); | 942 | cdev->ops->get_max_state(cdev, &max_state); |
932 | 943 | ||
933 | /* lower default 0, upper default max_state */ | 944 | /* lower default 0, upper default max_state */ |
934 | lower = lower == THERMAL_NO_LIMIT ? 0 : lower; | 945 | lower = lower == THERMAL_NO_LIMIT ? 0 : lower; |
935 | upper = upper == THERMAL_NO_LIMIT ? max_state : upper; | 946 | upper = upper == THERMAL_NO_LIMIT ? max_state : upper; |
936 | 947 | ||
937 | if (lower > upper || upper > max_state) | 948 | if (lower > upper || upper > max_state) |
938 | return -EINVAL; | 949 | return -EINVAL; |
939 | 950 | ||
940 | dev = | 951 | dev = |
941 | kzalloc(sizeof(struct thermal_instance), GFP_KERNEL); | 952 | kzalloc(sizeof(struct thermal_instance), GFP_KERNEL); |
942 | if (!dev) | 953 | if (!dev) |
943 | return -ENOMEM; | 954 | return -ENOMEM; |
944 | dev->tz = tz; | 955 | dev->tz = tz; |
945 | dev->cdev = cdev; | 956 | dev->cdev = cdev; |
946 | dev->trip = trip; | 957 | dev->trip = trip; |
947 | dev->upper = upper; | 958 | dev->upper = upper; |
948 | dev->lower = lower; | 959 | dev->lower = lower; |
949 | dev->target = THERMAL_NO_TARGET; | 960 | dev->target = THERMAL_NO_TARGET; |
950 | 961 | ||
951 | result = get_idr(&tz->idr, &tz->lock, &dev->id); | 962 | result = get_idr(&tz->idr, &tz->lock, &dev->id); |
952 | if (result) | 963 | if (result) |
953 | goto free_mem; | 964 | goto free_mem; |
954 | 965 | ||
955 | sprintf(dev->name, "cdev%d", dev->id); | 966 | sprintf(dev->name, "cdev%d", dev->id); |
956 | result = | 967 | result = |
957 | sysfs_create_link(&tz->device.kobj, &cdev->device.kobj, dev->name); | 968 | sysfs_create_link(&tz->device.kobj, &cdev->device.kobj, dev->name); |
958 | if (result) | 969 | if (result) |
959 | goto release_idr; | 970 | goto release_idr; |
960 | 971 | ||
961 | sprintf(dev->attr_name, "cdev%d_trip_point", dev->id); | 972 | sprintf(dev->attr_name, "cdev%d_trip_point", dev->id); |
962 | sysfs_attr_init(&dev->attr.attr); | 973 | sysfs_attr_init(&dev->attr.attr); |
963 | dev->attr.attr.name = dev->attr_name; | 974 | dev->attr.attr.name = dev->attr_name; |
964 | dev->attr.attr.mode = 0444; | 975 | dev->attr.attr.mode = 0444; |
965 | dev->attr.show = thermal_cooling_device_trip_point_show; | 976 | dev->attr.show = thermal_cooling_device_trip_point_show; |
966 | result = device_create_file(&tz->device, &dev->attr); | 977 | result = device_create_file(&tz->device, &dev->attr); |
967 | if (result) | 978 | if (result) |
968 | goto remove_symbol_link; | 979 | goto remove_symbol_link; |
969 | 980 | ||
970 | mutex_lock(&tz->lock); | 981 | mutex_lock(&tz->lock); |
971 | mutex_lock(&cdev->lock); | 982 | mutex_lock(&cdev->lock); |
972 | list_for_each_entry(pos, &tz->thermal_instances, tz_node) | 983 | list_for_each_entry(pos, &tz->thermal_instances, tz_node) |
973 | if (pos->tz == tz && pos->trip == trip && pos->cdev == cdev) { | 984 | if (pos->tz == tz && pos->trip == trip && pos->cdev == cdev) { |
974 | result = -EEXIST; | 985 | result = -EEXIST; |
975 | break; | 986 | break; |
976 | } | 987 | } |
977 | if (!result) { | 988 | if (!result) { |
978 | list_add_tail(&dev->tz_node, &tz->thermal_instances); | 989 | list_add_tail(&dev->tz_node, &tz->thermal_instances); |
979 | list_add_tail(&dev->cdev_node, &cdev->thermal_instances); | 990 | list_add_tail(&dev->cdev_node, &cdev->thermal_instances); |
980 | } | 991 | } |
981 | mutex_unlock(&cdev->lock); | 992 | mutex_unlock(&cdev->lock); |
982 | mutex_unlock(&tz->lock); | 993 | mutex_unlock(&tz->lock); |
983 | 994 | ||
984 | if (!result) | 995 | if (!result) |
985 | return 0; | 996 | return 0; |
986 | 997 | ||
987 | device_remove_file(&tz->device, &dev->attr); | 998 | device_remove_file(&tz->device, &dev->attr); |
988 | remove_symbol_link: | 999 | remove_symbol_link: |
989 | sysfs_remove_link(&tz->device.kobj, dev->name); | 1000 | sysfs_remove_link(&tz->device.kobj, dev->name); |
990 | release_idr: | 1001 | release_idr: |
991 | release_idr(&tz->idr, &tz->lock, dev->id); | 1002 | release_idr(&tz->idr, &tz->lock, dev->id); |
992 | free_mem: | 1003 | free_mem: |
993 | kfree(dev); | 1004 | kfree(dev); |
994 | return result; | 1005 | return result; |
995 | } | 1006 | } |
996 | EXPORT_SYMBOL_GPL(thermal_zone_bind_cooling_device); | 1007 | EXPORT_SYMBOL_GPL(thermal_zone_bind_cooling_device); |
997 | 1008 | ||
998 | /** | 1009 | /** |
999 | * thermal_zone_unbind_cooling_device() - unbind a cooling device from a | 1010 | * thermal_zone_unbind_cooling_device() - unbind a cooling device from a |
1000 | * thermal zone. | 1011 | * thermal zone. |
1001 | * @tz: pointer to a struct thermal_zone_device. | 1012 | * @tz: pointer to a struct thermal_zone_device. |
1002 | * @trip: indicates which trip point the cooling devices is | 1013 | * @trip: indicates which trip point the cooling devices is |
1003 | * associated with in this thermal zone. | 1014 | * associated with in this thermal zone. |
1004 | * @cdev: pointer to a struct thermal_cooling_device. | 1015 | * @cdev: pointer to a struct thermal_cooling_device. |
1005 | * | 1016 | * |
1006 | * This interface function unbind a thermal cooling device from the certain | 1017 | * This interface function unbind a thermal cooling device from the certain |
1007 | * trip point of a thermal zone device. | 1018 | * trip point of a thermal zone device. |
1008 | * This function is usually called in the thermal zone device .unbind callback. | 1019 | * This function is usually called in the thermal zone device .unbind callback. |
1009 | * | 1020 | * |
1010 | * Return: 0 on success, the proper error value otherwise. | 1021 | * Return: 0 on success, the proper error value otherwise. |
1011 | */ | 1022 | */ |
1012 | int thermal_zone_unbind_cooling_device(struct thermal_zone_device *tz, | 1023 | int thermal_zone_unbind_cooling_device(struct thermal_zone_device *tz, |
1013 | int trip, | 1024 | int trip, |
1014 | struct thermal_cooling_device *cdev) | 1025 | struct thermal_cooling_device *cdev) |
1015 | { | 1026 | { |
1016 | struct thermal_instance *pos, *next; | 1027 | struct thermal_instance *pos, *next; |
1017 | 1028 | ||
1018 | mutex_lock(&tz->lock); | 1029 | mutex_lock(&tz->lock); |
1019 | mutex_lock(&cdev->lock); | 1030 | mutex_lock(&cdev->lock); |
1020 | list_for_each_entry_safe(pos, next, &tz->thermal_instances, tz_node) { | 1031 | list_for_each_entry_safe(pos, next, &tz->thermal_instances, tz_node) { |
1021 | if (pos->tz == tz && pos->trip == trip && pos->cdev == cdev) { | 1032 | if (pos->tz == tz && pos->trip == trip && pos->cdev == cdev) { |
1022 | list_del(&pos->tz_node); | 1033 | list_del(&pos->tz_node); |
1023 | list_del(&pos->cdev_node); | 1034 | list_del(&pos->cdev_node); |
1024 | mutex_unlock(&cdev->lock); | 1035 | mutex_unlock(&cdev->lock); |
1025 | mutex_unlock(&tz->lock); | 1036 | mutex_unlock(&tz->lock); |
1026 | goto unbind; | 1037 | goto unbind; |
1027 | } | 1038 | } |
1028 | } | 1039 | } |
1029 | mutex_unlock(&cdev->lock); | 1040 | mutex_unlock(&cdev->lock); |
1030 | mutex_unlock(&tz->lock); | 1041 | mutex_unlock(&tz->lock); |
1031 | 1042 | ||
1032 | return -ENODEV; | 1043 | return -ENODEV; |
1033 | 1044 | ||
1034 | unbind: | 1045 | unbind: |
1035 | device_remove_file(&tz->device, &pos->attr); | 1046 | device_remove_file(&tz->device, &pos->attr); |
1036 | sysfs_remove_link(&tz->device.kobj, pos->name); | 1047 | sysfs_remove_link(&tz->device.kobj, pos->name); |
1037 | release_idr(&tz->idr, &tz->lock, pos->id); | 1048 | release_idr(&tz->idr, &tz->lock, pos->id); |
1038 | kfree(pos); | 1049 | kfree(pos); |
1039 | return 0; | 1050 | return 0; |
1040 | } | 1051 | } |
1041 | EXPORT_SYMBOL_GPL(thermal_zone_unbind_cooling_device); | 1052 | EXPORT_SYMBOL_GPL(thermal_zone_unbind_cooling_device); |
1042 | 1053 | ||
1043 | static void thermal_release(struct device *dev) | 1054 | static void thermal_release(struct device *dev) |
1044 | { | 1055 | { |
1045 | struct thermal_zone_device *tz; | 1056 | struct thermal_zone_device *tz; |
1046 | struct thermal_cooling_device *cdev; | 1057 | struct thermal_cooling_device *cdev; |
1047 | 1058 | ||
1048 | if (!strncmp(dev_name(dev), "thermal_zone", | 1059 | if (!strncmp(dev_name(dev), "thermal_zone", |
1049 | sizeof("thermal_zone") - 1)) { | 1060 | sizeof("thermal_zone") - 1)) { |
1050 | tz = to_thermal_zone(dev); | 1061 | tz = to_thermal_zone(dev); |
1051 | kfree(tz); | 1062 | kfree(tz); |
1052 | } else if(!strncmp(dev_name(dev), "cooling_device", | 1063 | } else if(!strncmp(dev_name(dev), "cooling_device", |
1053 | sizeof("cooling_device") - 1)){ | 1064 | sizeof("cooling_device") - 1)){ |
1054 | cdev = to_cooling_device(dev); | 1065 | cdev = to_cooling_device(dev); |
1055 | kfree(cdev); | 1066 | kfree(cdev); |
1056 | } | 1067 | } |
1057 | } | 1068 | } |
1058 | 1069 | ||
1059 | static struct class thermal_class = { | 1070 | static struct class thermal_class = { |
1060 | .name = "thermal", | 1071 | .name = "thermal", |
1061 | .dev_release = thermal_release, | 1072 | .dev_release = thermal_release, |
1062 | }; | 1073 | }; |
1063 | 1074 | ||
1064 | /** | 1075 | /** |
1065 | * __thermal_cooling_device_register() - register a new thermal cooling device | 1076 | * __thermal_cooling_device_register() - register a new thermal cooling device |
1066 | * @np: a pointer to a device tree node. | 1077 | * @np: a pointer to a device tree node. |
1067 | * @type: the thermal cooling device type. | 1078 | * @type: the thermal cooling device type. |
1068 | * @devdata: device private data. | 1079 | * @devdata: device private data. |
1069 | * @ops: standard thermal cooling devices callbacks. | 1080 | * @ops: standard thermal cooling devices callbacks. |
1070 | * | 1081 | * |
1071 | * This interface function adds a new thermal cooling device (fan/processor/...) | 1082 | * This interface function adds a new thermal cooling device (fan/processor/...) |
1072 | * to /sys/class/thermal/ folder as cooling_device[0-*]. It tries to bind itself | 1083 | * to /sys/class/thermal/ folder as cooling_device[0-*]. It tries to bind itself |
1073 | * to all the thermal zone devices registered at the same time. | 1084 | * to all the thermal zone devices registered at the same time. |
1074 | * It also gives the opportunity to link the cooling device to a device tree | 1085 | * It also gives the opportunity to link the cooling device to a device tree |
1075 | * node, so that it can be bound to a thermal zone created out of device tree. | 1086 | * node, so that it can be bound to a thermal zone created out of device tree. |
1076 | * | 1087 | * |
1077 | * Return: a pointer to the created struct thermal_cooling_device or an | 1088 | * Return: a pointer to the created struct thermal_cooling_device or an |
1078 | * ERR_PTR. Caller must check return value with IS_ERR*() helpers. | 1089 | * ERR_PTR. Caller must check return value with IS_ERR*() helpers. |
1079 | */ | 1090 | */ |
1080 | static struct thermal_cooling_device * | 1091 | static struct thermal_cooling_device * |
1081 | __thermal_cooling_device_register(struct device_node *np, | 1092 | __thermal_cooling_device_register(struct device_node *np, |
1082 | char *type, void *devdata, | 1093 | char *type, void *devdata, |
1083 | const struct thermal_cooling_device_ops *ops) | 1094 | const struct thermal_cooling_device_ops *ops) |
1084 | { | 1095 | { |
1085 | struct thermal_cooling_device *cdev; | 1096 | struct thermal_cooling_device *cdev; |
1086 | int result; | 1097 | int result; |
1087 | 1098 | ||
1088 | if (type && strlen(type) >= THERMAL_NAME_LENGTH) | 1099 | if (type && strlen(type) >= THERMAL_NAME_LENGTH) |
1089 | return ERR_PTR(-EINVAL); | 1100 | return ERR_PTR(-EINVAL); |
1090 | 1101 | ||
1091 | if (!ops || !ops->get_max_state || !ops->get_cur_state || | 1102 | if (!ops || !ops->get_max_state || !ops->get_cur_state || |
1092 | !ops->set_cur_state) | 1103 | !ops->set_cur_state) |
1093 | return ERR_PTR(-EINVAL); | 1104 | return ERR_PTR(-EINVAL); |
1094 | 1105 | ||
1095 | cdev = kzalloc(sizeof(struct thermal_cooling_device), GFP_KERNEL); | 1106 | cdev = kzalloc(sizeof(struct thermal_cooling_device), GFP_KERNEL); |
1096 | if (!cdev) | 1107 | if (!cdev) |
1097 | return ERR_PTR(-ENOMEM); | 1108 | return ERR_PTR(-ENOMEM); |
1098 | 1109 | ||
1099 | result = get_idr(&thermal_cdev_idr, &thermal_idr_lock, &cdev->id); | 1110 | result = get_idr(&thermal_cdev_idr, &thermal_idr_lock, &cdev->id); |
1100 | if (result) { | 1111 | if (result) { |
1101 | kfree(cdev); | 1112 | kfree(cdev); |
1102 | return ERR_PTR(result); | 1113 | return ERR_PTR(result); |
1103 | } | 1114 | } |
1104 | 1115 | ||
1105 | strlcpy(cdev->type, type ? : "", sizeof(cdev->type)); | 1116 | strlcpy(cdev->type, type ? : "", sizeof(cdev->type)); |
1106 | mutex_init(&cdev->lock); | 1117 | mutex_init(&cdev->lock); |
1107 | INIT_LIST_HEAD(&cdev->thermal_instances); | 1118 | INIT_LIST_HEAD(&cdev->thermal_instances); |
1108 | cdev->np = np; | 1119 | cdev->np = np; |
1109 | cdev->ops = ops; | 1120 | cdev->ops = ops; |
1110 | cdev->updated = true; | 1121 | cdev->updated = false; |
1111 | cdev->device.class = &thermal_class; | 1122 | cdev->device.class = &thermal_class; |
1112 | cdev->devdata = devdata; | 1123 | cdev->devdata = devdata; |
1113 | dev_set_name(&cdev->device, "cooling_device%d", cdev->id); | 1124 | dev_set_name(&cdev->device, "cooling_device%d", cdev->id); |
1114 | result = device_register(&cdev->device); | 1125 | result = device_register(&cdev->device); |
1115 | if (result) { | 1126 | if (result) { |
1116 | release_idr(&thermal_cdev_idr, &thermal_idr_lock, cdev->id); | 1127 | release_idr(&thermal_cdev_idr, &thermal_idr_lock, cdev->id); |
1117 | kfree(cdev); | 1128 | kfree(cdev); |
1118 | return ERR_PTR(result); | 1129 | return ERR_PTR(result); |
1119 | } | 1130 | } |
1120 | 1131 | ||
1121 | /* sys I/F */ | 1132 | /* sys I/F */ |
1122 | if (type) { | 1133 | if (type) { |
1123 | result = device_create_file(&cdev->device, &dev_attr_cdev_type); | 1134 | result = device_create_file(&cdev->device, &dev_attr_cdev_type); |
1124 | if (result) | 1135 | if (result) |
1125 | goto unregister; | 1136 | goto unregister; |
1126 | } | 1137 | } |
1127 | 1138 | ||
1128 | result = device_create_file(&cdev->device, &dev_attr_max_state); | 1139 | result = device_create_file(&cdev->device, &dev_attr_max_state); |
1129 | if (result) | 1140 | if (result) |
1130 | goto unregister; | 1141 | goto unregister; |
1131 | 1142 | ||
1132 | result = device_create_file(&cdev->device, &dev_attr_cur_state); | 1143 | result = device_create_file(&cdev->device, &dev_attr_cur_state); |
1133 | if (result) | 1144 | if (result) |
1134 | goto unregister; | 1145 | goto unregister; |
1135 | 1146 | ||
1136 | /* Add 'this' new cdev to the global cdev list */ | 1147 | /* Add 'this' new cdev to the global cdev list */ |
1137 | mutex_lock(&thermal_list_lock); | 1148 | mutex_lock(&thermal_list_lock); |
1138 | list_add(&cdev->node, &thermal_cdev_list); | 1149 | list_add(&cdev->node, &thermal_cdev_list); |
1139 | mutex_unlock(&thermal_list_lock); | 1150 | mutex_unlock(&thermal_list_lock); |
1140 | 1151 | ||
1141 | /* Update binding information for 'this' new cdev */ | 1152 | /* Update binding information for 'this' new cdev */ |
1142 | bind_cdev(cdev); | 1153 | bind_cdev(cdev); |
1143 | 1154 | ||
1144 | return cdev; | 1155 | return cdev; |
1145 | 1156 | ||
1146 | unregister: | 1157 | unregister: |
1147 | release_idr(&thermal_cdev_idr, &thermal_idr_lock, cdev->id); | 1158 | release_idr(&thermal_cdev_idr, &thermal_idr_lock, cdev->id); |
1148 | device_unregister(&cdev->device); | 1159 | device_unregister(&cdev->device); |
1149 | return ERR_PTR(result); | 1160 | return ERR_PTR(result); |
1150 | } | 1161 | } |
1151 | 1162 | ||
1152 | /** | 1163 | /** |
1153 | * thermal_cooling_device_register() - register a new thermal cooling device | 1164 | * thermal_cooling_device_register() - register a new thermal cooling device |
1154 | * @type: the thermal cooling device type. | 1165 | * @type: the thermal cooling device type. |
1155 | * @devdata: device private data. | 1166 | * @devdata: device private data. |
1156 | * @ops: standard thermal cooling devices callbacks. | 1167 | * @ops: standard thermal cooling devices callbacks. |
1157 | * | 1168 | * |
1158 | * This interface function adds a new thermal cooling device (fan/processor/...) | 1169 | * This interface function adds a new thermal cooling device (fan/processor/...) |
1159 | * to /sys/class/thermal/ folder as cooling_device[0-*]. It tries to bind itself | 1170 | * to /sys/class/thermal/ folder as cooling_device[0-*]. It tries to bind itself |
1160 | * to all the thermal zone devices registered at the same time. | 1171 | * to all the thermal zone devices registered at the same time. |
1161 | * | 1172 | * |
1162 | * Return: a pointer to the created struct thermal_cooling_device or an | 1173 | * Return: a pointer to the created struct thermal_cooling_device or an |
1163 | * ERR_PTR. Caller must check return value with IS_ERR*() helpers. | 1174 | * ERR_PTR. Caller must check return value with IS_ERR*() helpers. |
1164 | */ | 1175 | */ |
1165 | struct thermal_cooling_device * | 1176 | struct thermal_cooling_device * |
1166 | thermal_cooling_device_register(char *type, void *devdata, | 1177 | thermal_cooling_device_register(char *type, void *devdata, |
1167 | const struct thermal_cooling_device_ops *ops) | 1178 | const struct thermal_cooling_device_ops *ops) |
1168 | { | 1179 | { |
1169 | return __thermal_cooling_device_register(NULL, type, devdata, ops); | 1180 | return __thermal_cooling_device_register(NULL, type, devdata, ops); |
1170 | } | 1181 | } |
1171 | EXPORT_SYMBOL_GPL(thermal_cooling_device_register); | 1182 | EXPORT_SYMBOL_GPL(thermal_cooling_device_register); |
1172 | 1183 | ||
1173 | /** | 1184 | /** |
1174 | * thermal_of_cooling_device_register() - register an OF thermal cooling device | 1185 | * thermal_of_cooling_device_register() - register an OF thermal cooling device |
1175 | * @np: a pointer to a device tree node. | 1186 | * @np: a pointer to a device tree node. |
1176 | * @type: the thermal cooling device type. | 1187 | * @type: the thermal cooling device type. |
1177 | * @devdata: device private data. | 1188 | * @devdata: device private data. |
1178 | * @ops: standard thermal cooling devices callbacks. | 1189 | * @ops: standard thermal cooling devices callbacks. |
1179 | * | 1190 | * |
1180 | * This function will register a cooling device with device tree node reference. | 1191 | * This function will register a cooling device with device tree node reference. |
1181 | * This interface function adds a new thermal cooling device (fan/processor/...) | 1192 | * This interface function adds a new thermal cooling device (fan/processor/...) |
1182 | * to /sys/class/thermal/ folder as cooling_device[0-*]. It tries to bind itself | 1193 | * to /sys/class/thermal/ folder as cooling_device[0-*]. It tries to bind itself |
1183 | * to all the thermal zone devices registered at the same time. | 1194 | * to all the thermal zone devices registered at the same time. |
1184 | * | 1195 | * |
1185 | * Return: a pointer to the created struct thermal_cooling_device or an | 1196 | * Return: a pointer to the created struct thermal_cooling_device or an |
1186 | * ERR_PTR. Caller must check return value with IS_ERR*() helpers. | 1197 | * ERR_PTR. Caller must check return value with IS_ERR*() helpers. |
1187 | */ | 1198 | */ |
1188 | struct thermal_cooling_device * | 1199 | struct thermal_cooling_device * |
1189 | thermal_of_cooling_device_register(struct device_node *np, | 1200 | thermal_of_cooling_device_register(struct device_node *np, |
1190 | char *type, void *devdata, | 1201 | char *type, void *devdata, |
1191 | const struct thermal_cooling_device_ops *ops) | 1202 | const struct thermal_cooling_device_ops *ops) |
1192 | { | 1203 | { |
1193 | return __thermal_cooling_device_register(np, type, devdata, ops); | 1204 | return __thermal_cooling_device_register(np, type, devdata, ops); |
1194 | } | 1205 | } |
1195 | EXPORT_SYMBOL_GPL(thermal_of_cooling_device_register); | 1206 | EXPORT_SYMBOL_GPL(thermal_of_cooling_device_register); |
1196 | 1207 | ||
1197 | /** | 1208 | /** |
1198 | * thermal_cooling_device_unregister - removes the registered thermal cooling device | 1209 | * thermal_cooling_device_unregister - removes the registered thermal cooling device |
1199 | * @cdev: the thermal cooling device to remove. | 1210 | * @cdev: the thermal cooling device to remove. |
1200 | * | 1211 | * |
1201 | * thermal_cooling_device_unregister() must be called when the device is no | 1212 | * thermal_cooling_device_unregister() must be called when the device is no |
1202 | * longer needed. | 1213 | * longer needed. |
1203 | */ | 1214 | */ |
1204 | void thermal_cooling_device_unregister(struct thermal_cooling_device *cdev) | 1215 | void thermal_cooling_device_unregister(struct thermal_cooling_device *cdev) |
1205 | { | 1216 | { |
1206 | int i; | 1217 | int i; |
1207 | const struct thermal_zone_params *tzp; | 1218 | const struct thermal_zone_params *tzp; |
1208 | struct thermal_zone_device *tz; | 1219 | struct thermal_zone_device *tz; |
1209 | struct thermal_cooling_device *pos = NULL; | 1220 | struct thermal_cooling_device *pos = NULL; |
1210 | 1221 | ||
1211 | if (!cdev) | 1222 | if (!cdev) |
1212 | return; | 1223 | return; |
1213 | 1224 | ||
1214 | mutex_lock(&thermal_list_lock); | 1225 | mutex_lock(&thermal_list_lock); |
1215 | list_for_each_entry(pos, &thermal_cdev_list, node) | 1226 | list_for_each_entry(pos, &thermal_cdev_list, node) |
1216 | if (pos == cdev) | 1227 | if (pos == cdev) |
1217 | break; | 1228 | break; |
1218 | if (pos != cdev) { | 1229 | if (pos != cdev) { |
1219 | /* thermal cooling device not found */ | 1230 | /* thermal cooling device not found */ |
1220 | mutex_unlock(&thermal_list_lock); | 1231 | mutex_unlock(&thermal_list_lock); |
1221 | return; | 1232 | return; |
1222 | } | 1233 | } |
1223 | list_del(&cdev->node); | 1234 | list_del(&cdev->node); |
1224 | 1235 | ||
1225 | /* Unbind all thermal zones associated with 'this' cdev */ | 1236 | /* Unbind all thermal zones associated with 'this' cdev */ |
1226 | list_for_each_entry(tz, &thermal_tz_list, node) { | 1237 | list_for_each_entry(tz, &thermal_tz_list, node) { |
1227 | if (tz->ops->unbind) { | 1238 | if (tz->ops->unbind) { |
1228 | tz->ops->unbind(tz, cdev); | 1239 | tz->ops->unbind(tz, cdev); |
1229 | continue; | 1240 | continue; |
1230 | } | 1241 | } |
1231 | 1242 | ||
1232 | if (!tz->tzp || !tz->tzp->tbp) | 1243 | if (!tz->tzp || !tz->tzp->tbp) |
1233 | continue; | 1244 | continue; |
1234 | 1245 | ||
1235 | tzp = tz->tzp; | 1246 | tzp = tz->tzp; |
1236 | for (i = 0; i < tzp->num_tbps; i++) { | 1247 | for (i = 0; i < tzp->num_tbps; i++) { |
1237 | if (tzp->tbp[i].cdev == cdev) { | 1248 | if (tzp->tbp[i].cdev == cdev) { |
1238 | __unbind(tz, tzp->tbp[i].trip_mask, cdev); | 1249 | __unbind(tz, tzp->tbp[i].trip_mask, cdev); |
1239 | tzp->tbp[i].cdev = NULL; | 1250 | tzp->tbp[i].cdev = NULL; |
1240 | } | 1251 | } |
1241 | } | 1252 | } |
1242 | } | 1253 | } |
1243 | 1254 | ||
1244 | mutex_unlock(&thermal_list_lock); | 1255 | mutex_unlock(&thermal_list_lock); |
1245 | 1256 | ||
1246 | if (cdev->type[0]) | 1257 | if (cdev->type[0]) |
1247 | device_remove_file(&cdev->device, &dev_attr_cdev_type); | 1258 | device_remove_file(&cdev->device, &dev_attr_cdev_type); |
1248 | device_remove_file(&cdev->device, &dev_attr_max_state); | 1259 | device_remove_file(&cdev->device, &dev_attr_max_state); |
1249 | device_remove_file(&cdev->device, &dev_attr_cur_state); | 1260 | device_remove_file(&cdev->device, &dev_attr_cur_state); |
1250 | 1261 | ||
1251 | release_idr(&thermal_cdev_idr, &thermal_idr_lock, cdev->id); | 1262 | release_idr(&thermal_cdev_idr, &thermal_idr_lock, cdev->id); |
1252 | device_unregister(&cdev->device); | 1263 | device_unregister(&cdev->device); |
1253 | return; | 1264 | return; |
1254 | } | 1265 | } |
1255 | EXPORT_SYMBOL_GPL(thermal_cooling_device_unregister); | 1266 | EXPORT_SYMBOL_GPL(thermal_cooling_device_unregister); |
1256 | 1267 | ||
1257 | void thermal_cdev_update(struct thermal_cooling_device *cdev) | 1268 | void thermal_cdev_update(struct thermal_cooling_device *cdev) |
1258 | { | 1269 | { |
1259 | struct thermal_instance *instance; | 1270 | struct thermal_instance *instance; |
1260 | unsigned long target = 0; | 1271 | unsigned long target = 0; |
1261 | 1272 | ||
1262 | /* cooling device is updated*/ | 1273 | /* cooling device is updated*/ |
1263 | if (cdev->updated) | 1274 | if (cdev->updated) |
1264 | return; | 1275 | return; |
1265 | 1276 | ||
1266 | mutex_lock(&cdev->lock); | 1277 | mutex_lock(&cdev->lock); |
1267 | /* Make sure cdev enters the deepest cooling state */ | 1278 | /* Make sure cdev enters the deepest cooling state */ |
1268 | list_for_each_entry(instance, &cdev->thermal_instances, cdev_node) { | 1279 | list_for_each_entry(instance, &cdev->thermal_instances, cdev_node) { |
1269 | dev_dbg(&cdev->device, "zone%d->target=%lu\n", | 1280 | dev_dbg(&cdev->device, "zone%d->target=%lu\n", |
1270 | instance->tz->id, instance->target); | 1281 | instance->tz->id, instance->target); |
1271 | if (instance->target == THERMAL_NO_TARGET) | 1282 | if (instance->target == THERMAL_NO_TARGET) |
1272 | continue; | 1283 | continue; |
1273 | if (instance->target > target) | 1284 | if (instance->target > target) |
1274 | target = instance->target; | 1285 | target = instance->target; |
1275 | } | 1286 | } |
1276 | mutex_unlock(&cdev->lock); | 1287 | mutex_unlock(&cdev->lock); |
1277 | cdev->ops->set_cur_state(cdev, target); | 1288 | cdev->ops->set_cur_state(cdev, target); |
1278 | cdev->updated = true; | 1289 | cdev->updated = true; |
1279 | dev_dbg(&cdev->device, "set to state %lu\n", target); | 1290 | dev_dbg(&cdev->device, "set to state %lu\n", target); |
1280 | } | 1291 | } |
1281 | EXPORT_SYMBOL(thermal_cdev_update); | 1292 | EXPORT_SYMBOL(thermal_cdev_update); |
1282 | 1293 | ||
1283 | /** | 1294 | /** |
1284 | * thermal_notify_framework - Sensor drivers use this API to notify framework | 1295 | * thermal_notify_framework - Sensor drivers use this API to notify framework |
1285 | * @tz: thermal zone device | 1296 | * @tz: thermal zone device |
1286 | * @trip: indicates which trip point has been crossed | 1297 | * @trip: indicates which trip point has been crossed |
1287 | * | 1298 | * |
1288 | * This function handles the trip events from sensor drivers. It starts | 1299 | * This function handles the trip events from sensor drivers. It starts |
1289 | * throttling the cooling devices according to the policy configured. | 1300 | * throttling the cooling devices according to the policy configured. |
1290 | * For CRITICAL and HOT trip points, this notifies the respective drivers, | 1301 | * For CRITICAL and HOT trip points, this notifies the respective drivers, |
1291 | * and does actual throttling for other trip points i.e ACTIVE and PASSIVE. | 1302 | * and does actual throttling for other trip points i.e ACTIVE and PASSIVE. |
1292 | * The throttling policy is based on the configured platform data; if no | 1303 | * The throttling policy is based on the configured platform data; if no |
1293 | * platform data is provided, this uses the step_wise throttling policy. | 1304 | * platform data is provided, this uses the step_wise throttling policy. |
1294 | */ | 1305 | */ |
1295 | void thermal_notify_framework(struct thermal_zone_device *tz, int trip) | 1306 | void thermal_notify_framework(struct thermal_zone_device *tz, int trip) |
1296 | { | 1307 | { |
1297 | handle_thermal_trip(tz, trip); | 1308 | handle_thermal_trip(tz, trip); |
1298 | } | 1309 | } |
1299 | EXPORT_SYMBOL_GPL(thermal_notify_framework); | 1310 | EXPORT_SYMBOL_GPL(thermal_notify_framework); |
1300 | 1311 | ||
1301 | /** | 1312 | /** |
1302 | * create_trip_attrs() - create attributes for trip points | 1313 | * create_trip_attrs() - create attributes for trip points |
1303 | * @tz: the thermal zone device | 1314 | * @tz: the thermal zone device |
1304 | * @mask: Writeable trip point bitmap. | 1315 | * @mask: Writeable trip point bitmap. |
1305 | * | 1316 | * |
1306 | * helper function to instantiate sysfs entries for every trip | 1317 | * helper function to instantiate sysfs entries for every trip |
1307 | * point and its properties of a struct thermal_zone_device. | 1318 | * point and its properties of a struct thermal_zone_device. |
1308 | * | 1319 | * |
1309 | * Return: 0 on success, the proper error value otherwise. | 1320 | * Return: 0 on success, the proper error value otherwise. |
1310 | */ | 1321 | */ |
1311 | static int create_trip_attrs(struct thermal_zone_device *tz, int mask) | 1322 | static int create_trip_attrs(struct thermal_zone_device *tz, int mask) |
1312 | { | 1323 | { |
1313 | int indx; | 1324 | int indx; |
1314 | int size = sizeof(struct thermal_attr) * tz->trips; | 1325 | int size = sizeof(struct thermal_attr) * tz->trips; |
1315 | 1326 | ||
1316 | tz->trip_type_attrs = kzalloc(size, GFP_KERNEL); | 1327 | tz->trip_type_attrs = kzalloc(size, GFP_KERNEL); |
1317 | if (!tz->trip_type_attrs) | 1328 | if (!tz->trip_type_attrs) |
1318 | return -ENOMEM; | 1329 | return -ENOMEM; |
1319 | 1330 | ||
1320 | tz->trip_temp_attrs = kzalloc(size, GFP_KERNEL); | 1331 | tz->trip_temp_attrs = kzalloc(size, GFP_KERNEL); |
1321 | if (!tz->trip_temp_attrs) { | 1332 | if (!tz->trip_temp_attrs) { |
1322 | kfree(tz->trip_type_attrs); | 1333 | kfree(tz->trip_type_attrs); |
1323 | return -ENOMEM; | 1334 | return -ENOMEM; |
1324 | } | 1335 | } |
1325 | 1336 | ||
1326 | if (tz->ops->get_trip_hyst) { | 1337 | if (tz->ops->get_trip_hyst) { |
1327 | tz->trip_hyst_attrs = kzalloc(size, GFP_KERNEL); | 1338 | tz->trip_hyst_attrs = kzalloc(size, GFP_KERNEL); |
1328 | if (!tz->trip_hyst_attrs) { | 1339 | if (!tz->trip_hyst_attrs) { |
1329 | kfree(tz->trip_type_attrs); | 1340 | kfree(tz->trip_type_attrs); |
1330 | kfree(tz->trip_temp_attrs); | 1341 | kfree(tz->trip_temp_attrs); |
1331 | return -ENOMEM; | 1342 | return -ENOMEM; |
1332 | } | 1343 | } |
1333 | } | 1344 | } |
1334 | 1345 | ||
1335 | 1346 | ||
1336 | for (indx = 0; indx < tz->trips; indx++) { | 1347 | for (indx = 0; indx < tz->trips; indx++) { |
1337 | /* create trip type attribute */ | 1348 | /* create trip type attribute */ |
1338 | snprintf(tz->trip_type_attrs[indx].name, THERMAL_NAME_LENGTH, | 1349 | snprintf(tz->trip_type_attrs[indx].name, THERMAL_NAME_LENGTH, |
1339 | "trip_point_%d_type", indx); | 1350 | "trip_point_%d_type", indx); |
1340 | 1351 | ||
1341 | sysfs_attr_init(&tz->trip_type_attrs[indx].attr.attr); | 1352 | sysfs_attr_init(&tz->trip_type_attrs[indx].attr.attr); |
1342 | tz->trip_type_attrs[indx].attr.attr.name = | 1353 | tz->trip_type_attrs[indx].attr.attr.name = |
1343 | tz->trip_type_attrs[indx].name; | 1354 | tz->trip_type_attrs[indx].name; |
1344 | tz->trip_type_attrs[indx].attr.attr.mode = S_IRUGO; | 1355 | tz->trip_type_attrs[indx].attr.attr.mode = S_IRUGO; |
1345 | tz->trip_type_attrs[indx].attr.show = trip_point_type_show; | 1356 | tz->trip_type_attrs[indx].attr.show = trip_point_type_show; |
1346 | 1357 | ||
1347 | device_create_file(&tz->device, | 1358 | device_create_file(&tz->device, |
1348 | &tz->trip_type_attrs[indx].attr); | 1359 | &tz->trip_type_attrs[indx].attr); |
1349 | 1360 | ||
1350 | /* create trip temp attribute */ | 1361 | /* create trip temp attribute */ |
1351 | snprintf(tz->trip_temp_attrs[indx].name, THERMAL_NAME_LENGTH, | 1362 | snprintf(tz->trip_temp_attrs[indx].name, THERMAL_NAME_LENGTH, |
1352 | "trip_point_%d_temp", indx); | 1363 | "trip_point_%d_temp", indx); |
1353 | 1364 | ||
1354 | sysfs_attr_init(&tz->trip_temp_attrs[indx].attr.attr); | 1365 | sysfs_attr_init(&tz->trip_temp_attrs[indx].attr.attr); |
1355 | tz->trip_temp_attrs[indx].attr.attr.name = | 1366 | tz->trip_temp_attrs[indx].attr.attr.name = |
1356 | tz->trip_temp_attrs[indx].name; | 1367 | tz->trip_temp_attrs[indx].name; |
1357 | tz->trip_temp_attrs[indx].attr.attr.mode = S_IRUGO; | 1368 | tz->trip_temp_attrs[indx].attr.attr.mode = S_IRUGO; |
1358 | tz->trip_temp_attrs[indx].attr.show = trip_point_temp_show; | 1369 | tz->trip_temp_attrs[indx].attr.show = trip_point_temp_show; |
1359 | if (mask & (1 << indx)) { | 1370 | if (mask & (1 << indx)) { |
1360 | tz->trip_temp_attrs[indx].attr.attr.mode |= S_IWUSR; | 1371 | tz->trip_temp_attrs[indx].attr.attr.mode |= S_IWUSR; |
1361 | tz->trip_temp_attrs[indx].attr.store = | 1372 | tz->trip_temp_attrs[indx].attr.store = |
1362 | trip_point_temp_store; | 1373 | trip_point_temp_store; |
1363 | } | 1374 | } |
1364 | 1375 | ||
1365 | device_create_file(&tz->device, | 1376 | device_create_file(&tz->device, |
1366 | &tz->trip_temp_attrs[indx].attr); | 1377 | &tz->trip_temp_attrs[indx].attr); |
1367 | 1378 | ||
1368 | /* create Optional trip hyst attribute */ | 1379 | /* create Optional trip hyst attribute */ |
1369 | if (!tz->ops->get_trip_hyst) | 1380 | if (!tz->ops->get_trip_hyst) |
1370 | continue; | 1381 | continue; |
1371 | snprintf(tz->trip_hyst_attrs[indx].name, THERMAL_NAME_LENGTH, | 1382 | snprintf(tz->trip_hyst_attrs[indx].name, THERMAL_NAME_LENGTH, |
1372 | "trip_point_%d_hyst", indx); | 1383 | "trip_point_%d_hyst", indx); |
1373 | 1384 | ||
1374 | sysfs_attr_init(&tz->trip_hyst_attrs[indx].attr.attr); | 1385 | sysfs_attr_init(&tz->trip_hyst_attrs[indx].attr.attr); |
1375 | tz->trip_hyst_attrs[indx].attr.attr.name = | 1386 | tz->trip_hyst_attrs[indx].attr.attr.name = |
1376 | tz->trip_hyst_attrs[indx].name; | 1387 | tz->trip_hyst_attrs[indx].name; |
1377 | tz->trip_hyst_attrs[indx].attr.attr.mode = S_IRUGO; | 1388 | tz->trip_hyst_attrs[indx].attr.attr.mode = S_IRUGO; |
1378 | tz->trip_hyst_attrs[indx].attr.show = trip_point_hyst_show; | 1389 | tz->trip_hyst_attrs[indx].attr.show = trip_point_hyst_show; |
1379 | if (tz->ops->set_trip_hyst) { | 1390 | if (tz->ops->set_trip_hyst) { |
1380 | tz->trip_hyst_attrs[indx].attr.attr.mode |= S_IWUSR; | 1391 | tz->trip_hyst_attrs[indx].attr.attr.mode |= S_IWUSR; |
1381 | tz->trip_hyst_attrs[indx].attr.store = | 1392 | tz->trip_hyst_attrs[indx].attr.store = |
1382 | trip_point_hyst_store; | 1393 | trip_point_hyst_store; |
1383 | } | 1394 | } |
1384 | 1395 | ||
1385 | device_create_file(&tz->device, | 1396 | device_create_file(&tz->device, |
1386 | &tz->trip_hyst_attrs[indx].attr); | 1397 | &tz->trip_hyst_attrs[indx].attr); |
1387 | } | 1398 | } |
1388 | return 0; | 1399 | return 0; |
1389 | } | 1400 | } |
1390 | 1401 | ||
1391 | static void remove_trip_attrs(struct thermal_zone_device *tz) | 1402 | static void remove_trip_attrs(struct thermal_zone_device *tz) |
1392 | { | 1403 | { |
1393 | int indx; | 1404 | int indx; |
1394 | 1405 | ||
1395 | for (indx = 0; indx < tz->trips; indx++) { | 1406 | for (indx = 0; indx < tz->trips; indx++) { |
1396 | device_remove_file(&tz->device, | 1407 | device_remove_file(&tz->device, |
1397 | &tz->trip_type_attrs[indx].attr); | 1408 | &tz->trip_type_attrs[indx].attr); |
1398 | device_remove_file(&tz->device, | 1409 | device_remove_file(&tz->device, |
1399 | &tz->trip_temp_attrs[indx].attr); | 1410 | &tz->trip_temp_attrs[indx].attr); |
1400 | if (tz->ops->get_trip_hyst) | 1411 | if (tz->ops->get_trip_hyst) |
1401 | device_remove_file(&tz->device, | 1412 | device_remove_file(&tz->device, |
1402 | &tz->trip_hyst_attrs[indx].attr); | 1413 | &tz->trip_hyst_attrs[indx].attr); |
1403 | } | 1414 | } |
1404 | kfree(tz->trip_type_attrs); | 1415 | kfree(tz->trip_type_attrs); |
1405 | kfree(tz->trip_temp_attrs); | 1416 | kfree(tz->trip_temp_attrs); |
1406 | kfree(tz->trip_hyst_attrs); | 1417 | kfree(tz->trip_hyst_attrs); |
1407 | } | 1418 | } |
1408 | 1419 | ||
1409 | /** | 1420 | /** |
1410 | * thermal_zone_device_register() - register a new thermal zone device | 1421 | * thermal_zone_device_register() - register a new thermal zone device |
1411 | * @type: the thermal zone device type | 1422 | * @type: the thermal zone device type |
1412 | * @trips: the number of trip points the thermal zone support | 1423 | * @trips: the number of trip points the thermal zone support |
1413 | * @mask: a bit string indicating the writeablility of trip points | 1424 | * @mask: a bit string indicating the writeablility of trip points |
1414 | * @devdata: private device data | 1425 | * @devdata: private device data |
1415 | * @ops: standard thermal zone device callbacks | 1426 | * @ops: standard thermal zone device callbacks |
1416 | * @tzp: thermal zone platform parameters | 1427 | * @tzp: thermal zone platform parameters |
1417 | * @passive_delay: number of milliseconds to wait between polls when | 1428 | * @passive_delay: number of milliseconds to wait between polls when |
1418 | * performing passive cooling | 1429 | * performing passive cooling |
1419 | * @polling_delay: number of milliseconds to wait between polls when checking | 1430 | * @polling_delay: number of milliseconds to wait between polls when checking |
1420 | * whether trip points have been crossed (0 for interrupt | 1431 | * whether trip points have been crossed (0 for interrupt |
1421 | * driven systems) | 1432 | * driven systems) |
1422 | * | 1433 | * |
1423 | * This interface function adds a new thermal zone device (sensor) to | 1434 | * This interface function adds a new thermal zone device (sensor) to |
1424 | * /sys/class/thermal folder as thermal_zone[0-*]. It tries to bind all the | 1435 | * /sys/class/thermal folder as thermal_zone[0-*]. It tries to bind all the |
1425 | * thermal cooling devices registered at the same time. | 1436 | * thermal cooling devices registered at the same time. |
1426 | * thermal_zone_device_unregister() must be called when the device is no | 1437 | * thermal_zone_device_unregister() must be called when the device is no |
1427 | * longer needed. The passive cooling depends on the .get_trend() return value. | 1438 | * longer needed. The passive cooling depends on the .get_trend() return value. |
1428 | * | 1439 | * |
1429 | * Return: a pointer to the created struct thermal_zone_device or an | 1440 | * Return: a pointer to the created struct thermal_zone_device or an |
1430 | * in case of error, an ERR_PTR. Caller must check return value with | 1441 | * in case of error, an ERR_PTR. Caller must check return value with |
1431 | * IS_ERR*() helpers. | 1442 | * IS_ERR*() helpers. |
1432 | */ | 1443 | */ |
1433 | struct thermal_zone_device *thermal_zone_device_register(const char *type, | 1444 | struct thermal_zone_device *thermal_zone_device_register(const char *type, |
1434 | int trips, int mask, void *devdata, | 1445 | int trips, int mask, void *devdata, |
1435 | struct thermal_zone_device_ops *ops, | 1446 | struct thermal_zone_device_ops *ops, |
1436 | const struct thermal_zone_params *tzp, | 1447 | const struct thermal_zone_params *tzp, |
1437 | int passive_delay, int polling_delay) | 1448 | int passive_delay, int polling_delay) |
1438 | { | 1449 | { |
1439 | struct thermal_zone_device *tz; | 1450 | struct thermal_zone_device *tz; |
1440 | enum thermal_trip_type trip_type; | 1451 | enum thermal_trip_type trip_type; |
1441 | int result; | 1452 | int result; |
1442 | int count; | 1453 | int count; |
1443 | int passive = 0; | 1454 | int passive = 0; |
1444 | 1455 | ||
1445 | if (type && strlen(type) >= THERMAL_NAME_LENGTH) | 1456 | if (type && strlen(type) >= THERMAL_NAME_LENGTH) |
1446 | return ERR_PTR(-EINVAL); | 1457 | return ERR_PTR(-EINVAL); |
1447 | 1458 | ||
1448 | if (trips > THERMAL_MAX_TRIPS || trips < 0 || mask >> trips) | 1459 | if (trips > THERMAL_MAX_TRIPS || trips < 0 || mask >> trips) |
1449 | return ERR_PTR(-EINVAL); | 1460 | return ERR_PTR(-EINVAL); |
1450 | 1461 | ||
1451 | if (!ops) | 1462 | if (!ops) |
1452 | return ERR_PTR(-EINVAL); | 1463 | return ERR_PTR(-EINVAL); |
1453 | 1464 | ||
1454 | if (trips > 0 && (!ops->get_trip_type || !ops->get_trip_temp)) | 1465 | if (trips > 0 && (!ops->get_trip_type || !ops->get_trip_temp)) |
1455 | return ERR_PTR(-EINVAL); | 1466 | return ERR_PTR(-EINVAL); |
1456 | 1467 | ||
1457 | tz = kzalloc(sizeof(struct thermal_zone_device), GFP_KERNEL); | 1468 | tz = kzalloc(sizeof(struct thermal_zone_device), GFP_KERNEL); |
1458 | if (!tz) | 1469 | if (!tz) |
1459 | return ERR_PTR(-ENOMEM); | 1470 | return ERR_PTR(-ENOMEM); |
1460 | 1471 | ||
1461 | INIT_LIST_HEAD(&tz->thermal_instances); | 1472 | INIT_LIST_HEAD(&tz->thermal_instances); |
1462 | idr_init(&tz->idr); | 1473 | idr_init(&tz->idr); |
1463 | mutex_init(&tz->lock); | 1474 | mutex_init(&tz->lock); |
1464 | result = get_idr(&thermal_tz_idr, &thermal_idr_lock, &tz->id); | 1475 | result = get_idr(&thermal_tz_idr, &thermal_idr_lock, &tz->id); |
1465 | if (result) { | 1476 | if (result) { |
1466 | kfree(tz); | 1477 | kfree(tz); |
1467 | return ERR_PTR(result); | 1478 | return ERR_PTR(result); |
1468 | } | 1479 | } |
1469 | 1480 | ||
1470 | strlcpy(tz->type, type ? : "", sizeof(tz->type)); | 1481 | strlcpy(tz->type, type ? : "", sizeof(tz->type)); |
1471 | tz->ops = ops; | 1482 | tz->ops = ops; |
1472 | tz->tzp = tzp; | 1483 | tz->tzp = tzp; |
1473 | tz->device.class = &thermal_class; | 1484 | tz->device.class = &thermal_class; |
1474 | tz->devdata = devdata; | 1485 | tz->devdata = devdata; |
1475 | tz->trips = trips; | 1486 | tz->trips = trips; |
1476 | tz->passive_delay = passive_delay; | 1487 | tz->passive_delay = passive_delay; |
1477 | tz->polling_delay = polling_delay; | 1488 | tz->polling_delay = polling_delay; |
1478 | 1489 | ||
1479 | dev_set_name(&tz->device, "thermal_zone%d", tz->id); | 1490 | dev_set_name(&tz->device, "thermal_zone%d", tz->id); |
1480 | result = device_register(&tz->device); | 1491 | result = device_register(&tz->device); |
1481 | if (result) { | 1492 | if (result) { |
1482 | release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id); | 1493 | release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id); |
1483 | kfree(tz); | 1494 | kfree(tz); |
1484 | return ERR_PTR(result); | 1495 | return ERR_PTR(result); |
1485 | } | 1496 | } |
1486 | 1497 | ||
1487 | /* sys I/F */ | 1498 | /* sys I/F */ |
1488 | if (type) { | 1499 | if (type) { |
1489 | result = device_create_file(&tz->device, &dev_attr_type); | 1500 | result = device_create_file(&tz->device, &dev_attr_type); |
1490 | if (result) | 1501 | if (result) |
1491 | goto unregister; | 1502 | goto unregister; |
1492 | } | 1503 | } |
1493 | 1504 | ||
1494 | result = device_create_file(&tz->device, &dev_attr_temp); | 1505 | result = device_create_file(&tz->device, &dev_attr_temp); |
1495 | if (result) | 1506 | if (result) |
1496 | goto unregister; | 1507 | goto unregister; |
1497 | 1508 | ||
1498 | if (ops->get_mode) { | 1509 | if (ops->get_mode) { |
1499 | result = device_create_file(&tz->device, &dev_attr_mode); | 1510 | result = device_create_file(&tz->device, &dev_attr_mode); |
1500 | if (result) | 1511 | if (result) |
1501 | goto unregister; | 1512 | goto unregister; |
1502 | } | 1513 | } |
1503 | 1514 | ||
1504 | result = create_trip_attrs(tz, mask); | 1515 | result = create_trip_attrs(tz, mask); |
1505 | if (result) | 1516 | if (result) |
1506 | goto unregister; | 1517 | goto unregister; |
1507 | 1518 | ||
1508 | for (count = 0; count < trips; count++) { | 1519 | for (count = 0; count < trips; count++) { |
1509 | tz->ops->get_trip_type(tz, count, &trip_type); | 1520 | tz->ops->get_trip_type(tz, count, &trip_type); |
1510 | if (trip_type == THERMAL_TRIP_PASSIVE) | 1521 | if (trip_type == THERMAL_TRIP_PASSIVE) |
1511 | passive = 1; | 1522 | passive = 1; |
1512 | } | 1523 | } |
1513 | 1524 | ||
1514 | if (!passive) { | 1525 | if (!passive) { |
1515 | result = device_create_file(&tz->device, &dev_attr_passive); | 1526 | result = device_create_file(&tz->device, &dev_attr_passive); |
1516 | if (result) | 1527 | if (result) |
1517 | goto unregister; | 1528 | goto unregister; |
1518 | } | 1529 | } |
1519 | 1530 | ||
1520 | #ifdef CONFIG_THERMAL_EMULATION | 1531 | #ifdef CONFIG_THERMAL_EMULATION |
1521 | result = device_create_file(&tz->device, &dev_attr_emul_temp); | 1532 | result = device_create_file(&tz->device, &dev_attr_emul_temp); |
1522 | if (result) | 1533 | if (result) |
1523 | goto unregister; | 1534 | goto unregister; |
1524 | #endif | 1535 | #endif |
1525 | /* Create policy attribute */ | 1536 | /* Create policy attribute */ |
1526 | result = device_create_file(&tz->device, &dev_attr_policy); | 1537 | result = device_create_file(&tz->device, &dev_attr_policy); |
1527 | if (result) | 1538 | if (result) |
1528 | goto unregister; | 1539 | goto unregister; |
1529 | 1540 | ||
1530 | /* Update 'this' zone's governor information */ | 1541 | /* Update 'this' zone's governor information */ |
1531 | mutex_lock(&thermal_governor_lock); | 1542 | mutex_lock(&thermal_governor_lock); |
1532 | 1543 | ||
1533 | if (tz->tzp) | 1544 | if (tz->tzp) |
1534 | tz->governor = __find_governor(tz->tzp->governor_name); | 1545 | tz->governor = __find_governor(tz->tzp->governor_name); |
1535 | else | 1546 | else |
1536 | tz->governor = __find_governor(DEFAULT_THERMAL_GOVERNOR); | 1547 | tz->governor = def_governor; |
1537 | 1548 | ||
1538 | mutex_unlock(&thermal_governor_lock); | 1549 | mutex_unlock(&thermal_governor_lock); |
1539 | 1550 | ||
1540 | if (!tz->tzp || !tz->tzp->no_hwmon) { | 1551 | if (!tz->tzp || !tz->tzp->no_hwmon) { |
1541 | result = thermal_add_hwmon_sysfs(tz); | 1552 | result = thermal_add_hwmon_sysfs(tz); |
1542 | if (result) | 1553 | if (result) |
1543 | goto unregister; | 1554 | goto unregister; |
1544 | } | 1555 | } |
1545 | 1556 | ||
1546 | mutex_lock(&thermal_list_lock); | 1557 | mutex_lock(&thermal_list_lock); |
1547 | list_add_tail(&tz->node, &thermal_tz_list); | 1558 | list_add_tail(&tz->node, &thermal_tz_list); |
1548 | mutex_unlock(&thermal_list_lock); | 1559 | mutex_unlock(&thermal_list_lock); |
1549 | 1560 | ||
1550 | /* Bind cooling devices for this zone */ | 1561 | /* Bind cooling devices for this zone */ |
1551 | bind_tz(tz); | 1562 | bind_tz(tz); |
1552 | 1563 | ||
1553 | INIT_DELAYED_WORK(&(tz->poll_queue), thermal_zone_device_check); | 1564 | INIT_DELAYED_WORK(&(tz->poll_queue), thermal_zone_device_check); |
1554 | 1565 | ||
1555 | if (!tz->ops->get_temp) | 1566 | if (!tz->ops->get_temp) |
1556 | thermal_zone_device_set_polling(tz, 0); | 1567 | thermal_zone_device_set_polling(tz, 0); |
1557 | 1568 | ||
1558 | thermal_zone_device_update(tz); | 1569 | thermal_zone_device_update(tz); |
1559 | 1570 | ||
1560 | if (!result) | 1571 | if (!result) |
1561 | return tz; | 1572 | return tz; |
1562 | 1573 | ||
1563 | unregister: | 1574 | unregister: |
1564 | release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id); | 1575 | release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id); |
1565 | device_unregister(&tz->device); | 1576 | device_unregister(&tz->device); |
1566 | return ERR_PTR(result); | 1577 | return ERR_PTR(result); |
1567 | } | 1578 | } |
1568 | EXPORT_SYMBOL_GPL(thermal_zone_device_register); | 1579 | EXPORT_SYMBOL_GPL(thermal_zone_device_register); |
1569 | 1580 | ||
1570 | /** | 1581 | /** |
1571 | * thermal_device_unregister - removes the registered thermal zone device | 1582 | * thermal_device_unregister - removes the registered thermal zone device |
1572 | * @tz: the thermal zone device to remove | 1583 | * @tz: the thermal zone device to remove |
1573 | */ | 1584 | */ |
1574 | void thermal_zone_device_unregister(struct thermal_zone_device *tz) | 1585 | void thermal_zone_device_unregister(struct thermal_zone_device *tz) |
1575 | { | 1586 | { |
1576 | int i; | 1587 | int i; |
1577 | const struct thermal_zone_params *tzp; | 1588 | const struct thermal_zone_params *tzp; |
1578 | struct thermal_cooling_device *cdev; | 1589 | struct thermal_cooling_device *cdev; |
1579 | struct thermal_zone_device *pos = NULL; | 1590 | struct thermal_zone_device *pos = NULL; |
1580 | 1591 | ||
1581 | if (!tz) | 1592 | if (!tz) |
1582 | return; | 1593 | return; |
1583 | 1594 | ||
1584 | tzp = tz->tzp; | 1595 | tzp = tz->tzp; |
1585 | 1596 | ||
1586 | mutex_lock(&thermal_list_lock); | 1597 | mutex_lock(&thermal_list_lock); |
1587 | list_for_each_entry(pos, &thermal_tz_list, node) | 1598 | list_for_each_entry(pos, &thermal_tz_list, node) |
1588 | if (pos == tz) | 1599 | if (pos == tz) |
1589 | break; | 1600 | break; |
1590 | if (pos != tz) { | 1601 | if (pos != tz) { |
1591 | /* thermal zone device not found */ | 1602 | /* thermal zone device not found */ |
1592 | mutex_unlock(&thermal_list_lock); | 1603 | mutex_unlock(&thermal_list_lock); |
1593 | return; | 1604 | return; |
1594 | } | 1605 | } |
1595 | list_del(&tz->node); | 1606 | list_del(&tz->node); |
1596 | 1607 | ||
1597 | /* Unbind all cdevs associated with 'this' thermal zone */ | 1608 | /* Unbind all cdevs associated with 'this' thermal zone */ |
1598 | list_for_each_entry(cdev, &thermal_cdev_list, node) { | 1609 | list_for_each_entry(cdev, &thermal_cdev_list, node) { |
1599 | if (tz->ops->unbind) { | 1610 | if (tz->ops->unbind) { |
1600 | tz->ops->unbind(tz, cdev); | 1611 | tz->ops->unbind(tz, cdev); |
1601 | continue; | 1612 | continue; |
1602 | } | 1613 | } |
1603 | 1614 | ||
1604 | if (!tzp || !tzp->tbp) | 1615 | if (!tzp || !tzp->tbp) |
1605 | break; | 1616 | break; |
1606 | 1617 | ||
1607 | for (i = 0; i < tzp->num_tbps; i++) { | 1618 | for (i = 0; i < tzp->num_tbps; i++) { |
1608 | if (tzp->tbp[i].cdev == cdev) { | 1619 | if (tzp->tbp[i].cdev == cdev) { |
1609 | __unbind(tz, tzp->tbp[i].trip_mask, cdev); | 1620 | __unbind(tz, tzp->tbp[i].trip_mask, cdev); |
1610 | tzp->tbp[i].cdev = NULL; | 1621 | tzp->tbp[i].cdev = NULL; |
1611 | } | 1622 | } |
1612 | } | 1623 | } |
1613 | } | 1624 | } |
1614 | 1625 | ||
1615 | mutex_unlock(&thermal_list_lock); | 1626 | mutex_unlock(&thermal_list_lock); |
1616 | 1627 | ||
1617 | thermal_zone_device_set_polling(tz, 0); | 1628 | thermal_zone_device_set_polling(tz, 0); |
1618 | 1629 | ||
1619 | if (tz->type[0]) | 1630 | if (tz->type[0]) |
1620 | device_remove_file(&tz->device, &dev_attr_type); | 1631 | device_remove_file(&tz->device, &dev_attr_type); |
1621 | device_remove_file(&tz->device, &dev_attr_temp); | 1632 | device_remove_file(&tz->device, &dev_attr_temp); |
1622 | if (tz->ops->get_mode) | 1633 | if (tz->ops->get_mode) |
1623 | device_remove_file(&tz->device, &dev_attr_mode); | 1634 | device_remove_file(&tz->device, &dev_attr_mode); |
1624 | device_remove_file(&tz->device, &dev_attr_policy); | 1635 | device_remove_file(&tz->device, &dev_attr_policy); |
1625 | remove_trip_attrs(tz); | 1636 | remove_trip_attrs(tz); |
1626 | tz->governor = NULL; | 1637 | tz->governor = NULL; |
1627 | 1638 | ||
1628 | thermal_remove_hwmon_sysfs(tz); | 1639 | thermal_remove_hwmon_sysfs(tz); |
1629 | release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id); | 1640 | release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id); |
1630 | idr_destroy(&tz->idr); | 1641 | idr_destroy(&tz->idr); |
1631 | mutex_destroy(&tz->lock); | 1642 | mutex_destroy(&tz->lock); |
1632 | device_unregister(&tz->device); | 1643 | device_unregister(&tz->device); |
1633 | return; | 1644 | return; |
1634 | } | 1645 | } |
1635 | EXPORT_SYMBOL_GPL(thermal_zone_device_unregister); | 1646 | EXPORT_SYMBOL_GPL(thermal_zone_device_unregister); |
1636 | 1647 | ||
1637 | /** | 1648 | /** |
1638 | * thermal_zone_get_zone_by_name() - search for a zone and returns its ref | 1649 | * thermal_zone_get_zone_by_name() - search for a zone and returns its ref |
1639 | * @name: thermal zone name to fetch the temperature | 1650 | * @name: thermal zone name to fetch the temperature |
1640 | * | 1651 | * |
1641 | * When only one zone is found with the passed name, returns a reference to it. | 1652 | * When only one zone is found with the passed name, returns a reference to it. |
1642 | * | 1653 | * |
1643 | * Return: On success returns a reference to an unique thermal zone with | 1654 | * Return: On success returns a reference to an unique thermal zone with |
1644 | * matching name equals to @name, an ERR_PTR otherwise (-EINVAL for invalid | 1655 | * matching name equals to @name, an ERR_PTR otherwise (-EINVAL for invalid |
1645 | * paramenters, -ENODEV for not found and -EEXIST for multiple matches). | 1656 | * paramenters, -ENODEV for not found and -EEXIST for multiple matches). |
1646 | */ | 1657 | */ |
1647 | struct thermal_zone_device *thermal_zone_get_zone_by_name(const char *name) | 1658 | struct thermal_zone_device *thermal_zone_get_zone_by_name(const char *name) |
1648 | { | 1659 | { |
1649 | struct thermal_zone_device *pos = NULL, *ref = ERR_PTR(-EINVAL); | 1660 | struct thermal_zone_device *pos = NULL, *ref = ERR_PTR(-EINVAL); |
1650 | unsigned int found = 0; | 1661 | unsigned int found = 0; |
1651 | 1662 | ||
1652 | if (!name) | 1663 | if (!name) |
1653 | goto exit; | 1664 | goto exit; |
1654 | 1665 | ||
1655 | mutex_lock(&thermal_list_lock); | 1666 | mutex_lock(&thermal_list_lock); |
1656 | list_for_each_entry(pos, &thermal_tz_list, node) | 1667 | list_for_each_entry(pos, &thermal_tz_list, node) |
1657 | if (!strnicmp(name, pos->type, THERMAL_NAME_LENGTH)) { | 1668 | if (!strnicmp(name, pos->type, THERMAL_NAME_LENGTH)) { |
1658 | found++; | 1669 | found++; |
1659 | ref = pos; | 1670 | ref = pos; |
1660 | } | 1671 | } |
1661 | mutex_unlock(&thermal_list_lock); | 1672 | mutex_unlock(&thermal_list_lock); |
1662 | 1673 | ||
1663 | /* nothing has been found, thus an error code for it */ | 1674 | /* nothing has been found, thus an error code for it */ |
1664 | if (found == 0) | 1675 | if (found == 0) |
1665 | ref = ERR_PTR(-ENODEV); | 1676 | ref = ERR_PTR(-ENODEV); |
1666 | else if (found > 1) | 1677 | else if (found > 1) |
1667 | /* Success only when an unique zone is found */ | 1678 | /* Success only when an unique zone is found */ |
1668 | ref = ERR_PTR(-EEXIST); | 1679 | ref = ERR_PTR(-EEXIST); |
1669 | 1680 | ||
1670 | exit: | 1681 | exit: |
1671 | return ref; | 1682 | return ref; |
1672 | } | 1683 | } |
1673 | EXPORT_SYMBOL_GPL(thermal_zone_get_zone_by_name); | 1684 | EXPORT_SYMBOL_GPL(thermal_zone_get_zone_by_name); |
1674 | 1685 | ||
1675 | #ifdef CONFIG_NET | 1686 | #ifdef CONFIG_NET |
1676 | static const struct genl_multicast_group thermal_event_mcgrps[] = { | 1687 | static const struct genl_multicast_group thermal_event_mcgrps[] = { |
1677 | { .name = THERMAL_GENL_MCAST_GROUP_NAME, }, | 1688 | { .name = THERMAL_GENL_MCAST_GROUP_NAME, }, |
1678 | }; | 1689 | }; |
1679 | 1690 | ||
1680 | static struct genl_family thermal_event_genl_family = { | 1691 | static struct genl_family thermal_event_genl_family = { |
1681 | .id = GENL_ID_GENERATE, | 1692 | .id = GENL_ID_GENERATE, |
1682 | .name = THERMAL_GENL_FAMILY_NAME, | 1693 | .name = THERMAL_GENL_FAMILY_NAME, |
1683 | .version = THERMAL_GENL_VERSION, | 1694 | .version = THERMAL_GENL_VERSION, |
1684 | .maxattr = THERMAL_GENL_ATTR_MAX, | 1695 | .maxattr = THERMAL_GENL_ATTR_MAX, |
1685 | .mcgrps = thermal_event_mcgrps, | 1696 | .mcgrps = thermal_event_mcgrps, |
1686 | .n_mcgrps = ARRAY_SIZE(thermal_event_mcgrps), | 1697 | .n_mcgrps = ARRAY_SIZE(thermal_event_mcgrps), |
1687 | }; | 1698 | }; |
1688 | 1699 | ||
1689 | int thermal_generate_netlink_event(struct thermal_zone_device *tz, | 1700 | int thermal_generate_netlink_event(struct thermal_zone_device *tz, |
1690 | enum events event) | 1701 | enum events event) |
1691 | { | 1702 | { |
1692 | struct sk_buff *skb; | 1703 | struct sk_buff *skb; |
1693 | struct nlattr *attr; | 1704 | struct nlattr *attr; |
1694 | struct thermal_genl_event *thermal_event; | 1705 | struct thermal_genl_event *thermal_event; |
1695 | void *msg_header; | 1706 | void *msg_header; |
1696 | int size; | 1707 | int size; |
1697 | int result; | 1708 | int result; |
1698 | static unsigned int thermal_event_seqnum; | 1709 | static unsigned int thermal_event_seqnum; |
1699 | 1710 | ||
1700 | if (!tz) | 1711 | if (!tz) |
1701 | return -EINVAL; | 1712 | return -EINVAL; |
1702 | 1713 | ||
1703 | /* allocate memory */ | 1714 | /* allocate memory */ |
1704 | size = nla_total_size(sizeof(struct thermal_genl_event)) + | 1715 | size = nla_total_size(sizeof(struct thermal_genl_event)) + |
1705 | nla_total_size(0); | 1716 | nla_total_size(0); |
1706 | 1717 | ||
1707 | skb = genlmsg_new(size, GFP_ATOMIC); | 1718 | skb = genlmsg_new(size, GFP_ATOMIC); |
1708 | if (!skb) | 1719 | if (!skb) |
1709 | return -ENOMEM; | 1720 | return -ENOMEM; |
1710 | 1721 | ||
1711 | /* add the genetlink message header */ | 1722 | /* add the genetlink message header */ |
1712 | msg_header = genlmsg_put(skb, 0, thermal_event_seqnum++, | 1723 | msg_header = genlmsg_put(skb, 0, thermal_event_seqnum++, |
1713 | &thermal_event_genl_family, 0, | 1724 | &thermal_event_genl_family, 0, |
1714 | THERMAL_GENL_CMD_EVENT); | 1725 | THERMAL_GENL_CMD_EVENT); |
1715 | if (!msg_header) { | 1726 | if (!msg_header) { |
1716 | nlmsg_free(skb); | 1727 | nlmsg_free(skb); |
1717 | return -ENOMEM; | 1728 | return -ENOMEM; |
1718 | } | 1729 | } |
1719 | 1730 | ||
1720 | /* fill the data */ | 1731 | /* fill the data */ |
1721 | attr = nla_reserve(skb, THERMAL_GENL_ATTR_EVENT, | 1732 | attr = nla_reserve(skb, THERMAL_GENL_ATTR_EVENT, |
1722 | sizeof(struct thermal_genl_event)); | 1733 | sizeof(struct thermal_genl_event)); |
1723 | 1734 | ||
1724 | if (!attr) { | 1735 | if (!attr) { |
1725 | nlmsg_free(skb); | 1736 | nlmsg_free(skb); |
1726 | return -EINVAL; | 1737 | return -EINVAL; |
1727 | } | 1738 | } |
1728 | 1739 | ||
1729 | thermal_event = nla_data(attr); | 1740 | thermal_event = nla_data(attr); |
1730 | if (!thermal_event) { | 1741 | if (!thermal_event) { |
1731 | nlmsg_free(skb); | 1742 | nlmsg_free(skb); |
1732 | return -EINVAL; | 1743 | return -EINVAL; |
1733 | } | 1744 | } |
1734 | 1745 | ||
1735 | memset(thermal_event, 0, sizeof(struct thermal_genl_event)); | 1746 | memset(thermal_event, 0, sizeof(struct thermal_genl_event)); |
1736 | 1747 | ||
1737 | thermal_event->orig = tz->id; | 1748 | thermal_event->orig = tz->id; |
1738 | thermal_event->event = event; | 1749 | thermal_event->event = event; |
1739 | 1750 | ||
1740 | /* send multicast genetlink message */ | 1751 | /* send multicast genetlink message */ |
1741 | result = genlmsg_end(skb, msg_header); | 1752 | result = genlmsg_end(skb, msg_header); |
1742 | if (result < 0) { | 1753 | if (result < 0) { |
1743 | nlmsg_free(skb); | 1754 | nlmsg_free(skb); |
1744 | return result; | 1755 | return result; |
1745 | } | 1756 | } |
1746 | 1757 | ||
1747 | result = genlmsg_multicast(&thermal_event_genl_family, skb, 0, | 1758 | result = genlmsg_multicast(&thermal_event_genl_family, skb, 0, |
1748 | 0, GFP_ATOMIC); | 1759 | 0, GFP_ATOMIC); |
1749 | if (result) | 1760 | if (result) |
1750 | dev_err(&tz->device, "Failed to send netlink event:%d", result); | 1761 | dev_err(&tz->device, "Failed to send netlink event:%d", result); |
1751 | 1762 | ||
1752 | return result; | 1763 | return result; |
1753 | } | 1764 | } |
1754 | EXPORT_SYMBOL_GPL(thermal_generate_netlink_event); | 1765 | EXPORT_SYMBOL_GPL(thermal_generate_netlink_event); |
1755 | 1766 | ||
1756 | static int genetlink_init(void) | 1767 | static int genetlink_init(void) |
1757 | { | 1768 | { |
1758 | return genl_register_family(&thermal_event_genl_family); | 1769 | return genl_register_family(&thermal_event_genl_family); |
1759 | } | 1770 | } |
1760 | 1771 | ||
1761 | static void genetlink_exit(void) | 1772 | static void genetlink_exit(void) |
1762 | { | 1773 | { |
1763 | genl_unregister_family(&thermal_event_genl_family); | 1774 | genl_unregister_family(&thermal_event_genl_family); |
1764 | } | 1775 | } |
1765 | #else /* !CONFIG_NET */ | 1776 | #else /* !CONFIG_NET */ |
1766 | static inline int genetlink_init(void) { return 0; } | 1777 | static inline int genetlink_init(void) { return 0; } |
1767 | static inline void genetlink_exit(void) {} | 1778 | static inline void genetlink_exit(void) {} |
1768 | #endif /* !CONFIG_NET */ | 1779 | #endif /* !CONFIG_NET */ |
1769 | 1780 | ||
1770 | static int __init thermal_register_governors(void) | 1781 | static int __init thermal_register_governors(void) |
1771 | { | 1782 | { |
1772 | int result; | 1783 | int result; |
1773 | 1784 | ||
1774 | result = thermal_gov_step_wise_register(); | 1785 | result = thermal_gov_step_wise_register(); |
1775 | if (result) | 1786 | if (result) |
1776 | return result; | 1787 | return result; |
1777 | 1788 | ||
1778 | result = thermal_gov_fair_share_register(); | 1789 | result = thermal_gov_fair_share_register(); |
1779 | if (result) | 1790 | if (result) |
1780 | return result; | 1791 | return result; |
1781 | 1792 | ||
1782 | return thermal_gov_user_space_register(); | 1793 | return thermal_gov_user_space_register(); |
1783 | } | 1794 | } |
1784 | 1795 | ||
1785 | static void thermal_unregister_governors(void) | 1796 | static void thermal_unregister_governors(void) |
1786 | { | 1797 | { |
1787 | thermal_gov_step_wise_unregister(); | 1798 | thermal_gov_step_wise_unregister(); |
1788 | thermal_gov_fair_share_unregister(); | 1799 | thermal_gov_fair_share_unregister(); |
1789 | thermal_gov_user_space_unregister(); | 1800 | thermal_gov_user_space_unregister(); |
1790 | } | 1801 | } |
1791 | 1802 | ||
1792 | static int __init thermal_init(void) | 1803 | static int __init thermal_init(void) |
1793 | { | 1804 | { |
1794 | int result; | 1805 | int result; |
1795 | 1806 | ||
1796 | result = thermal_register_governors(); | 1807 | result = thermal_register_governors(); |
1797 | if (result) | 1808 | if (result) |
1798 | goto error; | 1809 | goto error; |
1799 | 1810 | ||
1800 | result = class_register(&thermal_class); | 1811 | result = class_register(&thermal_class); |
1801 | if (result) | 1812 | if (result) |
1802 | goto unregister_governors; | 1813 | goto unregister_governors; |
1803 | 1814 | ||
1804 | result = genetlink_init(); | 1815 | result = genetlink_init(); |
1805 | if (result) | 1816 | if (result) |
1806 | goto unregister_class; | 1817 | goto unregister_class; |
1807 | 1818 | ||
1808 | result = of_parse_thermal_zones(); | 1819 | result = of_parse_thermal_zones(); |
1809 | if (result) | 1820 | if (result) |
1810 | goto exit_netlink; | 1821 | goto exit_netlink; |
1811 | 1822 | ||
1812 | return 0; | 1823 | return 0; |
1813 | 1824 | ||
1814 | exit_netlink: | 1825 | exit_netlink: |
1815 | genetlink_exit(); | 1826 | genetlink_exit(); |
1816 | unregister_governors: | 1827 | unregister_governors: |
1817 | thermal_unregister_governors(); | 1828 | thermal_unregister_governors(); |
1818 | unregister_class: | 1829 | unregister_class: |
1819 | class_unregister(&thermal_class); | 1830 | class_unregister(&thermal_class); |
1820 | error: | 1831 | error: |
1821 | idr_destroy(&thermal_tz_idr); | 1832 | idr_destroy(&thermal_tz_idr); |
1822 | idr_destroy(&thermal_cdev_idr); | 1833 | idr_destroy(&thermal_cdev_idr); |
1823 | mutex_destroy(&thermal_idr_lock); | 1834 | mutex_destroy(&thermal_idr_lock); |
1824 | mutex_destroy(&thermal_list_lock); | 1835 | mutex_destroy(&thermal_list_lock); |
1825 | mutex_destroy(&thermal_governor_lock); | 1836 | mutex_destroy(&thermal_governor_lock); |
1826 | return result; | 1837 | return result; |
1827 | } | 1838 | } |
1828 | 1839 | ||
1829 | static void __exit thermal_exit(void) | 1840 | static void __exit thermal_exit(void) |
1830 | { | 1841 | { |
1831 | of_thermal_destroy_zones(); | 1842 | of_thermal_destroy_zones(); |
1832 | genetlink_exit(); | 1843 | genetlink_exit(); |
1833 | class_unregister(&thermal_class); | 1844 | class_unregister(&thermal_class); |
1834 | thermal_unregister_governors(); | 1845 | thermal_unregister_governors(); |
1835 | idr_destroy(&thermal_tz_idr); | 1846 | idr_destroy(&thermal_tz_idr); |
1836 | idr_destroy(&thermal_cdev_idr); | 1847 | idr_destroy(&thermal_cdev_idr); |
1837 | mutex_destroy(&thermal_idr_lock); | 1848 | mutex_destroy(&thermal_idr_lock); |
1838 | mutex_destroy(&thermal_list_lock); | 1849 | mutex_destroy(&thermal_list_lock); |
1839 | mutex_destroy(&thermal_governor_lock); | 1850 | mutex_destroy(&thermal_governor_lock); |
1840 | } | 1851 | } |
1841 | 1852 | ||
1842 | fs_initcall(thermal_init); | 1853 | fs_initcall(thermal_init); |
1843 | module_exit(thermal_exit); | 1854 | module_exit(thermal_exit); |
drivers/thermal/x86_pkg_temp_thermal.c
1 | /* | 1 | /* |
2 | * x86_pkg_temp_thermal driver | 2 | * x86_pkg_temp_thermal driver |
3 | * Copyright (c) 2013, Intel Corporation. | 3 | * Copyright (c) 2013, Intel Corporation. |
4 | * | 4 | * |
5 | * This program is free software; you can redistribute it and/or modify it | 5 | * This program is free software; you can redistribute it and/or modify it |
6 | * under the terms and conditions of the GNU General Public License, | 6 | * under the terms and conditions of the GNU General Public License, |
7 | * version 2, as published by the Free Software Foundation. | 7 | * version 2, as published by the Free Software Foundation. |
8 | * | 8 | * |
9 | * This program is distributed in the hope it will be useful, but WITHOUT | 9 | * This program is distributed in the hope it will be useful, but WITHOUT |
10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
12 | * more details. | 12 | * more details. |
13 | * | 13 | * |
14 | * You should have received a copy of the GNU General Public License along with | 14 | * You should have received a copy of the GNU General Public License along with |
15 | * this program; if not, write to the Free Software Foundation, Inc. | 15 | * this program; if not, write to the Free Software Foundation, Inc. |
16 | * | 16 | * |
17 | */ | 17 | */ |
18 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | 18 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
19 | 19 | ||
20 | #include <linux/module.h> | 20 | #include <linux/module.h> |
21 | #include <linux/init.h> | 21 | #include <linux/init.h> |
22 | #include <linux/err.h> | 22 | #include <linux/err.h> |
23 | #include <linux/param.h> | 23 | #include <linux/param.h> |
24 | #include <linux/device.h> | 24 | #include <linux/device.h> |
25 | #include <linux/platform_device.h> | 25 | #include <linux/platform_device.h> |
26 | #include <linux/cpu.h> | 26 | #include <linux/cpu.h> |
27 | #include <linux/smp.h> | 27 | #include <linux/smp.h> |
28 | #include <linux/slab.h> | 28 | #include <linux/slab.h> |
29 | #include <linux/pm.h> | 29 | #include <linux/pm.h> |
30 | #include <linux/thermal.h> | 30 | #include <linux/thermal.h> |
31 | #include <linux/debugfs.h> | 31 | #include <linux/debugfs.h> |
32 | #include <asm/cpu_device_id.h> | 32 | #include <asm/cpu_device_id.h> |
33 | #include <asm/mce.h> | 33 | #include <asm/mce.h> |
34 | 34 | ||
35 | /* | 35 | /* |
36 | * Rate control delay: Idea is to introduce denounce effect | 36 | * Rate control delay: Idea is to introduce denounce effect |
37 | * This should be long enough to avoid reduce events, when | 37 | * This should be long enough to avoid reduce events, when |
38 | * threshold is set to a temperature, which is constantly | 38 | * threshold is set to a temperature, which is constantly |
39 | * violated, but at the short enough to take any action. | 39 | * violated, but at the short enough to take any action. |
40 | * The action can be remove threshold or change it to next | 40 | * The action can be remove threshold or change it to next |
41 | * interesting setting. Based on experiments, in around | 41 | * interesting setting. Based on experiments, in around |
42 | * every 5 seconds under load will give us a significant | 42 | * every 5 seconds under load will give us a significant |
43 | * temperature change. | 43 | * temperature change. |
44 | */ | 44 | */ |
45 | #define PKG_TEMP_THERMAL_NOTIFY_DELAY 5000 | 45 | #define PKG_TEMP_THERMAL_NOTIFY_DELAY 5000 |
46 | static int notify_delay_ms = PKG_TEMP_THERMAL_NOTIFY_DELAY; | 46 | static int notify_delay_ms = PKG_TEMP_THERMAL_NOTIFY_DELAY; |
47 | module_param(notify_delay_ms, int, 0644); | 47 | module_param(notify_delay_ms, int, 0644); |
48 | MODULE_PARM_DESC(notify_delay_ms, | 48 | MODULE_PARM_DESC(notify_delay_ms, |
49 | "User space notification delay in milli seconds."); | 49 | "User space notification delay in milli seconds."); |
50 | 50 | ||
51 | /* Number of trip points in thermal zone. Currently it can't | 51 | /* Number of trip points in thermal zone. Currently it can't |
52 | * be more than 2. MSR can allow setting and getting notifications | 52 | * be more than 2. MSR can allow setting and getting notifications |
53 | * for only 2 thresholds. This define enforces this, if there | 53 | * for only 2 thresholds. This define enforces this, if there |
54 | * is some wrong values returned by cpuid for number of thresholds. | 54 | * is some wrong values returned by cpuid for number of thresholds. |
55 | */ | 55 | */ |
56 | #define MAX_NUMBER_OF_TRIPS 2 | 56 | #define MAX_NUMBER_OF_TRIPS 2 |
57 | /* Limit number of package temp zones */ | 57 | /* Limit number of package temp zones */ |
58 | #define MAX_PKG_TEMP_ZONE_IDS 256 | 58 | #define MAX_PKG_TEMP_ZONE_IDS 256 |
59 | 59 | ||
60 | struct phy_dev_entry { | 60 | struct phy_dev_entry { |
61 | struct list_head list; | 61 | struct list_head list; |
62 | u16 phys_proc_id; | 62 | u16 phys_proc_id; |
63 | u16 first_cpu; | 63 | u16 first_cpu; |
64 | u32 tj_max; | 64 | u32 tj_max; |
65 | int ref_cnt; | 65 | int ref_cnt; |
66 | u32 start_pkg_therm_low; | 66 | u32 start_pkg_therm_low; |
67 | u32 start_pkg_therm_high; | 67 | u32 start_pkg_therm_high; |
68 | struct thermal_zone_device *tzone; | 68 | struct thermal_zone_device *tzone; |
69 | }; | 69 | }; |
70 | 70 | ||
71 | static const struct thermal_zone_params pkg_temp_tz_params = { | ||
72 | .no_hwmon = true, | ||
73 | }; | ||
74 | |||
71 | /* List maintaining number of package instances */ | 75 | /* List maintaining number of package instances */ |
72 | static LIST_HEAD(phy_dev_list); | 76 | static LIST_HEAD(phy_dev_list); |
73 | static DEFINE_MUTEX(phy_dev_list_mutex); | 77 | static DEFINE_MUTEX(phy_dev_list_mutex); |
74 | 78 | ||
75 | /* Interrupt to work function schedule queue */ | 79 | /* Interrupt to work function schedule queue */ |
76 | static DEFINE_PER_CPU(struct delayed_work, pkg_temp_thermal_threshold_work); | 80 | static DEFINE_PER_CPU(struct delayed_work, pkg_temp_thermal_threshold_work); |
77 | 81 | ||
78 | /* To track if the work is already scheduled on a package */ | 82 | /* To track if the work is already scheduled on a package */ |
79 | static u8 *pkg_work_scheduled; | 83 | static u8 *pkg_work_scheduled; |
80 | 84 | ||
81 | /* Spin lock to prevent races with pkg_work_scheduled */ | 85 | /* Spin lock to prevent races with pkg_work_scheduled */ |
82 | static spinlock_t pkg_work_lock; | 86 | static spinlock_t pkg_work_lock; |
83 | static u16 max_phy_id; | 87 | static u16 max_phy_id; |
84 | 88 | ||
85 | /* Debug counters to show using debugfs */ | 89 | /* Debug counters to show using debugfs */ |
86 | static struct dentry *debugfs; | 90 | static struct dentry *debugfs; |
87 | static unsigned int pkg_interrupt_cnt; | 91 | static unsigned int pkg_interrupt_cnt; |
88 | static unsigned int pkg_work_cnt; | 92 | static unsigned int pkg_work_cnt; |
89 | 93 | ||
90 | static int pkg_temp_debugfs_init(void) | 94 | static int pkg_temp_debugfs_init(void) |
91 | { | 95 | { |
92 | struct dentry *d; | 96 | struct dentry *d; |
93 | 97 | ||
94 | debugfs = debugfs_create_dir("pkg_temp_thermal", NULL); | 98 | debugfs = debugfs_create_dir("pkg_temp_thermal", NULL); |
95 | if (!debugfs) | 99 | if (!debugfs) |
96 | return -ENOENT; | 100 | return -ENOENT; |
97 | 101 | ||
98 | d = debugfs_create_u32("pkg_thres_interrupt", S_IRUGO, debugfs, | 102 | d = debugfs_create_u32("pkg_thres_interrupt", S_IRUGO, debugfs, |
99 | (u32 *)&pkg_interrupt_cnt); | 103 | (u32 *)&pkg_interrupt_cnt); |
100 | if (!d) | 104 | if (!d) |
101 | goto err_out; | 105 | goto err_out; |
102 | 106 | ||
103 | d = debugfs_create_u32("pkg_thres_work", S_IRUGO, debugfs, | 107 | d = debugfs_create_u32("pkg_thres_work", S_IRUGO, debugfs, |
104 | (u32 *)&pkg_work_cnt); | 108 | (u32 *)&pkg_work_cnt); |
105 | if (!d) | 109 | if (!d) |
106 | goto err_out; | 110 | goto err_out; |
107 | 111 | ||
108 | return 0; | 112 | return 0; |
109 | 113 | ||
110 | err_out: | 114 | err_out: |
111 | debugfs_remove_recursive(debugfs); | 115 | debugfs_remove_recursive(debugfs); |
112 | return -ENOENT; | 116 | return -ENOENT; |
113 | } | 117 | } |
114 | 118 | ||
115 | static struct phy_dev_entry | 119 | static struct phy_dev_entry |
116 | *pkg_temp_thermal_get_phy_entry(unsigned int cpu) | 120 | *pkg_temp_thermal_get_phy_entry(unsigned int cpu) |
117 | { | 121 | { |
118 | u16 phys_proc_id = topology_physical_package_id(cpu); | 122 | u16 phys_proc_id = topology_physical_package_id(cpu); |
119 | struct phy_dev_entry *phy_ptr; | 123 | struct phy_dev_entry *phy_ptr; |
120 | 124 | ||
121 | mutex_lock(&phy_dev_list_mutex); | 125 | mutex_lock(&phy_dev_list_mutex); |
122 | 126 | ||
123 | list_for_each_entry(phy_ptr, &phy_dev_list, list) | 127 | list_for_each_entry(phy_ptr, &phy_dev_list, list) |
124 | if (phy_ptr->phys_proc_id == phys_proc_id) { | 128 | if (phy_ptr->phys_proc_id == phys_proc_id) { |
125 | mutex_unlock(&phy_dev_list_mutex); | 129 | mutex_unlock(&phy_dev_list_mutex); |
126 | return phy_ptr; | 130 | return phy_ptr; |
127 | } | 131 | } |
128 | 132 | ||
129 | mutex_unlock(&phy_dev_list_mutex); | 133 | mutex_unlock(&phy_dev_list_mutex); |
130 | 134 | ||
131 | return NULL; | 135 | return NULL; |
132 | } | 136 | } |
133 | 137 | ||
134 | /* | 138 | /* |
135 | * tj-max is is interesting because threshold is set relative to this | 139 | * tj-max is is interesting because threshold is set relative to this |
136 | * temperature. | 140 | * temperature. |
137 | */ | 141 | */ |
138 | static int get_tj_max(int cpu, u32 *tj_max) | 142 | static int get_tj_max(int cpu, u32 *tj_max) |
139 | { | 143 | { |
140 | u32 eax, edx; | 144 | u32 eax, edx; |
141 | u32 val; | 145 | u32 val; |
142 | int err; | 146 | int err; |
143 | 147 | ||
144 | err = rdmsr_safe_on_cpu(cpu, MSR_IA32_TEMPERATURE_TARGET, &eax, &edx); | 148 | err = rdmsr_safe_on_cpu(cpu, MSR_IA32_TEMPERATURE_TARGET, &eax, &edx); |
145 | if (err) | 149 | if (err) |
146 | goto err_ret; | 150 | goto err_ret; |
147 | else { | 151 | else { |
148 | val = (eax >> 16) & 0xff; | 152 | val = (eax >> 16) & 0xff; |
149 | if (val) | 153 | if (val) |
150 | *tj_max = val * 1000; | 154 | *tj_max = val * 1000; |
151 | else { | 155 | else { |
152 | err = -EINVAL; | 156 | err = -EINVAL; |
153 | goto err_ret; | 157 | goto err_ret; |
154 | } | 158 | } |
155 | } | 159 | } |
156 | 160 | ||
157 | return 0; | 161 | return 0; |
158 | err_ret: | 162 | err_ret: |
159 | *tj_max = 0; | 163 | *tj_max = 0; |
160 | return err; | 164 | return err; |
161 | } | 165 | } |
162 | 166 | ||
163 | static int sys_get_curr_temp(struct thermal_zone_device *tzd, unsigned long *temp) | 167 | static int sys_get_curr_temp(struct thermal_zone_device *tzd, unsigned long *temp) |
164 | { | 168 | { |
165 | u32 eax, edx; | 169 | u32 eax, edx; |
166 | struct phy_dev_entry *phy_dev_entry; | 170 | struct phy_dev_entry *phy_dev_entry; |
167 | 171 | ||
168 | phy_dev_entry = tzd->devdata; | 172 | phy_dev_entry = tzd->devdata; |
169 | rdmsr_on_cpu(phy_dev_entry->first_cpu, MSR_IA32_PACKAGE_THERM_STATUS, | 173 | rdmsr_on_cpu(phy_dev_entry->first_cpu, MSR_IA32_PACKAGE_THERM_STATUS, |
170 | &eax, &edx); | 174 | &eax, &edx); |
171 | if (eax & 0x80000000) { | 175 | if (eax & 0x80000000) { |
172 | *temp = phy_dev_entry->tj_max - | 176 | *temp = phy_dev_entry->tj_max - |
173 | ((eax >> 16) & 0x7f) * 1000; | 177 | ((eax >> 16) & 0x7f) * 1000; |
174 | pr_debug("sys_get_curr_temp %ld\n", *temp); | 178 | pr_debug("sys_get_curr_temp %ld\n", *temp); |
175 | return 0; | 179 | return 0; |
176 | } | 180 | } |
177 | 181 | ||
178 | return -EINVAL; | 182 | return -EINVAL; |
179 | } | 183 | } |
180 | 184 | ||
181 | static int sys_get_trip_temp(struct thermal_zone_device *tzd, | 185 | static int sys_get_trip_temp(struct thermal_zone_device *tzd, |
182 | int trip, unsigned long *temp) | 186 | int trip, unsigned long *temp) |
183 | { | 187 | { |
184 | u32 eax, edx; | 188 | u32 eax, edx; |
185 | struct phy_dev_entry *phy_dev_entry; | 189 | struct phy_dev_entry *phy_dev_entry; |
186 | u32 mask, shift; | 190 | u32 mask, shift; |
187 | unsigned long thres_reg_value; | 191 | unsigned long thres_reg_value; |
188 | int ret; | 192 | int ret; |
189 | 193 | ||
190 | if (trip >= MAX_NUMBER_OF_TRIPS) | 194 | if (trip >= MAX_NUMBER_OF_TRIPS) |
191 | return -EINVAL; | 195 | return -EINVAL; |
192 | 196 | ||
193 | phy_dev_entry = tzd->devdata; | 197 | phy_dev_entry = tzd->devdata; |
194 | 198 | ||
195 | if (trip) { | 199 | if (trip) { |
196 | mask = THERM_MASK_THRESHOLD1; | 200 | mask = THERM_MASK_THRESHOLD1; |
197 | shift = THERM_SHIFT_THRESHOLD1; | 201 | shift = THERM_SHIFT_THRESHOLD1; |
198 | } else { | 202 | } else { |
199 | mask = THERM_MASK_THRESHOLD0; | 203 | mask = THERM_MASK_THRESHOLD0; |
200 | shift = THERM_SHIFT_THRESHOLD0; | 204 | shift = THERM_SHIFT_THRESHOLD0; |
201 | } | 205 | } |
202 | 206 | ||
203 | ret = rdmsr_on_cpu(phy_dev_entry->first_cpu, | 207 | ret = rdmsr_on_cpu(phy_dev_entry->first_cpu, |
204 | MSR_IA32_PACKAGE_THERM_INTERRUPT, &eax, &edx); | 208 | MSR_IA32_PACKAGE_THERM_INTERRUPT, &eax, &edx); |
205 | if (ret < 0) | 209 | if (ret < 0) |
206 | return -EINVAL; | 210 | return -EINVAL; |
207 | 211 | ||
208 | thres_reg_value = (eax & mask) >> shift; | 212 | thres_reg_value = (eax & mask) >> shift; |
209 | if (thres_reg_value) | 213 | if (thres_reg_value) |
210 | *temp = phy_dev_entry->tj_max - thres_reg_value * 1000; | 214 | *temp = phy_dev_entry->tj_max - thres_reg_value * 1000; |
211 | else | 215 | else |
212 | *temp = 0; | 216 | *temp = 0; |
213 | pr_debug("sys_get_trip_temp %ld\n", *temp); | 217 | pr_debug("sys_get_trip_temp %ld\n", *temp); |
214 | 218 | ||
215 | return 0; | 219 | return 0; |
216 | } | 220 | } |
217 | 221 | ||
218 | static int sys_set_trip_temp(struct thermal_zone_device *tzd, int trip, | 222 | static int sys_set_trip_temp(struct thermal_zone_device *tzd, int trip, |
219 | unsigned long temp) | 223 | unsigned long temp) |
220 | { | 224 | { |
221 | u32 l, h; | 225 | u32 l, h; |
222 | struct phy_dev_entry *phy_dev_entry; | 226 | struct phy_dev_entry *phy_dev_entry; |
223 | u32 mask, shift, intr; | 227 | u32 mask, shift, intr; |
224 | int ret; | 228 | int ret; |
225 | 229 | ||
226 | phy_dev_entry = tzd->devdata; | 230 | phy_dev_entry = tzd->devdata; |
227 | 231 | ||
228 | if (trip >= MAX_NUMBER_OF_TRIPS || temp >= phy_dev_entry->tj_max) | 232 | if (trip >= MAX_NUMBER_OF_TRIPS || temp >= phy_dev_entry->tj_max) |
229 | return -EINVAL; | 233 | return -EINVAL; |
230 | 234 | ||
231 | ret = rdmsr_on_cpu(phy_dev_entry->first_cpu, | 235 | ret = rdmsr_on_cpu(phy_dev_entry->first_cpu, |
232 | MSR_IA32_PACKAGE_THERM_INTERRUPT, | 236 | MSR_IA32_PACKAGE_THERM_INTERRUPT, |
233 | &l, &h); | 237 | &l, &h); |
234 | if (ret < 0) | 238 | if (ret < 0) |
235 | return -EINVAL; | 239 | return -EINVAL; |
236 | 240 | ||
237 | if (trip) { | 241 | if (trip) { |
238 | mask = THERM_MASK_THRESHOLD1; | 242 | mask = THERM_MASK_THRESHOLD1; |
239 | shift = THERM_SHIFT_THRESHOLD1; | 243 | shift = THERM_SHIFT_THRESHOLD1; |
240 | intr = THERM_INT_THRESHOLD1_ENABLE; | 244 | intr = THERM_INT_THRESHOLD1_ENABLE; |
241 | } else { | 245 | } else { |
242 | mask = THERM_MASK_THRESHOLD0; | 246 | mask = THERM_MASK_THRESHOLD0; |
243 | shift = THERM_SHIFT_THRESHOLD0; | 247 | shift = THERM_SHIFT_THRESHOLD0; |
244 | intr = THERM_INT_THRESHOLD0_ENABLE; | 248 | intr = THERM_INT_THRESHOLD0_ENABLE; |
245 | } | 249 | } |
246 | l &= ~mask; | 250 | l &= ~mask; |
247 | /* | 251 | /* |
248 | * When users space sets a trip temperature == 0, which is indication | 252 | * When users space sets a trip temperature == 0, which is indication |
249 | * that, it is no longer interested in receiving notifications. | 253 | * that, it is no longer interested in receiving notifications. |
250 | */ | 254 | */ |
251 | if (!temp) | 255 | if (!temp) |
252 | l &= ~intr; | 256 | l &= ~intr; |
253 | else { | 257 | else { |
254 | l |= (phy_dev_entry->tj_max - temp)/1000 << shift; | 258 | l |= (phy_dev_entry->tj_max - temp)/1000 << shift; |
255 | l |= intr; | 259 | l |= intr; |
256 | } | 260 | } |
257 | 261 | ||
258 | return wrmsr_on_cpu(phy_dev_entry->first_cpu, | 262 | return wrmsr_on_cpu(phy_dev_entry->first_cpu, |
259 | MSR_IA32_PACKAGE_THERM_INTERRUPT, | 263 | MSR_IA32_PACKAGE_THERM_INTERRUPT, |
260 | l, h); | 264 | l, h); |
261 | } | 265 | } |
262 | 266 | ||
263 | static int sys_get_trip_type(struct thermal_zone_device *thermal, | 267 | static int sys_get_trip_type(struct thermal_zone_device *thermal, |
264 | int trip, enum thermal_trip_type *type) | 268 | int trip, enum thermal_trip_type *type) |
265 | { | 269 | { |
266 | 270 | ||
267 | *type = THERMAL_TRIP_PASSIVE; | 271 | *type = THERMAL_TRIP_PASSIVE; |
268 | 272 | ||
269 | return 0; | 273 | return 0; |
270 | } | 274 | } |
271 | 275 | ||
272 | /* Thermal zone callback registry */ | 276 | /* Thermal zone callback registry */ |
273 | static struct thermal_zone_device_ops tzone_ops = { | 277 | static struct thermal_zone_device_ops tzone_ops = { |
274 | .get_temp = sys_get_curr_temp, | 278 | .get_temp = sys_get_curr_temp, |
275 | .get_trip_temp = sys_get_trip_temp, | 279 | .get_trip_temp = sys_get_trip_temp, |
276 | .get_trip_type = sys_get_trip_type, | 280 | .get_trip_type = sys_get_trip_type, |
277 | .set_trip_temp = sys_set_trip_temp, | 281 | .set_trip_temp = sys_set_trip_temp, |
278 | }; | 282 | }; |
279 | 283 | ||
280 | static bool pkg_temp_thermal_platform_thermal_rate_control(void) | 284 | static bool pkg_temp_thermal_platform_thermal_rate_control(void) |
281 | { | 285 | { |
282 | return true; | 286 | return true; |
283 | } | 287 | } |
284 | 288 | ||
285 | /* Enable threshold interrupt on local package/cpu */ | 289 | /* Enable threshold interrupt on local package/cpu */ |
286 | static inline void enable_pkg_thres_interrupt(void) | 290 | static inline void enable_pkg_thres_interrupt(void) |
287 | { | 291 | { |
288 | u32 l, h; | 292 | u32 l, h; |
289 | u8 thres_0, thres_1; | 293 | u8 thres_0, thres_1; |
290 | 294 | ||
291 | rdmsr(MSR_IA32_PACKAGE_THERM_INTERRUPT, l, h); | 295 | rdmsr(MSR_IA32_PACKAGE_THERM_INTERRUPT, l, h); |
292 | /* only enable/disable if it had valid threshold value */ | 296 | /* only enable/disable if it had valid threshold value */ |
293 | thres_0 = (l & THERM_MASK_THRESHOLD0) >> THERM_SHIFT_THRESHOLD0; | 297 | thres_0 = (l & THERM_MASK_THRESHOLD0) >> THERM_SHIFT_THRESHOLD0; |
294 | thres_1 = (l & THERM_MASK_THRESHOLD1) >> THERM_SHIFT_THRESHOLD1; | 298 | thres_1 = (l & THERM_MASK_THRESHOLD1) >> THERM_SHIFT_THRESHOLD1; |
295 | if (thres_0) | 299 | if (thres_0) |
296 | l |= THERM_INT_THRESHOLD0_ENABLE; | 300 | l |= THERM_INT_THRESHOLD0_ENABLE; |
297 | if (thres_1) | 301 | if (thres_1) |
298 | l |= THERM_INT_THRESHOLD1_ENABLE; | 302 | l |= THERM_INT_THRESHOLD1_ENABLE; |
299 | wrmsr(MSR_IA32_PACKAGE_THERM_INTERRUPT, l, h); | 303 | wrmsr(MSR_IA32_PACKAGE_THERM_INTERRUPT, l, h); |
300 | } | 304 | } |
301 | 305 | ||
302 | /* Disable threshold interrupt on local package/cpu */ | 306 | /* Disable threshold interrupt on local package/cpu */ |
303 | static inline void disable_pkg_thres_interrupt(void) | 307 | static inline void disable_pkg_thres_interrupt(void) |
304 | { | 308 | { |
305 | u32 l, h; | 309 | u32 l, h; |
306 | rdmsr(MSR_IA32_PACKAGE_THERM_INTERRUPT, l, h); | 310 | rdmsr(MSR_IA32_PACKAGE_THERM_INTERRUPT, l, h); |
307 | wrmsr(MSR_IA32_PACKAGE_THERM_INTERRUPT, | 311 | wrmsr(MSR_IA32_PACKAGE_THERM_INTERRUPT, |
308 | l & (~THERM_INT_THRESHOLD0_ENABLE) & | 312 | l & (~THERM_INT_THRESHOLD0_ENABLE) & |
309 | (~THERM_INT_THRESHOLD1_ENABLE), h); | 313 | (~THERM_INT_THRESHOLD1_ENABLE), h); |
310 | } | 314 | } |
311 | 315 | ||
312 | static void pkg_temp_thermal_threshold_work_fn(struct work_struct *work) | 316 | static void pkg_temp_thermal_threshold_work_fn(struct work_struct *work) |
313 | { | 317 | { |
314 | __u64 msr_val; | 318 | __u64 msr_val; |
315 | int cpu = smp_processor_id(); | 319 | int cpu = smp_processor_id(); |
316 | int phy_id = topology_physical_package_id(cpu); | 320 | int phy_id = topology_physical_package_id(cpu); |
317 | struct phy_dev_entry *phdev = pkg_temp_thermal_get_phy_entry(cpu); | 321 | struct phy_dev_entry *phdev = pkg_temp_thermal_get_phy_entry(cpu); |
318 | bool notify = false; | 322 | bool notify = false; |
319 | unsigned long flags; | 323 | unsigned long flags; |
320 | 324 | ||
321 | if (!phdev) | 325 | if (!phdev) |
322 | return; | 326 | return; |
323 | 327 | ||
324 | spin_lock_irqsave(&pkg_work_lock, flags); | 328 | spin_lock_irqsave(&pkg_work_lock, flags); |
325 | ++pkg_work_cnt; | 329 | ++pkg_work_cnt; |
326 | if (unlikely(phy_id > max_phy_id)) { | 330 | if (unlikely(phy_id > max_phy_id)) { |
327 | spin_unlock_irqrestore(&pkg_work_lock, flags); | 331 | spin_unlock_irqrestore(&pkg_work_lock, flags); |
328 | return; | 332 | return; |
329 | } | 333 | } |
330 | pkg_work_scheduled[phy_id] = 0; | 334 | pkg_work_scheduled[phy_id] = 0; |
331 | spin_unlock_irqrestore(&pkg_work_lock, flags); | 335 | spin_unlock_irqrestore(&pkg_work_lock, flags); |
332 | 336 | ||
333 | enable_pkg_thres_interrupt(); | 337 | enable_pkg_thres_interrupt(); |
334 | rdmsrl(MSR_IA32_PACKAGE_THERM_STATUS, msr_val); | 338 | rdmsrl(MSR_IA32_PACKAGE_THERM_STATUS, msr_val); |
335 | if (msr_val & THERM_LOG_THRESHOLD0) { | 339 | if (msr_val & THERM_LOG_THRESHOLD0) { |
336 | wrmsrl(MSR_IA32_PACKAGE_THERM_STATUS, | 340 | wrmsrl(MSR_IA32_PACKAGE_THERM_STATUS, |
337 | msr_val & ~THERM_LOG_THRESHOLD0); | 341 | msr_val & ~THERM_LOG_THRESHOLD0); |
338 | notify = true; | 342 | notify = true; |
339 | } | 343 | } |
340 | if (msr_val & THERM_LOG_THRESHOLD1) { | 344 | if (msr_val & THERM_LOG_THRESHOLD1) { |
341 | wrmsrl(MSR_IA32_PACKAGE_THERM_STATUS, | 345 | wrmsrl(MSR_IA32_PACKAGE_THERM_STATUS, |
342 | msr_val & ~THERM_LOG_THRESHOLD1); | 346 | msr_val & ~THERM_LOG_THRESHOLD1); |
343 | notify = true; | 347 | notify = true; |
344 | } | 348 | } |
345 | if (notify) { | 349 | if (notify) { |
346 | pr_debug("thermal_zone_device_update\n"); | 350 | pr_debug("thermal_zone_device_update\n"); |
347 | thermal_zone_device_update(phdev->tzone); | 351 | thermal_zone_device_update(phdev->tzone); |
348 | } | 352 | } |
349 | } | 353 | } |
350 | 354 | ||
351 | static int pkg_temp_thermal_platform_thermal_notify(__u64 msr_val) | 355 | static int pkg_temp_thermal_platform_thermal_notify(__u64 msr_val) |
352 | { | 356 | { |
353 | unsigned long flags; | 357 | unsigned long flags; |
354 | int cpu = smp_processor_id(); | 358 | int cpu = smp_processor_id(); |
355 | int phy_id = topology_physical_package_id(cpu); | 359 | int phy_id = topology_physical_package_id(cpu); |
356 | 360 | ||
357 | /* | 361 | /* |
358 | * When a package is in interrupted state, all CPU's in that package | 362 | * When a package is in interrupted state, all CPU's in that package |
359 | * are in the same interrupt state. So scheduling on any one CPU in | 363 | * are in the same interrupt state. So scheduling on any one CPU in |
360 | * the package is enough and simply return for others. | 364 | * the package is enough and simply return for others. |
361 | */ | 365 | */ |
362 | spin_lock_irqsave(&pkg_work_lock, flags); | 366 | spin_lock_irqsave(&pkg_work_lock, flags); |
363 | ++pkg_interrupt_cnt; | 367 | ++pkg_interrupt_cnt; |
364 | if (unlikely(phy_id > max_phy_id) || unlikely(!pkg_work_scheduled) || | 368 | if (unlikely(phy_id > max_phy_id) || unlikely(!pkg_work_scheduled) || |
365 | pkg_work_scheduled[phy_id]) { | 369 | pkg_work_scheduled[phy_id]) { |
366 | disable_pkg_thres_interrupt(); | 370 | disable_pkg_thres_interrupt(); |
367 | spin_unlock_irqrestore(&pkg_work_lock, flags); | 371 | spin_unlock_irqrestore(&pkg_work_lock, flags); |
368 | return -EINVAL; | 372 | return -EINVAL; |
369 | } | 373 | } |
370 | pkg_work_scheduled[phy_id] = 1; | 374 | pkg_work_scheduled[phy_id] = 1; |
371 | spin_unlock_irqrestore(&pkg_work_lock, flags); | 375 | spin_unlock_irqrestore(&pkg_work_lock, flags); |
372 | 376 | ||
373 | disable_pkg_thres_interrupt(); | 377 | disable_pkg_thres_interrupt(); |
374 | schedule_delayed_work_on(cpu, | 378 | schedule_delayed_work_on(cpu, |
375 | &per_cpu(pkg_temp_thermal_threshold_work, cpu), | 379 | &per_cpu(pkg_temp_thermal_threshold_work, cpu), |
376 | msecs_to_jiffies(notify_delay_ms)); | 380 | msecs_to_jiffies(notify_delay_ms)); |
377 | return 0; | 381 | return 0; |
378 | } | 382 | } |
379 | 383 | ||
380 | static int find_siblings_cpu(int cpu) | 384 | static int find_siblings_cpu(int cpu) |
381 | { | 385 | { |
382 | int i; | 386 | int i; |
383 | int id = topology_physical_package_id(cpu); | 387 | int id = topology_physical_package_id(cpu); |
384 | 388 | ||
385 | for_each_online_cpu(i) | 389 | for_each_online_cpu(i) |
386 | if (i != cpu && topology_physical_package_id(i) == id) | 390 | if (i != cpu && topology_physical_package_id(i) == id) |
387 | return i; | 391 | return i; |
388 | 392 | ||
389 | return 0; | 393 | return 0; |
390 | } | 394 | } |
391 | 395 | ||
392 | static int pkg_temp_thermal_device_add(unsigned int cpu) | 396 | static int pkg_temp_thermal_device_add(unsigned int cpu) |
393 | { | 397 | { |
394 | int err; | 398 | int err; |
395 | u32 tj_max; | 399 | u32 tj_max; |
396 | struct phy_dev_entry *phy_dev_entry; | 400 | struct phy_dev_entry *phy_dev_entry; |
397 | char buffer[30]; | ||
398 | int thres_count; | 401 | int thres_count; |
399 | u32 eax, ebx, ecx, edx; | 402 | u32 eax, ebx, ecx, edx; |
400 | u8 *temp; | 403 | u8 *temp; |
401 | unsigned long flags; | 404 | unsigned long flags; |
402 | 405 | ||
403 | cpuid(6, &eax, &ebx, &ecx, &edx); | 406 | cpuid(6, &eax, &ebx, &ecx, &edx); |
404 | thres_count = ebx & 0x07; | 407 | thres_count = ebx & 0x07; |
405 | if (!thres_count) | 408 | if (!thres_count) |
406 | return -ENODEV; | 409 | return -ENODEV; |
407 | 410 | ||
408 | if (topology_physical_package_id(cpu) > MAX_PKG_TEMP_ZONE_IDS) | 411 | if (topology_physical_package_id(cpu) > MAX_PKG_TEMP_ZONE_IDS) |
409 | return -ENODEV; | 412 | return -ENODEV; |
410 | 413 | ||
411 | thres_count = clamp_val(thres_count, 0, MAX_NUMBER_OF_TRIPS); | 414 | thres_count = clamp_val(thres_count, 0, MAX_NUMBER_OF_TRIPS); |
412 | 415 | ||
413 | err = get_tj_max(cpu, &tj_max); | 416 | err = get_tj_max(cpu, &tj_max); |
414 | if (err) | 417 | if (err) |
415 | goto err_ret; | 418 | goto err_ret; |
416 | 419 | ||
417 | mutex_lock(&phy_dev_list_mutex); | 420 | mutex_lock(&phy_dev_list_mutex); |
418 | 421 | ||
419 | phy_dev_entry = kzalloc(sizeof(*phy_dev_entry), GFP_KERNEL); | 422 | phy_dev_entry = kzalloc(sizeof(*phy_dev_entry), GFP_KERNEL); |
420 | if (!phy_dev_entry) { | 423 | if (!phy_dev_entry) { |
421 | err = -ENOMEM; | 424 | err = -ENOMEM; |
422 | goto err_ret_unlock; | 425 | goto err_ret_unlock; |
423 | } | 426 | } |
424 | 427 | ||
425 | spin_lock_irqsave(&pkg_work_lock, flags); | 428 | spin_lock_irqsave(&pkg_work_lock, flags); |
426 | if (topology_physical_package_id(cpu) > max_phy_id) | 429 | if (topology_physical_package_id(cpu) > max_phy_id) |
427 | max_phy_id = topology_physical_package_id(cpu); | 430 | max_phy_id = topology_physical_package_id(cpu); |
428 | temp = krealloc(pkg_work_scheduled, | 431 | temp = krealloc(pkg_work_scheduled, |
429 | (max_phy_id+1) * sizeof(u8), GFP_ATOMIC); | 432 | (max_phy_id+1) * sizeof(u8), GFP_ATOMIC); |
430 | if (!temp) { | 433 | if (!temp) { |
431 | spin_unlock_irqrestore(&pkg_work_lock, flags); | 434 | spin_unlock_irqrestore(&pkg_work_lock, flags); |
432 | err = -ENOMEM; | 435 | err = -ENOMEM; |
433 | goto err_ret_free; | 436 | goto err_ret_free; |
434 | } | 437 | } |
435 | pkg_work_scheduled = temp; | 438 | pkg_work_scheduled = temp; |
436 | pkg_work_scheduled[topology_physical_package_id(cpu)] = 0; | 439 | pkg_work_scheduled[topology_physical_package_id(cpu)] = 0; |
437 | spin_unlock_irqrestore(&pkg_work_lock, flags); | 440 | spin_unlock_irqrestore(&pkg_work_lock, flags); |
438 | 441 | ||
439 | phy_dev_entry->phys_proc_id = topology_physical_package_id(cpu); | 442 | phy_dev_entry->phys_proc_id = topology_physical_package_id(cpu); |
440 | phy_dev_entry->first_cpu = cpu; | 443 | phy_dev_entry->first_cpu = cpu; |
441 | phy_dev_entry->tj_max = tj_max; | 444 | phy_dev_entry->tj_max = tj_max; |
442 | phy_dev_entry->ref_cnt = 1; | 445 | phy_dev_entry->ref_cnt = 1; |
443 | snprintf(buffer, sizeof(buffer), "pkg-temp-%d\n", | 446 | phy_dev_entry->tzone = thermal_zone_device_register("x86_pkg_temp", |
444 | phy_dev_entry->phys_proc_id); | ||
445 | phy_dev_entry->tzone = thermal_zone_device_register(buffer, | ||
446 | thres_count, | 447 | thres_count, |
447 | (thres_count == MAX_NUMBER_OF_TRIPS) ? | 448 | (thres_count == MAX_NUMBER_OF_TRIPS) ? |
448 | 0x03 : 0x01, | 449 | 0x03 : 0x01, |
449 | phy_dev_entry, &tzone_ops, NULL, 0, 0); | 450 | phy_dev_entry, &tzone_ops, &pkg_temp_tz_params, 0, 0); |
450 | if (IS_ERR(phy_dev_entry->tzone)) { | 451 | if (IS_ERR(phy_dev_entry->tzone)) { |
451 | err = PTR_ERR(phy_dev_entry->tzone); | 452 | err = PTR_ERR(phy_dev_entry->tzone); |
452 | goto err_ret_free; | 453 | goto err_ret_free; |
453 | } | 454 | } |
454 | /* Store MSR value for package thermal interrupt, to restore at exit */ | 455 | /* Store MSR value for package thermal interrupt, to restore at exit */ |
455 | rdmsr_on_cpu(cpu, MSR_IA32_PACKAGE_THERM_INTERRUPT, | 456 | rdmsr_on_cpu(cpu, MSR_IA32_PACKAGE_THERM_INTERRUPT, |
456 | &phy_dev_entry->start_pkg_therm_low, | 457 | &phy_dev_entry->start_pkg_therm_low, |
457 | &phy_dev_entry->start_pkg_therm_high); | 458 | &phy_dev_entry->start_pkg_therm_high); |
458 | 459 | ||
459 | list_add_tail(&phy_dev_entry->list, &phy_dev_list); | 460 | list_add_tail(&phy_dev_entry->list, &phy_dev_list); |
460 | pr_debug("pkg_temp_thermal_device_add :phy_id %d cpu %d\n", | 461 | pr_debug("pkg_temp_thermal_device_add :phy_id %d cpu %d\n", |
461 | phy_dev_entry->phys_proc_id, cpu); | 462 | phy_dev_entry->phys_proc_id, cpu); |
462 | 463 | ||
463 | mutex_unlock(&phy_dev_list_mutex); | 464 | mutex_unlock(&phy_dev_list_mutex); |
464 | 465 | ||
465 | return 0; | 466 | return 0; |
466 | 467 | ||
467 | err_ret_free: | 468 | err_ret_free: |
468 | kfree(phy_dev_entry); | 469 | kfree(phy_dev_entry); |
469 | err_ret_unlock: | 470 | err_ret_unlock: |
470 | mutex_unlock(&phy_dev_list_mutex); | 471 | mutex_unlock(&phy_dev_list_mutex); |
471 | 472 | ||
472 | err_ret: | 473 | err_ret: |
473 | return err; | 474 | return err; |
474 | } | 475 | } |
475 | 476 | ||
476 | static int pkg_temp_thermal_device_remove(unsigned int cpu) | 477 | static int pkg_temp_thermal_device_remove(unsigned int cpu) |
477 | { | 478 | { |
478 | struct phy_dev_entry *n; | 479 | struct phy_dev_entry *n; |
479 | u16 phys_proc_id = topology_physical_package_id(cpu); | 480 | u16 phys_proc_id = topology_physical_package_id(cpu); |
480 | struct phy_dev_entry *phdev = | 481 | struct phy_dev_entry *phdev = |
481 | pkg_temp_thermal_get_phy_entry(cpu); | 482 | pkg_temp_thermal_get_phy_entry(cpu); |
482 | 483 | ||
483 | if (!phdev) | 484 | if (!phdev) |
484 | return -ENODEV; | 485 | return -ENODEV; |
485 | 486 | ||
486 | mutex_lock(&phy_dev_list_mutex); | 487 | mutex_lock(&phy_dev_list_mutex); |
487 | /* If we are loosing the first cpu for this package, we need change */ | 488 | /* If we are loosing the first cpu for this package, we need change */ |
488 | if (phdev->first_cpu == cpu) { | 489 | if (phdev->first_cpu == cpu) { |
489 | phdev->first_cpu = find_siblings_cpu(cpu); | 490 | phdev->first_cpu = find_siblings_cpu(cpu); |
490 | pr_debug("thermal_device_remove: first cpu switched %d\n", | 491 | pr_debug("thermal_device_remove: first cpu switched %d\n", |
491 | phdev->first_cpu); | 492 | phdev->first_cpu); |
492 | } | 493 | } |
493 | /* | 494 | /* |
494 | * It is possible that no siblings left as this was the last cpu | 495 | * It is possible that no siblings left as this was the last cpu |
495 | * going offline. We don't need to worry about this assignment | 496 | * going offline. We don't need to worry about this assignment |
496 | * as the phydev entry will be removed in this case and | 497 | * as the phydev entry will be removed in this case and |
497 | * thermal zone is removed. | 498 | * thermal zone is removed. |
498 | */ | 499 | */ |
499 | --phdev->ref_cnt; | 500 | --phdev->ref_cnt; |
500 | pr_debug("thermal_device_remove: pkg: %d cpu %d ref_cnt %d\n", | 501 | pr_debug("thermal_device_remove: pkg: %d cpu %d ref_cnt %d\n", |
501 | phys_proc_id, cpu, phdev->ref_cnt); | 502 | phys_proc_id, cpu, phdev->ref_cnt); |
502 | if (!phdev->ref_cnt) | 503 | if (!phdev->ref_cnt) |
503 | list_for_each_entry_safe(phdev, n, &phy_dev_list, list) { | 504 | list_for_each_entry_safe(phdev, n, &phy_dev_list, list) { |
504 | if (phdev->phys_proc_id == phys_proc_id) { | 505 | if (phdev->phys_proc_id == phys_proc_id) { |
505 | thermal_zone_device_unregister(phdev->tzone); | 506 | thermal_zone_device_unregister(phdev->tzone); |
506 | list_del(&phdev->list); | 507 | list_del(&phdev->list); |
507 | kfree(phdev); | 508 | kfree(phdev); |
508 | break; | 509 | break; |
509 | } | 510 | } |
510 | } | 511 | } |
511 | mutex_unlock(&phy_dev_list_mutex); | 512 | mutex_unlock(&phy_dev_list_mutex); |
512 | 513 | ||
513 | return 0; | 514 | return 0; |
514 | } | 515 | } |
515 | 516 | ||
516 | static int get_core_online(unsigned int cpu) | 517 | static int get_core_online(unsigned int cpu) |
517 | { | 518 | { |
518 | struct cpuinfo_x86 *c = &cpu_data(cpu); | 519 | struct cpuinfo_x86 *c = &cpu_data(cpu); |
519 | struct phy_dev_entry *phdev = pkg_temp_thermal_get_phy_entry(cpu); | 520 | struct phy_dev_entry *phdev = pkg_temp_thermal_get_phy_entry(cpu); |
520 | 521 | ||
521 | /* Check if there is already an instance for this package */ | 522 | /* Check if there is already an instance for this package */ |
522 | if (!phdev) { | 523 | if (!phdev) { |
523 | if (!cpu_has(c, X86_FEATURE_DTHERM) || | 524 | if (!cpu_has(c, X86_FEATURE_DTHERM) || |
524 | !cpu_has(c, X86_FEATURE_PTS)) | 525 | !cpu_has(c, X86_FEATURE_PTS)) |
525 | return -ENODEV; | 526 | return -ENODEV; |
526 | if (pkg_temp_thermal_device_add(cpu)) | 527 | if (pkg_temp_thermal_device_add(cpu)) |
527 | return -ENODEV; | 528 | return -ENODEV; |
528 | } else { | 529 | } else { |
529 | mutex_lock(&phy_dev_list_mutex); | 530 | mutex_lock(&phy_dev_list_mutex); |
530 | ++phdev->ref_cnt; | 531 | ++phdev->ref_cnt; |
531 | pr_debug("get_core_online: cpu %d ref_cnt %d\n", | 532 | pr_debug("get_core_online: cpu %d ref_cnt %d\n", |
532 | cpu, phdev->ref_cnt); | 533 | cpu, phdev->ref_cnt); |
533 | mutex_unlock(&phy_dev_list_mutex); | 534 | mutex_unlock(&phy_dev_list_mutex); |
534 | } | 535 | } |
535 | INIT_DELAYED_WORK(&per_cpu(pkg_temp_thermal_threshold_work, cpu), | 536 | INIT_DELAYED_WORK(&per_cpu(pkg_temp_thermal_threshold_work, cpu), |
536 | pkg_temp_thermal_threshold_work_fn); | 537 | pkg_temp_thermal_threshold_work_fn); |
537 | 538 | ||
538 | pr_debug("get_core_online: cpu %d successful\n", cpu); | 539 | pr_debug("get_core_online: cpu %d successful\n", cpu); |
539 | 540 | ||
540 | return 0; | 541 | return 0; |
541 | } | 542 | } |
542 | 543 | ||
543 | static void put_core_offline(unsigned int cpu) | 544 | static void put_core_offline(unsigned int cpu) |
544 | { | 545 | { |
545 | if (!pkg_temp_thermal_device_remove(cpu)) | 546 | if (!pkg_temp_thermal_device_remove(cpu)) |
546 | cancel_delayed_work_sync( | 547 | cancel_delayed_work_sync( |
547 | &per_cpu(pkg_temp_thermal_threshold_work, cpu)); | 548 | &per_cpu(pkg_temp_thermal_threshold_work, cpu)); |
548 | 549 | ||
549 | pr_debug("put_core_offline: cpu %d\n", cpu); | 550 | pr_debug("put_core_offline: cpu %d\n", cpu); |
550 | } | 551 | } |
551 | 552 | ||
552 | static int pkg_temp_thermal_cpu_callback(struct notifier_block *nfb, | 553 | static int pkg_temp_thermal_cpu_callback(struct notifier_block *nfb, |
553 | unsigned long action, void *hcpu) | 554 | unsigned long action, void *hcpu) |
554 | { | 555 | { |
555 | unsigned int cpu = (unsigned long) hcpu; | 556 | unsigned int cpu = (unsigned long) hcpu; |
556 | 557 | ||
557 | switch (action) { | 558 | switch (action) { |
558 | case CPU_ONLINE: | 559 | case CPU_ONLINE: |
559 | case CPU_DOWN_FAILED: | 560 | case CPU_DOWN_FAILED: |
560 | get_core_online(cpu); | 561 | get_core_online(cpu); |
561 | break; | 562 | break; |
562 | case CPU_DOWN_PREPARE: | 563 | case CPU_DOWN_PREPARE: |
563 | put_core_offline(cpu); | 564 | put_core_offline(cpu); |
564 | break; | 565 | break; |
565 | } | 566 | } |
566 | return NOTIFY_OK; | 567 | return NOTIFY_OK; |
567 | } | 568 | } |
568 | 569 | ||
569 | static struct notifier_block pkg_temp_thermal_notifier __refdata = { | 570 | static struct notifier_block pkg_temp_thermal_notifier __refdata = { |
570 | .notifier_call = pkg_temp_thermal_cpu_callback, | 571 | .notifier_call = pkg_temp_thermal_cpu_callback, |
571 | }; | 572 | }; |
572 | 573 | ||
573 | static const struct x86_cpu_id __initconst pkg_temp_thermal_ids[] = { | 574 | static const struct x86_cpu_id __initconst pkg_temp_thermal_ids[] = { |
574 | { X86_VENDOR_INTEL, X86_FAMILY_ANY, X86_MODEL_ANY, X86_FEATURE_PTS }, | 575 | { X86_VENDOR_INTEL, X86_FAMILY_ANY, X86_MODEL_ANY, X86_FEATURE_PTS }, |
575 | {} | 576 | {} |
576 | }; | 577 | }; |
577 | MODULE_DEVICE_TABLE(x86cpu, pkg_temp_thermal_ids); | 578 | MODULE_DEVICE_TABLE(x86cpu, pkg_temp_thermal_ids); |
578 | 579 | ||
579 | static int __init pkg_temp_thermal_init(void) | 580 | static int __init pkg_temp_thermal_init(void) |
580 | { | 581 | { |
581 | int i; | 582 | int i; |
582 | 583 | ||
583 | if (!x86_match_cpu(pkg_temp_thermal_ids)) | 584 | if (!x86_match_cpu(pkg_temp_thermal_ids)) |
584 | return -ENODEV; | 585 | return -ENODEV; |
585 | 586 | ||
586 | spin_lock_init(&pkg_work_lock); | 587 | spin_lock_init(&pkg_work_lock); |
587 | platform_thermal_package_notify = | 588 | platform_thermal_package_notify = |
588 | pkg_temp_thermal_platform_thermal_notify; | 589 | pkg_temp_thermal_platform_thermal_notify; |
589 | platform_thermal_package_rate_control = | 590 | platform_thermal_package_rate_control = |
590 | pkg_temp_thermal_platform_thermal_rate_control; | 591 | pkg_temp_thermal_platform_thermal_rate_control; |
591 | 592 | ||
592 | get_online_cpus(); | 593 | get_online_cpus(); |
593 | for_each_online_cpu(i) | 594 | for_each_online_cpu(i) |
594 | if (get_core_online(i)) | 595 | if (get_core_online(i)) |
595 | goto err_ret; | 596 | goto err_ret; |
596 | register_hotcpu_notifier(&pkg_temp_thermal_notifier); | 597 | register_hotcpu_notifier(&pkg_temp_thermal_notifier); |
597 | put_online_cpus(); | 598 | put_online_cpus(); |
598 | 599 | ||
599 | pkg_temp_debugfs_init(); /* Don't care if fails */ | 600 | pkg_temp_debugfs_init(); /* Don't care if fails */ |
600 | 601 | ||
601 | return 0; | 602 | return 0; |
602 | 603 | ||
603 | err_ret: | 604 | err_ret: |
604 | for_each_online_cpu(i) | 605 | for_each_online_cpu(i) |
605 | put_core_offline(i); | 606 | put_core_offline(i); |
606 | put_online_cpus(); | 607 | put_online_cpus(); |
607 | kfree(pkg_work_scheduled); | 608 | kfree(pkg_work_scheduled); |
608 | platform_thermal_package_notify = NULL; | 609 | platform_thermal_package_notify = NULL; |
609 | platform_thermal_package_rate_control = NULL; | 610 | platform_thermal_package_rate_control = NULL; |
610 | 611 | ||
611 | return -ENODEV; | 612 | return -ENODEV; |
612 | } | 613 | } |
613 | 614 | ||
614 | static void __exit pkg_temp_thermal_exit(void) | 615 | static void __exit pkg_temp_thermal_exit(void) |
615 | { | 616 | { |
616 | struct phy_dev_entry *phdev, *n; | 617 | struct phy_dev_entry *phdev, *n; |
617 | int i; | 618 | int i; |
618 | 619 | ||
619 | get_online_cpus(); | 620 | get_online_cpus(); |
620 | unregister_hotcpu_notifier(&pkg_temp_thermal_notifier); | 621 | unregister_hotcpu_notifier(&pkg_temp_thermal_notifier); |
621 | mutex_lock(&phy_dev_list_mutex); | 622 | mutex_lock(&phy_dev_list_mutex); |
622 | list_for_each_entry_safe(phdev, n, &phy_dev_list, list) { | 623 | list_for_each_entry_safe(phdev, n, &phy_dev_list, list) { |
623 | /* Retore old MSR value for package thermal interrupt */ | 624 | /* Retore old MSR value for package thermal interrupt */ |
624 | wrmsr_on_cpu(phdev->first_cpu, | 625 | wrmsr_on_cpu(phdev->first_cpu, |
625 | MSR_IA32_PACKAGE_THERM_INTERRUPT, | 626 | MSR_IA32_PACKAGE_THERM_INTERRUPT, |
626 | phdev->start_pkg_therm_low, | 627 | phdev->start_pkg_therm_low, |
627 | phdev->start_pkg_therm_high); | 628 | phdev->start_pkg_therm_high); |
628 | thermal_zone_device_unregister(phdev->tzone); | 629 | thermal_zone_device_unregister(phdev->tzone); |
629 | list_del(&phdev->list); | 630 | list_del(&phdev->list); |
630 | kfree(phdev); | 631 | kfree(phdev); |
631 | } | 632 | } |
632 | mutex_unlock(&phy_dev_list_mutex); | 633 | mutex_unlock(&phy_dev_list_mutex); |
633 | platform_thermal_package_notify = NULL; | 634 | platform_thermal_package_notify = NULL; |
634 | platform_thermal_package_rate_control = NULL; | 635 | platform_thermal_package_rate_control = NULL; |
635 | for_each_online_cpu(i) | 636 | for_each_online_cpu(i) |
636 | cancel_delayed_work_sync( | 637 | cancel_delayed_work_sync( |
637 | &per_cpu(pkg_temp_thermal_threshold_work, i)); | 638 | &per_cpu(pkg_temp_thermal_threshold_work, i)); |
638 | put_online_cpus(); | 639 | put_online_cpus(); |
639 | 640 | ||
640 | kfree(pkg_work_scheduled); | 641 | kfree(pkg_work_scheduled); |
641 | 642 | ||
642 | debugfs_remove_recursive(debugfs); | 643 | debugfs_remove_recursive(debugfs); |
643 | } | 644 | } |
644 | 645 | ||
645 | module_init(pkg_temp_thermal_init) | 646 | module_init(pkg_temp_thermal_init) |
646 | module_exit(pkg_temp_thermal_exit) | 647 | module_exit(pkg_temp_thermal_exit) |
647 | 648 | ||
648 | MODULE_DESCRIPTION("X86 PKG TEMP Thermal Driver"); | 649 | MODULE_DESCRIPTION("X86 PKG TEMP Thermal Driver"); |