diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2017-05-12 14:58:45 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2017-05-12 14:58:45 -0400 |
| commit | 6a776e47a045462a1df1a3a9592598259ffd614f (patch) | |
| tree | 79c3ea4b96e48c7c4389114e738dcc0a972c97c0 | |
| parent | a34ab101a9d27a2995142b47f9857fb46fcb072a (diff) | |
| parent | cb15c81a0c1c1f7829b9809a209ecacc77f5aa63 (diff) | |
Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/rzhang/linux
Pull thermal management updates from Zhang Rui:
- Fix a problem where orderly_shutdown() is called for multiple times
due to multiple critical overheating events raised in a short period
by platform thermal driver. (Keerthy)
- Introduce a backup thermal shutdown mechanism, which invokes
kernel_power_off()/emergency_restart() directly, after
orderly_shutdown() being issued for certain amount of time(specified
via Kconfig). This is useful in certain conditions that userspace may
be unable to power off the system in a clean manner and leaves the
system in a critical state, like in the middle of driver probing
phase. (Keerthy)
- Introduce a new interface in thermal devfreq_cooling code so that the
driver can provide more precise data regarding actual power to the
thermal governor every time the power budget is calculated. (Lukasz
Luba)
- Introduce BCM 2835 soc thermal driver and northstar thermal driver,
within a new sub-folder. (Rafał Miłecki)
- Introduce DA9062/61 thermal driver. (Steve Twiss)
- Remove non-DT booting on TI-SoC driver. Also add support to fetching
coefficients from DT. (Keerthy)
- Refactorf RCAR Gen3 thermal driver. (Niklas Söderlund)
- Small fix on MTK and intel-soc-dts thermal driver. (Dawei Chien,
Brian Bian)
* 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/rzhang/linux: (25 commits)
thermal: core: Add a back up thermal shutdown mechanism
thermal: core: Allow orderly_poweroff to be called only once
Thermal: Intel SoC DTS: Change interrupt request behavior
trace: thermal: add another parameter 'power' to the tracing function
thermal: devfreq_cooling: add new interface for direct power read
thermal: devfreq_cooling: refactor code and add get_voltage function
thermal: mt8173: minor mtk_thermal.c cleanups
thermal: bcm2835: move to the broadcom subdirectory
thermal: broadcom: ns: specify myself as MODULE_AUTHOR
thermal: da9062/61: Thermal junction temperature monitoring driver
Documentation: devicetree: thermal: da9062/61 TJUNC temperature binding
thermal: broadcom: add Northstar thermal driver
dt-bindings: thermal: add support for Broadcom's Northstar thermal
thermal: bcm2835: add thermal driver for bcm2835 SoC
dt-bindings: Add thermal zone to bcm2835-thermal example
thermal: rcar_gen3_thermal: add suspend and resume support
thermal: rcar_gen3_thermal: store device match data in private structure
thermal: rcar_gen3_thermal: enable hardware interrupts for trip points
thermal: rcar_gen3_thermal: record and check number of TSCs found
thermal: rcar_gen3_thermal: check that TSC exists before memory allocation
...
25 files changed, 1305 insertions, 266 deletions
diff --git a/Documentation/devicetree/bindings/thermal/brcm,bcm2835-thermal.txt b/Documentation/devicetree/bindings/thermal/brcm,bcm2835-thermal.txt index 474531d2b2c5..da8c5b73ad10 100644 --- a/Documentation/devicetree/bindings/thermal/brcm,bcm2835-thermal.txt +++ b/Documentation/devicetree/bindings/thermal/brcm,bcm2835-thermal.txt | |||
| @@ -3,15 +3,39 @@ Binding for Thermal Sensor driver for BCM2835 SoCs. | |||
| 3 | Required parameters: | 3 | Required parameters: |
| 4 | ------------------- | 4 | ------------------- |
| 5 | 5 | ||
| 6 | compatible: should be one of: "brcm,bcm2835-thermal", | 6 | compatible: should be one of: "brcm,bcm2835-thermal", |
| 7 | "brcm,bcm2836-thermal" or "brcm,bcm2837-thermal" | 7 | "brcm,bcm2836-thermal" or "brcm,bcm2837-thermal" |
| 8 | reg: Address range of the thermal registers. | 8 | reg: Address range of the thermal registers. |
| 9 | clocks: Phandle of the clock used by the thermal sensor. | 9 | clocks: Phandle of the clock used by the thermal sensor. |
| 10 | #thermal-sensor-cells: should be 0 (see thermal.txt) | ||
| 10 | 11 | ||
| 11 | Example: | 12 | Example: |
| 12 | 13 | ||
| 14 | thermal-zones { | ||
| 15 | cpu_thermal: cpu-thermal { | ||
| 16 | polling-delay-passive = <0>; | ||
| 17 | polling-delay = <1000>; | ||
| 18 | |||
| 19 | thermal-sensors = <&thermal>; | ||
| 20 | |||
| 21 | trips { | ||
| 22 | cpu-crit { | ||
| 23 | temperature = <80000>; | ||
| 24 | hysteresis = <0>; | ||
| 25 | type = "critical"; | ||
| 26 | }; | ||
| 27 | }; | ||
| 28 | |||
| 29 | coefficients = <(-538) 407000>; | ||
| 30 | |||
| 31 | cooling-maps { | ||
| 32 | }; | ||
| 33 | }; | ||
| 34 | }; | ||
| 35 | |||
| 13 | thermal: thermal@7e212000 { | 36 | thermal: thermal@7e212000 { |
| 14 | compatible = "brcm,bcm2835-thermal"; | 37 | compatible = "brcm,bcm2835-thermal"; |
| 15 | reg = <0x7e212000 0x8>; | 38 | reg = <0x7e212000 0x8>; |
| 16 | clocks = <&clocks BCM2835_CLOCK_TSENS>; | 39 | clocks = <&clocks BCM2835_CLOCK_TSENS>; |
| 40 | #thermal-sensor-cells = <0>; | ||
| 17 | }; | 41 | }; |
diff --git a/Documentation/devicetree/bindings/thermal/brcm,ns-thermal b/Documentation/devicetree/bindings/thermal/brcm,ns-thermal new file mode 100644 index 000000000000..68e047170039 --- /dev/null +++ b/Documentation/devicetree/bindings/thermal/brcm,ns-thermal | |||
| @@ -0,0 +1,37 @@ | |||
| 1 | * Broadcom Northstar Thermal | ||
| 2 | |||
| 3 | This binding describes thermal sensor that is part of Northstar's DMU (Device | ||
| 4 | Management Unit). | ||
| 5 | |||
| 6 | Required properties: | ||
| 7 | - compatible : Must be "brcm,ns-thermal" | ||
| 8 | - reg : iomem address range of PVTMON registers | ||
| 9 | - #thermal-sensor-cells : Should be <0> | ||
| 10 | |||
| 11 | Example: | ||
| 12 | |||
| 13 | thermal: thermal@1800c2c0 { | ||
| 14 | compatible = "brcm,ns-thermal"; | ||
| 15 | reg = <0x1800c2c0 0x10>; | ||
| 16 | #thermal-sensor-cells = <0>; | ||
| 17 | }; | ||
| 18 | |||
| 19 | thermal-zones { | ||
| 20 | cpu_thermal: cpu-thermal { | ||
| 21 | polling-delay-passive = <0>; | ||
| 22 | polling-delay = <1000>; | ||
| 23 | coefficients = <(-556) 418000>; | ||
| 24 | thermal-sensors = <&thermal>; | ||
| 25 | |||
| 26 | trips { | ||
| 27 | cpu-crit { | ||
| 28 | temperature = <125000>; | ||
| 29 | hysteresis = <0>; | ||
| 30 | type = "critical"; | ||
| 31 | }; | ||
| 32 | }; | ||
| 33 | |||
| 34 | cooling-maps { | ||
| 35 | }; | ||
| 36 | }; | ||
| 37 | }; | ||
diff --git a/Documentation/devicetree/bindings/thermal/da9062-thermal.txt b/Documentation/devicetree/bindings/thermal/da9062-thermal.txt new file mode 100644 index 000000000000..e241bb5a5584 --- /dev/null +++ b/Documentation/devicetree/bindings/thermal/da9062-thermal.txt | |||
| @@ -0,0 +1,36 @@ | |||
| 1 | * Dialog DA9062/61 TJUNC Thermal Module | ||
| 2 | |||
| 3 | This module is part of the DA9061/DA9062. For more details about entire | ||
| 4 | DA9062 and DA9061 chips see Documentation/devicetree/bindings/mfd/da9062.txt | ||
| 5 | |||
| 6 | Junction temperature thermal module uses an interrupt signal to identify | ||
| 7 | high THERMAL_TRIP_HOT temperatures for the PMIC device. | ||
| 8 | |||
| 9 | Required properties: | ||
| 10 | |||
| 11 | - compatible: should be one of the following valid compatible string lines: | ||
| 12 | "dlg,da9061-thermal", "dlg,da9062-thermal" | ||
| 13 | "dlg,da9062-thermal" | ||
| 14 | |||
| 15 | Optional properties: | ||
| 16 | |||
| 17 | - polling-delay-passive : Specify the polling period, measured in | ||
| 18 | milliseconds, between thermal zone device update checks. | ||
| 19 | |||
| 20 | Example: DA9062 | ||
| 21 | |||
| 22 | pmic0: da9062@58 { | ||
| 23 | thermal { | ||
| 24 | compatible = "dlg,da9062-thermal"; | ||
| 25 | polling-delay-passive = <3000>; | ||
| 26 | }; | ||
| 27 | }; | ||
| 28 | |||
| 29 | Example: DA9061 using a fall-back compatible for the DA9062 onkey driver | ||
| 30 | |||
| 31 | pmic0: da9061@58 { | ||
| 32 | thermal { | ||
| 33 | compatible = "dlg,da9061-thermal", "dlg,da9062-thermal"; | ||
| 34 | polling-delay-passive = <3000>; | ||
| 35 | }; | ||
| 36 | }; | ||
diff --git a/Documentation/thermal/sysfs-api.txt b/Documentation/thermal/sysfs-api.txt index ef473dc7f55e..bb9a0a53e76b 100644 --- a/Documentation/thermal/sysfs-api.txt +++ b/Documentation/thermal/sysfs-api.txt | |||
| @@ -582,3 +582,24 @@ platform data is provided, this uses the step_wise throttling policy. | |||
| 582 | This function serves as an arbitrator to set the state of a cooling | 582 | This function serves as an arbitrator to set the state of a cooling |
| 583 | device. It sets the cooling device to the deepest cooling state if | 583 | device. It sets the cooling device to the deepest cooling state if |
| 584 | possible. | 584 | possible. |
| 585 | |||
| 586 | 6. thermal_emergency_poweroff: | ||
| 587 | |||
| 588 | On an event of critical trip temperature crossing. Thermal framework | ||
| 589 | allows the system to shutdown gracefully by calling orderly_poweroff(). | ||
| 590 | In the event of a failure of orderly_poweroff() to shut down the system | ||
| 591 | we are in danger of keeping the system alive at undesirably high | ||
| 592 | temperatures. To mitigate this high risk scenario we program a work | ||
| 593 | queue to fire after a pre-determined number of seconds to start | ||
| 594 | an emergency shutdown of the device using the kernel_power_off() | ||
| 595 | function. In case kernel_power_off() fails then finally | ||
| 596 | emergency_restart() is called in the worst case. | ||
| 597 | |||
| 598 | The delay should be carefully profiled so as to give adequate time for | ||
| 599 | orderly_poweroff(). In case of failure of an orderly_poweroff() the | ||
| 600 | emergency poweroff kicks in after the delay has elapsed and shuts down | ||
| 601 | the system. | ||
| 602 | |||
| 603 | If set to 0 emergency poweroff will not be supported. So a carefully | ||
| 604 | profiled non-zero positive value is a must for emergerncy poweroff to be | ||
| 605 | triggered. | ||
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index 6871ecc5b951..b5b5facb8747 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig | |||
| @@ -15,6 +15,23 @@ menuconfig THERMAL | |||
| 15 | 15 | ||
| 16 | if THERMAL | 16 | if THERMAL |
| 17 | 17 | ||
| 18 | config THERMAL_EMERGENCY_POWEROFF_DELAY_MS | ||
| 19 | int "Emergency poweroff delay in milli-seconds" | ||
| 20 | depends on THERMAL | ||
| 21 | default 0 | ||
| 22 | help | ||
| 23 | Thermal subsystem will issue a graceful shutdown when | ||
| 24 | critical temperatures are reached using orderly_poweroff(). In | ||
| 25 | case of failure of an orderly_poweroff(), the thermal emergency | ||
| 26 | poweroff kicks in after a delay has elapsed and shuts down the system. | ||
| 27 | This config is number of milliseconds to delay before emergency | ||
| 28 | poweroff kicks in. Similarly to the critical trip point, | ||
| 29 | the delay should be carefully profiled so as to give adequate | ||
| 30 | time for orderly_poweroff() to finish on regular execution. | ||
| 31 | If set to 0 emergency poweroff will not be supported. | ||
| 32 | |||
| 33 | In doubt, leave as 0. | ||
| 34 | |||
| 18 | config THERMAL_HWMON | 35 | config THERMAL_HWMON |
| 19 | bool | 36 | bool |
| 20 | prompt "Expose thermal sensors as hwmon device" | 37 | prompt "Expose thermal sensors as hwmon device" |
| @@ -291,6 +308,16 @@ config ARMADA_THERMAL | |||
| 291 | Enable this option if you want to have support for thermal management | 308 | Enable this option if you want to have support for thermal management |
| 292 | controller present in Armada 370 and Armada XP SoC. | 309 | controller present in Armada 370 and Armada XP SoC. |
| 293 | 310 | ||
| 311 | config DA9062_THERMAL | ||
| 312 | tristate "DA9062/DA9061 Dialog Semiconductor thermal driver" | ||
| 313 | depends on MFD_DA9062 || COMPILE_TEST | ||
| 314 | depends on OF | ||
| 315 | help | ||
| 316 | Enable this for the Dialog Semiconductor thermal sensor driver. | ||
| 317 | This will report PMIC junction over-temperature for one thermal trip | ||
| 318 | zone. | ||
| 319 | Compatible with the DA9062 and DA9061 PMICs. | ||
| 320 | |||
| 294 | config INTEL_POWERCLAMP | 321 | config INTEL_POWERCLAMP |
| 295 | tristate "Intel PowerClamp idle injection driver" | 322 | tristate "Intel PowerClamp idle injection driver" |
| 296 | depends on THERMAL | 323 | depends on THERMAL |
| @@ -380,6 +407,11 @@ config MTK_THERMAL | |||
| 380 | Enable this option if you want to have support for thermal management | 407 | Enable this option if you want to have support for thermal management |
| 381 | controller present in Mediatek SoCs | 408 | controller present in Mediatek SoCs |
| 382 | 409 | ||
| 410 | menu "Broadcom thermal drivers" | ||
| 411 | depends on ARCH_BCM || COMPILE_TEST | ||
| 412 | source "drivers/thermal/broadcom/Kconfig" | ||
| 413 | endmenu | ||
| 414 | |||
| 383 | menu "Texas Instruments thermal drivers" | 415 | menu "Texas Instruments thermal drivers" |
| 384 | depends on ARCH_HAS_BANDGAP || COMPILE_TEST | 416 | depends on ARCH_HAS_BANDGAP || COMPILE_TEST |
| 385 | depends on HAS_IOMEM | 417 | depends on HAS_IOMEM |
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile index c2372f10dae5..094d7039981c 100644 --- a/drivers/thermal/Makefile +++ b/drivers/thermal/Makefile | |||
| @@ -27,6 +27,7 @@ thermal_sys-$(CONFIG_CLOCK_THERMAL) += clock_cooling.o | |||
| 27 | thermal_sys-$(CONFIG_DEVFREQ_THERMAL) += devfreq_cooling.o | 27 | thermal_sys-$(CONFIG_DEVFREQ_THERMAL) += devfreq_cooling.o |
| 28 | 28 | ||
| 29 | # platform thermal drivers | 29 | # platform thermal drivers |
| 30 | obj-y += broadcom/ | ||
| 30 | obj-$(CONFIG_QCOM_SPMI_TEMP_ALARM) += qcom-spmi-temp-alarm.o | 31 | obj-$(CONFIG_QCOM_SPMI_TEMP_ALARM) += qcom-spmi-temp-alarm.o |
| 31 | obj-$(CONFIG_SPEAR_THERMAL) += spear_thermal.o | 32 | obj-$(CONFIG_SPEAR_THERMAL) += spear_thermal.o |
| 32 | obj-$(CONFIG_ROCKCHIP_THERMAL) += rockchip_thermal.o | 33 | obj-$(CONFIG_ROCKCHIP_THERMAL) += rockchip_thermal.o |
| @@ -41,6 +42,7 @@ obj-$(CONFIG_TANGO_THERMAL) += tango_thermal.o | |||
| 41 | obj-$(CONFIG_IMX_THERMAL) += imx_thermal.o | 42 | obj-$(CONFIG_IMX_THERMAL) += imx_thermal.o |
| 42 | obj-$(CONFIG_MAX77620_THERMAL) += max77620_thermal.o | 43 | obj-$(CONFIG_MAX77620_THERMAL) += max77620_thermal.o |
| 43 | obj-$(CONFIG_QORIQ_THERMAL) += qoriq_thermal.o | 44 | obj-$(CONFIG_QORIQ_THERMAL) += qoriq_thermal.o |
| 45 | obj-$(CONFIG_DA9062_THERMAL) += da9062-thermal.o | ||
| 44 | obj-$(CONFIG_INTEL_POWERCLAMP) += intel_powerclamp.o | 46 | obj-$(CONFIG_INTEL_POWERCLAMP) += intel_powerclamp.o |
| 45 | obj-$(CONFIG_X86_PKG_TEMP_THERMAL) += x86_pkg_temp_thermal.o | 47 | obj-$(CONFIG_X86_PKG_TEMP_THERMAL) += x86_pkg_temp_thermal.o |
| 46 | obj-$(CONFIG_INTEL_SOC_DTS_IOSF_CORE) += intel_soc_dts_iosf.o | 48 | obj-$(CONFIG_INTEL_SOC_DTS_IOSF_CORE) += intel_soc_dts_iosf.o |
diff --git a/drivers/thermal/broadcom/Kconfig b/drivers/thermal/broadcom/Kconfig new file mode 100644 index 000000000000..ab08af4654ef --- /dev/null +++ b/drivers/thermal/broadcom/Kconfig | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | config BCM2835_THERMAL | ||
| 2 | tristate "Thermal sensors on bcm2835 SoC" | ||
| 3 | depends on ARCH_BCM2835 || COMPILE_TEST | ||
| 4 | depends on HAS_IOMEM | ||
| 5 | depends on THERMAL_OF | ||
| 6 | help | ||
| 7 | Support for thermal sensors on Broadcom bcm2835 SoCs. | ||
| 8 | |||
| 9 | config BCM_NS_THERMAL | ||
| 10 | tristate "Northstar thermal driver" | ||
| 11 | depends on ARCH_BCM_IPROC || COMPILE_TEST | ||
| 12 | help | ||
| 13 | Northstar is a family of SoCs that includes e.g. BCM4708, BCM47081, | ||
| 14 | BCM4709 and BCM47094. It contains DMU (Device Management Unit) block | ||
| 15 | with a thermal sensor that allows checking CPU temperature. This | ||
| 16 | driver provides support for it. | ||
diff --git a/drivers/thermal/broadcom/Makefile b/drivers/thermal/broadcom/Makefile new file mode 100644 index 000000000000..c6f62e4fd0ee --- /dev/null +++ b/drivers/thermal/broadcom/Makefile | |||
| @@ -0,0 +1,2 @@ | |||
| 1 | obj-$(CONFIG_BCM2835_THERMAL) += bcm2835_thermal.o | ||
| 2 | obj-$(CONFIG_BCM_NS_THERMAL) += ns-thermal.o | ||
diff --git a/drivers/thermal/broadcom/bcm2835_thermal.c b/drivers/thermal/broadcom/bcm2835_thermal.c new file mode 100644 index 000000000000..0ecf80890c84 --- /dev/null +++ b/drivers/thermal/broadcom/bcm2835_thermal.c | |||
| @@ -0,0 +1,314 @@ | |||
| 1 | /* | ||
| 2 | * Driver for Broadcom BCM2835 SoC temperature sensor | ||
| 3 | * | ||
| 4 | * Copyright (C) 2016 Martin Sperl | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License as published by | ||
| 8 | * the Free Software Foundation; either version 2 of the License, or | ||
| 9 | * (at your option) any later version. | ||
| 10 | * | ||
| 11 | * This program is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | * GNU General Public License for more details. | ||
| 15 | */ | ||
| 16 | |||
| 17 | #include <linux/clk.h> | ||
| 18 | #include <linux/debugfs.h> | ||
| 19 | #include <linux/device.h> | ||
| 20 | #include <linux/err.h> | ||
| 21 | #include <linux/io.h> | ||
| 22 | #include <linux/kernel.h> | ||
| 23 | #include <linux/module.h> | ||
| 24 | #include <linux/of.h> | ||
| 25 | #include <linux/of_address.h> | ||
| 26 | #include <linux/of_device.h> | ||
| 27 | #include <linux/platform_device.h> | ||
| 28 | #include <linux/thermal.h> | ||
| 29 | |||
| 30 | #define BCM2835_TS_TSENSCTL 0x00 | ||
| 31 | #define BCM2835_TS_TSENSSTAT 0x04 | ||
| 32 | |||
| 33 | #define BCM2835_TS_TSENSCTL_PRWDW BIT(0) | ||
| 34 | #define BCM2835_TS_TSENSCTL_RSTB BIT(1) | ||
| 35 | |||
| 36 | /* | ||
| 37 | * bandgap reference voltage in 6 mV increments | ||
| 38 | * 000b = 1178 mV, 001b = 1184 mV, ... 111b = 1220 mV | ||
| 39 | */ | ||
| 40 | #define BCM2835_TS_TSENSCTL_CTRL_BITS 3 | ||
| 41 | #define BCM2835_TS_TSENSCTL_CTRL_SHIFT 2 | ||
| 42 | #define BCM2835_TS_TSENSCTL_CTRL_MASK \ | ||
| 43 | GENMASK(BCM2835_TS_TSENSCTL_CTRL_BITS + \ | ||
| 44 | BCM2835_TS_TSENSCTL_CTRL_SHIFT - 1, \ | ||
| 45 | BCM2835_TS_TSENSCTL_CTRL_SHIFT) | ||
| 46 | #define BCM2835_TS_TSENSCTL_CTRL_DEFAULT 1 | ||
| 47 | #define BCM2835_TS_TSENSCTL_EN_INT BIT(5) | ||
| 48 | #define BCM2835_TS_TSENSCTL_DIRECT BIT(6) | ||
| 49 | #define BCM2835_TS_TSENSCTL_CLR_INT BIT(7) | ||
| 50 | #define BCM2835_TS_TSENSCTL_THOLD_SHIFT 8 | ||
| 51 | #define BCM2835_TS_TSENSCTL_THOLD_BITS 10 | ||
| 52 | #define BCM2835_TS_TSENSCTL_THOLD_MASK \ | ||
| 53 | GENMASK(BCM2835_TS_TSENSCTL_THOLD_BITS + \ | ||
| 54 | BCM2835_TS_TSENSCTL_THOLD_SHIFT - 1, \ | ||
| 55 | BCM2835_TS_TSENSCTL_THOLD_SHIFT) | ||
| 56 | /* | ||
| 57 | * time how long the block to be asserted in reset | ||
| 58 | * which based on a clock counter (TSENS clock assumed) | ||
| 59 | */ | ||
| 60 | #define BCM2835_TS_TSENSCTL_RSTDELAY_SHIFT 18 | ||
| 61 | #define BCM2835_TS_TSENSCTL_RSTDELAY_BITS 8 | ||
| 62 | #define BCM2835_TS_TSENSCTL_REGULEN BIT(26) | ||
| 63 | |||
| 64 | #define BCM2835_TS_TSENSSTAT_DATA_BITS 10 | ||
| 65 | #define BCM2835_TS_TSENSSTAT_DATA_SHIFT 0 | ||
| 66 | #define BCM2835_TS_TSENSSTAT_DATA_MASK \ | ||
| 67 | GENMASK(BCM2835_TS_TSENSSTAT_DATA_BITS + \ | ||
| 68 | BCM2835_TS_TSENSSTAT_DATA_SHIFT - 1, \ | ||
| 69 | BCM2835_TS_TSENSSTAT_DATA_SHIFT) | ||
| 70 | #define BCM2835_TS_TSENSSTAT_VALID BIT(10) | ||
| 71 | #define BCM2835_TS_TSENSSTAT_INTERRUPT BIT(11) | ||
| 72 | |||
| 73 | struct bcm2835_thermal_data { | ||
| 74 | struct thermal_zone_device *tz; | ||
| 75 | void __iomem *regs; | ||
| 76 | struct clk *clk; | ||
| 77 | struct dentry *debugfsdir; | ||
| 78 | }; | ||
| 79 | |||
| 80 | static int bcm2835_thermal_adc2temp(u32 adc, int offset, int slope) | ||
| 81 | { | ||
| 82 | return offset + slope * adc; | ||
| 83 | } | ||
| 84 | |||
| 85 | static int bcm2835_thermal_temp2adc(int temp, int offset, int slope) | ||
| 86 | { | ||
| 87 | temp -= offset; | ||
| 88 | temp /= slope; | ||
| 89 | |||
| 90 | if (temp < 0) | ||
| 91 | temp = 0; | ||
| 92 | if (temp >= BIT(BCM2835_TS_TSENSSTAT_DATA_BITS)) | ||
| 93 | temp = BIT(BCM2835_TS_TSENSSTAT_DATA_BITS) - 1; | ||
| 94 | |||
| 95 | return temp; | ||
| 96 | } | ||
| 97 | |||
| 98 | static int bcm2835_thermal_get_temp(void *d, int *temp) | ||
| 99 | { | ||
| 100 | struct bcm2835_thermal_data *data = d; | ||
| 101 | u32 val = readl(data->regs + BCM2835_TS_TSENSSTAT); | ||
| 102 | |||
| 103 | if (!(val & BCM2835_TS_TSENSSTAT_VALID)) | ||
| 104 | return -EIO; | ||
| 105 | |||
| 106 | val &= BCM2835_TS_TSENSSTAT_DATA_MASK; | ||
| 107 | |||
| 108 | *temp = bcm2835_thermal_adc2temp( | ||
| 109 | val, | ||
| 110 | thermal_zone_get_offset(data->tz), | ||
| 111 | thermal_zone_get_slope(data->tz)); | ||
| 112 | |||
| 113 | return 0; | ||
| 114 | } | ||
| 115 | |||
| 116 | static const struct debugfs_reg32 bcm2835_thermal_regs[] = { | ||
| 117 | { | ||
| 118 | .name = "ctl", | ||
| 119 | .offset = 0 | ||
| 120 | }, | ||
| 121 | { | ||
| 122 | .name = "stat", | ||
| 123 | .offset = 4 | ||
| 124 | } | ||
| 125 | }; | ||
| 126 | |||
| 127 | static void bcm2835_thermal_debugfs(struct platform_device *pdev) | ||
| 128 | { | ||
| 129 | struct thermal_zone_device *tz = platform_get_drvdata(pdev); | ||
| 130 | struct bcm2835_thermal_data *data = tz->devdata; | ||
| 131 | struct debugfs_regset32 *regset; | ||
| 132 | |||
| 133 | data->debugfsdir = debugfs_create_dir("bcm2835_thermal", NULL); | ||
| 134 | if (!data->debugfsdir) | ||
| 135 | return; | ||
| 136 | |||
| 137 | regset = devm_kzalloc(&pdev->dev, sizeof(*regset), GFP_KERNEL); | ||
| 138 | if (!regset) | ||
| 139 | return; | ||
| 140 | |||
| 141 | regset->regs = bcm2835_thermal_regs; | ||
| 142 | regset->nregs = ARRAY_SIZE(bcm2835_thermal_regs); | ||
| 143 | regset->base = data->regs; | ||
| 144 | |||
| 145 | debugfs_create_regset32("regset", 0444, data->debugfsdir, regset); | ||
| 146 | } | ||
| 147 | |||
| 148 | static struct thermal_zone_of_device_ops bcm2835_thermal_ops = { | ||
| 149 | .get_temp = bcm2835_thermal_get_temp, | ||
| 150 | }; | ||
| 151 | |||
| 152 | /* | ||
| 153 | * Note: as per Raspberry Foundation FAQ | ||
| 154 | * (https://www.raspberrypi.org/help/faqs/#performanceOperatingTemperature) | ||
| 155 | * the recommended temperature range for the SoC -40C to +85C | ||
| 156 | * so the trip limit is set to 80C. | ||
| 157 | * this applies to all the BCM283X SoC | ||
| 158 | */ | ||
| 159 | |||
| 160 | static const struct of_device_id bcm2835_thermal_of_match_table[] = { | ||
| 161 | { | ||
| 162 | .compatible = "brcm,bcm2835-thermal", | ||
| 163 | }, | ||
| 164 | { | ||
| 165 | .compatible = "brcm,bcm2836-thermal", | ||
| 166 | }, | ||
| 167 | { | ||
| 168 | .compatible = "brcm,bcm2837-thermal", | ||
| 169 | }, | ||
| 170 | {}, | ||
| 171 | }; | ||
| 172 | MODULE_DEVICE_TABLE(of, bcm2835_thermal_of_match_table); | ||
| 173 | |||
| 174 | static int bcm2835_thermal_probe(struct platform_device *pdev) | ||
| 175 | { | ||
| 176 | const struct of_device_id *match; | ||
| 177 | struct thermal_zone_device *tz; | ||
| 178 | struct bcm2835_thermal_data *data; | ||
| 179 | struct resource *res; | ||
| 180 | int err = 0; | ||
| 181 | u32 val; | ||
| 182 | unsigned long rate; | ||
| 183 | |||
| 184 | data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); | ||
| 185 | if (!data) | ||
| 186 | return -ENOMEM; | ||
| 187 | |||
| 188 | match = of_match_device(bcm2835_thermal_of_match_table, | ||
| 189 | &pdev->dev); | ||
| 190 | if (!match) | ||
| 191 | return -EINVAL; | ||
| 192 | |||
| 193 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
| 194 | data->regs = devm_ioremap_resource(&pdev->dev, res); | ||
| 195 | if (IS_ERR(data->regs)) { | ||
| 196 | err = PTR_ERR(data->regs); | ||
| 197 | dev_err(&pdev->dev, "Could not get registers: %d\n", err); | ||
| 198 | return err; | ||
| 199 | } | ||
| 200 | |||
| 201 | data->clk = devm_clk_get(&pdev->dev, NULL); | ||
| 202 | if (IS_ERR(data->clk)) { | ||
| 203 | err = PTR_ERR(data->clk); | ||
| 204 | if (err != -EPROBE_DEFER) | ||
| 205 | dev_err(&pdev->dev, "Could not get clk: %d\n", err); | ||
| 206 | return err; | ||
| 207 | } | ||
| 208 | |||
| 209 | err = clk_prepare_enable(data->clk); | ||
| 210 | if (err) | ||
| 211 | return err; | ||
| 212 | |||
| 213 | rate = clk_get_rate(data->clk); | ||
| 214 | if ((rate < 1920000) || (rate > 5000000)) | ||
| 215 | dev_warn(&pdev->dev, | ||
| 216 | "Clock %pCn running at %pCr Hz is outside of the recommended range: 1.92 to 5MHz\n", | ||
| 217 | data->clk, data->clk); | ||
| 218 | |||
| 219 | /* register of thermal sensor and get info from DT */ | ||
| 220 | tz = thermal_zone_of_sensor_register(&pdev->dev, 0, data, | ||
| 221 | &bcm2835_thermal_ops); | ||
| 222 | if (IS_ERR(tz)) { | ||
| 223 | err = PTR_ERR(tz); | ||
| 224 | dev_err(&pdev->dev, | ||
| 225 | "Failed to register the thermal device: %d\n", | ||
| 226 | err); | ||
| 227 | goto err_clk; | ||
| 228 | } | ||
| 229 | |||
| 230 | /* | ||
| 231 | * right now the FW does set up the HW-block, so we are not | ||
| 232 | * touching the configuration registers. | ||
| 233 | * But if the HW is not enabled, then set it up | ||
| 234 | * using "sane" values used by the firmware right now. | ||
| 235 | */ | ||
| 236 | val = readl(data->regs + BCM2835_TS_TSENSCTL); | ||
| 237 | if (!(val & BCM2835_TS_TSENSCTL_RSTB)) { | ||
| 238 | int trip_temp, offset, slope; | ||
| 239 | |||
| 240 | slope = thermal_zone_get_slope(tz); | ||
| 241 | offset = thermal_zone_get_offset(tz); | ||
| 242 | /* | ||
| 243 | * For now we deal only with critical, otherwise | ||
| 244 | * would need to iterate | ||
| 245 | */ | ||
| 246 | err = tz->ops->get_trip_temp(tz, 0, &trip_temp); | ||
| 247 | if (err < 0) { | ||
| 248 | err = PTR_ERR(tz); | ||
| 249 | dev_err(&pdev->dev, | ||
| 250 | "Not able to read trip_temp: %d\n", | ||
| 251 | err); | ||
| 252 | goto err_tz; | ||
| 253 | } | ||
| 254 | |||
| 255 | /* set bandgap reference voltage and enable voltage regulator */ | ||
| 256 | val = (BCM2835_TS_TSENSCTL_CTRL_DEFAULT << | ||
| 257 | BCM2835_TS_TSENSCTL_CTRL_SHIFT) | | ||
| 258 | BCM2835_TS_TSENSCTL_REGULEN; | ||
| 259 | |||
| 260 | /* use the recommended reset duration */ | ||
| 261 | val |= (0xFE << BCM2835_TS_TSENSCTL_RSTDELAY_SHIFT); | ||
| 262 | |||
| 263 | /* trip_adc value from info */ | ||
| 264 | val |= bcm2835_thermal_temp2adc(trip_temp, | ||
| 265 | offset, | ||
| 266 | slope) | ||
| 267 | << BCM2835_TS_TSENSCTL_THOLD_SHIFT; | ||
| 268 | |||
| 269 | /* write the value back to the register as 2 steps */ | ||
| 270 | writel(val, data->regs + BCM2835_TS_TSENSCTL); | ||
| 271 | val |= BCM2835_TS_TSENSCTL_RSTB; | ||
| 272 | writel(val, data->regs + BCM2835_TS_TSENSCTL); | ||
| 273 | } | ||
| 274 | |||
| 275 | data->tz = tz; | ||
| 276 | |||
| 277 | platform_set_drvdata(pdev, tz); | ||
| 278 | |||
| 279 | bcm2835_thermal_debugfs(pdev); | ||
| 280 | |||
| 281 | return 0; | ||
| 282 | err_tz: | ||
| 283 | thermal_zone_of_sensor_unregister(&pdev->dev, tz); | ||
| 284 | err_clk: | ||
| 285 | clk_disable_unprepare(data->clk); | ||
| 286 | |||
| 287 | return err; | ||
| 288 | } | ||
| 289 | |||
| 290 | static int bcm2835_thermal_remove(struct platform_device *pdev) | ||
| 291 | { | ||
| 292 | struct thermal_zone_device *tz = platform_get_drvdata(pdev); | ||
| 293 | struct bcm2835_thermal_data *data = tz->devdata; | ||
| 294 | |||
| 295 | debugfs_remove_recursive(data->debugfsdir); | ||
| 296 | thermal_zone_of_sensor_unregister(&pdev->dev, tz); | ||
| 297 | clk_disable_unprepare(data->clk); | ||
| 298 | |||
| 299 | return 0; | ||
| 300 | } | ||
| 301 | |||
| 302 | static struct platform_driver bcm2835_thermal_driver = { | ||
| 303 | .probe = bcm2835_thermal_probe, | ||
| 304 | .remove = bcm2835_thermal_remove, | ||
| 305 | .driver = { | ||
| 306 | .name = "bcm2835_thermal", | ||
| 307 | .of_match_table = bcm2835_thermal_of_match_table, | ||
| 308 | }, | ||
| 309 | }; | ||
| 310 | module_platform_driver(bcm2835_thermal_driver); | ||
| 311 | |||
| 312 | MODULE_AUTHOR("Martin Sperl"); | ||
| 313 | MODULE_DESCRIPTION("Thermal driver for bcm2835 chip"); | ||
| 314 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/thermal/broadcom/ns-thermal.c b/drivers/thermal/broadcom/ns-thermal.c new file mode 100644 index 000000000000..322e741a2463 --- /dev/null +++ b/drivers/thermal/broadcom/ns-thermal.c | |||
| @@ -0,0 +1,106 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2017 Rafał Miłecki <rafal@milecki.pl> | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify | ||
| 5 | * it under the terms of the GNU General Public License version 2 as | ||
| 6 | * published by the Free Software Foundation. | ||
| 7 | */ | ||
| 8 | |||
| 9 | #include <linux/module.h> | ||
| 10 | #include <linux/of_address.h> | ||
| 11 | #include <linux/platform_device.h> | ||
| 12 | #include <linux/thermal.h> | ||
| 13 | |||
| 14 | #define PVTMON_CONTROL0 0x00 | ||
| 15 | #define PVTMON_CONTROL0_SEL_MASK 0x0000000e | ||
| 16 | #define PVTMON_CONTROL0_SEL_TEMP_MONITOR 0x00000000 | ||
| 17 | #define PVTMON_CONTROL0_SEL_TEST_MODE 0x0000000e | ||
| 18 | #define PVTMON_STATUS 0x08 | ||
| 19 | |||
| 20 | struct ns_thermal { | ||
| 21 | struct thermal_zone_device *tz; | ||
| 22 | void __iomem *pvtmon; | ||
| 23 | }; | ||
| 24 | |||
| 25 | static int ns_thermal_get_temp(void *data, int *temp) | ||
| 26 | { | ||
| 27 | struct ns_thermal *ns_thermal = data; | ||
| 28 | int offset = thermal_zone_get_offset(ns_thermal->tz); | ||
| 29 | int slope = thermal_zone_get_slope(ns_thermal->tz); | ||
| 30 | u32 val; | ||
| 31 | |||
| 32 | val = readl(ns_thermal->pvtmon + PVTMON_CONTROL0); | ||
| 33 | if ((val & PVTMON_CONTROL0_SEL_MASK) != PVTMON_CONTROL0_SEL_TEMP_MONITOR) { | ||
| 34 | /* Clear current mode selection */ | ||
| 35 | val &= ~PVTMON_CONTROL0_SEL_MASK; | ||
| 36 | |||
| 37 | /* Set temp monitor mode (it's the default actually) */ | ||
| 38 | val |= PVTMON_CONTROL0_SEL_TEMP_MONITOR; | ||
| 39 | |||
| 40 | writel(val, ns_thermal->pvtmon + PVTMON_CONTROL0); | ||
| 41 | } | ||
| 42 | |||
| 43 | val = readl(ns_thermal->pvtmon + PVTMON_STATUS); | ||
| 44 | *temp = slope * val + offset; | ||
| 45 | |||
| 46 | return 0; | ||
| 47 | } | ||
| 48 | |||
| 49 | static const struct thermal_zone_of_device_ops ns_thermal_ops = { | ||
| 50 | .get_temp = ns_thermal_get_temp, | ||
| 51 | }; | ||
| 52 | |||
| 53 | static int ns_thermal_probe(struct platform_device *pdev) | ||
| 54 | { | ||
| 55 | struct device *dev = &pdev->dev; | ||
| 56 | struct ns_thermal *ns_thermal; | ||
| 57 | |||
| 58 | ns_thermal = devm_kzalloc(dev, sizeof(*ns_thermal), GFP_KERNEL); | ||
| 59 | if (!ns_thermal) | ||
| 60 | return -ENOMEM; | ||
| 61 | |||
| 62 | ns_thermal->pvtmon = of_iomap(dev_of_node(dev), 0); | ||
| 63 | if (WARN_ON(!ns_thermal->pvtmon)) | ||
| 64 | return -ENOENT; | ||
| 65 | |||
| 66 | ns_thermal->tz = devm_thermal_zone_of_sensor_register(dev, 0, | ||
| 67 | ns_thermal, | ||
| 68 | &ns_thermal_ops); | ||
| 69 | if (IS_ERR(ns_thermal->tz)) { | ||
| 70 | iounmap(ns_thermal->pvtmon); | ||
| 71 | return PTR_ERR(ns_thermal->tz); | ||
| 72 | } | ||
| 73 | |||
| 74 | platform_set_drvdata(pdev, ns_thermal); | ||
| 75 | |||
| 76 | return 0; | ||
| 77 | } | ||
| 78 | |||
| 79 | static int ns_thermal_remove(struct platform_device *pdev) | ||
| 80 | { | ||
| 81 | struct ns_thermal *ns_thermal = platform_get_drvdata(pdev); | ||
| 82 | |||
| 83 | iounmap(ns_thermal->pvtmon); | ||
| 84 | |||
| 85 | return 0; | ||
| 86 | } | ||
| 87 | |||
| 88 | static const struct of_device_id ns_thermal_of_match[] = { | ||
| 89 | { .compatible = "brcm,ns-thermal", }, | ||
| 90 | {}, | ||
| 91 | }; | ||
| 92 | MODULE_DEVICE_TABLE(of, ns_thermal_of_match); | ||
| 93 | |||
| 94 | static struct platform_driver ns_thermal_driver = { | ||
| 95 | .probe = ns_thermal_probe, | ||
| 96 | .remove = ns_thermal_remove, | ||
| 97 | .driver = { | ||
| 98 | .name = "ns-thermal", | ||
| 99 | .of_match_table = ns_thermal_of_match, | ||
| 100 | }, | ||
| 101 | }; | ||
| 102 | module_platform_driver(ns_thermal_driver); | ||
| 103 | |||
| 104 | MODULE_AUTHOR("Rafał Miłecki <rafal@milecki.pl>"); | ||
| 105 | MODULE_DESCRIPTION("Northstar thermal driver"); | ||
| 106 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/thermal/da9062-thermal.c b/drivers/thermal/da9062-thermal.c new file mode 100644 index 000000000000..dd8dd947b7f0 --- /dev/null +++ b/drivers/thermal/da9062-thermal.c | |||
| @@ -0,0 +1,315 @@ | |||
| 1 | /* | ||
| 2 | * Thermal device driver for DA9062 and DA9061 | ||
| 3 | * Copyright (C) 2017 Dialog Semiconductor | ||
| 4 | * | ||
| 5 | * This program is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU General Public License | ||
| 7 | * as published by the Free Software Foundation; either version 2 | ||
| 8 | * of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | * GNU General Public License for more details. | ||
| 14 | */ | ||
| 15 | |||
| 16 | /* When over-temperature is reached, an interrupt from the device will be | ||
| 17 | * triggered. Following this event the interrupt will be disabled and | ||
| 18 | * periodic transmission of uevents (HOT trip point) should define the | ||
| 19 | * first level of temperature supervision. It is expected that any final | ||
| 20 | * implementation of the thermal driver will include a .notify() function | ||
| 21 | * to implement these uevents to userspace. | ||
| 22 | * | ||
| 23 | * These uevents are intended to indicate non-invasive temperature control | ||
| 24 | * of the system, where the necessary measures for cooling are the | ||
| 25 | * responsibility of the host software. Once the temperature falls again, | ||
| 26 | * the IRQ is re-enabled so the start of a new over-temperature event can | ||
| 27 | * be detected without constant software monitoring. | ||
| 28 | */ | ||
| 29 | |||
| 30 | #include <linux/errno.h> | ||
| 31 | #include <linux/interrupt.h> | ||
| 32 | #include <linux/module.h> | ||
| 33 | #include <linux/of.h> | ||
| 34 | #include <linux/platform_device.h> | ||
| 35 | #include <linux/regmap.h> | ||
| 36 | #include <linux/thermal.h> | ||
| 37 | #include <linux/workqueue.h> | ||
| 38 | |||
| 39 | #include <linux/mfd/da9062/core.h> | ||
| 40 | #include <linux/mfd/da9062/registers.h> | ||
| 41 | |||
| 42 | /* Minimum, maximum and default polling millisecond periods are provided | ||
| 43 | * here as an example. It is expected that any final implementation to also | ||
| 44 | * include a modification of these settings to match the required | ||
| 45 | * application. | ||
| 46 | */ | ||
| 47 | #define DA9062_DEFAULT_POLLING_MS_PERIOD 3000 | ||
| 48 | #define DA9062_MAX_POLLING_MS_PERIOD 10000 | ||
| 49 | #define DA9062_MIN_POLLING_MS_PERIOD 1000 | ||
| 50 | |||
| 51 | #define DA9062_MILLI_CELSIUS(t) ((t) * 1000) | ||
| 52 | |||
| 53 | struct da9062_thermal_config { | ||
| 54 | const char *name; | ||
| 55 | }; | ||
| 56 | |||
| 57 | struct da9062_thermal { | ||
| 58 | struct da9062 *hw; | ||
| 59 | struct delayed_work work; | ||
| 60 | struct thermal_zone_device *zone; | ||
| 61 | enum thermal_device_mode mode; | ||
| 62 | struct mutex lock; /* protection for da9062_thermal temperature */ | ||
| 63 | int temperature; | ||
| 64 | int irq; | ||
| 65 | const struct da9062_thermal_config *config; | ||
| 66 | struct device *dev; | ||
| 67 | }; | ||
| 68 | |||
| 69 | static void da9062_thermal_poll_on(struct work_struct *work) | ||
| 70 | { | ||
| 71 | struct da9062_thermal *thermal = container_of(work, | ||
| 72 | struct da9062_thermal, | ||
| 73 | work.work); | ||
| 74 | unsigned long delay; | ||
| 75 | unsigned int val; | ||
| 76 | int ret; | ||
| 77 | |||
| 78 | /* clear E_TEMP */ | ||
| 79 | ret = regmap_write(thermal->hw->regmap, | ||
| 80 | DA9062AA_EVENT_B, | ||
| 81 | DA9062AA_E_TEMP_MASK); | ||
| 82 | if (ret < 0) { | ||
| 83 | dev_err(thermal->dev, | ||
| 84 | "Cannot clear the TJUNC temperature status\n"); | ||
| 85 | goto err_enable_irq; | ||
| 86 | } | ||
| 87 | |||
| 88 | /* Now read E_TEMP again: it is acting like a status bit. | ||
| 89 | * If over-temperature, then this status will be true. | ||
| 90 | * If not over-temperature, this status will be false. | ||
| 91 | */ | ||
| 92 | ret = regmap_read(thermal->hw->regmap, | ||
| 93 | DA9062AA_EVENT_B, | ||
| 94 | &val); | ||
| 95 | if (ret < 0) { | ||
| 96 | dev_err(thermal->dev, | ||
| 97 | "Cannot check the TJUNC temperature status\n"); | ||
| 98 | goto err_enable_irq; | ||
| 99 | } | ||
| 100 | |||
| 101 | if (val & DA9062AA_E_TEMP_MASK) { | ||
| 102 | mutex_lock(&thermal->lock); | ||
| 103 | thermal->temperature = DA9062_MILLI_CELSIUS(125); | ||
| 104 | mutex_unlock(&thermal->lock); | ||
| 105 | thermal_zone_device_update(thermal->zone, | ||
| 106 | THERMAL_EVENT_UNSPECIFIED); | ||
| 107 | |||
| 108 | delay = msecs_to_jiffies(thermal->zone->passive_delay); | ||
| 109 | schedule_delayed_work(&thermal->work, delay); | ||
| 110 | return; | ||
| 111 | } | ||
| 112 | |||
| 113 | mutex_lock(&thermal->lock); | ||
| 114 | thermal->temperature = DA9062_MILLI_CELSIUS(0); | ||
| 115 | mutex_unlock(&thermal->lock); | ||
| 116 | thermal_zone_device_update(thermal->zone, | ||
| 117 | THERMAL_EVENT_UNSPECIFIED); | ||
| 118 | |||
| 119 | err_enable_irq: | ||
| 120 | enable_irq(thermal->irq); | ||
| 121 | } | ||
| 122 | |||
| 123 | static irqreturn_t da9062_thermal_irq_handler(int irq, void *data) | ||
| 124 | { | ||
| 125 | struct da9062_thermal *thermal = data; | ||
| 126 | |||
| 127 | disable_irq_nosync(thermal->irq); | ||
| 128 | schedule_delayed_work(&thermal->work, 0); | ||
| 129 | |||
| 130 | return IRQ_HANDLED; | ||
| 131 | } | ||
| 132 | |||
| 133 | static int da9062_thermal_get_mode(struct thermal_zone_device *z, | ||
| 134 | enum thermal_device_mode *mode) | ||
| 135 | { | ||
| 136 | struct da9062_thermal *thermal = z->devdata; | ||
| 137 | *mode = thermal->mode; | ||
| 138 | return 0; | ||
| 139 | } | ||
| 140 | |||
| 141 | static int da9062_thermal_get_trip_type(struct thermal_zone_device *z, | ||
| 142 | int trip, | ||
| 143 | enum thermal_trip_type *type) | ||
| 144 | { | ||
| 145 | struct da9062_thermal *thermal = z->devdata; | ||
| 146 | |||
| 147 | switch (trip) { | ||
| 148 | case 0: | ||
| 149 | *type = THERMAL_TRIP_HOT; | ||
| 150 | break; | ||
| 151 | default: | ||
| 152 | dev_err(thermal->dev, | ||
| 153 | "Driver does not support more than 1 trip-wire\n"); | ||
| 154 | return -EINVAL; | ||
| 155 | } | ||
| 156 | |||
| 157 | return 0; | ||
| 158 | } | ||
| 159 | |||
| 160 | static int da9062_thermal_get_trip_temp(struct thermal_zone_device *z, | ||
| 161 | int trip, | ||
| 162 | int *temp) | ||
| 163 | { | ||
| 164 | struct da9062_thermal *thermal = z->devdata; | ||
| 165 | |||
| 166 | switch (trip) { | ||
| 167 | case 0: | ||
| 168 | *temp = DA9062_MILLI_CELSIUS(125); | ||
| 169 | break; | ||
| 170 | default: | ||
| 171 | dev_err(thermal->dev, | ||
| 172 | "Driver does not support more than 1 trip-wire\n"); | ||
| 173 | return -EINVAL; | ||
| 174 | } | ||
| 175 | |||
| 176 | return 0; | ||
| 177 | } | ||
| 178 | |||
| 179 | static int da9062_thermal_get_temp(struct thermal_zone_device *z, | ||
| 180 | int *temp) | ||
| 181 | { | ||
| 182 | struct da9062_thermal *thermal = z->devdata; | ||
| 183 | |||
| 184 | mutex_lock(&thermal->lock); | ||
| 185 | *temp = thermal->temperature; | ||
| 186 | mutex_unlock(&thermal->lock); | ||
| 187 | |||
| 188 | return 0; | ||
| 189 | } | ||
| 190 | |||
| 191 | static struct thermal_zone_device_ops da9062_thermal_ops = { | ||
| 192 | .get_temp = da9062_thermal_get_temp, | ||
| 193 | .get_mode = da9062_thermal_get_mode, | ||
| 194 | .get_trip_type = da9062_thermal_get_trip_type, | ||
| 195 | .get_trip_temp = da9062_thermal_get_trip_temp, | ||
| 196 | }; | ||
| 197 | |||
| 198 | static const struct da9062_thermal_config da9062_config = { | ||
| 199 | .name = "da9062-thermal", | ||
| 200 | }; | ||
| 201 | |||
| 202 | static const struct of_device_id da9062_compatible_reg_id_table[] = { | ||
| 203 | { .compatible = "dlg,da9062-thermal", .data = &da9062_config }, | ||
| 204 | { }, | ||
| 205 | }; | ||
| 206 | |||
| 207 | MODULE_DEVICE_TABLE(of, da9062_compatible_reg_id_table); | ||
| 208 | |||
| 209 | static int da9062_thermal_probe(struct platform_device *pdev) | ||
| 210 | { | ||
| 211 | struct da9062 *chip = dev_get_drvdata(pdev->dev.parent); | ||
| 212 | struct da9062_thermal *thermal; | ||
| 213 | unsigned int pp_tmp = DA9062_DEFAULT_POLLING_MS_PERIOD; | ||
| 214 | const struct of_device_id *match; | ||
| 215 | int ret = 0; | ||
| 216 | |||
| 217 | match = of_match_node(da9062_compatible_reg_id_table, | ||
| 218 | pdev->dev.of_node); | ||
| 219 | if (!match) | ||
| 220 | return -ENXIO; | ||
| 221 | |||
| 222 | if (pdev->dev.of_node) { | ||
| 223 | if (!of_property_read_u32(pdev->dev.of_node, | ||
| 224 | "polling-delay-passive", | ||
| 225 | &pp_tmp)) { | ||
| 226 | if (pp_tmp < DA9062_MIN_POLLING_MS_PERIOD || | ||
| 227 | pp_tmp > DA9062_MAX_POLLING_MS_PERIOD) { | ||
| 228 | dev_warn(&pdev->dev, | ||
| 229 | "Out-of-range polling period %d ms\n", | ||
| 230 | pp_tmp); | ||
| 231 | pp_tmp = DA9062_DEFAULT_POLLING_MS_PERIOD; | ||
| 232 | } | ||
| 233 | } | ||
| 234 | } | ||
| 235 | |||
| 236 | thermal = devm_kzalloc(&pdev->dev, sizeof(struct da9062_thermal), | ||
| 237 | GFP_KERNEL); | ||
| 238 | if (!thermal) { | ||
| 239 | ret = -ENOMEM; | ||
| 240 | goto err; | ||
| 241 | } | ||
| 242 | |||
| 243 | thermal->config = match->data; | ||
| 244 | thermal->hw = chip; | ||
| 245 | thermal->mode = THERMAL_DEVICE_ENABLED; | ||
| 246 | thermal->dev = &pdev->dev; | ||
| 247 | |||
| 248 | INIT_DELAYED_WORK(&thermal->work, da9062_thermal_poll_on); | ||
| 249 | mutex_init(&thermal->lock); | ||
| 250 | |||
| 251 | thermal->zone = thermal_zone_device_register(thermal->config->name, | ||
| 252 | 1, 0, thermal, | ||
| 253 | &da9062_thermal_ops, NULL, pp_tmp, | ||
| 254 | 0); | ||
| 255 | if (IS_ERR(thermal->zone)) { | ||
| 256 | dev_err(&pdev->dev, "Cannot register thermal zone device\n"); | ||
| 257 | ret = PTR_ERR(thermal->zone); | ||
| 258 | goto err; | ||
| 259 | } | ||
| 260 | |||
| 261 | dev_dbg(&pdev->dev, | ||
| 262 | "TJUNC temperature polling period set at %d ms\n", | ||
| 263 | thermal->zone->passive_delay); | ||
| 264 | |||
| 265 | ret = platform_get_irq_byname(pdev, "THERMAL"); | ||
| 266 | if (ret < 0) { | ||
| 267 | dev_err(&pdev->dev, "Failed to get platform IRQ.\n"); | ||
| 268 | goto err_zone; | ||
| 269 | } | ||
| 270 | thermal->irq = ret; | ||
| 271 | |||
| 272 | ret = request_threaded_irq(thermal->irq, NULL, | ||
| 273 | da9062_thermal_irq_handler, | ||
| 274 | IRQF_TRIGGER_LOW | IRQF_ONESHOT, | ||
| 275 | "THERMAL", thermal); | ||
| 276 | if (ret) { | ||
| 277 | dev_err(&pdev->dev, | ||
| 278 | "Failed to request thermal device IRQ.\n"); | ||
| 279 | goto err_zone; | ||
| 280 | } | ||
| 281 | |||
| 282 | platform_set_drvdata(pdev, thermal); | ||
| 283 | return 0; | ||
| 284 | |||
| 285 | err_zone: | ||
| 286 | thermal_zone_device_unregister(thermal->zone); | ||
| 287 | err: | ||
| 288 | return ret; | ||
| 289 | } | ||
| 290 | |||
| 291 | static int da9062_thermal_remove(struct platform_device *pdev) | ||
| 292 | { | ||
| 293 | struct da9062_thermal *thermal = platform_get_drvdata(pdev); | ||
| 294 | |||
| 295 | free_irq(thermal->irq, thermal); | ||
| 296 | cancel_delayed_work_sync(&thermal->work); | ||
| 297 | thermal_zone_device_unregister(thermal->zone); | ||
| 298 | return 0; | ||
| 299 | } | ||
| 300 | |||
| 301 | static struct platform_driver da9062_thermal_driver = { | ||
| 302 | .probe = da9062_thermal_probe, | ||
| 303 | .remove = da9062_thermal_remove, | ||
| 304 | .driver = { | ||
| 305 | .name = "da9062-thermal", | ||
| 306 | .of_match_table = da9062_compatible_reg_id_table, | ||
| 307 | }, | ||
| 308 | }; | ||
| 309 | |||
| 310 | module_platform_driver(da9062_thermal_driver); | ||
| 311 | |||
| 312 | MODULE_AUTHOR("Steve Twiss"); | ||
| 313 | MODULE_DESCRIPTION("Thermal TJUNC device driver for Dialog DA9062 and DA9061"); | ||
| 314 | MODULE_LICENSE("GPL"); | ||
| 315 | MODULE_ALIAS("platform:da9062-thermal"); | ||
diff --git a/drivers/thermal/devfreq_cooling.c b/drivers/thermal/devfreq_cooling.c index 4bf4ad58cffd..ef59256887ff 100644 --- a/drivers/thermal/devfreq_cooling.c +++ b/drivers/thermal/devfreq_cooling.c | |||
| @@ -28,6 +28,8 @@ | |||
| 28 | 28 | ||
| 29 | #include <trace/events/thermal.h> | 29 | #include <trace/events/thermal.h> |
| 30 | 30 | ||
| 31 | #define SCALE_ERROR_MITIGATION 100 | ||
| 32 | |||
| 31 | static DEFINE_IDA(devfreq_ida); | 33 | static DEFINE_IDA(devfreq_ida); |
| 32 | 34 | ||
| 33 | /** | 35 | /** |
| @@ -45,6 +47,12 @@ static DEFINE_IDA(devfreq_ida); | |||
| 45 | * @freq_table_size: Size of the @freq_table and @power_table | 47 | * @freq_table_size: Size of the @freq_table and @power_table |
| 46 | * @power_ops: Pointer to devfreq_cooling_power, used to generate the | 48 | * @power_ops: Pointer to devfreq_cooling_power, used to generate the |
| 47 | * @power_table. | 49 | * @power_table. |
| 50 | * @res_util: Resource utilization scaling factor for the power. | ||
| 51 | * It is multiplied by 100 to minimize the error. It is used | ||
| 52 | * for estimation of the power budget instead of using | ||
| 53 | * 'utilization' (which is 'busy_time / 'total_time'). | ||
| 54 | * The 'res_util' range is from 100 to (power_table[state] * 100) | ||
| 55 | * for the corresponding 'state'. | ||
| 48 | */ | 56 | */ |
| 49 | struct devfreq_cooling_device { | 57 | struct devfreq_cooling_device { |
| 50 | int id; | 58 | int id; |
| @@ -55,6 +63,8 @@ struct devfreq_cooling_device { | |||
| 55 | u32 *freq_table; | 63 | u32 *freq_table; |
| 56 | size_t freq_table_size; | 64 | size_t freq_table_size; |
| 57 | struct devfreq_cooling_power *power_ops; | 65 | struct devfreq_cooling_power *power_ops; |
| 66 | u32 res_util; | ||
| 67 | int capped_state; | ||
| 58 | }; | 68 | }; |
| 59 | 69 | ||
| 60 | /** | 70 | /** |
| @@ -164,27 +174,12 @@ freq_get_state(struct devfreq_cooling_device *dfc, unsigned long freq) | |||
| 164 | return THERMAL_CSTATE_INVALID; | 174 | return THERMAL_CSTATE_INVALID; |
| 165 | } | 175 | } |
| 166 | 176 | ||
| 167 | /** | 177 | static unsigned long get_voltage(struct devfreq *df, unsigned long freq) |
| 168 | * get_static_power() - calculate the static power | ||
| 169 | * @dfc: Pointer to devfreq cooling device | ||
| 170 | * @freq: Frequency in Hz | ||
| 171 | * | ||
| 172 | * Calculate the static power in milliwatts using the supplied | ||
| 173 | * get_static_power(). The current voltage is calculated using the | ||
| 174 | * OPP library. If no get_static_power() was supplied, assume the | ||
| 175 | * static power is negligible. | ||
| 176 | */ | ||
| 177 | static unsigned long | ||
| 178 | get_static_power(struct devfreq_cooling_device *dfc, unsigned long freq) | ||
| 179 | { | 178 | { |
| 180 | struct devfreq *df = dfc->devfreq; | ||
| 181 | struct device *dev = df->dev.parent; | 179 | struct device *dev = df->dev.parent; |
| 182 | unsigned long voltage; | 180 | unsigned long voltage; |
| 183 | struct dev_pm_opp *opp; | 181 | struct dev_pm_opp *opp; |
| 184 | 182 | ||
| 185 | if (!dfc->power_ops->get_static_power) | ||
| 186 | return 0; | ||
| 187 | |||
| 188 | opp = dev_pm_opp_find_freq_exact(dev, freq, true); | 183 | opp = dev_pm_opp_find_freq_exact(dev, freq, true); |
| 189 | if (PTR_ERR(opp) == -ERANGE) | 184 | if (PTR_ERR(opp) == -ERANGE) |
| 190 | opp = dev_pm_opp_find_freq_exact(dev, freq, false); | 185 | opp = dev_pm_opp_find_freq_exact(dev, freq, false); |
| @@ -202,9 +197,35 @@ get_static_power(struct devfreq_cooling_device *dfc, unsigned long freq) | |||
| 202 | dev_err_ratelimited(dev, | 197 | dev_err_ratelimited(dev, |
| 203 | "Failed to get voltage for frequency %lu\n", | 198 | "Failed to get voltage for frequency %lu\n", |
| 204 | freq); | 199 | freq); |
| 205 | return 0; | ||
| 206 | } | 200 | } |
| 207 | 201 | ||
| 202 | return voltage; | ||
| 203 | } | ||
| 204 | |||
| 205 | /** | ||
| 206 | * get_static_power() - calculate the static power | ||
| 207 | * @dfc: Pointer to devfreq cooling device | ||
| 208 | * @freq: Frequency in Hz | ||
| 209 | * | ||
| 210 | * Calculate the static power in milliwatts using the supplied | ||
| 211 | * get_static_power(). The current voltage is calculated using the | ||
| 212 | * OPP library. If no get_static_power() was supplied, assume the | ||
| 213 | * static power is negligible. | ||
| 214 | */ | ||
| 215 | static unsigned long | ||
| 216 | get_static_power(struct devfreq_cooling_device *dfc, unsigned long freq) | ||
| 217 | { | ||
| 218 | struct devfreq *df = dfc->devfreq; | ||
| 219 | unsigned long voltage; | ||
| 220 | |||
| 221 | if (!dfc->power_ops->get_static_power) | ||
| 222 | return 0; | ||
| 223 | |||
| 224 | voltage = get_voltage(df, freq); | ||
| 225 | |||
| 226 | if (voltage == 0) | ||
| 227 | return 0; | ||
| 228 | |||
| 208 | return dfc->power_ops->get_static_power(df, voltage); | 229 | return dfc->power_ops->get_static_power(df, voltage); |
| 209 | } | 230 | } |
| 210 | 231 | ||
| @@ -239,6 +260,16 @@ get_dynamic_power(struct devfreq_cooling_device *dfc, unsigned long freq, | |||
| 239 | return power; | 260 | return power; |
| 240 | } | 261 | } |
| 241 | 262 | ||
| 263 | |||
| 264 | static inline unsigned long get_total_power(struct devfreq_cooling_device *dfc, | ||
| 265 | unsigned long freq, | ||
| 266 | unsigned long voltage) | ||
| 267 | { | ||
| 268 | return get_static_power(dfc, freq) + get_dynamic_power(dfc, freq, | ||
| 269 | voltage); | ||
| 270 | } | ||
| 271 | |||
| 272 | |||
| 242 | static int devfreq_cooling_get_requested_power(struct thermal_cooling_device *cdev, | 273 | static int devfreq_cooling_get_requested_power(struct thermal_cooling_device *cdev, |
| 243 | struct thermal_zone_device *tz, | 274 | struct thermal_zone_device *tz, |
| 244 | u32 *power) | 275 | u32 *power) |
| @@ -248,27 +279,55 @@ static int devfreq_cooling_get_requested_power(struct thermal_cooling_device *cd | |||
| 248 | struct devfreq_dev_status *status = &df->last_status; | 279 | struct devfreq_dev_status *status = &df->last_status; |
| 249 | unsigned long state; | 280 | unsigned long state; |
| 250 | unsigned long freq = status->current_frequency; | 281 | unsigned long freq = status->current_frequency; |
| 251 | u32 dyn_power, static_power; | 282 | unsigned long voltage; |
| 283 | u32 dyn_power = 0; | ||
| 284 | u32 static_power = 0; | ||
| 285 | int res; | ||
| 252 | 286 | ||
| 253 | /* Get dynamic power for state */ | ||
| 254 | state = freq_get_state(dfc, freq); | 287 | state = freq_get_state(dfc, freq); |
| 255 | if (state == THERMAL_CSTATE_INVALID) | 288 | if (state == THERMAL_CSTATE_INVALID) { |
| 256 | return -EAGAIN; | 289 | res = -EAGAIN; |
| 290 | goto fail; | ||
| 291 | } | ||
| 257 | 292 | ||
| 258 | dyn_power = dfc->power_table[state]; | 293 | if (dfc->power_ops->get_real_power) { |
| 294 | voltage = get_voltage(df, freq); | ||
| 295 | if (voltage == 0) { | ||
| 296 | res = -EINVAL; | ||
| 297 | goto fail; | ||
| 298 | } | ||
| 259 | 299 | ||
| 260 | /* Scale dynamic power for utilization */ | 300 | res = dfc->power_ops->get_real_power(df, power, freq, voltage); |
| 261 | dyn_power = (dyn_power * status->busy_time) / status->total_time; | 301 | if (!res) { |
| 302 | state = dfc->capped_state; | ||
| 303 | dfc->res_util = dfc->power_table[state]; | ||
| 304 | dfc->res_util *= SCALE_ERROR_MITIGATION; | ||
| 262 | 305 | ||
| 263 | /* Get static power */ | 306 | if (*power > 1) |
| 264 | static_power = get_static_power(dfc, freq); | 307 | dfc->res_util /= *power; |
| 308 | } else { | ||
| 309 | goto fail; | ||
| 310 | } | ||
| 311 | } else { | ||
| 312 | dyn_power = dfc->power_table[state]; | ||
| 265 | 313 | ||
| 266 | trace_thermal_power_devfreq_get_power(cdev, status, freq, dyn_power, | 314 | /* Scale dynamic power for utilization */ |
| 267 | static_power); | 315 | dyn_power *= status->busy_time; |
| 316 | dyn_power /= status->total_time; | ||
| 317 | /* Get static power */ | ||
| 318 | static_power = get_static_power(dfc, freq); | ||
| 268 | 319 | ||
| 269 | *power = dyn_power + static_power; | 320 | *power = dyn_power + static_power; |
| 321 | } | ||
| 322 | |||
| 323 | trace_thermal_power_devfreq_get_power(cdev, status, freq, dyn_power, | ||
| 324 | static_power, *power); | ||
| 270 | 325 | ||
| 271 | return 0; | 326 | return 0; |
| 327 | fail: | ||
| 328 | /* It is safe to set max in this case */ | ||
| 329 | dfc->res_util = SCALE_ERROR_MITIGATION; | ||
| 330 | return res; | ||
| 272 | } | 331 | } |
| 273 | 332 | ||
| 274 | static int devfreq_cooling_state2power(struct thermal_cooling_device *cdev, | 333 | static int devfreq_cooling_state2power(struct thermal_cooling_device *cdev, |
| @@ -301,26 +360,34 @@ static int devfreq_cooling_power2state(struct thermal_cooling_device *cdev, | |||
| 301 | unsigned long busy_time; | 360 | unsigned long busy_time; |
| 302 | s32 dyn_power; | 361 | s32 dyn_power; |
| 303 | u32 static_power; | 362 | u32 static_power; |
| 363 | s32 est_power; | ||
| 304 | int i; | 364 | int i; |
| 305 | 365 | ||
| 306 | static_power = get_static_power(dfc, freq); | 366 | if (dfc->power_ops->get_real_power) { |
| 367 | /* Scale for resource utilization */ | ||
| 368 | est_power = power * dfc->res_util; | ||
| 369 | est_power /= SCALE_ERROR_MITIGATION; | ||
| 370 | } else { | ||
| 371 | static_power = get_static_power(dfc, freq); | ||
| 307 | 372 | ||
| 308 | dyn_power = power - static_power; | 373 | dyn_power = power - static_power; |
| 309 | dyn_power = dyn_power > 0 ? dyn_power : 0; | 374 | dyn_power = dyn_power > 0 ? dyn_power : 0; |
| 310 | 375 | ||
| 311 | /* Scale dynamic power for utilization */ | 376 | /* Scale dynamic power for utilization */ |
| 312 | busy_time = status->busy_time ?: 1; | 377 | busy_time = status->busy_time ?: 1; |
| 313 | dyn_power = (dyn_power * status->total_time) / busy_time; | 378 | est_power = (dyn_power * status->total_time) / busy_time; |
| 379 | } | ||
| 314 | 380 | ||
| 315 | /* | 381 | /* |
| 316 | * Find the first cooling state that is within the power | 382 | * Find the first cooling state that is within the power |
| 317 | * budget for dynamic power. | 383 | * budget for dynamic power. |
| 318 | */ | 384 | */ |
| 319 | for (i = 0; i < dfc->freq_table_size - 1; i++) | 385 | for (i = 0; i < dfc->freq_table_size - 1; i++) |
| 320 | if (dyn_power >= dfc->power_table[i]) | 386 | if (est_power >= dfc->power_table[i]) |
| 321 | break; | 387 | break; |
| 322 | 388 | ||
| 323 | *state = i; | 389 | *state = i; |
| 390 | dfc->capped_state = i; | ||
| 324 | trace_thermal_power_devfreq_limit(cdev, freq, *state, power); | 391 | trace_thermal_power_devfreq_limit(cdev, freq, *state, power); |
| 325 | return 0; | 392 | return 0; |
| 326 | } | 393 | } |
| @@ -376,7 +443,7 @@ static int devfreq_cooling_gen_tables(struct devfreq_cooling_device *dfc) | |||
| 376 | } | 443 | } |
| 377 | 444 | ||
| 378 | for (i = 0, freq = ULONG_MAX; i < num_opps; i++, freq--) { | 445 | for (i = 0, freq = ULONG_MAX; i < num_opps; i++, freq--) { |
| 379 | unsigned long power_dyn, voltage; | 446 | unsigned long power, voltage; |
| 380 | struct dev_pm_opp *opp; | 447 | struct dev_pm_opp *opp; |
| 381 | 448 | ||
| 382 | opp = dev_pm_opp_find_freq_floor(dev, &freq); | 449 | opp = dev_pm_opp_find_freq_floor(dev, &freq); |
| @@ -389,12 +456,15 @@ static int devfreq_cooling_gen_tables(struct devfreq_cooling_device *dfc) | |||
| 389 | dev_pm_opp_put(opp); | 456 | dev_pm_opp_put(opp); |
| 390 | 457 | ||
| 391 | if (dfc->power_ops) { | 458 | if (dfc->power_ops) { |
| 392 | power_dyn = get_dynamic_power(dfc, freq, voltage); | 459 | if (dfc->power_ops->get_real_power) |
| 460 | power = get_total_power(dfc, freq, voltage); | ||
| 461 | else | ||
| 462 | power = get_dynamic_power(dfc, freq, voltage); | ||
| 393 | 463 | ||
| 394 | dev_dbg(dev, "Dynamic power table: %lu MHz @ %lu mV: %lu = %lu mW\n", | 464 | dev_dbg(dev, "Power table: %lu MHz @ %lu mV: %lu = %lu mW\n", |
| 395 | freq / 1000000, voltage, power_dyn, power_dyn); | 465 | freq / 1000000, voltage, power, power); |
| 396 | 466 | ||
| 397 | power_table[i] = power_dyn; | 467 | power_table[i] = power; |
| 398 | } | 468 | } |
| 399 | 469 | ||
| 400 | freq_table[i] = freq; | 470 | freq_table[i] = freq; |
diff --git a/drivers/thermal/intel_soc_dts_thermal.c b/drivers/thermal/intel_soc_dts_thermal.c index b2bbaa1c60b0..c27868b2c6af 100644 --- a/drivers/thermal/intel_soc_dts_thermal.c +++ b/drivers/thermal/intel_soc_dts_thermal.c | |||
| @@ -73,8 +73,12 @@ static int __init intel_soc_thermal_init(void) | |||
| 73 | IRQF_TRIGGER_RISING | IRQF_ONESHOT, | 73 | IRQF_TRIGGER_RISING | IRQF_ONESHOT, |
| 74 | "soc_dts", soc_dts); | 74 | "soc_dts", soc_dts); |
| 75 | if (err) { | 75 | if (err) { |
| 76 | pr_err("request_threaded_irq ret %d\n", err); | 76 | /* |
| 77 | goto error_irq; | 77 | * Do not just error out because the user space thermal |
| 78 | * daemon such as DPTF may use polling instead of being | ||
| 79 | * interrupt driven. | ||
| 80 | */ | ||
| 81 | pr_warn("request_threaded_irq ret %d\n", err); | ||
| 78 | } | 82 | } |
| 79 | } | 83 | } |
| 80 | 84 | ||
| @@ -88,7 +92,6 @@ static int __init intel_soc_thermal_init(void) | |||
| 88 | error_trips: | 92 | error_trips: |
| 89 | if (soc_dts_thres_irq) | 93 | if (soc_dts_thres_irq) |
| 90 | free_irq(soc_dts_thres_irq, soc_dts); | 94 | free_irq(soc_dts_thres_irq, soc_dts); |
| 91 | error_irq: | ||
| 92 | intel_soc_dts_iosf_exit(soc_dts); | 95 | intel_soc_dts_iosf_exit(soc_dts); |
| 93 | 96 | ||
| 94 | return err; | 97 | return err; |
diff --git a/drivers/thermal/mtk_thermal.c b/drivers/thermal/mtk_thermal.c index 1aff7fde54b1..7737f14846f9 100644 --- a/drivers/thermal/mtk_thermal.c +++ b/drivers/thermal/mtk_thermal.c | |||
| @@ -191,7 +191,7 @@ static const int mt8173_bank_data[MT8173_NUM_ZONES][3] = { | |||
| 191 | }; | 191 | }; |
| 192 | 192 | ||
| 193 | static const int mt8173_msr[MT8173_NUM_SENSORS_PER_ZONE] = { | 193 | static const int mt8173_msr[MT8173_NUM_SENSORS_PER_ZONE] = { |
| 194 | TEMP_MSR0, TEMP_MSR1, TEMP_MSR2, TEMP_MSR2 | 194 | TEMP_MSR0, TEMP_MSR1, TEMP_MSR2, TEMP_MSR3 |
| 195 | }; | 195 | }; |
| 196 | 196 | ||
| 197 | static const int mt8173_adcpnp[MT8173_NUM_SENSORS_PER_ZONE] = { | 197 | static const int mt8173_adcpnp[MT8173_NUM_SENSORS_PER_ZONE] = { |
diff --git a/drivers/thermal/rcar_gen3_thermal.c b/drivers/thermal/rcar_gen3_thermal.c index d33c845244b1..37fcefd06d9f 100644 --- a/drivers/thermal/rcar_gen3_thermal.c +++ b/drivers/thermal/rcar_gen3_thermal.c | |||
| @@ -20,12 +20,14 @@ | |||
| 20 | #include <linux/interrupt.h> | 20 | #include <linux/interrupt.h> |
| 21 | #include <linux/io.h> | 21 | #include <linux/io.h> |
| 22 | #include <linux/module.h> | 22 | #include <linux/module.h> |
| 23 | #include <linux/mutex.h> | ||
| 24 | #include <linux/of_device.h> | 23 | #include <linux/of_device.h> |
| 25 | #include <linux/platform_device.h> | 24 | #include <linux/platform_device.h> |
| 26 | #include <linux/pm_runtime.h> | 25 | #include <linux/pm_runtime.h> |
| 26 | #include <linux/spinlock.h> | ||
| 27 | #include <linux/thermal.h> | 27 | #include <linux/thermal.h> |
| 28 | 28 | ||
| 29 | #include "thermal_core.h" | ||
| 30 | |||
| 29 | /* Register offsets */ | 31 | /* Register offsets */ |
| 30 | #define REG_GEN3_IRQSTR 0x04 | 32 | #define REG_GEN3_IRQSTR 0x04 |
| 31 | #define REG_GEN3_IRQMSK 0x08 | 33 | #define REG_GEN3_IRQMSK 0x08 |
| @@ -41,6 +43,14 @@ | |||
| 41 | #define REG_GEN3_THCODE2 0x54 | 43 | #define REG_GEN3_THCODE2 0x54 |
| 42 | #define REG_GEN3_THCODE3 0x58 | 44 | #define REG_GEN3_THCODE3 0x58 |
| 43 | 45 | ||
| 46 | /* IRQ{STR,MSK,EN} bits */ | ||
| 47 | #define IRQ_TEMP1 BIT(0) | ||
| 48 | #define IRQ_TEMP2 BIT(1) | ||
| 49 | #define IRQ_TEMP3 BIT(2) | ||
| 50 | #define IRQ_TEMPD1 BIT(3) | ||
| 51 | #define IRQ_TEMPD2 BIT(4) | ||
| 52 | #define IRQ_TEMPD3 BIT(5) | ||
| 53 | |||
| 44 | /* CTSR bits */ | 54 | /* CTSR bits */ |
| 45 | #define CTSR_PONM BIT(8) | 55 | #define CTSR_PONM BIT(8) |
| 46 | #define CTSR_AOUT BIT(7) | 56 | #define CTSR_AOUT BIT(7) |
| @@ -72,11 +82,15 @@ struct rcar_gen3_thermal_tsc { | |||
| 72 | void __iomem *base; | 82 | void __iomem *base; |
| 73 | struct thermal_zone_device *zone; | 83 | struct thermal_zone_device *zone; |
| 74 | struct equation_coefs coef; | 84 | struct equation_coefs coef; |
| 75 | struct mutex lock; | 85 | int low; |
| 86 | int high; | ||
| 76 | }; | 87 | }; |
| 77 | 88 | ||
| 78 | struct rcar_gen3_thermal_priv { | 89 | struct rcar_gen3_thermal_priv { |
| 79 | struct rcar_gen3_thermal_tsc *tscs[TSC_MAX_NUM]; | 90 | struct rcar_gen3_thermal_tsc *tscs[TSC_MAX_NUM]; |
| 91 | unsigned int num_tscs; | ||
| 92 | spinlock_t lock; /* Protect interrupts on and off */ | ||
| 93 | const struct rcar_gen3_thermal_data *data; | ||
| 80 | }; | 94 | }; |
| 81 | 95 | ||
| 82 | struct rcar_gen3_thermal_data { | 96 | struct rcar_gen3_thermal_data { |
| @@ -114,6 +128,7 @@ static inline void rcar_gen3_thermal_write(struct rcar_gen3_thermal_tsc *tsc, | |||
| 114 | 128 | ||
| 115 | #define FIXPT_SHIFT 7 | 129 | #define FIXPT_SHIFT 7 |
| 116 | #define FIXPT_INT(_x) ((_x) << FIXPT_SHIFT) | 130 | #define FIXPT_INT(_x) ((_x) << FIXPT_SHIFT) |
| 131 | #define INT_FIXPT(_x) ((_x) >> FIXPT_SHIFT) | ||
| 117 | #define FIXPT_DIV(_a, _b) DIV_ROUND_CLOSEST(((_a) << FIXPT_SHIFT), (_b)) | 132 | #define FIXPT_DIV(_a, _b) DIV_ROUND_CLOSEST(((_a) << FIXPT_SHIFT), (_b)) |
| 118 | #define FIXPT_TO_MCELSIUS(_x) ((_x) * 1000 >> FIXPT_SHIFT) | 133 | #define FIXPT_TO_MCELSIUS(_x) ((_x) * 1000 >> FIXPT_SHIFT) |
| 119 | 134 | ||
| @@ -163,16 +178,12 @@ static int rcar_gen3_thermal_get_temp(void *devdata, int *temp) | |||
| 163 | u32 reg; | 178 | u32 reg; |
| 164 | 179 | ||
| 165 | /* Read register and convert to mili Celsius */ | 180 | /* Read register and convert to mili Celsius */ |
| 166 | mutex_lock(&tsc->lock); | ||
| 167 | |||
| 168 | reg = rcar_gen3_thermal_read(tsc, REG_GEN3_TEMP) & CTEMP_MASK; | 181 | reg = rcar_gen3_thermal_read(tsc, REG_GEN3_TEMP) & CTEMP_MASK; |
| 169 | 182 | ||
| 170 | val1 = FIXPT_DIV(FIXPT_INT(reg) - tsc->coef.b1, tsc->coef.a1); | 183 | val1 = FIXPT_DIV(FIXPT_INT(reg) - tsc->coef.b1, tsc->coef.a1); |
| 171 | val2 = FIXPT_DIV(FIXPT_INT(reg) - tsc->coef.b2, tsc->coef.a2); | 184 | val2 = FIXPT_DIV(FIXPT_INT(reg) - tsc->coef.b2, tsc->coef.a2); |
| 172 | mcelsius = FIXPT_TO_MCELSIUS((val1 + val2) / 2); | 185 | mcelsius = FIXPT_TO_MCELSIUS((val1 + val2) / 2); |
| 173 | 186 | ||
| 174 | mutex_unlock(&tsc->lock); | ||
| 175 | |||
| 176 | /* Make sure we are inside specifications */ | 187 | /* Make sure we are inside specifications */ |
| 177 | if ((mcelsius < MCELSIUS(-40)) || (mcelsius > MCELSIUS(125))) | 188 | if ((mcelsius < MCELSIUS(-40)) || (mcelsius > MCELSIUS(125))) |
| 178 | return -EIO; | 189 | return -EIO; |
| @@ -183,10 +194,90 @@ static int rcar_gen3_thermal_get_temp(void *devdata, int *temp) | |||
| 183 | return 0; | 194 | return 0; |
| 184 | } | 195 | } |
| 185 | 196 | ||
| 197 | static int rcar_gen3_thermal_mcelsius_to_temp(struct rcar_gen3_thermal_tsc *tsc, | ||
| 198 | int mcelsius) | ||
| 199 | { | ||
| 200 | int celsius, val1, val2; | ||
| 201 | |||
| 202 | celsius = DIV_ROUND_CLOSEST(mcelsius, 1000); | ||
| 203 | val1 = celsius * tsc->coef.a1 + tsc->coef.b1; | ||
| 204 | val2 = celsius * tsc->coef.a2 + tsc->coef.b2; | ||
| 205 | |||
| 206 | return INT_FIXPT((val1 + val2) / 2); | ||
| 207 | } | ||
| 208 | |||
| 209 | static int rcar_gen3_thermal_set_trips(void *devdata, int low, int high) | ||
| 210 | { | ||
| 211 | struct rcar_gen3_thermal_tsc *tsc = devdata; | ||
| 212 | |||
| 213 | low = clamp_val(low, -40000, 125000); | ||
| 214 | high = clamp_val(high, -40000, 125000); | ||
| 215 | |||
| 216 | rcar_gen3_thermal_write(tsc, REG_GEN3_IRQTEMP1, | ||
| 217 | rcar_gen3_thermal_mcelsius_to_temp(tsc, low)); | ||
| 218 | |||
| 219 | rcar_gen3_thermal_write(tsc, REG_GEN3_IRQTEMP2, | ||
| 220 | rcar_gen3_thermal_mcelsius_to_temp(tsc, high)); | ||
| 221 | |||
| 222 | tsc->low = low; | ||
| 223 | tsc->high = high; | ||
| 224 | |||
| 225 | return 0; | ||
| 226 | } | ||
| 227 | |||
| 186 | static struct thermal_zone_of_device_ops rcar_gen3_tz_of_ops = { | 228 | static struct thermal_zone_of_device_ops rcar_gen3_tz_of_ops = { |
| 187 | .get_temp = rcar_gen3_thermal_get_temp, | 229 | .get_temp = rcar_gen3_thermal_get_temp, |
| 230 | .set_trips = rcar_gen3_thermal_set_trips, | ||
| 188 | }; | 231 | }; |
| 189 | 232 | ||
| 233 | static void rcar_thermal_irq_set(struct rcar_gen3_thermal_priv *priv, bool on) | ||
| 234 | { | ||
| 235 | unsigned int i; | ||
| 236 | u32 val = on ? IRQ_TEMPD1 | IRQ_TEMP2 : 0; | ||
| 237 | |||
| 238 | for (i = 0; i < priv->num_tscs; i++) | ||
| 239 | rcar_gen3_thermal_write(priv->tscs[i], REG_GEN3_IRQMSK, val); | ||
| 240 | } | ||
| 241 | |||
| 242 | static irqreturn_t rcar_gen3_thermal_irq(int irq, void *data) | ||
| 243 | { | ||
| 244 | struct rcar_gen3_thermal_priv *priv = data; | ||
| 245 | u32 status; | ||
| 246 | int i, ret = IRQ_HANDLED; | ||
| 247 | |||
| 248 | spin_lock(&priv->lock); | ||
| 249 | for (i = 0; i < priv->num_tscs; i++) { | ||
| 250 | status = rcar_gen3_thermal_read(priv->tscs[i], REG_GEN3_IRQSTR); | ||
| 251 | rcar_gen3_thermal_write(priv->tscs[i], REG_GEN3_IRQSTR, 0); | ||
| 252 | if (status) | ||
| 253 | ret = IRQ_WAKE_THREAD; | ||
| 254 | } | ||
| 255 | |||
| 256 | if (ret == IRQ_WAKE_THREAD) | ||
| 257 | rcar_thermal_irq_set(priv, false); | ||
| 258 | |||
| 259 | spin_unlock(&priv->lock); | ||
| 260 | |||
| 261 | return ret; | ||
| 262 | } | ||
| 263 | |||
| 264 | static irqreturn_t rcar_gen3_thermal_irq_thread(int irq, void *data) | ||
| 265 | { | ||
| 266 | struct rcar_gen3_thermal_priv *priv = data; | ||
| 267 | unsigned long flags; | ||
| 268 | int i; | ||
| 269 | |||
| 270 | for (i = 0; i < priv->num_tscs; i++) | ||
| 271 | thermal_zone_device_update(priv->tscs[i]->zone, | ||
| 272 | THERMAL_EVENT_UNSPECIFIED); | ||
| 273 | |||
| 274 | spin_lock_irqsave(&priv->lock, flags); | ||
| 275 | rcar_thermal_irq_set(priv, true); | ||
| 276 | spin_unlock_irqrestore(&priv->lock, flags); | ||
| 277 | |||
| 278 | return IRQ_HANDLED; | ||
| 279 | } | ||
| 280 | |||
| 190 | static void r8a7795_thermal_init(struct rcar_gen3_thermal_tsc *tsc) | 281 | static void r8a7795_thermal_init(struct rcar_gen3_thermal_tsc *tsc) |
| 191 | { | 282 | { |
| 192 | rcar_gen3_thermal_write(tsc, REG_GEN3_CTSR, CTSR_THBGR); | 283 | rcar_gen3_thermal_write(tsc, REG_GEN3_CTSR, CTSR_THBGR); |
| @@ -195,7 +286,11 @@ static void r8a7795_thermal_init(struct rcar_gen3_thermal_tsc *tsc) | |||
| 195 | usleep_range(1000, 2000); | 286 | usleep_range(1000, 2000); |
| 196 | 287 | ||
| 197 | rcar_gen3_thermal_write(tsc, REG_GEN3_CTSR, CTSR_PONM); | 288 | rcar_gen3_thermal_write(tsc, REG_GEN3_CTSR, CTSR_PONM); |
| 289 | |||
| 198 | rcar_gen3_thermal_write(tsc, REG_GEN3_IRQCTL, 0x3F); | 290 | rcar_gen3_thermal_write(tsc, REG_GEN3_IRQCTL, 0x3F); |
| 291 | rcar_gen3_thermal_write(tsc, REG_GEN3_IRQMSK, 0); | ||
| 292 | rcar_gen3_thermal_write(tsc, REG_GEN3_IRQEN, IRQ_TEMPD1 | IRQ_TEMP2); | ||
| 293 | |||
| 199 | rcar_gen3_thermal_write(tsc, REG_GEN3_CTSR, | 294 | rcar_gen3_thermal_write(tsc, REG_GEN3_CTSR, |
| 200 | CTSR_PONM | CTSR_AOUT | CTSR_THBGR | CTSR_VMEN); | 295 | CTSR_PONM | CTSR_AOUT | CTSR_THBGR | CTSR_VMEN); |
| 201 | 296 | ||
| @@ -219,9 +314,14 @@ static void r8a7796_thermal_init(struct rcar_gen3_thermal_tsc *tsc) | |||
| 219 | usleep_range(1000, 2000); | 314 | usleep_range(1000, 2000); |
| 220 | 315 | ||
| 221 | rcar_gen3_thermal_write(tsc, REG_GEN3_IRQCTL, 0x3F); | 316 | rcar_gen3_thermal_write(tsc, REG_GEN3_IRQCTL, 0x3F); |
| 317 | rcar_gen3_thermal_write(tsc, REG_GEN3_IRQMSK, 0); | ||
| 318 | rcar_gen3_thermal_write(tsc, REG_GEN3_IRQEN, IRQ_TEMPD1 | IRQ_TEMP2); | ||
| 319 | |||
| 222 | reg_val = rcar_gen3_thermal_read(tsc, REG_GEN3_THCTR); | 320 | reg_val = rcar_gen3_thermal_read(tsc, REG_GEN3_THCTR); |
| 223 | reg_val |= THCTR_THSST; | 321 | reg_val |= THCTR_THSST; |
| 224 | rcar_gen3_thermal_write(tsc, REG_GEN3_THCTR, reg_val); | 322 | rcar_gen3_thermal_write(tsc, REG_GEN3_THCTR, reg_val); |
| 323 | |||
| 324 | usleep_range(1000, 2000); | ||
| 225 | } | 325 | } |
| 226 | 326 | ||
| 227 | static const struct rcar_gen3_thermal_data r8a7795_data = { | 327 | static const struct rcar_gen3_thermal_data r8a7795_data = { |
| @@ -255,9 +355,8 @@ static int rcar_gen3_thermal_probe(struct platform_device *pdev) | |||
| 255 | struct device *dev = &pdev->dev; | 355 | struct device *dev = &pdev->dev; |
| 256 | struct resource *res; | 356 | struct resource *res; |
| 257 | struct thermal_zone_device *zone; | 357 | struct thermal_zone_device *zone; |
| 258 | int ret, i; | 358 | int ret, irq, i; |
| 259 | const struct rcar_gen3_thermal_data *match_data = | 359 | char *irqname; |
| 260 | of_device_get_match_data(dev); | ||
| 261 | 360 | ||
| 262 | /* default values if FUSEs are missing */ | 361 | /* default values if FUSEs are missing */ |
| 263 | /* TODO: Read values from hardware on supported platforms */ | 362 | /* TODO: Read values from hardware on supported platforms */ |
| @@ -272,24 +371,50 @@ static int rcar_gen3_thermal_probe(struct platform_device *pdev) | |||
| 272 | if (!priv) | 371 | if (!priv) |
| 273 | return -ENOMEM; | 372 | return -ENOMEM; |
| 274 | 373 | ||
| 374 | priv->data = of_device_get_match_data(dev); | ||
| 375 | |||
| 376 | spin_lock_init(&priv->lock); | ||
| 377 | |||
| 275 | platform_set_drvdata(pdev, priv); | 378 | platform_set_drvdata(pdev, priv); |
| 276 | 379 | ||
| 380 | /* | ||
| 381 | * Request 2 (of the 3 possible) IRQs, the driver only needs to | ||
| 382 | * to trigger on the low and high trip points of the current | ||
| 383 | * temp window at this point. | ||
| 384 | */ | ||
| 385 | for (i = 0; i < 2; i++) { | ||
| 386 | irq = platform_get_irq(pdev, i); | ||
| 387 | if (irq < 0) | ||
| 388 | return irq; | ||
| 389 | |||
| 390 | irqname = devm_kasprintf(dev, GFP_KERNEL, "%s:ch%d", | ||
| 391 | dev_name(dev), i); | ||
| 392 | if (!irqname) | ||
| 393 | return -ENOMEM; | ||
| 394 | |||
| 395 | ret = devm_request_threaded_irq(dev, irq, rcar_gen3_thermal_irq, | ||
| 396 | rcar_gen3_thermal_irq_thread, | ||
| 397 | IRQF_SHARED, irqname, priv); | ||
| 398 | if (ret) | ||
| 399 | return ret; | ||
| 400 | } | ||
| 401 | |||
| 277 | pm_runtime_enable(dev); | 402 | pm_runtime_enable(dev); |
| 278 | pm_runtime_get_sync(dev); | 403 | pm_runtime_get_sync(dev); |
| 279 | 404 | ||
| 280 | for (i = 0; i < TSC_MAX_NUM; i++) { | 405 | for (i = 0; i < TSC_MAX_NUM; i++) { |
| 281 | struct rcar_gen3_thermal_tsc *tsc; | 406 | struct rcar_gen3_thermal_tsc *tsc; |
| 282 | 407 | ||
| 408 | res = platform_get_resource(pdev, IORESOURCE_MEM, i); | ||
| 409 | if (!res) | ||
| 410 | break; | ||
| 411 | |||
| 283 | tsc = devm_kzalloc(dev, sizeof(*tsc), GFP_KERNEL); | 412 | tsc = devm_kzalloc(dev, sizeof(*tsc), GFP_KERNEL); |
| 284 | if (!tsc) { | 413 | if (!tsc) { |
| 285 | ret = -ENOMEM; | 414 | ret = -ENOMEM; |
| 286 | goto error_unregister; | 415 | goto error_unregister; |
| 287 | } | 416 | } |
| 288 | 417 | ||
| 289 | res = platform_get_resource(pdev, IORESOURCE_MEM, i); | ||
| 290 | if (!res) | ||
| 291 | break; | ||
| 292 | |||
| 293 | tsc->base = devm_ioremap_resource(dev, res); | 418 | tsc->base = devm_ioremap_resource(dev, res); |
| 294 | if (IS_ERR(tsc->base)) { | 419 | if (IS_ERR(tsc->base)) { |
| 295 | ret = PTR_ERR(tsc->base); | 420 | ret = PTR_ERR(tsc->base); |
| @@ -297,9 +422,8 @@ static int rcar_gen3_thermal_probe(struct platform_device *pdev) | |||
| 297 | } | 422 | } |
| 298 | 423 | ||
| 299 | priv->tscs[i] = tsc; | 424 | priv->tscs[i] = tsc; |
| 300 | mutex_init(&tsc->lock); | ||
| 301 | 425 | ||
| 302 | match_data->thermal_init(tsc); | 426 | priv->data->thermal_init(tsc); |
| 303 | rcar_gen3_thermal_calc_coefs(&tsc->coef, ptat, thcode[i]); | 427 | rcar_gen3_thermal_calc_coefs(&tsc->coef, ptat, thcode[i]); |
| 304 | 428 | ||
| 305 | zone = devm_thermal_zone_of_sensor_register(dev, i, tsc, | 429 | zone = devm_thermal_zone_of_sensor_register(dev, i, tsc, |
| @@ -310,8 +434,23 @@ static int rcar_gen3_thermal_probe(struct platform_device *pdev) | |||
| 310 | goto error_unregister; | 434 | goto error_unregister; |
| 311 | } | 435 | } |
| 312 | tsc->zone = zone; | 436 | tsc->zone = zone; |
| 437 | |||
| 438 | ret = of_thermal_get_ntrips(tsc->zone); | ||
| 439 | if (ret < 0) | ||
| 440 | goto error_unregister; | ||
| 441 | |||
| 442 | dev_info(dev, "TSC%d: Loaded %d trip points\n", i, ret); | ||
| 313 | } | 443 | } |
| 314 | 444 | ||
| 445 | priv->num_tscs = i; | ||
| 446 | |||
| 447 | if (!priv->num_tscs) { | ||
| 448 | ret = -ENODEV; | ||
| 449 | goto error_unregister; | ||
| 450 | } | ||
| 451 | |||
| 452 | rcar_thermal_irq_set(priv, true); | ||
| 453 | |||
| 315 | return 0; | 454 | return 0; |
| 316 | 455 | ||
| 317 | error_unregister: | 456 | error_unregister: |
| @@ -320,9 +459,39 @@ error_unregister: | |||
| 320 | return ret; | 459 | return ret; |
| 321 | } | 460 | } |
| 322 | 461 | ||
| 462 | static int __maybe_unused rcar_gen3_thermal_suspend(struct device *dev) | ||
| 463 | { | ||
| 464 | struct rcar_gen3_thermal_priv *priv = dev_get_drvdata(dev); | ||
| 465 | |||
| 466 | rcar_thermal_irq_set(priv, false); | ||
| 467 | |||
| 468 | return 0; | ||
| 469 | } | ||
| 470 | |||
| 471 | static int __maybe_unused rcar_gen3_thermal_resume(struct device *dev) | ||
| 472 | { | ||
| 473 | struct rcar_gen3_thermal_priv *priv = dev_get_drvdata(dev); | ||
| 474 | unsigned int i; | ||
| 475 | |||
| 476 | for (i = 0; i < priv->num_tscs; i++) { | ||
| 477 | struct rcar_gen3_thermal_tsc *tsc = priv->tscs[i]; | ||
| 478 | |||
| 479 | priv->data->thermal_init(tsc); | ||
| 480 | rcar_gen3_thermal_set_trips(tsc, tsc->low, tsc->high); | ||
| 481 | } | ||
| 482 | |||
| 483 | rcar_thermal_irq_set(priv, true); | ||
| 484 | |||
| 485 | return 0; | ||
| 486 | } | ||
| 487 | |||
| 488 | static SIMPLE_DEV_PM_OPS(rcar_gen3_thermal_pm_ops, rcar_gen3_thermal_suspend, | ||
| 489 | rcar_gen3_thermal_resume); | ||
| 490 | |||
| 323 | static struct platform_driver rcar_gen3_thermal_driver = { | 491 | static struct platform_driver rcar_gen3_thermal_driver = { |
| 324 | .driver = { | 492 | .driver = { |
| 325 | .name = "rcar_gen3_thermal", | 493 | .name = "rcar_gen3_thermal", |
| 494 | .pm = &rcar_gen3_thermal_pm_ops, | ||
| 326 | .of_match_table = rcar_gen3_thermal_dt_ids, | 495 | .of_match_table = rcar_gen3_thermal_dt_ids, |
| 327 | }, | 496 | }, |
| 328 | .probe = rcar_gen3_thermal_probe, | 497 | .probe = rcar_gen3_thermal_probe, |
diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 11f0675cb7e5..b21b9cc2c8d6 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c | |||
| @@ -45,8 +45,10 @@ static LIST_HEAD(thermal_governor_list); | |||
| 45 | 45 | ||
| 46 | static DEFINE_MUTEX(thermal_list_lock); | 46 | static DEFINE_MUTEX(thermal_list_lock); |
| 47 | static DEFINE_MUTEX(thermal_governor_lock); | 47 | static DEFINE_MUTEX(thermal_governor_lock); |
| 48 | static DEFINE_MUTEX(poweroff_lock); | ||
| 48 | 49 | ||
| 49 | static atomic_t in_suspend; | 50 | static atomic_t in_suspend; |
| 51 | static bool power_off_triggered; | ||
| 50 | 52 | ||
| 51 | static struct thermal_governor *def_governor; | 53 | static struct thermal_governor *def_governor; |
| 52 | 54 | ||
| @@ -322,6 +324,54 @@ static void handle_non_critical_trips(struct thermal_zone_device *tz, | |||
| 322 | def_governor->throttle(tz, trip); | 324 | def_governor->throttle(tz, trip); |
| 323 | } | 325 | } |
| 324 | 326 | ||
| 327 | /** | ||
| 328 | * thermal_emergency_poweroff_func - emergency poweroff work after a known delay | ||
| 329 | * @work: work_struct associated with the emergency poweroff function | ||
| 330 | * | ||
| 331 | * This function is called in very critical situations to force | ||
| 332 | * a kernel poweroff after a configurable timeout value. | ||
| 333 | */ | ||
| 334 | static void thermal_emergency_poweroff_func(struct work_struct *work) | ||
| 335 | { | ||
| 336 | /* | ||
| 337 | * We have reached here after the emergency thermal shutdown | ||
| 338 | * Waiting period has expired. This means orderly_poweroff has | ||
| 339 | * not been able to shut off the system for some reason. | ||
| 340 | * Try to shut down the system immediately using kernel_power_off | ||
| 341 | * if populated | ||
| 342 | */ | ||
| 343 | WARN(1, "Attempting kernel_power_off: Temperature too high\n"); | ||
| 344 | kernel_power_off(); | ||
| 345 | |||
| 346 | /* | ||
| 347 | * Worst of the worst case trigger emergency restart | ||
| 348 | */ | ||
| 349 | WARN(1, "Attempting emergency_restart: Temperature too high\n"); | ||
| 350 | emergency_restart(); | ||
| 351 | } | ||
| 352 | |||
| 353 | static DECLARE_DELAYED_WORK(thermal_emergency_poweroff_work, | ||
| 354 | thermal_emergency_poweroff_func); | ||
| 355 | |||
| 356 | /** | ||
| 357 | * thermal_emergency_poweroff - Trigger an emergency system poweroff | ||
| 358 | * | ||
| 359 | * This may be called from any critical situation to trigger a system shutdown | ||
| 360 | * after a known period of time. By default this is not scheduled. | ||
| 361 | */ | ||
| 362 | void thermal_emergency_poweroff(void) | ||
| 363 | { | ||
| 364 | int poweroff_delay_ms = CONFIG_THERMAL_EMERGENCY_POWEROFF_DELAY_MS; | ||
| 365 | /* | ||
| 366 | * poweroff_delay_ms must be a carefully profiled positive value. | ||
| 367 | * Its a must for thermal_emergency_poweroff_work to be scheduled | ||
| 368 | */ | ||
| 369 | if (poweroff_delay_ms <= 0) | ||
| 370 | return; | ||
| 371 | schedule_delayed_work(&thermal_emergency_poweroff_work, | ||
| 372 | msecs_to_jiffies(poweroff_delay_ms)); | ||
| 373 | } | ||
| 374 | |||
| 325 | static void handle_critical_trips(struct thermal_zone_device *tz, | 375 | static void handle_critical_trips(struct thermal_zone_device *tz, |
| 326 | int trip, enum thermal_trip_type trip_type) | 376 | int trip, enum thermal_trip_type trip_type) |
| 327 | { | 377 | { |
| @@ -342,7 +392,17 @@ static void handle_critical_trips(struct thermal_zone_device *tz, | |||
| 342 | dev_emerg(&tz->device, | 392 | dev_emerg(&tz->device, |
| 343 | "critical temperature reached(%d C),shutting down\n", | 393 | "critical temperature reached(%d C),shutting down\n", |
| 344 | tz->temperature / 1000); | 394 | tz->temperature / 1000); |
| 345 | orderly_poweroff(true); | 395 | mutex_lock(&poweroff_lock); |
| 396 | if (!power_off_triggered) { | ||
| 397 | /* | ||
| 398 | * Queue a backup emergency shutdown in the event of | ||
| 399 | * orderly_poweroff failure | ||
| 400 | */ | ||
| 401 | thermal_emergency_poweroff(); | ||
| 402 | orderly_poweroff(true); | ||
| 403 | power_off_triggered = true; | ||
| 404 | } | ||
| 405 | mutex_unlock(&poweroff_lock); | ||
| 346 | } | 406 | } |
| 347 | } | 407 | } |
| 348 | 408 | ||
| @@ -1463,6 +1523,7 @@ static int __init thermal_init(void) | |||
| 1463 | { | 1523 | { |
| 1464 | int result; | 1524 | int result; |
| 1465 | 1525 | ||
| 1526 | mutex_init(&poweroff_lock); | ||
| 1466 | result = thermal_register_governors(); | 1527 | result = thermal_register_governors(); |
| 1467 | if (result) | 1528 | if (result) |
| 1468 | goto error; | 1529 | goto error; |
| @@ -1497,6 +1558,7 @@ error: | |||
| 1497 | ida_destroy(&thermal_cdev_ida); | 1558 | ida_destroy(&thermal_cdev_ida); |
| 1498 | mutex_destroy(&thermal_list_lock); | 1559 | mutex_destroy(&thermal_list_lock); |
| 1499 | mutex_destroy(&thermal_governor_lock); | 1560 | mutex_destroy(&thermal_governor_lock); |
| 1561 | mutex_destroy(&poweroff_lock); | ||
| 1500 | return result; | 1562 | return result; |
| 1501 | } | 1563 | } |
| 1502 | 1564 | ||
diff --git a/drivers/thermal/ti-soc-thermal/dra752-thermal-data.c b/drivers/thermal/ti-soc-thermal/dra752-thermal-data.c index 118d7d847715..4167373327d9 100644 --- a/drivers/thermal/ti-soc-thermal/dra752-thermal-data.c +++ b/drivers/thermal/ti-soc-thermal/dra752-thermal-data.c | |||
| @@ -410,8 +410,6 @@ const struct ti_bandgap_data dra752_data = { | |||
| 410 | .domain = "cpu", | 410 | .domain = "cpu", |
| 411 | .register_cooling = ti_thermal_register_cpu_cooling, | 411 | .register_cooling = ti_thermal_register_cpu_cooling, |
| 412 | .unregister_cooling = ti_thermal_unregister_cpu_cooling, | 412 | .unregister_cooling = ti_thermal_unregister_cpu_cooling, |
| 413 | .slope = DRA752_GRADIENT_SLOPE, | ||
| 414 | .constant = DRA752_GRADIENT_CONST, | ||
| 415 | .slope_pcb = DRA752_GRADIENT_SLOPE_W_PCB, | 413 | .slope_pcb = DRA752_GRADIENT_SLOPE_W_PCB, |
| 416 | .constant_pcb = DRA752_GRADIENT_CONST_W_PCB, | 414 | .constant_pcb = DRA752_GRADIENT_CONST_W_PCB, |
| 417 | }, | 415 | }, |
| @@ -419,8 +417,6 @@ const struct ti_bandgap_data dra752_data = { | |||
| 419 | .registers = &dra752_gpu_temp_sensor_registers, | 417 | .registers = &dra752_gpu_temp_sensor_registers, |
| 420 | .ts_data = &dra752_gpu_temp_sensor_data, | 418 | .ts_data = &dra752_gpu_temp_sensor_data, |
| 421 | .domain = "gpu", | 419 | .domain = "gpu", |
| 422 | .slope = DRA752_GRADIENT_SLOPE, | ||
| 423 | .constant = DRA752_GRADIENT_CONST, | ||
| 424 | .slope_pcb = DRA752_GRADIENT_SLOPE_W_PCB, | 420 | .slope_pcb = DRA752_GRADIENT_SLOPE_W_PCB, |
| 425 | .constant_pcb = DRA752_GRADIENT_CONST_W_PCB, | 421 | .constant_pcb = DRA752_GRADIENT_CONST_W_PCB, |
| 426 | }, | 422 | }, |
| @@ -428,8 +424,6 @@ const struct ti_bandgap_data dra752_data = { | |||
| 428 | .registers = &dra752_core_temp_sensor_registers, | 424 | .registers = &dra752_core_temp_sensor_registers, |
| 429 | .ts_data = &dra752_core_temp_sensor_data, | 425 | .ts_data = &dra752_core_temp_sensor_data, |
| 430 | .domain = "core", | 426 | .domain = "core", |
| 431 | .slope = DRA752_GRADIENT_SLOPE, | ||
| 432 | .constant = DRA752_GRADIENT_CONST, | ||
| 433 | .slope_pcb = DRA752_GRADIENT_SLOPE_W_PCB, | 427 | .slope_pcb = DRA752_GRADIENT_SLOPE_W_PCB, |
| 434 | .constant_pcb = DRA752_GRADIENT_CONST_W_PCB, | 428 | .constant_pcb = DRA752_GRADIENT_CONST_W_PCB, |
| 435 | }, | 429 | }, |
| @@ -437,8 +431,6 @@ const struct ti_bandgap_data dra752_data = { | |||
| 437 | .registers = &dra752_dspeve_temp_sensor_registers, | 431 | .registers = &dra752_dspeve_temp_sensor_registers, |
| 438 | .ts_data = &dra752_dspeve_temp_sensor_data, | 432 | .ts_data = &dra752_dspeve_temp_sensor_data, |
| 439 | .domain = "dspeve", | 433 | .domain = "dspeve", |
| 440 | .slope = DRA752_GRADIENT_SLOPE, | ||
| 441 | .constant = DRA752_GRADIENT_CONST, | ||
| 442 | .slope_pcb = DRA752_GRADIENT_SLOPE_W_PCB, | 434 | .slope_pcb = DRA752_GRADIENT_SLOPE_W_PCB, |
| 443 | .constant_pcb = DRA752_GRADIENT_CONST_W_PCB, | 435 | .constant_pcb = DRA752_GRADIENT_CONST_W_PCB, |
| 444 | }, | 436 | }, |
| @@ -446,8 +438,6 @@ const struct ti_bandgap_data dra752_data = { | |||
| 446 | .registers = &dra752_iva_temp_sensor_registers, | 438 | .registers = &dra752_iva_temp_sensor_registers, |
| 447 | .ts_data = &dra752_iva_temp_sensor_data, | 439 | .ts_data = &dra752_iva_temp_sensor_data, |
| 448 | .domain = "iva", | 440 | .domain = "iva", |
| 449 | .slope = DRA752_GRADIENT_SLOPE, | ||
| 450 | .constant = DRA752_GRADIENT_CONST, | ||
| 451 | .slope_pcb = DRA752_GRADIENT_SLOPE_W_PCB, | 441 | .slope_pcb = DRA752_GRADIENT_SLOPE_W_PCB, |
| 452 | .constant_pcb = DRA752_GRADIENT_CONST_W_PCB, | 442 | .constant_pcb = DRA752_GRADIENT_CONST_W_PCB, |
| 453 | }, | 443 | }, |
diff --git a/drivers/thermal/ti-soc-thermal/omap3-thermal-data.c b/drivers/thermal/ti-soc-thermal/omap3-thermal-data.c index 3ee34340edab..c6d217913dd1 100644 --- a/drivers/thermal/ti-soc-thermal/omap3-thermal-data.c +++ b/drivers/thermal/ti-soc-thermal/omap3-thermal-data.c | |||
| @@ -91,8 +91,6 @@ const struct ti_bandgap_data omap34xx_data = { | |||
| 91 | .registers = &omap34xx_mpu_temp_sensor_registers, | 91 | .registers = &omap34xx_mpu_temp_sensor_registers, |
| 92 | .ts_data = &omap34xx_mpu_temp_sensor_data, | 92 | .ts_data = &omap34xx_mpu_temp_sensor_data, |
| 93 | .domain = "cpu", | 93 | .domain = "cpu", |
| 94 | .slope = 0, | ||
| 95 | .constant = 20000, | ||
| 96 | .slope_pcb = 0, | 94 | .slope_pcb = 0, |
| 97 | .constant_pcb = 20000, | 95 | .constant_pcb = 20000, |
| 98 | .register_cooling = NULL, | 96 | .register_cooling = NULL, |
| @@ -164,8 +162,6 @@ const struct ti_bandgap_data omap36xx_data = { | |||
| 164 | .registers = &omap36xx_mpu_temp_sensor_registers, | 162 | .registers = &omap36xx_mpu_temp_sensor_registers, |
| 165 | .ts_data = &omap36xx_mpu_temp_sensor_data, | 163 | .ts_data = &omap36xx_mpu_temp_sensor_data, |
| 166 | .domain = "cpu", | 164 | .domain = "cpu", |
| 167 | .slope = 0, | ||
| 168 | .constant = 20000, | ||
| 169 | .slope_pcb = 0, | 165 | .slope_pcb = 0, |
| 170 | .constant_pcb = 20000, | 166 | .constant_pcb = 20000, |
| 171 | .register_cooling = NULL, | 167 | .register_cooling = NULL, |
diff --git a/drivers/thermal/ti-soc-thermal/omap4-thermal-data.c b/drivers/thermal/ti-soc-thermal/omap4-thermal-data.c index d255d33da9eb..fd1113360603 100644 --- a/drivers/thermal/ti-soc-thermal/omap4-thermal-data.c +++ b/drivers/thermal/ti-soc-thermal/omap4-thermal-data.c | |||
| @@ -82,8 +82,6 @@ const struct ti_bandgap_data omap4430_data = { | |||
| 82 | .registers = &omap4430_mpu_temp_sensor_registers, | 82 | .registers = &omap4430_mpu_temp_sensor_registers, |
| 83 | .ts_data = &omap4430_mpu_temp_sensor_data, | 83 | .ts_data = &omap4430_mpu_temp_sensor_data, |
| 84 | .domain = "cpu", | 84 | .domain = "cpu", |
| 85 | .slope = OMAP_GRADIENT_SLOPE_4430, | ||
| 86 | .constant = OMAP_GRADIENT_CONST_4430, | ||
| 87 | .slope_pcb = OMAP_GRADIENT_SLOPE_W_PCB_4430, | 85 | .slope_pcb = OMAP_GRADIENT_SLOPE_W_PCB_4430, |
| 88 | .constant_pcb = OMAP_GRADIENT_CONST_W_PCB_4430, | 86 | .constant_pcb = OMAP_GRADIENT_CONST_W_PCB_4430, |
| 89 | .register_cooling = ti_thermal_register_cpu_cooling, | 87 | .register_cooling = ti_thermal_register_cpu_cooling, |
| @@ -222,8 +220,6 @@ const struct ti_bandgap_data omap4460_data = { | |||
| 222 | .registers = &omap4460_mpu_temp_sensor_registers, | 220 | .registers = &omap4460_mpu_temp_sensor_registers, |
| 223 | .ts_data = &omap4460_mpu_temp_sensor_data, | 221 | .ts_data = &omap4460_mpu_temp_sensor_data, |
| 224 | .domain = "cpu", | 222 | .domain = "cpu", |
| 225 | .slope = OMAP_GRADIENT_SLOPE_4460, | ||
| 226 | .constant = OMAP_GRADIENT_CONST_4460, | ||
| 227 | .slope_pcb = OMAP_GRADIENT_SLOPE_W_PCB_4460, | 223 | .slope_pcb = OMAP_GRADIENT_SLOPE_W_PCB_4460, |
| 228 | .constant_pcb = OMAP_GRADIENT_CONST_W_PCB_4460, | 224 | .constant_pcb = OMAP_GRADIENT_CONST_W_PCB_4460, |
| 229 | .register_cooling = ti_thermal_register_cpu_cooling, | 225 | .register_cooling = ti_thermal_register_cpu_cooling, |
| @@ -255,8 +251,6 @@ const struct ti_bandgap_data omap4470_data = { | |||
| 255 | .registers = &omap4460_mpu_temp_sensor_registers, | 251 | .registers = &omap4460_mpu_temp_sensor_registers, |
| 256 | .ts_data = &omap4460_mpu_temp_sensor_data, | 252 | .ts_data = &omap4460_mpu_temp_sensor_data, |
| 257 | .domain = "cpu", | 253 | .domain = "cpu", |
| 258 | .slope = OMAP_GRADIENT_SLOPE_4470, | ||
| 259 | .constant = OMAP_GRADIENT_CONST_4470, | ||
| 260 | .slope_pcb = OMAP_GRADIENT_SLOPE_W_PCB_4470, | 254 | .slope_pcb = OMAP_GRADIENT_SLOPE_W_PCB_4470, |
| 261 | .constant_pcb = OMAP_GRADIENT_CONST_W_PCB_4470, | 255 | .constant_pcb = OMAP_GRADIENT_CONST_W_PCB_4470, |
| 262 | .register_cooling = ti_thermal_register_cpu_cooling, | 256 | .register_cooling = ti_thermal_register_cpu_cooling, |
diff --git a/drivers/thermal/ti-soc-thermal/omap5-thermal-data.c b/drivers/thermal/ti-soc-thermal/omap5-thermal-data.c index 79ff70c446ba..cd9a304fb571 100644 --- a/drivers/thermal/ti-soc-thermal/omap5-thermal-data.c +++ b/drivers/thermal/ti-soc-thermal/omap5-thermal-data.c | |||
| @@ -336,8 +336,6 @@ const struct ti_bandgap_data omap5430_data = { | |||
| 336 | .domain = "cpu", | 336 | .domain = "cpu", |
| 337 | .register_cooling = ti_thermal_register_cpu_cooling, | 337 | .register_cooling = ti_thermal_register_cpu_cooling, |
| 338 | .unregister_cooling = ti_thermal_unregister_cpu_cooling, | 338 | .unregister_cooling = ti_thermal_unregister_cpu_cooling, |
| 339 | .slope = OMAP_GRADIENT_SLOPE_5430_CPU, | ||
| 340 | .constant = OMAP_GRADIENT_CONST_5430_CPU, | ||
| 341 | .slope_pcb = OMAP_GRADIENT_SLOPE_W_PCB_5430_CPU, | 339 | .slope_pcb = OMAP_GRADIENT_SLOPE_W_PCB_5430_CPU, |
| 342 | .constant_pcb = OMAP_GRADIENT_CONST_W_PCB_5430_CPU, | 340 | .constant_pcb = OMAP_GRADIENT_CONST_W_PCB_5430_CPU, |
| 343 | }, | 341 | }, |
| @@ -345,8 +343,6 @@ const struct ti_bandgap_data omap5430_data = { | |||
| 345 | .registers = &omap5430_gpu_temp_sensor_registers, | 343 | .registers = &omap5430_gpu_temp_sensor_registers, |
| 346 | .ts_data = &omap5430_gpu_temp_sensor_data, | 344 | .ts_data = &omap5430_gpu_temp_sensor_data, |
| 347 | .domain = "gpu", | 345 | .domain = "gpu", |
| 348 | .slope = OMAP_GRADIENT_SLOPE_5430_GPU, | ||
| 349 | .constant = OMAP_GRADIENT_CONST_5430_GPU, | ||
| 350 | .slope_pcb = OMAP_GRADIENT_SLOPE_W_PCB_5430_GPU, | 346 | .slope_pcb = OMAP_GRADIENT_SLOPE_W_PCB_5430_GPU, |
| 351 | .constant_pcb = OMAP_GRADIENT_CONST_W_PCB_5430_GPU, | 347 | .constant_pcb = OMAP_GRADIENT_CONST_W_PCB_5430_GPU, |
| 352 | }, | 348 | }, |
diff --git a/drivers/thermal/ti-soc-thermal/ti-bandgap.h b/drivers/thermal/ti-soc-thermal/ti-bandgap.h index fe0adb898764..209c664c2823 100644 --- a/drivers/thermal/ti-soc-thermal/ti-bandgap.h +++ b/drivers/thermal/ti-soc-thermal/ti-bandgap.h | |||
| @@ -254,8 +254,6 @@ struct ti_bandgap { | |||
| 254 | * @ts_data: pointer to struct with thresholds, limits of temperature sensor | 254 | * @ts_data: pointer to struct with thresholds, limits of temperature sensor |
| 255 | * @registers: pointer to the list of register offsets and bitfields | 255 | * @registers: pointer to the list of register offsets and bitfields |
| 256 | * @domain: the name of the domain where the sensor is located | 256 | * @domain: the name of the domain where the sensor is located |
| 257 | * @slope: sensor gradient slope info for hotspot extrapolation equation | ||
| 258 | * @constant: sensor gradient const info for hotspot extrapolation equation | ||
| 259 | * @slope_pcb: sensor gradient slope info for hotspot extrapolation equation | 257 | * @slope_pcb: sensor gradient slope info for hotspot extrapolation equation |
| 260 | * with no external influence | 258 | * with no external influence |
| 261 | * @constant_pcb: sensor gradient const info for hotspot extrapolation equation | 259 | * @constant_pcb: sensor gradient const info for hotspot extrapolation equation |
| @@ -274,8 +272,6 @@ struct ti_temp_sensor { | |||
| 274 | struct temp_sensor_registers *registers; | 272 | struct temp_sensor_registers *registers; |
| 275 | char *domain; | 273 | char *domain; |
| 276 | /* for hotspot extrapolation */ | 274 | /* for hotspot extrapolation */ |
| 277 | const int slope; | ||
| 278 | const int constant; | ||
| 279 | const int slope_pcb; | 275 | const int slope_pcb; |
| 280 | const int constant_pcb; | 276 | const int constant_pcb; |
| 281 | int (*register_cooling)(struct ti_bandgap *bgp, int id); | 277 | int (*register_cooling)(struct ti_bandgap *bgp, int id); |
diff --git a/drivers/thermal/ti-soc-thermal/ti-thermal-common.c b/drivers/thermal/ti-soc-thermal/ti-thermal-common.c index 0586bd0f2bab..02790f69e26c 100644 --- a/drivers/thermal/ti-soc-thermal/ti-thermal-common.c +++ b/drivers/thermal/ti-soc-thermal/ti-thermal-common.c | |||
| @@ -96,8 +96,8 @@ static inline int __ti_thermal_get_temp(void *devdata, int *temp) | |||
| 96 | return ret; | 96 | return ret; |
| 97 | 97 | ||
| 98 | /* Default constants */ | 98 | /* Default constants */ |
| 99 | slope = s->slope; | 99 | slope = thermal_zone_get_slope(data->ti_thermal); |
| 100 | constant = s->constant; | 100 | constant = thermal_zone_get_offset(data->ti_thermal); |
| 101 | 101 | ||
| 102 | pcb_tz = data->pcb_tz; | 102 | pcb_tz = data->pcb_tz; |
| 103 | /* In case pcb zone is available, use the extrapolation rule with it */ | 103 | /* In case pcb zone is available, use the extrapolation rule with it */ |
| @@ -126,119 +126,6 @@ static inline int ti_thermal_get_temp(struct thermal_zone_device *thermal, | |||
| 126 | return __ti_thermal_get_temp(data, temp); | 126 | return __ti_thermal_get_temp(data, temp); |
| 127 | } | 127 | } |
| 128 | 128 | ||
| 129 | /* Bind callback functions for thermal zone */ | ||
| 130 | static int ti_thermal_bind(struct thermal_zone_device *thermal, | ||
| 131 | struct thermal_cooling_device *cdev) | ||
| 132 | { | ||
| 133 | struct ti_thermal_data *data = thermal->devdata; | ||
| 134 | int id; | ||
| 135 | |||
| 136 | if (!data || IS_ERR(data)) | ||
| 137 | return -ENODEV; | ||
| 138 | |||
| 139 | /* check if this is the cooling device we registered */ | ||
| 140 | if (data->cool_dev != cdev) | ||
| 141 | return 0; | ||
| 142 | |||
| 143 | id = data->sensor_id; | ||
| 144 | |||
| 145 | /* Simple thing, two trips, one passive another critical */ | ||
| 146 | return thermal_zone_bind_cooling_device(thermal, 0, cdev, | ||
| 147 | /* bind with min and max states defined by cpu_cooling */ | ||
| 148 | THERMAL_NO_LIMIT, | ||
| 149 | THERMAL_NO_LIMIT, | ||
| 150 | THERMAL_WEIGHT_DEFAULT); | ||
| 151 | } | ||
| 152 | |||
| 153 | /* Unbind callback functions for thermal zone */ | ||
| 154 | static int ti_thermal_unbind(struct thermal_zone_device *thermal, | ||
| 155 | struct thermal_cooling_device *cdev) | ||
| 156 | { | ||
| 157 | struct ti_thermal_data *data = thermal->devdata; | ||
| 158 | |||
| 159 | if (!data || IS_ERR(data)) | ||
| 160 | return -ENODEV; | ||
| 161 | |||
| 162 | /* check if this is the cooling device we registered */ | ||
| 163 | if (data->cool_dev != cdev) | ||
| 164 | return 0; | ||
| 165 | |||
| 166 | /* Simple thing, two trips, one passive another critical */ | ||
| 167 | return thermal_zone_unbind_cooling_device(thermal, 0, cdev); | ||
| 168 | } | ||
| 169 | |||
| 170 | /* Get mode callback functions for thermal zone */ | ||
| 171 | static int ti_thermal_get_mode(struct thermal_zone_device *thermal, | ||
| 172 | enum thermal_device_mode *mode) | ||
| 173 | { | ||
| 174 | struct ti_thermal_data *data = thermal->devdata; | ||
| 175 | |||
| 176 | if (data) | ||
| 177 | *mode = data->mode; | ||
| 178 | |||
| 179 | return 0; | ||
| 180 | } | ||
| 181 | |||
| 182 | /* Set mode callback functions for thermal zone */ | ||
| 183 | static int ti_thermal_set_mode(struct thermal_zone_device *thermal, | ||
| 184 | enum thermal_device_mode mode) | ||
| 185 | { | ||
| 186 | struct ti_thermal_data *data = thermal->devdata; | ||
| 187 | struct ti_bandgap *bgp; | ||
| 188 | |||
| 189 | bgp = data->bgp; | ||
| 190 | |||
| 191 | if (!data->ti_thermal) { | ||
| 192 | dev_notice(&thermal->device, "thermal zone not registered\n"); | ||
| 193 | return 0; | ||
| 194 | } | ||
| 195 | |||
| 196 | mutex_lock(&data->ti_thermal->lock); | ||
| 197 | |||
| 198 | if (mode == THERMAL_DEVICE_ENABLED) | ||
| 199 | data->ti_thermal->polling_delay = FAST_TEMP_MONITORING_RATE; | ||
| 200 | else | ||
| 201 | data->ti_thermal->polling_delay = 0; | ||
| 202 | |||
| 203 | mutex_unlock(&data->ti_thermal->lock); | ||
| 204 | |||
| 205 | data->mode = mode; | ||
| 206 | ti_bandgap_write_update_interval(bgp, data->sensor_id, | ||
| 207 | data->ti_thermal->polling_delay); | ||
| 208 | thermal_zone_device_update(data->ti_thermal, THERMAL_EVENT_UNSPECIFIED); | ||
| 209 | dev_dbg(&thermal->device, "thermal polling set for duration=%d msec\n", | ||
| 210 | data->ti_thermal->polling_delay); | ||
| 211 | |||
| 212 | return 0; | ||
| 213 | } | ||
| 214 | |||
| 215 | /* Get trip type callback functions for thermal zone */ | ||
| 216 | static int ti_thermal_get_trip_type(struct thermal_zone_device *thermal, | ||
| 217 | int trip, enum thermal_trip_type *type) | ||
| 218 | { | ||
| 219 | if (!ti_thermal_is_valid_trip(trip)) | ||
| 220 | return -EINVAL; | ||
| 221 | |||
| 222 | if (trip + 1 == OMAP_TRIP_NUMBER) | ||
| 223 | *type = THERMAL_TRIP_CRITICAL; | ||
| 224 | else | ||
| 225 | *type = THERMAL_TRIP_PASSIVE; | ||
| 226 | |||
| 227 | return 0; | ||
| 228 | } | ||
| 229 | |||
| 230 | /* Get trip temperature callback functions for thermal zone */ | ||
| 231 | static int ti_thermal_get_trip_temp(struct thermal_zone_device *thermal, | ||
| 232 | int trip, int *temp) | ||
| 233 | { | ||
| 234 | if (!ti_thermal_is_valid_trip(trip)) | ||
| 235 | return -EINVAL; | ||
| 236 | |||
| 237 | *temp = ti_thermal_get_trip_value(trip); | ||
| 238 | |||
| 239 | return 0; | ||
| 240 | } | ||
| 241 | |||
| 242 | static int __ti_thermal_get_trend(void *p, int trip, enum thermal_trend *trend) | 129 | static int __ti_thermal_get_trend(void *p, int trip, enum thermal_trend *trend) |
| 243 | { | 130 | { |
| 244 | struct ti_thermal_data *data = p; | 131 | struct ti_thermal_data *data = p; |
| @@ -262,38 +149,11 @@ static int __ti_thermal_get_trend(void *p, int trip, enum thermal_trend *trend) | |||
| 262 | return 0; | 149 | return 0; |
| 263 | } | 150 | } |
| 264 | 151 | ||
| 265 | /* Get the temperature trend callback functions for thermal zone */ | ||
| 266 | static int ti_thermal_get_trend(struct thermal_zone_device *thermal, | ||
| 267 | int trip, enum thermal_trend *trend) | ||
| 268 | { | ||
| 269 | return __ti_thermal_get_trend(thermal->devdata, trip, trend); | ||
| 270 | } | ||
| 271 | |||
| 272 | /* Get critical temperature callback functions for thermal zone */ | ||
| 273 | static int ti_thermal_get_crit_temp(struct thermal_zone_device *thermal, | ||
| 274 | int *temp) | ||
| 275 | { | ||
| 276 | /* shutdown zone */ | ||
| 277 | return ti_thermal_get_trip_temp(thermal, OMAP_TRIP_NUMBER - 1, temp); | ||
| 278 | } | ||
| 279 | |||
| 280 | static const struct thermal_zone_of_device_ops ti_of_thermal_ops = { | 152 | static const struct thermal_zone_of_device_ops ti_of_thermal_ops = { |
| 281 | .get_temp = __ti_thermal_get_temp, | 153 | .get_temp = __ti_thermal_get_temp, |
| 282 | .get_trend = __ti_thermal_get_trend, | 154 | .get_trend = __ti_thermal_get_trend, |
| 283 | }; | 155 | }; |
| 284 | 156 | ||
| 285 | static struct thermal_zone_device_ops ti_thermal_ops = { | ||
| 286 | .get_temp = ti_thermal_get_temp, | ||
| 287 | .get_trend = ti_thermal_get_trend, | ||
| 288 | .bind = ti_thermal_bind, | ||
| 289 | .unbind = ti_thermal_unbind, | ||
| 290 | .get_mode = ti_thermal_get_mode, | ||
| 291 | .set_mode = ti_thermal_set_mode, | ||
| 292 | .get_trip_type = ti_thermal_get_trip_type, | ||
| 293 | .get_trip_temp = ti_thermal_get_trip_temp, | ||
| 294 | .get_crit_temp = ti_thermal_get_crit_temp, | ||
| 295 | }; | ||
| 296 | |||
| 297 | static struct ti_thermal_data | 157 | static struct ti_thermal_data |
| 298 | *ti_thermal_build_data(struct ti_bandgap *bgp, int id) | 158 | *ti_thermal_build_data(struct ti_bandgap *bgp, int id) |
| 299 | { | 159 | { |
| @@ -331,18 +191,10 @@ int ti_thermal_expose_sensor(struct ti_bandgap *bgp, int id, | |||
| 331 | data->ti_thermal = devm_thermal_zone_of_sensor_register(bgp->dev, id, | 191 | data->ti_thermal = devm_thermal_zone_of_sensor_register(bgp->dev, id, |
| 332 | data, &ti_of_thermal_ops); | 192 | data, &ti_of_thermal_ops); |
| 333 | if (IS_ERR(data->ti_thermal)) { | 193 | if (IS_ERR(data->ti_thermal)) { |
| 334 | /* Create thermal zone */ | 194 | dev_err(bgp->dev, "thermal zone device is NULL\n"); |
| 335 | data->ti_thermal = thermal_zone_device_register(domain, | 195 | return PTR_ERR(data->ti_thermal); |
| 336 | OMAP_TRIP_NUMBER, 0, data, &ti_thermal_ops, | ||
| 337 | NULL, FAST_TEMP_MONITORING_RATE, | ||
| 338 | FAST_TEMP_MONITORING_RATE); | ||
| 339 | if (IS_ERR(data->ti_thermal)) { | ||
| 340 | dev_err(bgp->dev, "thermal zone device is NULL\n"); | ||
| 341 | return PTR_ERR(data->ti_thermal); | ||
| 342 | } | ||
| 343 | data->ti_thermal->polling_delay = FAST_TEMP_MONITORING_RATE; | ||
| 344 | data->our_zone = true; | ||
| 345 | } | 196 | } |
| 197 | |||
| 346 | ti_bandgap_set_sensor_data(bgp, id, data); | 198 | ti_bandgap_set_sensor_data(bgp, id, data); |
| 347 | ti_bandgap_write_update_interval(bgp, data->sensor_id, | 199 | ti_bandgap_write_update_interval(bgp, data->sensor_id, |
| 348 | data->ti_thermal->polling_delay); | 200 | data->ti_thermal->polling_delay); |
diff --git a/drivers/thermal/ti-soc-thermal/ti-thermal.h b/drivers/thermal/ti-soc-thermal/ti-thermal.h index f8b7ffea6194..8e85ca973967 100644 --- a/drivers/thermal/ti-soc-thermal/ti-thermal.h +++ b/drivers/thermal/ti-soc-thermal/ti-thermal.h | |||
| @@ -25,22 +25,6 @@ | |||
| 25 | 25 | ||
| 26 | #include "ti-bandgap.h" | 26 | #include "ti-bandgap.h" |
| 27 | 27 | ||
| 28 | /* sensors gradient and offsets */ | ||
| 29 | #define OMAP_GRADIENT_SLOPE_4430 0 | ||
| 30 | #define OMAP_GRADIENT_CONST_4430 20000 | ||
| 31 | #define OMAP_GRADIENT_SLOPE_4460 348 | ||
| 32 | #define OMAP_GRADIENT_CONST_4460 -9301 | ||
| 33 | #define OMAP_GRADIENT_SLOPE_4470 308 | ||
| 34 | #define OMAP_GRADIENT_CONST_4470 -7896 | ||
| 35 | |||
| 36 | #define OMAP_GRADIENT_SLOPE_5430_CPU 65 | ||
| 37 | #define OMAP_GRADIENT_CONST_5430_CPU -1791 | ||
| 38 | #define OMAP_GRADIENT_SLOPE_5430_GPU 117 | ||
| 39 | #define OMAP_GRADIENT_CONST_5430_GPU -2992 | ||
| 40 | |||
| 41 | #define DRA752_GRADIENT_SLOPE 0 | ||
| 42 | #define DRA752_GRADIENT_CONST 2000 | ||
| 43 | |||
| 44 | /* PCB sensor calculation constants */ | 28 | /* PCB sensor calculation constants */ |
| 45 | #define OMAP_GRADIENT_SLOPE_W_PCB_4430 0 | 29 | #define OMAP_GRADIENT_SLOPE_W_PCB_4430 0 |
| 46 | #define OMAP_GRADIENT_CONST_W_PCB_4430 20000 | 30 | #define OMAP_GRADIENT_CONST_W_PCB_4430 20000 |
diff --git a/include/linux/devfreq_cooling.h b/include/linux/devfreq_cooling.h index c35d0c0e0ada..4635f95000a4 100644 --- a/include/linux/devfreq_cooling.h +++ b/include/linux/devfreq_cooling.h | |||
| @@ -34,6 +34,23 @@ | |||
| 34 | * If get_dynamic_power() is NULL, then the | 34 | * If get_dynamic_power() is NULL, then the |
| 35 | * dynamic power is calculated as | 35 | * dynamic power is calculated as |
| 36 | * @dyn_power_coeff * frequency * voltage^2 | 36 | * @dyn_power_coeff * frequency * voltage^2 |
| 37 | * @get_real_power: When this is set, the framework uses it to ask the | ||
| 38 | * device driver for the actual power. | ||
| 39 | * Some devices have more sophisticated methods | ||
| 40 | * (like power counters) to approximate the actual power | ||
| 41 | * that they use. | ||
| 42 | * This function provides more accurate data to the | ||
| 43 | * thermal governor. When the driver does not provide | ||
| 44 | * such function, framework just uses pre-calculated | ||
| 45 | * table and scale the power by 'utilization' | ||
| 46 | * (based on 'busy_time' and 'total_time' taken from | ||
| 47 | * devfreq 'last_status'). | ||
| 48 | * The value returned by this function must be lower | ||
| 49 | * or equal than the maximum power value | ||
| 50 | * for the current state | ||
| 51 | * (which can be found in power_table[state]). | ||
| 52 | * When this interface is used, the power_table holds | ||
| 53 | * max total (static + dynamic) power value for each OPP. | ||
| 37 | */ | 54 | */ |
| 38 | struct devfreq_cooling_power { | 55 | struct devfreq_cooling_power { |
| 39 | unsigned long (*get_static_power)(struct devfreq *devfreq, | 56 | unsigned long (*get_static_power)(struct devfreq *devfreq, |
| @@ -41,6 +58,8 @@ struct devfreq_cooling_power { | |||
| 41 | unsigned long (*get_dynamic_power)(struct devfreq *devfreq, | 58 | unsigned long (*get_dynamic_power)(struct devfreq *devfreq, |
| 42 | unsigned long freq, | 59 | unsigned long freq, |
| 43 | unsigned long voltage); | 60 | unsigned long voltage); |
| 61 | int (*get_real_power)(struct devfreq *df, u32 *power, | ||
| 62 | unsigned long freq, unsigned long voltage); | ||
| 44 | unsigned long dyn_power_coeff; | 63 | unsigned long dyn_power_coeff; |
| 45 | }; | 64 | }; |
| 46 | 65 | ||
diff --git a/include/trace/events/thermal.h b/include/trace/events/thermal.h index 2b4a8ff72d0d..6cde5b3514c2 100644 --- a/include/trace/events/thermal.h +++ b/include/trace/events/thermal.h | |||
| @@ -151,9 +151,9 @@ TRACE_EVENT(thermal_power_cpu_limit, | |||
| 151 | TRACE_EVENT(thermal_power_devfreq_get_power, | 151 | TRACE_EVENT(thermal_power_devfreq_get_power, |
| 152 | TP_PROTO(struct thermal_cooling_device *cdev, | 152 | TP_PROTO(struct thermal_cooling_device *cdev, |
| 153 | struct devfreq_dev_status *status, unsigned long freq, | 153 | struct devfreq_dev_status *status, unsigned long freq, |
| 154 | u32 dynamic_power, u32 static_power), | 154 | u32 dynamic_power, u32 static_power, u32 power), |
| 155 | 155 | ||
| 156 | TP_ARGS(cdev, status, freq, dynamic_power, static_power), | 156 | TP_ARGS(cdev, status, freq, dynamic_power, static_power, power), |
| 157 | 157 | ||
| 158 | TP_STRUCT__entry( | 158 | TP_STRUCT__entry( |
| 159 | __string(type, cdev->type ) | 159 | __string(type, cdev->type ) |
| @@ -161,6 +161,7 @@ TRACE_EVENT(thermal_power_devfreq_get_power, | |||
| 161 | __field(u32, load ) | 161 | __field(u32, load ) |
| 162 | __field(u32, dynamic_power ) | 162 | __field(u32, dynamic_power ) |
| 163 | __field(u32, static_power ) | 163 | __field(u32, static_power ) |
| 164 | __field(u32, power) | ||
| 164 | ), | 165 | ), |
| 165 | 166 | ||
| 166 | TP_fast_assign( | 167 | TP_fast_assign( |
| @@ -169,11 +170,13 @@ TRACE_EVENT(thermal_power_devfreq_get_power, | |||
| 169 | __entry->load = (100 * status->busy_time) / status->total_time; | 170 | __entry->load = (100 * status->busy_time) / status->total_time; |
| 170 | __entry->dynamic_power = dynamic_power; | 171 | __entry->dynamic_power = dynamic_power; |
| 171 | __entry->static_power = static_power; | 172 | __entry->static_power = static_power; |
| 173 | __entry->power = power; | ||
| 172 | ), | 174 | ), |
| 173 | 175 | ||
| 174 | TP_printk("type=%s freq=%lu load=%u dynamic_power=%u static_power=%u", | 176 | TP_printk("type=%s freq=%lu load=%u dynamic_power=%u static_power=%u power=%u", |
| 175 | __get_str(type), __entry->freq, | 177 | __get_str(type), __entry->freq, |
| 176 | __entry->load, __entry->dynamic_power, __entry->static_power) | 178 | __entry->load, __entry->dynamic_power, __entry->static_power, |
| 179 | __entry->power) | ||
| 177 | ); | 180 | ); |
| 178 | 181 | ||
| 179 | TRACE_EVENT(thermal_power_devfreq_limit, | 182 | TRACE_EVENT(thermal_power_devfreq_limit, |
