Pull thermal management update from Zhang Rui: "Summary: - of-thermal extension to allow drivers to register and use its functionality in a better way, without exploiting thermal core. From Lukasz Majewski. - Fix a bug in intel_soc_dts_thermal driver which calls a sleep function in interrupt handler. From Maurice Petallo. - add a thermal UAPI header file for exporting the thermal generic netlink information to user-space. From Florian Fainelli. - First round of refactoring in Exynos driver. Bartlomiej and Lukasz are attempting to make it lean and easier to understand. - New thermal driver for Rockchip (rk3288), with support for DT thermal. From Caesar Wang. - New thermal driver for Nvidia, Tegra124 SOCTHERM driver, with support for DT thermal. From Mikko Perttunen. - New cooling device, based on common clock framework. From Eduardo Valentin. - a couple of small fixes in thermal core framework. From Srinivas Pandruvada, Javi Merino, Luis Henriques. - Dropping Armada A375-Z1 SoC thermal support as the chip is not in the market, armada folks decided to drop its support. - a couple of small fixes and cleanups in int340x thermal driver" * 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/rzhang/linux: (58 commits) thermal: provide an UAPI header file Thermal/int340x: Clear the error value of the last acpi_bus_get_device() call thermal/powerclamp: add id for braswell cpu thermal: Intel SoC DTS: Don't do thermal zone update inside spin_lock Thermal: fix platform_no_drv_owner.cocci warnings Thermal/int340x: avoid unnecessary pointer casting thermal: int3403: Delete a check before thermal_zone_device_unregister() thermal/int3400: export uuids thermal: of: Extend current of-thermal.c code to allow setting emulated temp thermal: of: Extend of-thermal to export table of trip points thermal: of: Rename struct __thermal_trip to struct thermal_trip thermal: of: Extend of-thermal.c to provide check if trip point is valid thermal: of: Extend of-thermal.c to provide number of trip points thermal: Fix error path in thermal_init() thermal: lock the thermal zone when switching governors thermal: core: ignore invalid trip temperature thermal: armada: Remove support for A375-Z1 SoC thermal: rockchip: add driver for thermal dt-bindings: document Rockchip thermal thermal: exynos: remove exynos_tmu_data.h include ...
Unified · Split
| @@ -5,17 +5,9 @@ Required properties: · 4 unmodified lines | ||
| 5 | 5 | - compatible: Should be set to one of the following: |
| 6 | 6 | marvell,armada370-thermal |
| 7 | 7 | marvell,armada375-thermal |
| 8 | − marvell,armada375-z1-thermal | |
| 9 | 8 | marvell,armada380-thermal |
| 10 | 9 | marvell,armadaxp-thermal |
| 11 | 10 | |
| 12 | − Note: As the name suggests, "marvell,armada375-z1-thermal" | |
| 13 | − applies for the SoC Z1 stepping only. On such stepping | |
| 14 | − some quirks need to be done and the register offset differs | |
| 15 | − from the one in the A0 stepping. | |
| 16 | − The operating system may auto-detect the SoC stepping and | |
| 17 | − update the compatible and register offsets at runtime. | |
| 18 | − | |
| 19 | 11 | - reg: Device's register space. |
| 20 | 12 | Two entries are expected, see the examples below. |
| 21 | 13 | The first one is required for the sensor register; |
| @@ -0,0 +1,68 @@ | ||
| 1 | +* Temperature Sensor ADC (TSADC) on rockchip SoCs | |
| 2 | + | |
| 3 | +Required properties: | |
| 4 | +- compatible : "rockchip,rk3288-tsadc" | |
| 5 | +- reg : physical base address of the controller and length of memory mapped | |
| 6 | + region. | |
| 7 | +- interrupts : The interrupt number to the cpu. The interrupt specifier format | |
| 8 | + depends on the interrupt controller. | |
| 9 | +- clocks : Must contain an entry for each entry in clock-names. | |
| 10 | +- clock-names : Shall be "tsadc" for the converter-clock, and "apb_pclk" for | |
| 11 | + the peripheral clock. | |
| 12 | +- resets : Must contain an entry for each entry in reset-names. | |
| 13 | + See ../reset/reset.txt for details. | |
| 14 | +- reset-names : Must include the name "tsadc-apb". | |
| 15 | +- #thermal-sensor-cells : Should be 1. See ./thermal.txt for a description. | |
| 16 | +- rockchip,hw-tshut-temp : The hardware-controlled shutdown temperature value. | |
| 17 | +- rockchip,hw-tshut-mode : The hardware-controlled shutdown mode 0:CRU 1:GPIO. | |
| 18 | +- rockchip,hw-tshut-polarity : The hardware-controlled active polarity 0:LOW | |
| 19 | + 1:HIGH. | |
| 20 | + | |
| 21 | +Exiample: | |
| 22 | +tsadc: tsadc@ff280000 { | |
| 23 | + compatible = "rockchip,rk3288-tsadc"; | |
| 24 | + reg = <0xff280000 0x100>; | |
| 25 | + interrupts = <GIC_SPI 37 IRQ_TYPE_LEVEL_HIGH>; | |
| 26 | + clocks = <&cru SCLK_TSADC>, <&cru PCLK_TSADC>; | |
| 27 | + clock-names = "tsadc", "apb_pclk"; | |
| 28 | + resets = <&cru SRST_TSADC>; | |
| 29 | + reset-names = "tsadc-apb"; | |
| 30 | + pinctrl-names = "default"; | |
| 31 | + pinctrl-0 = <&otp_out>; | |
| 32 | + #thermal-sensor-cells = <1>; | |
| 33 | + rockchip,hw-tshut-temp = <95000>; | |
| 34 | + rockchip,hw-tshut-mode = <0>; | |
| 35 | + rockchip,hw-tshut-polarity = <0>; | |
| 36 | +}; | |
| 37 | + | |
| 38 | +Example: referring to thermal sensors: | |
| 39 | +thermal-zones { | |
| 40 | + cpu_thermal: cpu_thermal { | |
| 41 | + polling-delay-passive = <1000>; /* milliseconds */ | |
| 42 | + polling-delay = <5000>; /* milliseconds */ | |
| 43 | + | |
| 44 | + /* sensor ID */ | |
| 45 | + thermal-sensors = <&tsadc 1>; | |
| 46 | + | |
| 47 | + trips { | |
| 48 | + cpu_alert0: cpu_alert { | |
| 49 | + temperature = <70000>; /* millicelsius */ | |
| 50 | + hysteresis = <2000>; /* millicelsius */ | |
| 51 | + type = "passive"; | |
| 52 | + }; | |
| 53 | + cpu_crit: cpu_crit { | |
| 54 | + temperature = <90000>; /* millicelsius */ | |
| 55 | + hysteresis = <2000>; /* millicelsius */ | |
| 56 | + type = "critical"; | |
| 57 | + }; | |
| 58 | + }; | |
| 59 | + | |
| 60 | + cooling-maps { | |
| 61 | + map0 { | |
| 62 | + trip = <&cpu_alert0>; | |
| 63 | + cooling-device = | |
| 64 | + <&cpu0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; | |
| 65 | + }; | |
| 66 | + }; | |
| 67 | + }; | |
| 68 | +}; | |
| @@ -0,0 +1,53 @@ | ||
| 1 | +Tegra124 SOCTHERM thermal management system | |
| 2 | + | |
| 3 | +The SOCTHERM IP block contains thermal sensors, support for polled | |
| 4 | +or interrupt-based thermal monitoring, CPU and GPU throttling based | |
| 5 | +on temperature trip points, and handling external overcurrent | |
| 6 | +notifications. It is also used to manage emergency shutdown in an | |
| 7 | +overheating situation. | |
| 8 | + | |
| 9 | +Required properties : | |
| 10 | +- compatible : "nvidia,tegra124-soctherm". | |
| 11 | +- reg : Should contain 1 entry: | |
| 12 | + - SOCTHERM register set | |
| 13 | +- interrupts : Defines the interrupt used by SOCTHERM | |
| 14 | +- clocks : Must contain an entry for each entry in clock-names. | |
| 15 | + See ../clocks/clock-bindings.txt for details. | |
| 16 | +- clock-names : Must include the following entries: | |
| 17 | + - tsensor | |
| 18 | + - soctherm | |
| 19 | +- resets : Must contain an entry for each entry in reset-names. | |
| 20 | + See ../reset/reset.txt for details. | |
| 21 | +- reset-names : Must include the following entries: | |
| 22 | + - soctherm | |
| 23 | +- #thermal-sensor-cells : Should be 1. See ./thermal.txt for a description | |
| 24 | + of this property. See <dt-bindings/thermal/tegra124-soctherm.h> for a | |
| 25 | + list of valid values when referring to thermal sensors. | |
| 26 | + | |
| 27 | + | |
| 28 | +Example : | |
| 29 | + | |
| 30 | + soctherm@0,700e2000 { | |
| 31 | + compatible = "nvidia,tegra124-soctherm"; | |
| 32 | + reg = <0x0 0x700e2000 0x0 0x1000>; | |
| 33 | + interrupts = <GIC_SPI 48 IRQ_TYPE_LEVEL_HIGH>; | |
| 34 | + clocks = <&tegra_car TEGRA124_CLK_TSENSOR>, | |
| 35 | + <&tegra_car TEGRA124_CLK_SOC_THERM>; | |
| 36 | + clock-names = "tsensor", "soctherm"; | |
| 37 | + resets = <&tegra_car 78>; | |
| 38 | + reset-names = "soctherm"; | |
| 39 | + | |
| 40 | + #thermal-sensor-cells = <1>; | |
| 41 | + }; | |
| 42 | + | |
| 43 | +Example: referring to thermal sensors : | |
| 44 | + | |
| 45 | + thermal-zones { | |
| 46 | + cpu { | |
| 47 | + polling-delay-passive = <1000>; | |
| 48 | + polling-delay = <1000>; | |
| 49 | + | |
| 50 | + thermal-sensors = | |
| 51 | + <&soctherm TEGRA124_SOCTHERM_SENSOR_CPU>; | |
| 52 | + }; | |
| 53 | + }; | |
| @@ -9516,6 +9516,7 @@ Q: https://patchwork.kernel.org/project/linux-pm/list/ · 9515 unmodified lines | ||
| 9516 | 9516 | S: Supported |
| 9517 | 9517 | F: drivers/thermal/ |
| 9518 | 9518 | F: include/linux/thermal.h |
| 9519 | +F: include/uapi/linux/thermal.h | |
| 9519 | 9520 | F: include/linux/cpu_cooling.h |
| 9520 | 9521 | F: Documentation/devicetree/bindings/thermal/ |
| 9521 | 9522 | |
| @@ -1942,4 +1942,48 @@ · 1941 unmodified lines | ||
| 1942 | 1942 | <&tegra_car TEGRA124_CLK_EXTERN1>; |
| 1943 | 1943 | clock-names = "pll_a", "pll_a_out0", "mclk"; |
| 1944 | 1944 | }; |
| 1945 | + | |
| 1946 | + thermal-zones { | |
| 1947 | + cpu { | |
| 1948 | + trips { | |
| 1949 | + trip@0 { | |
| 1950 | + temperature = <101000>; | |
| 1951 | + hysteresis = <0>; | |
| 1952 | + type = "critical"; | |
| 1953 | + }; | |
| 1954 | + }; | |
| 1955 | + | |
| 1956 | + cooling-maps { | |
| 1957 | + /* There are currently no cooling maps because there are no cooling devices */ | |
| 1958 | + }; | |
| 1959 | + }; | |
| 1960 | + | |
| 1961 | + mem { | |
| 1962 | + trips { | |
| 1963 | + trip@0 { | |
| 1964 | + temperature = <101000>; | |
| 1965 | + hysteresis = <0>; | |
| 1966 | + type = "critical"; | |
| 1967 | + }; | |
| 1968 | + }; | |
| 1969 | + | |
| 1970 | + cooling-maps { | |
| 1971 | + /* There are currently no cooling maps because there are no cooling devices */ | |
| 1972 | + }; | |
| 1973 | + }; | |
| 1974 | + | |
| 1975 | + gpu { | |
| 1976 | + trips { | |
| 1977 | + trip@0 { | |
| 1978 | + temperature = <101000>; | |
| 1979 | + hysteresis = <0>; | |
| 1980 | + type = "critical"; | |
| 1981 | + }; | |
| 1982 | + }; | |
| 1983 | + | |
| 1984 | + cooling-maps { | |
| 1985 | + /* There are currently no cooling maps because there are no cooling devices */ | |
| 1986 | + }; | |
| 1987 | + }; | |
| 1988 | + }; | |
| 1945 | 1989 | }; |
| @@ -4,6 +4,7 @@ · 3 unmodified lines | ||
| 4 | 4 | #include <dt-bindings/pinctrl/pinctrl-tegra.h> |
| 5 | 5 | #include <dt-bindings/pinctrl/pinctrl-tegra-xusb.h> |
| 6 | 6 | #include <dt-bindings/interrupt-controller/arm-gic.h> |
| 7 | +#include <dt-bindings/thermal/tegra124-soctherm.h> | |
| 7 | 8 | |
| 8 | 9 | #include "skeleton.dtsi" |
| 9 | 10 | |
| @@ -657,6 +658,18 @@ · 647 unmodified lines | ||
| 657 | 658 | status = "disabled"; |
| 658 | 659 | }; |
| 659 | 660 | |
| 661 | + soctherm: thermal-sensor@0,700e2000 { | |
| 662 | + compatible = "nvidia,tegra124-soctherm"; | |
| 663 | + reg = <0x0 0x700e2000 0x0 0x1000>; | |
| 664 | + interrupts = <GIC_SPI 48 IRQ_TYPE_LEVEL_HIGH>; | |
| 665 | + clocks = <&tegra_car TEGRA124_CLK_TSENSOR>, | |
| 666 | + <&tegra_car TEGRA124_CLK_SOC_THERM>; | |
| 667 | + clock-names = "tsensor", "soctherm"; | |
| 668 | + resets = <&tegra_car 78>; | |
| 669 | + reset-names = "soctherm"; | |
| 670 | + #thermal-sensor-cells = <1>; | |
| 671 | + }; | |
| 672 | + | |
| 660 | 673 | ahub@0,70300000 { |
| 661 | 674 | compatible = "nvidia,tegra124-ahub"; |
| 662 | 675 | reg = <0x0 0x70300000 0x0 0x200>, |
| @@ -898,6 +911,40 @@ · 235 unmodified lines | ||
| 898 | 911 | }; |
| 899 | 912 | }; |
| 900 | 913 | |
| 914 | + thermal-zones { | |
| 915 | + cpu { | |
| 916 | + polling-delay-passive = <1000>; | |
| 917 | + polling-delay = <1000>; | |
| 918 | + | |
| 919 | + thermal-sensors = | |
| 920 | + <&soctherm TEGRA124_SOCTHERM_SENSOR_CPU>; | |
| 921 | + }; | |
| 922 | + | |
| 923 | + mem { | |
| 924 | + polling-delay-passive = <1000>; | |
| 925 | + polling-delay = <1000>; | |
| 926 | + | |
| 927 | + thermal-sensors = | |
| 928 | + <&soctherm TEGRA124_SOCTHERM_SENSOR_MEM>; | |
| 929 | + }; | |
| 930 | + | |
| 931 | + gpu { | |
| 932 | + polling-delay-passive = <1000>; | |
| 933 | + polling-delay = <1000>; | |
| 934 | + | |
| 935 | + thermal-sensors = | |
| 936 | + <&soctherm TEGRA124_SOCTHERM_SENSOR_GPU>; | |
| 937 | + }; | |
| 938 | + | |
| 939 | + pllx { | |
| 940 | + polling-delay-passive = <1000>; | |
| 941 | + polling-delay = <1000>; | |
| 942 | + | |
| 943 | + thermal-sensors = | |
| 944 | + <&soctherm TEGRA124_SOCTHERM_SENSOR_PLLX>; | |
| 945 | + }; | |
| 946 | + }; | |
| 947 | + | |
| 901 | 948 | timer { |
| 902 | 949 | compatible = "arm,armv7-timer"; |
| 903 | 950 | interrupts = <GIC_PPI 13 |
| @@ -177,6 +177,10 @@ static struct attribute *lm75_attrs[] = { · 176 unmodified lines | ||
| 177 | 177 | }; |
| 178 | 178 | ATTRIBUTE_GROUPS(lm75); |
| 179 | 179 | |
| 180 | +static const struct thermal_zone_of_device_ops lm75_of_thermal_ops = { | |
| 181 | + .get_temp = lm75_read_temp, | |
| 182 | +}; | |
| 183 | + | |
| 180 | 184 | /*-----------------------------------------------------------------------*/ |
| 181 | 185 | |
| 182 | 186 | /* device probe and removal */ |
| @@ -296,10 +300,9 @@ lm75_probe(struct i2c_client *client, const struct i2c_device_id *id) · 113 unmodified lines | ||
| 296 | 300 | if (IS_ERR(data->hwmon_dev)) |
| 297 | 301 | return PTR_ERR(data->hwmon_dev); |
| 298 | 302 | |
| 299 | − data->tz = thermal_zone_of_sensor_register(data->hwmon_dev, | |
| 300 | − 0, | |
| 303 | + data->tz = thermal_zone_of_sensor_register(data->hwmon_dev, 0, | |
| 301 | 304 | data->hwmon_dev, |
| 302 | − lm75_read_temp, NULL); | |
| 305 | + &lm75_of_thermal_ops); | |
| 303 | 306 | if (IS_ERR(data->tz)) |
| 304 | 307 | data->tz = NULL; |
| 305 | 308 | |
| @@ -486,6 +486,10 @@ static const struct attribute_group ntc_attr_group = { · 485 unmodified lines | ||
| 486 | 486 | .attrs = ntc_attributes, |
| 487 | 487 | }; |
| 488 | 488 | |
| 489 | +static const struct thermal_zone_of_device_ops ntc_of_thermal_ops = { | |
| 490 | + .get_temp = ntc_read_temp, | |
| 491 | +}; | |
| 492 | + | |
| 489 | 493 | static int ntc_thermistor_probe(struct platform_device *pdev) |
| 490 | 494 | { |
| 491 | 495 | const struct of_device_id *of_id = |
| @@ -579,7 +583,7 @@ static int ntc_thermistor_probe(struct platform_device *pdev) · 87 unmodified lines | ||
| 579 | 583 | pdev_id->name); |
| 580 | 584 | |
| 581 | 585 | data->tz = thermal_zone_of_sensor_register(data->dev, 0, data->dev, |
| 582 | − ntc_read_temp, NULL); | |
| 586 | + &ntc_of_thermal_ops); | |
| 583 | 587 | if (IS_ERR(data->tz)) { |
| 584 | 588 | dev_dbg(&pdev->dev, "Failed to register to thermal fw.\n"); |
| 585 | 589 | data->tz = NULL; |
| @@ -158,6 +158,10 @@ ATTRIBUTE_GROUPS(tmp102); · 157 unmodified lines | ||
| 158 | 158 | #define TMP102_CONFIG (TMP102_CONF_TM | TMP102_CONF_EM | TMP102_CONF_CR1) |
| 159 | 159 | #define TMP102_CONFIG_RD_ONLY (TMP102_CONF_R0 | TMP102_CONF_R1 | TMP102_CONF_AL) |
| 160 | 160 | |
| 161 | +static const struct thermal_zone_of_device_ops tmp102_of_thermal_ops = { | |
| 162 | + .get_temp = tmp102_read_temp, | |
| 163 | +}; | |
| 164 | + | |
| 161 | 165 | static int tmp102_probe(struct i2c_client *client, |
| 162 | 166 | const struct i2c_device_id *id) |
| 163 | 167 | { |
| @@ -215,7 +219,7 @@ static int tmp102_probe(struct i2c_client *client, · 51 unmodified lines | ||
| 215 | 219 | } |
| 216 | 220 | tmp102->hwmon_dev = hwmon_dev; |
| 217 | 221 | tmp102->tz = thermal_zone_of_sensor_register(hwmon_dev, 0, hwmon_dev, |
| 218 | − tmp102_read_temp, NULL); | |
| 222 | + &tmp102_of_thermal_ops); | |
| 219 | 223 | if (IS_ERR(tmp102->tz)) |
| 220 | 224 | tmp102->tz = NULL; |
| 221 | 225 | |
| @@ -112,6 +112,18 @@ config CPU_THERMAL · 111 unmodified lines | ||
| 112 | 112 | |
| 113 | 113 | If you want this support, you should say Y here. |
| 114 | 114 | |
| 115 | +config CLOCK_THERMAL | |
| 116 | + bool "Generic clock cooling support" | |
| 117 | + depends on COMMON_CLK | |
| 118 | + depends on PM_OPP | |
| 119 | + help | |
| 120 | + This entry implements the generic clock cooling mechanism through | |
| 121 | + frequency clipping. Typically used to cool off co-processors. The | |
| 122 | + device that is configured to use this cooling mechanism will be | |
| 123 | + controlled to reduce clock frequency whenever temperature is high. | |
| 124 | + | |
| 125 | + If you want this support, you should say Y here. | |
| 126 | + | |
| 115 | 127 | config THERMAL_EMULATION |
| 116 | 128 | bool "Thermal emulation mode support" |
| 117 | 129 | help |
| @@ -143,6 +155,16 @@ config SPEAR_THERMAL · 25 unmodified lines | ||
| 143 | 155 | Enable this to plug the SPEAr thermal sensor driver into the Linux |
| 144 | 156 | thermal framework. |
| 145 | 157 | |
| 158 | +config ROCKCHIP_THERMAL | |
| 159 | + tristate "Rockchip thermal driver" | |
| 160 | + depends on ARCH_ROCKCHIP | |
| 161 | + depends on RESET_CONTROLLER | |
| 162 | + help | |
| 163 | + Rockchip thermal driver provides support for Temperature sensor | |
| 164 | + ADC (TS-ADC) found on Rockchip SoCs. It supports one critical | |
| 165 | + trip point. Cpufreq is used as the cooling device and will throttle | |
| 166 | + CPUs when the Temperature crosses the passive trip point. | |
| 167 | + | |
| 146 | 168 | config RCAR_THERMAL |
| 147 | 169 | tristate "Renesas R-Car thermal driver" |
| 148 | 170 | depends on ARCH_SHMOBILE || COMPILE_TEST |
| @@ -185,6 +207,16 @@ config ARMADA_THERMAL · 36 unmodified lines | ||
| 185 | 207 | Enable this option if you want to have support for thermal management |
| 186 | 208 | controller present in Armada 370 and Armada XP SoC. |
| 187 | 209 | |
| 210 | +config TEGRA_SOCTHERM | |
| 211 | + tristate "Tegra SOCTHERM thermal management" | |
| 212 | + depends on ARCH_TEGRA | |
| 213 | + help | |
| 214 | + Enable this option for integrated thermal management support on NVIDIA | |
| 215 | + Tegra124 systems-on-chip. The driver supports four thermal zones | |
| 216 | + (CPU, GPU, MEM, PLLX). Cooling devices can be bound to the thermal | |
| 217 | + zones to manage temperatures. This option is also required for the | |
| 218 | + emergency thermal reset (thermtrip) feature to function. | |
| 219 | + | |
| 188 | 220 | config DB8500_CPUFREQ_COOLING |
| 189 | 221 | tristate "DB8500 cpufreq cooling" |
| 190 | 222 | depends on ARCH_U8500 |
| @@ -18,8 +18,12 @@ thermal_sys-$(CONFIG_THERMAL_GOV_USER_SPACE) += user_space.o · 17 unmodified lines | ||
| 18 | 18 | # cpufreq cooling |
| 19 | 19 | thermal_sys-$(CONFIG_CPU_THERMAL) += cpu_cooling.o |
| 20 | 20 | |
| 21 | +# clock cooling | |
| 22 | +thermal_sys-$(CONFIG_CLOCK_THERMAL) += clock_cooling.o | |
| 23 | + | |
| 21 | 24 | # platform thermal drivers |
| 22 | 25 | obj-$(CONFIG_SPEAR_THERMAL) += spear_thermal.o |
| 26 | +obj-$(CONFIG_ROCKCHIP_THERMAL) += rockchip_thermal.o | |
| 23 | 27 | obj-$(CONFIG_RCAR_THERMAL) += rcar_thermal.o |
| 24 | 28 | obj-$(CONFIG_KIRKWOOD_THERMAL) += kirkwood_thermal.o |
| 25 | 29 | obj-y += samsung/ |
| @@ -34,3 +38,4 @@ obj-$(CONFIG_INTEL_SOC_DTS_THERMAL) += intel_soc_dts_thermal.o · 8 unmodified lines | ||
| 34 | 38 | obj-$(CONFIG_TI_SOC_THERMAL) += ti-soc-thermal/ |
| 35 | 39 | obj-$(CONFIG_INT340X_THERMAL) += int340x_thermal/ |
| 36 | 40 | obj-$(CONFIG_ST_THERMAL) += st/ |
| 41 | +obj-$(CONFIG_TEGRA_SOCTHERM) += tegra_soctherm.o | |
| @@ -35,10 +35,6 @@ · 34 unmodified lines | ||
| 35 | 35 | #define PMU_TDC0_OTF_CAL_MASK (0x1 << 30) |
| 36 | 36 | #define PMU_TDC0_START_CAL_MASK (0x1 << 25) |
| 37 | 37 | |
| 38 | −#define A375_Z1_CAL_RESET_LSB 0x8011e214 | |
| 39 | −#define A375_Z1_CAL_RESET_MSB 0x30a88019 | |
| 40 | −#define A375_Z1_WORKAROUND_BIT BIT(9) | |
| 41 | − | |
| 42 | 38 | #define A375_UNIT_CONTROL_SHIFT 27 |
| 43 | 39 | #define A375_UNIT_CONTROL_MASK 0x7 |
| 44 | 40 | #define A375_READOUT_INVERT BIT(15) |
| @@ -124,24 +120,12 @@ static void armada375_init_sensor(struct platform_device *pdev, · 79 unmodified lines | ||
| 124 | 120 | struct armada_thermal_priv *priv) |
| 125 | 121 | { |
| 126 | 122 | unsigned long reg; |
| 127 | − bool quirk_needed = | |
| 128 | − !!of_device_is_compatible(pdev->dev.of_node, | |
| 129 | − "marvell,armada375-z1-thermal"); | |
| 130 | − | |
| 131 | − if (quirk_needed) { | |
| 132 | − /* Ensure these registers have the default (reset) values */ | |
| 133 | − writel(A375_Z1_CAL_RESET_LSB, priv->control); | |
| 134 | − writel(A375_Z1_CAL_RESET_MSB, priv->control + 0x4); | |
| 135 | − } | |
| 136 | 123 | |
| 137 | 124 | reg = readl(priv->control + 4); |
| 138 | 125 | reg &= ~(A375_UNIT_CONTROL_MASK << A375_UNIT_CONTROL_SHIFT); |
| 139 | 126 | reg &= ~A375_READOUT_INVERT; |
| 140 | 127 | reg &= ~A375_HW_RESETn; |
| 141 | 128 | |
| 142 | − if (quirk_needed) | |
| 143 | − reg |= A375_Z1_WORKAROUND_BIT; | |
| 144 | − | |
| 145 | 129 | writel(reg, priv->control + 4); |
| 146 | 130 | mdelay(20); |
| 147 | 131 | |
| @@ -259,10 +243,6 @@ static const struct of_device_id armada_thermal_id_table[] = { · 111 unmodified lines | ||
| 259 | 243 | .compatible = "marvell,armada375-thermal", |
| 260 | 244 | .data = &armada375_data, |
| 261 | 245 | }, |
| 262 | − { | |
| 263 | − .compatible = "marvell,armada375-z1-thermal", | |
| 264 | − .data = &armada375_data, | |
| 265 | − }, | |
| 266 | 246 | { |
| 267 | 247 | .compatible = "marvell,armada380-thermal", |
| 268 | 248 | .data = &armada380_data, |
| @@ -0,0 +1,485 @@ | ||
| 1 | +/* | |
| 2 | + * drivers/thermal/clock_cooling.c | |
| 3 | + * | |
| 4 | + * Copyright (C) 2014 Eduardo Valentin <edubezval@gmail.com> | |
| 5 | + * | |
| 6 | + * Copyright (C) 2013 Texas Instruments Inc. | |
| 7 | + * Contact: Eduardo Valentin <eduardo.valentin@ti.com> | |
| 8 | + * | |
| 9 | + * Highly based on cpu_cooling.c. | |
| 10 | + * Copyright (C) 2012 Samsung Electronics Co., Ltd(http://www.samsung.com) | |
| 11 | + * Copyright (C) 2012 Amit Daniel <amit.kachhap@linaro.org> | |
| 12 | + * | |
| 13 | + * This program is free software; you can redistribute it and/or modify | |
| 14 | + * it under the terms of the GNU General Public License as published by | |
| 15 | + * the Free Software Foundation; version 2 of the License. | |
| 16 | + * | |
| 17 | + * This program is distributed in the hope that it will be useful, but | |
| 18 | + * WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 19 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| 20 | + * General Public License for more details. | |
| 21 | + */ | |
| 22 | +#include <linux/clk.h> | |
| 23 | +#include <linux/cpufreq.h> | |
| 24 | +#include <linux/device.h> | |
| 25 | +#include <linux/err.h> | |
| 26 | +#include <linux/idr.h> | |
| 27 | +#include <linux/mutex.h> | |
| 28 | +#include <linux/pm_opp.h> | |
| 29 | +#include <linux/slab.h> | |
| 30 | +#include <linux/thermal.h> | |
| 31 | +#include <linux/clock_cooling.h> | |
| 32 | + | |
| 33 | +/** | |
| 34 | + * struct clock_cooling_device - data for cooling device with clock | |
| 35 | + * @id: unique integer value corresponding to each clock_cooling_device | |
| 36 | + * registered. | |
| 37 | + * @dev: struct device pointer to the device being used to cool off using | |
| 38 | + * clock frequencies. | |
| 39 | + * @cdev: thermal_cooling_device pointer to keep track of the | |
| 40 | + * registered cooling device. | |
| 41 | + * @clk_rate_change_nb: reference to notifier block used to receive clock | |
| 42 | + * rate changes. | |
| 43 | + * @freq_table: frequency table used to keep track of available frequencies. | |
| 44 | + * @clock_state: integer value representing the current state of clock | |
| 45 | + * cooling devices. | |
| 46 | + * @clock_val: integer value representing the absolute value of the clipped | |
| 47 | + * frequency. | |
| 48 | + * @clk: struct clk reference used to enforce clock limits. | |
| 49 | + * @lock: mutex lock to protect this struct. | |
| 50 | + * | |
| 51 | + * This structure is required for keeping information of each | |
| 52 | + * clock_cooling_device registered. In order to prevent corruption of this a | |
| 53 | + * mutex @lock is used. | |
| 54 | + */ | |
| 55 | +struct clock_cooling_device { | |
| 56 | + int id; | |
| 57 | + struct device *dev; | |
| 58 | + struct thermal_cooling_device *cdev; | |
| 59 | + struct notifier_block clk_rate_change_nb; | |
| 60 | + struct cpufreq_frequency_table *freq_table; | |
| 61 | + unsigned long clock_state; | |
| 62 | + unsigned long clock_val; | |
| 63 | + struct clk *clk; | |
| 64 | + struct mutex lock; /* lock to protect the content of this struct */ | |
| 65 | +}; | |
| 66 | +#define to_clock_cooling_device(x) \ | |
| 67 | + container_of(x, struct clock_cooling_device, clk_rate_change_nb) | |
| 68 | +static DEFINE_IDR(clock_idr); | |
| 69 | +static DEFINE_MUTEX(cooling_clock_lock); | |
| 70 | + | |
| 71 | +/** | |
| 72 | + * clock_cooling_get_idr - function to get an unique id. | |
| 73 | + * @id: int * value generated by this function. | |
| 74 | + * | |
| 75 | + * This function will populate @id with an unique | |
| 76 | + * id, using the idr API. | |
| 77 | + * | |
| 78 | + * Return: 0 on success, an error code on failure. | |
| 79 | + */ | |
| 80 | +static int clock_cooling_get_idr(int *id) | |
| 81 | +{ | |
| 82 | + int ret; | |
| 83 | + | |
| 84 | + mutex_lock(&cooling_clock_lock); | |
| 85 | + ret = idr_alloc(&clock_idr, NULL, 0, 0, GFP_KERNEL); | |
| 86 | + mutex_unlock(&cooling_clock_lock); | |
| 87 | + if (unlikely(ret < 0)) | |
| 88 | + return ret; | |
| 89 | + *id = ret; | |
| 90 | + | |
| 91 | + return 0; | |
| 92 | +} | |
| 93 | + | |
| 94 | +/** | |
| 95 | + * release_idr - function to free the unique id. | |
| 96 | + * @id: int value representing the unique id. | |
| 97 | + */ | |
| 98 | +static void release_idr(int id) | |
| 99 | +{ | |
| 100 | + mutex_lock(&cooling_clock_lock); | |
| 101 | + idr_remove(&clock_idr, id); | |
| 102 | + mutex_unlock(&cooling_clock_lock); | |
| 103 | +} | |
| 104 | + | |
| 105 | +/* Below code defines functions to be used for clock as cooling device */ | |
| 106 | + | |
| 107 | +enum clock_cooling_property { | |
| 108 | + GET_LEVEL, | |
| 109 | + GET_FREQ, | |
| 110 | + GET_MAXL, | |
| 111 | +}; | |
| 112 | + | |
| 113 | +/** | |
| 114 | + * clock_cooling_get_property - fetch a property of interest for a give cpu. | |
| 115 | + * @ccdev: clock cooling device reference | |
| 116 | + * @input: query parameter | |
| 117 | + * @output: query return | |
| 118 | + * @property: type of query (frequency, level, max level) | |
| 119 | + * | |
| 120 | + * This is the common function to | |
| 121 | + * 1. get maximum clock cooling states | |
| 122 | + * 2. translate frequency to cooling state | |
| 123 | + * 3. translate cooling state to frequency | |
| 124 | + * Note that the code may be not in good shape | |
| 125 | + * but it is written in this way in order to: | |
| 126 | + * a) reduce duplicate code as most of the code can be shared. | |
| 127 | + * b) make sure the logic is consistent when translating between | |
| 128 | + * cooling states and frequencies. | |
| 129 | + * | |
| 130 | + * Return: 0 on success, -EINVAL when invalid parameters are passed. | |
| 131 | + */ | |
| 132 | +static int clock_cooling_get_property(struct clock_cooling_device *ccdev, | |
| 133 | + unsigned long input, | |
| 134 | + unsigned long *output, | |
| 135 | + enum clock_cooling_property property) | |
| 136 | +{ | |
| 137 | + int i; | |
| 138 | + unsigned long max_level = 0, level = 0; | |
| 139 | + unsigned int freq = CPUFREQ_ENTRY_INVALID; | |
| 140 | + int descend = -1; | |
| 141 | + struct cpufreq_frequency_table *pos, *table = ccdev->freq_table; | |
| 142 | + | |
| 143 | + if (!output) | |
| 144 | + return -EINVAL; | |
| 145 | + | |
| 146 | + if (!table) | |
| 147 | + return -EINVAL; | |
| 148 | + | |
| 149 | + cpufreq_for_each_valid_entry(pos, table) { | |
| 150 | + /* ignore duplicate entry */ | |
| 151 | + if (freq == pos->frequency) | |
| 152 | + continue; | |
| 153 | + | |
| 154 | + /* get the frequency order */ | |
| 155 | + if (freq != CPUFREQ_ENTRY_INVALID && descend == -1) | |
| 156 | + descend = freq > pos->frequency; | |
| 157 | + | |
| 158 | + freq = pos->frequency; | |
| 159 | + max_level++; | |
| 160 | + } | |
| 161 | + | |
| 162 | + /* No valid cpu frequency entry */ | |
| 163 | + if (max_level == 0) | |
| 164 | + return -EINVAL; | |
| 165 | + | |
| 166 | + /* max_level is an index, not a counter */ | |
| 167 | + max_level--; | |
| 168 | + | |
| 169 | + /* get max level */ | |
| 170 | + if (property == GET_MAXL) { | |
| 171 | + *output = max_level; | |
| 172 | + return 0; | |
| 173 | + } | |
| 174 | + | |
| 175 | + if (property == GET_FREQ) | |
| 176 | + level = descend ? input : (max_level - input); | |
| 177 | + | |
| 178 | + i = 0; | |
| 179 | + cpufreq_for_each_valid_entry(pos, table) { | |
| 180 | + /* ignore duplicate entry */ | |
| 181 | + if (freq == pos->frequency) | |
| 182 | + continue; | |
| 183 | + | |
| 184 | + /* now we have a valid frequency entry */ | |
| 185 | + freq = pos->frequency; | |
| 186 | + | |
| 187 | + if (property == GET_LEVEL && (unsigned int)input == freq) { | |
| 188 | + /* get level by frequency */ | |
| 189 | + *output = descend ? i : (max_level - i); | |
| 190 | + return 0; | |
| 191 | + } | |
| 192 | + if (property == GET_FREQ && level == i) { | |
| 193 | + /* get frequency by level */ | |
| 194 | + *output = freq; | |
| 195 | + return 0; | |
| 196 | + } | |
| 197 | + i++; | |
| 198 | + } | |
| 199 | + | |
| 200 | + return -EINVAL; | |
| 201 | +} | |
| 202 | + | |
| 203 | +/** | |
| 204 | + * clock_cooling_get_level - return the cooling level of given clock cooling. | |
| 205 | + * @cdev: reference of a thermal cooling device of used as clock cooling device | |
| 206 | + * @freq: the frequency of interest | |
| 207 | + * | |
| 208 | + * This function will match the cooling level corresponding to the | |
| 209 | + * requested @freq and return it. | |
| 210 | + * | |
| 211 | + * Return: The matched cooling level on success or THERMAL_CSTATE_INVALID | |
| 212 | + * otherwise. | |
| 213 | + */ | |
| 214 | +unsigned long clock_cooling_get_level(struct thermal_cooling_device *cdev, | |
| 215 | + unsigned long freq) | |
| 216 | +{ | |
| 217 | + struct clock_cooling_device *ccdev = cdev->devdata; | |
| 218 | + unsigned long val; | |
| 219 | + | |
| 220 | + if (clock_cooling_get_property(ccdev, (unsigned long)freq, &val, | |
| 221 | + GET_LEVEL)) | |
| 222 | + return THERMAL_CSTATE_INVALID; | |
| 223 | + | |
| 224 | + return val; | |
| 225 | +} | |
| 226 | +EXPORT_SYMBOL_GPL(clock_cooling_get_level); | |
| 227 | + | |
| 228 | +/** | |
| 229 | + * clock_cooling_get_frequency - get the absolute value of frequency from level. | |
| 230 | + * @ccdev: clock cooling device reference | |
| 231 | + * @level: cooling level | |
| 232 | + * | |
| 233 | + * This function matches cooling level with frequency. Based on a cooling level | |
| 234 | + * of frequency, equals cooling state of cpu cooling device, it will return | |
| 235 | + * the corresponding frequency. | |
| 236 | + * e.g level=0 --> 1st MAX FREQ, level=1 ---> 2nd MAX FREQ, .... etc | |
| 237 | + * | |
| 238 | + * Return: 0 on error, the corresponding frequency otherwise. | |
| 239 | + */ | |
| 240 | +static unsigned long | |
| 241 | +clock_cooling_get_frequency(struct clock_cooling_device *ccdev, | |
| 242 | + unsigned long level) | |
| 243 | +{ | |
| 244 | + int ret = 0; | |
| 245 | + unsigned long freq; | |
| 246 | + | |
| 247 | + ret = clock_cooling_get_property(ccdev, level, &freq, GET_FREQ); | |
| 248 | + if (ret) | |
| 249 | + return 0; | |
| 250 | + | |
| 251 | + return freq; | |
| 252 | +} | |
| 253 | + | |
| 254 | +/** | |
| 255 | + * clock_cooling_apply - function to apply frequency clipping. | |
| 256 | + * @ccdev: clock_cooling_device pointer containing frequency clipping data. | |
| 257 | + * @cooling_state: value of the cooling state. | |
| 258 | + * | |
| 259 | + * Function used to make sure the clock layer is aware of current thermal | |
| 260 | + * limits. The limits are applied by updating the clock rate in case it is | |
| 261 | + * higher than the corresponding frequency based on the requested cooling_state. | |
| 262 | + * | |
| 263 | + * Return: 0 on success, an error code otherwise (-EINVAL in case wrong | |
| 264 | + * cooling state). | |
| 265 | + */ | |
| 266 | +static int clock_cooling_apply(struct clock_cooling_device *ccdev, | |
| 267 | + unsigned long cooling_state) | |
| 268 | +{ | |
| 269 | + unsigned long clip_freq, cur_freq; | |
| 270 | + int ret = 0; | |
| 271 | + | |
| 272 | + /* Here we write the clipping */ | |
| 273 | + /* Check if the old cooling action is same as new cooling action */ | |
| 274 | + if (ccdev->clock_state == cooling_state) | |
| 275 | + return 0; | |
| 276 | + | |
| 277 | + clip_freq = clock_cooling_get_frequency(ccdev, cooling_state); | |
| 278 | + if (!clip_freq) | |
| 279 | + return -EINVAL; | |
| 280 | + | |
| 281 | + cur_freq = clk_get_rate(ccdev->clk); | |
| 282 | + | |
| 283 | + mutex_lock(&ccdev->lock); | |
| 284 | + ccdev->clock_state = cooling_state; | |
| 285 | + ccdev->clock_val = clip_freq; | |
| 286 | + /* enforce clock level */ | |
| 287 | + if (cur_freq > clip_freq) | |
| 288 | + ret = clk_set_rate(ccdev->clk, clip_freq); | |
| 289 | + mutex_unlock(&ccdev->lock); | |
| 290 | + | |
| 291 | + return ret; | |
| 292 | +} | |
| 293 | + | |
| 294 | +/** | |
| 295 | + * clock_cooling_clock_notifier - notifier callback on clock rate changes. | |
| 296 | + * @nb: struct notifier_block * with callback info. | |
| 297 | + * @event: value showing clock event for which this function invoked. | |
| 298 | + * @data: callback-specific data | |
| 299 | + * | |
| 300 | + * Callback to hijack the notification on clock transition. | |
| 301 | + * Every time there is a clock change, we intercept all pre change events | |
| 302 | + * and block the transition in case the new rate infringes thermal limits. | |
| 303 | + * | |
| 304 | + * Return: NOTIFY_DONE (success) or NOTIFY_BAD (new_rate > thermal limit). | |
| 305 | + */ | |
| 306 | +static int clock_cooling_clock_notifier(struct notifier_block *nb, | |
| 307 | + unsigned long event, void *data) | |
| 308 | +{ | |
| 309 | + struct clk_notifier_data *ndata = data; | |
| 310 | + struct clock_cooling_device *ccdev = to_clock_cooling_device(nb); | |
| 311 | + | |
| 312 | + switch (event) { | |
| 313 | + case PRE_RATE_CHANGE: | |
| 314 | + /* | |
| 315 | + * checks on current state | |
| 316 | + * TODO: current method is not best we can find as it | |
| 317 | + * allows possibly voltage transitions, in case DVFS | |
| 318 | + * layer is also hijacking clock pre notifications. | |
| 319 | + */ | |
| 320 | + if (ndata->new_rate > ccdev->clock_val) | |
| 321 | + return NOTIFY_BAD; | |
| 322 | + /* fall through */ | |
| 323 | + case POST_RATE_CHANGE: | |
| 324 | + case ABORT_RATE_CHANGE: | |
| 325 | + default: | |
| 326 | + return NOTIFY_DONE; | |
| 327 | + } | |
| 328 | +} | |
| 329 | + | |
| 330 | +/* clock cooling device thermal callback functions are defined below */ | |
| 331 | + | |
| 332 | +/** | |
| 333 | + * clock_cooling_get_max_state - callback function to get the max cooling state. | |
| 334 | + * @cdev: thermal cooling device pointer. | |
| 335 | + * @state: fill this variable with the max cooling state. | |
| 336 | + * | |
| 337 | + * Callback for the thermal cooling device to return the clock | |
| 338 | + * max cooling state. | |
| 339 | + * | |
| 340 | + * Return: 0 on success, an error code otherwise. | |
| 341 | + */ | |
| 342 | +static int clock_cooling_get_max_state(struct thermal_cooling_device *cdev, | |
| 343 | + unsigned long *state) | |
| 344 | +{ | |
| 345 | + struct clock_cooling_device *ccdev = cdev->devdata; | |
| 346 | + unsigned long count = 0; | |
| 347 | + int ret; | |
| 348 | + | |
| 349 | + ret = clock_cooling_get_property(ccdev, 0, &count, GET_MAXL); | |
| 350 | + if (!ret) | |
| 351 | + *state = count; | |
| 352 | + | |
| 353 | + return ret; | |
| 354 | +} | |
| 355 | + | |
| 356 | +/** | |
| 357 | + * clock_cooling_get_cur_state - function to get the current cooling state. | |
| 358 | + * @cdev: thermal cooling device pointer. | |
| 359 | + * @state: fill this variable with the current cooling state. | |
| 360 | + * | |
| 361 | + * Callback for the thermal cooling device to return the clock | |
| 362 | + * current cooling state. | |
| 363 | + * | |
| 364 | + * Return: 0 (success) | |
| 365 | + */ | |
| 366 | +static int clock_cooling_get_cur_state(struct thermal_cooling_device *cdev, | |
| 367 | + unsigned long *state) | |
| 368 | +{ | |
| 369 | + struct clock_cooling_device *ccdev = cdev->devdata; | |
| 370 | + | |
| 371 | + *state = ccdev->clock_state; | |
| 372 | + | |
| 373 | + return 0; | |
| 374 | +} | |
| 375 | + | |
| 376 | +/** | |
| 377 | + * clock_cooling_set_cur_state - function to set the current cooling state. | |
| 378 | + * @cdev: thermal cooling device pointer. | |
| 379 | + * @state: set this variable to the current cooling state. | |
| 380 | + * | |
| 381 | + * Callback for the thermal cooling device to change the clock cooling | |
| 382 | + * current cooling state. | |
| 383 | + * | |
| 384 | + * Return: 0 on success, an error code otherwise. | |
| 385 | + */ | |
| 386 | +static int clock_cooling_set_cur_state(struct thermal_cooling_device *cdev, | |
| 387 | + unsigned long state) | |
| 388 | +{ | |
| 389 | + struct clock_cooling_device *clock_device = cdev->devdata; | |
| 390 | + | |
| 391 | + return clock_cooling_apply(clock_device, state); | |
| 392 | +} | |
| 393 | + | |
| 394 | +/* Bind clock callbacks to thermal cooling device ops */ | |
| 395 | +static struct thermal_cooling_device_ops const clock_cooling_ops = { | |
| 396 | + .get_max_state = clock_cooling_get_max_state, | |
| 397 | + .get_cur_state = clock_cooling_get_cur_state, | |
| 398 | + .set_cur_state = clock_cooling_set_cur_state, | |
| 399 | +}; | |
| 400 | + | |
| 401 | +/** | |
| 402 | + * clock_cooling_register - function to create clock cooling device. | |
| 403 | + * @dev: struct device pointer to the device used as clock cooling device. | |
| 404 | + * @clock_name: string containing the clock used as cooling mechanism. | |
| 405 | + * | |
| 406 | + * This interface function registers the clock cooling device with the name | |
| 407 | + * "thermal-clock-%x". The cooling device is based on clock frequencies. | |
| 408 | + * The struct device is assumed to be capable of DVFS transitions. | |
| 409 | + * The OPP layer is used to fetch and fill the available frequencies for | |
| 410 | + * the referred device. The ordered frequency table is used to control | |
| 411 | + * the clock cooling device cooling states and to limit clock transitions | |
| 412 | + * based on the cooling state requested by the thermal framework. | |
| 413 | + * | |
| 414 | + * Return: a valid struct thermal_cooling_device pointer on success, | |
| 415 | + * on failure, it returns a corresponding ERR_PTR(). | |
| 416 | + */ | |
| 417 | +struct thermal_cooling_device * | |
| 418 | +clock_cooling_register(struct device *dev, const char *clock_name) | |
| 419 | +{ | |
| 420 | + struct thermal_cooling_device *cdev; | |
| 421 | + struct clock_cooling_device *ccdev = NULL; | |
| 422 | + char dev_name[THERMAL_NAME_LENGTH]; | |
| 423 | + int ret = 0; | |
| 424 | + | |
| 425 | + ccdev = devm_kzalloc(dev, sizeof(*ccdev), GFP_KERNEL); | |
| 426 | + if (!ccdev) | |
| 427 | + return ERR_PTR(-ENOMEM); | |
| 428 | + | |
| 429 | + ccdev->dev = dev; | |
| 430 | + ccdev->clk = devm_clk_get(dev, clock_name); | |
| 431 | + if (IS_ERR(ccdev->clk)) | |
| 432 | + return ERR_CAST(ccdev->clk); | |
| 433 | + | |
| 434 | + ret = clock_cooling_get_idr(&ccdev->id); | |
| 435 | + if (ret) | |
| 436 | + return ERR_PTR(-EINVAL); | |
| 437 | + | |
| 438 | + snprintf(dev_name, sizeof(dev_name), "thermal-clock-%d", ccdev->id); | |
| 439 | + | |
| 440 | + cdev = thermal_cooling_device_register(dev_name, ccdev, | |
| 441 | + &clock_cooling_ops); | |
| 442 | + if (IS_ERR(cdev)) { | |
| 443 | + release_idr(ccdev->id); | |
| 444 | + return ERR_PTR(-EINVAL); | |
| 445 | + } | |
| 446 | + ccdev->cdev = cdev; | |
| 447 | + ccdev->clk_rate_change_nb.notifier_call = clock_cooling_clock_notifier; | |
| 448 | + | |
| 449 | + /* Assuming someone has already filled the opp table for this device */ | |
| 450 | + ret = dev_pm_opp_init_cpufreq_table(dev, &ccdev->freq_table); | |
| 451 | + if (ret) { | |
| 452 | + release_idr(ccdev->id); | |
| 453 | + return ERR_PTR(ret); | |
| 454 | + } | |
| 455 | + ccdev->clock_state = 0; | |
| 456 | + ccdev->clock_val = clock_cooling_get_frequency(ccdev, 0); | |
| 457 | + | |
| 458 | + clk_notifier_register(ccdev->clk, &ccdev->clk_rate_change_nb); | |
| 459 | + | |
| 460 | + return cdev; | |
| 461 | +} | |
| 462 | +EXPORT_SYMBOL_GPL(clock_cooling_register); | |
| 463 | + | |
| 464 | +/** | |
| 465 | + * clock_cooling_unregister - function to remove clock cooling device. | |
| 466 | + * @cdev: thermal cooling device pointer. | |
| 467 | + * | |
| 468 | + * This interface function unregisters the "thermal-clock-%x" cooling device. | |
| 469 | + */ | |
| 470 | +void clock_cooling_unregister(struct thermal_cooling_device *cdev) | |
| 471 | +{ | |
| 472 | + struct clock_cooling_device *ccdev; | |
| 473 | + | |
| 474 | + if (!cdev) | |
| 475 | + return; | |
| 476 | + | |
| 477 | + ccdev = cdev->devdata; | |
| 478 | + | |
| 479 | + clk_notifier_unregister(ccdev->clk, &ccdev->clk_rate_change_nb); | |
| 480 | + dev_pm_opp_free_cpufreq_table(ccdev->dev, &ccdev->freq_table); | |
| 481 | + | |
| 482 | + thermal_cooling_device_unregister(ccdev->cdev); | |
| 483 | + release_idr(ccdev->id); | |
| 484 | +} | |
| 485 | +EXPORT_SYMBOL_GPL(clock_cooling_unregister); | |
| @@ -131,6 +131,8 @@ int acpi_parse_trt(acpi_handle handle, int *trt_count, struct trt **trtp, · 130 unmodified lines | ||
| 131 | 131 | pr_warn("Failed to get target ACPI device\n"); |
| 132 | 132 | } |
| 133 | 133 | |
| 134 | + result = 0; | |
| 135 | + | |
| 134 | 136 | *trtp = trts; |
| 135 | 137 | /* don't count bad entries */ |
| 136 | 138 | *trt_count -= nr_bad_entries; |
| @@ -317,21 +319,21 @@ static long acpi_thermal_rel_ioctl(struct file *f, unsigned int cmd, · 180 unmodified lines | ||
| 317 | 319 | { |
| 318 | 320 | int ret = 0; |
| 319 | 321 | unsigned long length = 0; |
| 320 | − unsigned long count = 0; | |
| 322 | + int count = 0; | |
| 321 | 323 | char __user *arg = (void __user *)__arg; |
| 322 | 324 | struct trt *trts; |
| 323 | 325 | struct art *arts; |
| 324 | 326 | |
| 325 | 327 | switch (cmd) { |
| 326 | 328 | case ACPI_THERMAL_GET_TRT_COUNT: |
| 327 | − ret = acpi_parse_trt(acpi_thermal_rel_handle, (int *)&count, | |
| 329 | + ret = acpi_parse_trt(acpi_thermal_rel_handle, &count, | |
| 328 | 330 | &trts, false); |
| 329 | 331 | kfree(trts); |
| 330 | 332 | if (!ret) |
| 331 | 333 | return put_user(count, (unsigned long __user *)__arg); |
| 332 | 334 | return ret; |
| 333 | 335 | case ACPI_THERMAL_GET_TRT_LEN: |
| 334 | − ret = acpi_parse_trt(acpi_thermal_rel_handle, (int *)&count, | |
| 336 | + ret = acpi_parse_trt(acpi_thermal_rel_handle, &count, | |
| 335 | 337 | &trts, false); |
| 336 | 338 | kfree(trts); |
| 337 | 339 | length = count * sizeof(union trt_object); |
| @@ -341,14 +343,14 @@ static long acpi_thermal_rel_ioctl(struct file *f, unsigned int cmd, · 3 unmodified lines | ||
| 341 | 343 | case ACPI_THERMAL_GET_TRT: |
| 342 | 344 | return fill_trt(arg); |
| 343 | 345 | case ACPI_THERMAL_GET_ART_COUNT: |
| 344 | − ret = acpi_parse_art(acpi_thermal_rel_handle, (int *)&count, | |
| 346 | + ret = acpi_parse_art(acpi_thermal_rel_handle, &count, | |
| 345 | 347 | &arts, false); |
| 346 | 348 | kfree(arts); |
| 347 | 349 | if (!ret) |
| 348 | 350 | return put_user(count, (unsigned long __user *)__arg); |
| 349 | 351 | return ret; |
| 350 | 352 | case ACPI_THERMAL_GET_ART_LEN: |
| 351 | − ret = acpi_parse_art(acpi_thermal_rel_handle, (int *)&count, | |
| 353 | + ret = acpi_parse_art(acpi_thermal_rel_handle, &count, | |
| 352 | 354 | &arts, false); |
| 353 | 355 | kfree(arts); |
| 354 | 356 | length = count * sizeof(union art_object); |
| @@ -43,6 +43,74 @@ struct int3400_thermal_priv { · 42 unmodified lines | ||
| 43 | 43 | struct trt *trts; |
| 44 | 44 | u8 uuid_bitmap; |
| 45 | 45 | int rel_misc_dev_res; |
| 46 | + int current_uuid_index; | |
| 47 | +}; | |
| 48 | + | |
| 49 | +static ssize_t available_uuids_show(struct device *dev, | |
| 50 | + struct device_attribute *attr, | |
| 51 | + char *buf) | |
| 52 | +{ | |
| 53 | + struct platform_device *pdev = to_platform_device(dev); | |
| 54 | + struct int3400_thermal_priv *priv = platform_get_drvdata(pdev); | |
| 55 | + int i; | |
| 56 | + int length = 0; | |
| 57 | + | |
| 58 | + for (i = 0; i < INT3400_THERMAL_MAXIMUM_UUID; i++) { | |
| 59 | + if (priv->uuid_bitmap & (1 << i)) | |
| 60 | + if (PAGE_SIZE - length > 0) | |
| 61 | + length += snprintf(&buf[length], | |
| 62 | + PAGE_SIZE - length, | |
| 63 | + "%s\n", | |
| 64 | + int3400_thermal_uuids[i]); | |
| 65 | + } | |
| 66 | + | |
| 67 | + return length; | |
| 68 | +} | |
| 69 | + | |
| 70 | +static ssize_t current_uuid_show(struct device *dev, | |
| 71 | + struct device_attribute *devattr, char *buf) | |
| 72 | +{ | |
| 73 | + struct platform_device *pdev = to_platform_device(dev); | |
| 74 | + struct int3400_thermal_priv *priv = platform_get_drvdata(pdev); | |
| 75 | + | |
| 76 | + if (priv->uuid_bitmap & (1 << priv->current_uuid_index)) | |
| 77 | + return sprintf(buf, "%s\n", | |
| 78 | + int3400_thermal_uuids[priv->current_uuid_index]); | |
| 79 | + else | |
| 80 | + return sprintf(buf, "INVALID\n"); | |
| 81 | +} | |
| 82 | + | |
| 83 | +static ssize_t current_uuid_store(struct device *dev, | |
| 84 | + struct device_attribute *attr, | |
| 85 | + const char *buf, size_t count) | |
| 86 | +{ | |
| 87 | + struct platform_device *pdev = to_platform_device(dev); | |
| 88 | + struct int3400_thermal_priv *priv = platform_get_drvdata(pdev); | |
| 89 | + int i; | |
| 90 | + | |
| 91 | + for (i = 0; i < INT3400_THERMAL_MAXIMUM_UUID; ++i) { | |
| 92 | + if ((priv->uuid_bitmap & (1 << i)) && | |
| 93 | + !(strncmp(buf, int3400_thermal_uuids[i], | |
| 94 | + sizeof(int3400_thermal_uuids[i]) - 1))) { | |
| 95 | + priv->current_uuid_index = i; | |
| 96 | + return count; | |
| 97 | + } | |
| 98 | + } | |
| 99 | + | |
| 100 | + return -EINVAL; | |
| 101 | +} | |
| 102 | + | |
| 103 | +static DEVICE_ATTR(current_uuid, 0644, current_uuid_show, current_uuid_store); | |
| 104 | +static DEVICE_ATTR_RO(available_uuids); | |
| 105 | +static struct attribute *uuid_attrs[] = { | |
| 106 | + &dev_attr_available_uuids.attr, | |
| 107 | + &dev_attr_current_uuid.attr, | |
| 108 | + NULL | |
| 109 | +}; | |
| 110 | + | |
| 111 | +static struct attribute_group uuid_attribute_group = { | |
| 112 | + .attrs = uuid_attrs, | |
| 113 | + .name = "uuids" | |
| 46 | 114 | }; |
| 47 | 115 | |
| 48 | 116 | static int int3400_thermal_get_uuids(struct int3400_thermal_priv *priv) |
| @@ -160,9 +228,9 @@ static int int3400_thermal_set_mode(struct thermal_zone_device *thermal, · 111 unmodified lines | ||
| 160 | 228 | |
| 161 | 229 | if (enable != priv->mode) { |
| 162 | 230 | priv->mode = enable; |
| 163 | − /* currently, only PASSIVE COOLING is supported */ | |
| 164 | 231 | result = int3400_thermal_run_osc(priv->adev->handle, |
| 165 | − INT3400_THERMAL_PASSIVE_1, enable); | |
| 232 | + priv->current_uuid_index, | |
| 233 | + enable); | |
| 166 | 234 | } |
| 167 | 235 | return result; |
| 168 | 236 | } |
| @@ -223,7 +291,14 @@ static int int3400_thermal_probe(struct platform_device *pdev) · 54 unmodified lines | ||
| 223 | 291 | priv->rel_misc_dev_res = acpi_thermal_rel_misc_device_add( |
| 224 | 292 | priv->adev->handle); |
| 225 | 293 | |
| 294 | + result = sysfs_create_group(&pdev->dev.kobj, &uuid_attribute_group); | |
| 295 | + if (result) | |
| 296 | + goto free_zone; | |
| 297 | + | |
| 226 | 298 | return 0; |
| 299 | + | |
| 300 | +free_zone: | |
| 301 | + thermal_zone_device_unregister(priv->thermal); | |
| 227 | 302 | free_trt: |
| 228 | 303 | kfree(priv->trts); |
| 229 | 304 | free_art: |
| @@ -240,6 +315,7 @@ static int int3400_thermal_remove(struct platform_device *pdev) · 10 unmodified lines | ||
| 240 | 315 | if (!priv->rel_misc_dev_res) |
| 241 | 316 | acpi_thermal_rel_misc_device_remove(priv->adev->handle); |
| 242 | 317 | |
| 318 | + sysfs_remove_group(&pdev->dev.kobj, &uuid_attribute_group); | |
| 243 | 319 | thermal_zone_device_unregister(priv->thermal); |
| 244 | 320 | kfree(priv->trts); |
| 245 | 321 | kfree(priv->arts); |
| @@ -293,8 +293,7 @@ static int int3403_sensor_add(struct int3403_priv *priv) · 292 unmodified lines | ||
| 293 | 293 | return 0; |
| 294 | 294 | |
| 295 | 295 | err_free_obj: |
| 296 | − if (obj->tzone) | |
| 297 | − thermal_zone_device_unregister(obj->tzone); | |
| 296 | + thermal_zone_device_unregister(obj->tzone); | |
| 298 | 297 | return result; |
| 299 | 298 | } |
| 300 | 299 | |
| @@ -471,7 +470,6 @@ static struct platform_driver int3403_driver = { · 170 unmodified lines | ||
| 471 | 470 | .remove = int3403_remove, |
| 472 | 471 | .driver = { |
| 473 | 472 | .name = "int3403 thermal", |
| 474 | − .owner = THIS_MODULE, | |
| 475 | 473 | .acpi_match_table = int3403_device_ids, |
| 476 | 474 | }, |
| 477 | 475 | }; |
| @@ -689,6 +689,7 @@ static const struct x86_cpu_id intel_powerclamp_ids[] = { · 688 unmodified lines | ||
| 689 | 689 | { X86_VENDOR_INTEL, 6, 0x3f}, |
| 690 | 690 | { X86_VENDOR_INTEL, 6, 0x45}, |
| 691 | 691 | { X86_VENDOR_INTEL, 6, 0x46}, |
| 692 | + { X86_VENDOR_INTEL, 6, 0x4c}, | |
| 692 | 693 | {} |
| 693 | 694 | }; |
| 694 | 695 | MODULE_DEVICE_TABLE(x86cpu, intel_powerclamp_ids); |
| @@ -360,6 +360,9 @@ static void proc_thermal_interrupt(void) · 359 unmodified lines | ||
| 360 | 360 | u32 sticky_out; |
| 361 | 361 | int status; |
| 362 | 362 | u32 ptmc_out; |
| 363 | + unsigned long flags; | |
| 364 | + | |
| 365 | + spin_lock_irqsave(&intr_notify_lock, flags); | |
| 363 | 366 | |
| 364 | 367 | /* Clear APIC interrupt */ |
| 365 | 368 | status = iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_READ, |
| @@ -378,21 +381,20 @@ static void proc_thermal_interrupt(void) · 12 unmodified lines | ||
| 378 | 381 | /* reset sticky bit */ |
| 379 | 382 | status = iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_WRITE, |
| 380 | 383 | SOC_DTS_OFFSET_PTTSS, sticky_out); |
| 384 | + spin_unlock_irqrestore(&intr_notify_lock, flags); | |
| 385 | + | |
| 381 | 386 | for (i = 0; i < SOC_MAX_DTS_SENSORS; ++i) { |
| 382 | 387 | pr_debug("TZD update for zone %d\n", i); |
| 383 | 388 | thermal_zone_device_update(soc_dts[i]->tzone); |
| 384 | 389 | } |
| 385 | − } | |
| 390 | + } else | |
| 391 | + spin_unlock_irqrestore(&intr_notify_lock, flags); | |
| 386 | 392 | |
| 387 | 393 | } |
| 388 | 394 | |
| 389 | 395 | static irqreturn_t soc_irq_thread_fn(int irq, void *dev_data) |
| 390 | 396 | { |
| 391 | − unsigned long flags; | |
| 392 | − | |
| 393 | − spin_lock_irqsave(&intr_notify_lock, flags); | |
| 394 | 397 | proc_thermal_interrupt(); |
| 395 | − spin_unlock_irqrestore(&intr_notify_lock, flags); | |
| 396 | 398 | pr_debug("proc_thermal_interrupt\n"); |
| 397 | 399 | |
| 398 | 400 | return IRQ_HANDLED; |
| @@ -30,26 +30,12 @@ · 29 unmodified lines | ||
| 30 | 30 | #include <linux/err.h> |
| 31 | 31 | #include <linux/export.h> |
| 32 | 32 | #include <linux/string.h> |
| 33 | +#include <linux/thermal.h> | |
| 33 | 34 | |
| 34 | 35 | #include "thermal_core.h" |
| 35 | 36 | |
| 36 | 37 | /*** Private data structures to represent thermal device tree data ***/ |
| 37 | 38 | |
| 38 | −/** | |
| 39 | − * struct __thermal_trip - representation of a point in temperature domain | |
| 40 | − * @np: pointer to struct device_node that this trip point was created from | |
| 41 | − * @temperature: temperature value in miliCelsius | |
| 42 | − * @hysteresis: relative hysteresis in miliCelsius | |
| 43 | − * @type: trip point type | |
| 44 | − */ | |
| 45 | − | |
| 46 | −struct __thermal_trip { | |
| 47 | − struct device_node *np; | |
| 48 | − unsigned long int temperature; | |
| 49 | − unsigned long int hysteresis; | |
| 50 | − enum thermal_trip_type type; | |
| 51 | −}; | |
| 52 | − | |
| 53 | 39 | /** |
| 54 | 40 | * struct __thermal_bind_param - a match between trip and cooling device |
| 55 | 41 | * @cooling_device: a pointer to identify the referred cooling device |
| @@ -77,8 +63,7 @@ struct __thermal_bind_params { · 21 unmodified lines | ||
| 77 | 63 | * @num_tbps: number of thermal bind params |
| 78 | 64 | * @tbps: an array of thermal bind params (0..num_tbps - 1) |
| 79 | 65 | * @sensor_data: sensor private data used while reading temperature and trend |
| 80 | − * @get_temp: sensor callback to read temperature | |
| 81 | − * @get_trend: sensor callback to read temperature trend | |
| 66 | + * @ops: set of callbacks to handle the thermal zone based on DT | |
| 82 | 67 | */ |
| 83 | 68 | |
| 84 | 69 | struct __thermal_zone { |
| @@ -88,7 +73,7 @@ struct __thermal_zone { · 3 unmodified lines | ||
| 88 | 73 | |
| 89 | 74 | /* trip data */ |
| 90 | 75 | int ntrips; |
| 91 | − struct __thermal_trip *trips; | |
| 76 | + struct thermal_trip *trips; | |
| 92 | 77 | |
| 93 | 78 | /* cooling binding data */ |
| 94 | 79 | int num_tbps; |
| @@ -96,8 +81,7 @@ struct __thermal_zone { · 1 unmodified line | ||
| 96 | 81 | |
| 97 | 82 | /* sensor interface */ |
| 98 | 83 | void *sensor_data; |
| 99 | − int (*get_temp)(void *, long *); | |
| 100 | − int (*get_trend)(void *, long *); | |
| 84 | + const struct thermal_zone_of_device_ops *ops; | |
| 101 | 85 | }; |
| 102 | 86 | |
| 103 | 87 | /*** DT thermal zone device callbacks ***/ |
| @@ -107,10 +91,96 @@ static int of_thermal_get_temp(struct thermal_zone_device *tz, · 3 unmodified lines | ||
| 107 | 91 | { |
| 108 | 92 | struct __thermal_zone *data = tz->devdata; |
| 109 | 93 | |
| 110 | − if (!data->get_temp) | |
| 94 | + if (!data->ops->get_temp) | |
| 111 | 95 | return -EINVAL; |
| 112 | 96 | |
| 113 | − return data->get_temp(data->sensor_data, temp); | |
| 97 | + return data->ops->get_temp(data->sensor_data, temp); | |
| 98 | +} | |
| 99 | + | |
| 100 | +/** | |
| 101 | + * of_thermal_get_ntrips - function to export number of available trip | |
| 102 | + * points. | |
| 103 | + * @tz: pointer to a thermal zone | |
| 104 | + * | |
| 105 | + * This function is a globally visible wrapper to get number of trip points | |
| 106 | + * stored in the local struct __thermal_zone | |
| 107 | + * | |
| 108 | + * Return: number of available trip points, -ENODEV when data not available | |
| 109 | + */ | |
| 110 | +int of_thermal_get_ntrips(struct thermal_zone_device *tz) | |
| 111 | +{ | |
| 112 | + struct __thermal_zone *data = tz->devdata; | |
| 113 | + | |
| 114 | + if (!data || IS_ERR(data)) | |
| 115 | + return -ENODEV; | |
| 116 | + | |
| 117 | + return data->ntrips; | |
| 118 | +} | |
| 119 | +EXPORT_SYMBOL_GPL(of_thermal_get_ntrips); | |
| 120 | + | |
| 121 | +/** | |
| 122 | + * of_thermal_is_trip_valid - function to check if trip point is valid | |
| 123 | + * | |
| 124 | + * @tz: pointer to a thermal zone | |
| 125 | + * @trip: trip point to evaluate | |
| 126 | + * | |
| 127 | + * This function is responsible for checking if passed trip point is valid | |
| 128 | + * | |
| 129 | + * Return: true if trip point is valid, false otherwise | |
| 130 | + */ | |
| 131 | +bool of_thermal_is_trip_valid(struct thermal_zone_device *tz, int trip) | |
| 132 | +{ | |
| 133 | + struct __thermal_zone *data = tz->devdata; | |
| 134 | + | |
| 135 | + if (!data || trip >= data->ntrips || trip < 0) | |
| 136 | + return false; | |
| 137 | + | |
| 138 | + return true; | |
| 139 | +} | |
| 140 | +EXPORT_SYMBOL_GPL(of_thermal_is_trip_valid); | |
| 141 | + | |
| 142 | +/** | |
| 143 | + * of_thermal_get_trip_points - function to get access to a globally exported | |
| 144 | + * trip points | |
| 145 | + * | |
| 146 | + * @tz: pointer to a thermal zone | |
| 147 | + * | |
| 148 | + * This function provides a pointer to trip points table | |
| 149 | + * | |
| 150 | + * Return: pointer to trip points table, NULL otherwise | |
| 151 | + */ | |
| 152 | +const struct thermal_trip * const | |
| 153 | +of_thermal_get_trip_points(struct thermal_zone_device *tz) | |
| 154 | +{ | |
| 155 | + struct __thermal_zone *data = tz->devdata; | |
| 156 | + | |
| 157 | + if (!data) | |
| 158 | + return NULL; | |
| 159 | + | |
| 160 | + return data->trips; | |
| 161 | +} | |
| 162 | +EXPORT_SYMBOL_GPL(of_thermal_get_trip_points); | |
| 163 | + | |
| 164 | +/** | |
| 165 | + * of_thermal_set_emul_temp - function to set emulated temperature | |
| 166 | + * | |
| 167 | + * @tz: pointer to a thermal zone | |
| 168 | + * @temp: temperature to set | |
| 169 | + * | |
| 170 | + * This function gives the ability to set emulated value of temperature, | |
| 171 | + * which is handy for debugging | |
| 172 | + * | |
| 173 | + * Return: zero on success, error code otherwise | |
| 174 | + */ | |
| 175 | +static int of_thermal_set_emul_temp(struct thermal_zone_device *tz, | |
| 176 | + unsigned long temp) | |
| 177 | +{ | |
| 178 | + struct __thermal_zone *data = tz->devdata; | |
| 179 | + | |
| 180 | + if (!data->ops || !data->ops->set_emul_temp) | |
| 181 | + return -EINVAL; | |
| 182 | + | |
| 183 | + return data->ops->set_emul_temp(data->sensor_data, temp); | |
| 114 | 184 | } |
| 115 | 185 | |
| 116 | 186 | static int of_thermal_get_trend(struct thermal_zone_device *tz, int trip, |
| @@ -120,10 +190,10 @@ static int of_thermal_get_trend(struct thermal_zone_device *tz, int trip, · 3 unmodified lines | ||
| 120 | 190 | long dev_trend; |
| 121 | 191 | int r; |
| 122 | 192 | |
| 123 | − if (!data->get_trend) | |
| 193 | + if (!data->ops->get_trend) | |
| 124 | 194 | return -EINVAL; |
| 125 | 195 | |
| 126 | − r = data->get_trend(data->sensor_data, &dev_trend); | |
| 196 | + r = data->ops->get_trend(data->sensor_data, &dev_trend); | |
| 127 | 197 | if (r) |
| 128 | 198 | return r; |
| 129 | 199 | |
| @@ -324,8 +394,7 @@ static struct thermal_zone_device_ops of_thermal_ops = { · 194 unmodified lines | ||
| 324 | 394 | static struct thermal_zone_device * |
| 325 | 395 | thermal_zone_of_add_sensor(struct device_node *zone, |
| 326 | 396 | struct device_node *sensor, void *data, |
| 327 | − int (*get_temp)(void *, long *), | |
| 328 | − int (*get_trend)(void *, long *)) | |
| 397 | + const struct thermal_zone_of_device_ops *ops) | |
| 329 | 398 | { |
| 330 | 399 | struct thermal_zone_device *tzd; |
| 331 | 400 | struct __thermal_zone *tz; |
| @@ -336,13 +405,16 @@ thermal_zone_of_add_sensor(struct device_node *zone, · 4 unmodified lines | ||
| 336 | 405 | |
| 337 | 406 | tz = tzd->devdata; |
| 338 | 407 | |
| 408 | + if (!ops) | |
| 409 | + return ERR_PTR(-EINVAL); | |
| 410 | + | |
| 339 | 411 | mutex_lock(&tzd->lock); |
| 340 | − tz->get_temp = get_temp; | |
| 341 | − tz->get_trend = get_trend; | |
| 412 | + tz->ops = ops; | |
| 342 | 413 | tz->sensor_data = data; |
| 343 | 414 | |
| 344 | 415 | tzd->ops->get_temp = of_thermal_get_temp; |
| 345 | 416 | tzd->ops->get_trend = of_thermal_get_trend; |
| 417 | + tzd->ops->set_emul_temp = of_thermal_set_emul_temp; | |
| 346 | 418 | mutex_unlock(&tzd->lock); |
| 347 | 419 | |
| 348 | 420 | return tzd; |
| @@ -356,8 +428,7 @@ thermal_zone_of_add_sensor(struct device_node *zone, · 7 unmodified lines | ||
| 356 | 428 | * than one sensors |
| 357 | 429 | * @data: a private pointer (owned by the caller) that will be passed |
| 358 | 430 | * back, when a temperature reading is needed. |
| 359 | − * @get_temp: a pointer to a function that reads the sensor temperature. | |
| 360 | − * @get_trend: a pointer to a function that reads the sensor temperature trend. | |
| 431 | + * @ops: struct thermal_zone_of_device_ops *. Must contain at least .get_temp. | |
| 361 | 432 | * |
| 362 | 433 | * This function will search the list of thermal zones described in device |
| 363 | 434 | * tree and look for the zone that refer to the sensor device pointed by |
| @@ -382,9 +453,8 @@ thermal_zone_of_add_sensor(struct device_node *zone, · 18 unmodified lines | ||
| 382 | 453 | * check the return value with help of IS_ERR() helper. |
| 383 | 454 | */ |
| 384 | 455 | struct thermal_zone_device * |
| 385 | −thermal_zone_of_sensor_register(struct device *dev, int sensor_id, | |
| 386 | − void *data, int (*get_temp)(void *, long *), | |
| 387 | − int (*get_trend)(void *, long *)) | |
| 456 | +thermal_zone_of_sensor_register(struct device *dev, int sensor_id, void *data, | |
| 457 | + const struct thermal_zone_of_device_ops *ops) | |
| 388 | 458 | { |
| 389 | 459 | struct device_node *np, *child, *sensor_np; |
| 390 | 460 | struct thermal_zone_device *tzd = ERR_PTR(-ENODEV); |
| @@ -426,9 +496,7 @@ thermal_zone_of_sensor_register(struct device *dev, int sensor_id, · 35 unmodified lines | ||
| 426 | 496 | |
| 427 | 497 | if (sensor_specs.np == sensor_np && id == sensor_id) { |
| 428 | 498 | tzd = thermal_zone_of_add_sensor(child, sensor_np, |
| 429 | − data, | |
| 430 | − get_temp, | |
| 431 | − get_trend); | |
| 499 | + data, ops); | |
| 432 | 500 | of_node_put(sensor_specs.np); |
| 433 | 501 | of_node_put(child); |
| 434 | 502 | goto exit; |
| @@ -475,9 +543,9 @@ void thermal_zone_of_sensor_unregister(struct device *dev, · 40 unmodified lines | ||
| 475 | 543 | mutex_lock(&tzd->lock); |
| 476 | 544 | tzd->ops->get_temp = NULL; |
| 477 | 545 | tzd->ops->get_trend = NULL; |
| 546 | + tzd->ops->set_emul_temp = NULL; | |
| 478 | 547 | |
| 479 | − tz->get_temp = NULL; | |
| 480 | − tz->get_trend = NULL; | |
| 548 | + tz->ops = NULL; | |
| 481 | 549 | tz->sensor_data = NULL; |
| 482 | 550 | mutex_unlock(&tzd->lock); |
| 483 | 551 | } |
| @@ -501,7 +569,7 @@ EXPORT_SYMBOL_GPL(thermal_zone_of_sensor_unregister); · 17 unmodified lines | ||
| 501 | 569 | */ |
| 502 | 570 | static int thermal_of_populate_bind_params(struct device_node *np, |
| 503 | 571 | struct __thermal_bind_params *__tbp, |
| 504 | − struct __thermal_trip *trips, | |
| 572 | + struct thermal_trip *trips, | |
| 505 | 573 | int ntrips) |
| 506 | 574 | { |
| 507 | 575 | struct of_phandle_args cooling_spec; |
| @@ -604,7 +672,7 @@ static int thermal_of_get_trip_type(struct device_node *np, · 96 unmodified lines | ||
| 604 | 672 | * Return: 0 on success, proper error code otherwise |
| 605 | 673 | */ |
| 606 | 674 | static int thermal_of_populate_trip(struct device_node *np, |
| 607 | − struct __thermal_trip *trip) | |
| 675 | + struct thermal_trip *trip) | |
| 608 | 676 | { |
| 609 | 677 | int prop; |
| 610 | 678 | int ret; |
| @@ -0,0 +1,693 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd | |
| 3 | + * | |
| 4 | + * This program is free software; you can redistribute it and/or modify it | |
| 5 | + * under the terms and conditions of the GNU General Public License, | |
| 6 | + * version 2, as published by the Free Software Foundation. | |
| 7 | + * | |
| 8 | + * This program is distributed in the hope it will be useful, but WITHOUT | |
| 9 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| 10 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
| 11 | + * more details. | |
| 12 | + */ | |
| 13 | + | |
| 14 | +#include <linux/clk.h> | |
| 15 | +#include <linux/delay.h> | |
| 16 | +#include <linux/interrupt.h> | |
| 17 | +#include <linux/io.h> | |
| 18 | +#include <linux/module.h> | |
| 19 | +#include <linux/of.h> | |
| 20 | +#include <linux/of_address.h> | |
| 21 | +#include <linux/of_irq.h> | |
| 22 | +#include <linux/platform_device.h> | |
| 23 | +#include <linux/reset.h> | |
| 24 | +#include <linux/thermal.h> | |
| 25 | + | |
| 26 | +/** | |
| 27 | + * If the temperature over a period of time High, | |
| 28 | + * the resulting TSHUT gave CRU module,let it reset the entire chip, | |
| 29 | + * or via GPIO give PMIC. | |
| 30 | + */ | |
| 31 | +enum tshut_mode { | |
| 32 | + TSHUT_MODE_CRU = 0, | |
| 33 | + TSHUT_MODE_GPIO, | |
| 34 | +}; | |
| 35 | + | |
| 36 | +/** | |
| 37 | + * the system Temperature Sensors tshut(tshut) polarity | |
| 38 | + * the bit 8 is tshut polarity. | |
| 39 | + * 0: low active, 1: high active | |
| 40 | + */ | |
| 41 | +enum tshut_polarity { | |
| 42 | + TSHUT_LOW_ACTIVE = 0, | |
| 43 | + TSHUT_HIGH_ACTIVE, | |
| 44 | +}; | |
| 45 | + | |
| 46 | +/** | |
| 47 | + * The system has three Temperature Sensors. channel 0 is reserved, | |
| 48 | + * channel 1 is for CPU, and channel 2 is for GPU. | |
| 49 | + */ | |
| 50 | +enum sensor_id { | |
| 51 | + SENSOR_CPU = 1, | |
| 52 | + SENSOR_GPU, | |
| 53 | +}; | |
| 54 | + | |
| 55 | +struct rockchip_tsadc_chip { | |
| 56 | + /* The hardware-controlled tshut property */ | |
| 57 | + long tshut_temp; | |
| 58 | + enum tshut_mode tshut_mode; | |
| 59 | + enum tshut_polarity tshut_polarity; | |
| 60 | + | |
| 61 | + /* Chip-wide methods */ | |
| 62 | + void (*initialize)(void __iomem *reg, enum tshut_polarity p); | |
| 63 | + void (*irq_ack)(void __iomem *reg); | |
| 64 | + void (*control)(void __iomem *reg, bool on); | |
| 65 | + | |
| 66 | + /* Per-sensor methods */ | |
| 67 | + int (*get_temp)(int chn, void __iomem *reg, long *temp); | |
| 68 | + void (*set_tshut_temp)(int chn, void __iomem *reg, long temp); | |
| 69 | + void (*set_tshut_mode)(int chn, void __iomem *reg, enum tshut_mode m); | |
| 70 | +}; | |
| 71 | + | |
| 72 | +struct rockchip_thermal_sensor { | |
| 73 | + struct rockchip_thermal_data *thermal; | |
| 74 | + struct thermal_zone_device *tzd; | |
| 75 | + enum sensor_id id; | |
| 76 | +}; | |
| 77 | + | |
| 78 | +#define NUM_SENSORS 2 /* Ignore unused sensor 0 */ | |
| 79 | + | |
| 80 | +struct rockchip_thermal_data { | |
| 81 | + const struct rockchip_tsadc_chip *chip; | |
| 82 | + struct platform_device *pdev; | |
| 83 | + struct reset_control *reset; | |
| 84 | + | |
| 85 | + struct rockchip_thermal_sensor sensors[NUM_SENSORS]; | |
| 86 | + | |
| 87 | + struct clk *clk; | |
| 88 | + struct clk *pclk; | |
| 89 | + | |
| 90 | + void __iomem *regs; | |
| 91 | + | |
| 92 | + long tshut_temp; | |
| 93 | + enum tshut_mode tshut_mode; | |
| 94 | + enum tshut_polarity tshut_polarity; | |
| 95 | +}; | |
| 96 | + | |
| 97 | +/* TSADC V2 Sensor info define: */ | |
| 98 | +#define TSADCV2_AUTO_CON 0x04 | |
| 99 | +#define TSADCV2_INT_EN 0x08 | |
| 100 | +#define TSADCV2_INT_PD 0x0c | |
| 101 | +#define TSADCV2_DATA(chn) (0x20 + (chn) * 0x04) | |
| 102 | +#define TSADCV2_COMP_SHUT(chn) (0x40 + (chn) * 0x04) | |
| 103 | +#define TSADCV2_HIGHT_INT_DEBOUNCE 0x60 | |
| 104 | +#define TSADCV2_HIGHT_TSHUT_DEBOUNCE 0x64 | |
| 105 | +#define TSADCV2_AUTO_PERIOD 0x68 | |
| 106 | +#define TSADCV2_AUTO_PERIOD_HT 0x6c | |
| 107 | + | |
| 108 | +#define TSADCV2_AUTO_EN BIT(0) | |
| 109 | +#define TSADCV2_AUTO_DISABLE ~BIT(0) | |
| 110 | +#define TSADCV2_AUTO_SRC_EN(chn) BIT(4 + (chn)) | |
| 111 | +#define TSADCV2_AUTO_TSHUT_POLARITY_HIGH BIT(8) | |
| 112 | +#define TSADCV2_AUTO_TSHUT_POLARITY_LOW ~BIT(8) | |
| 113 | + | |
| 114 | +#define TSADCV2_INT_SRC_EN(chn) BIT(chn) | |
| 115 | +#define TSADCV2_SHUT_2GPIO_SRC_EN(chn) BIT(4 + (chn)) | |
| 116 | +#define TSADCV2_SHUT_2CRU_SRC_EN(chn) BIT(8 + (chn)) | |
| 117 | + | |
| 118 | +#define TSADCV2_INT_PD_CLEAR ~BIT(8) | |
| 119 | + | |
| 120 | +#define TSADCV2_DATA_MASK 0xfff | |
| 121 | +#define TSADCV2_HIGHT_INT_DEBOUNCE_COUNT 4 | |
| 122 | +#define TSADCV2_HIGHT_TSHUT_DEBOUNCE_COUNT 4 | |
| 123 | +#define TSADCV2_AUTO_PERIOD_TIME 250 /* msec */ | |
| 124 | +#define TSADCV2_AUTO_PERIOD_HT_TIME 50 /* msec */ | |
| 125 | + | |
| 126 | +struct tsadc_table { | |
| 127 | + unsigned long code; | |
| 128 | + long temp; | |
| 129 | +}; | |
| 130 | + | |
| 131 | +static const struct tsadc_table v2_code_table[] = { | |
| 132 | + {TSADCV2_DATA_MASK, -40000}, | |
| 133 | + {3800, -40000}, | |
| 134 | + {3792, -35000}, | |
| 135 | + {3783, -30000}, | |
| 136 | + {3774, -25000}, | |
| 137 | + {3765, -20000}, | |
| 138 | + {3756, -15000}, | |
| 139 | + {3747, -10000}, | |
| 140 | + {3737, -5000}, | |
| 141 | + {3728, 0}, | |
| 142 | + {3718, 5000}, | |
| 143 | + {3708, 10000}, | |
| 144 | + {3698, 15000}, | |
| 145 | + {3688, 20000}, | |
| 146 | + {3678, 25000}, | |
| 147 | + {3667, 30000}, | |
| 148 | + {3656, 35000}, | |
| 149 | + {3645, 40000}, | |
| 150 | + {3634, 45000}, | |
| 151 | + {3623, 50000}, | |
| 152 | + {3611, 55000}, | |
| 153 | + {3600, 60000}, | |
| 154 | + {3588, 65000}, | |
| 155 | + {3575, 70000}, | |
| 156 | + {3563, 75000}, | |
| 157 | + {3550, 80000}, | |
| 158 | + {3537, 85000}, | |
| 159 | + {3524, 90000}, | |
| 160 | + {3510, 95000}, | |
| 161 | + {3496, 100000}, | |
| 162 | + {3482, 105000}, | |
| 163 | + {3467, 110000}, | |
| 164 | + {3452, 115000}, | |
| 165 | + {3437, 120000}, | |
| 166 | + {3421, 125000}, | |
| 167 | + {0, 125000}, | |
| 168 | +}; | |
| 169 | + | |
| 170 | +static u32 rk_tsadcv2_temp_to_code(long temp) | |
| 171 | +{ | |
| 172 | + int high, low, mid; | |
| 173 | + | |
| 174 | + low = 0; | |
| 175 | + high = ARRAY_SIZE(v2_code_table) - 1; | |
| 176 | + mid = (high + low) / 2; | |
| 177 | + | |
| 178 | + if (temp < v2_code_table[low].temp || temp > v2_code_table[high].temp) | |
| 179 | + return 0; | |
| 180 | + | |
| 181 | + while (low <= high) { | |
| 182 | + if (temp == v2_code_table[mid].temp) | |
| 183 | + return v2_code_table[mid].code; | |
| 184 | + else if (temp < v2_code_table[mid].temp) | |
| 185 | + high = mid - 1; | |
| 186 | + else | |
| 187 | + low = mid + 1; | |
| 188 | + mid = (low + high) / 2; | |
| 189 | + } | |
| 190 | + | |
| 191 | + return 0; | |
| 192 | +} | |
| 193 | + | |
| 194 | +static long rk_tsadcv2_code_to_temp(u32 code) | |
| 195 | +{ | |
| 196 | + int high, low, mid; | |
| 197 | + | |
| 198 | + low = 0; | |
| 199 | + high = ARRAY_SIZE(v2_code_table) - 1; | |
| 200 | + mid = (high + low) / 2; | |
| 201 | + | |
| 202 | + if (code > v2_code_table[low].code || code < v2_code_table[high].code) | |
| 203 | + return 125000; /* No code available, return max temperature */ | |
| 204 | + | |
| 205 | + while (low <= high) { | |
| 206 | + if (code >= v2_code_table[mid].code && code < | |
| 207 | + v2_code_table[mid - 1].code) | |
| 208 | + return v2_code_table[mid].temp; | |
| 209 | + else if (code < v2_code_table[mid].code) | |
| 210 | + low = mid + 1; | |
| 211 | + else | |
| 212 | + high = mid - 1; | |
| 213 | + mid = (low + high) / 2; | |
| 214 | + } | |
| 215 | + | |
| 216 | + return 125000; | |
| 217 | +} | |
| 218 | + | |
| 219 | +/** | |
| 220 | + * rk_tsadcv2_initialize - initialize TASDC Controller | |
| 221 | + * (1) Set TSADCV2_AUTO_PERIOD, configure the interleave between | |
| 222 | + * every two accessing of TSADC in normal operation. | |
| 223 | + * (2) Set TSADCV2_AUTO_PERIOD_HT, configure the interleave between | |
| 224 | + * every two accessing of TSADC after the temperature is higher | |
| 225 | + * than COM_SHUT or COM_INT. | |
| 226 | + * (3) Set TSADCV2_HIGH_INT_DEBOUNCE and TSADC_HIGHT_TSHUT_DEBOUNCE, | |
| 227 | + * if the temperature is higher than COMP_INT or COMP_SHUT for | |
| 228 | + * "debounce" times, TSADC controller will generate interrupt or TSHUT. | |
| 229 | + */ | |
| 230 | +static void rk_tsadcv2_initialize(void __iomem *regs, | |
| 231 | + enum tshut_polarity tshut_polarity) | |
| 232 | +{ | |
| 233 | + if (tshut_polarity == TSHUT_HIGH_ACTIVE) | |
| 234 | + writel_relaxed(0 | (TSADCV2_AUTO_TSHUT_POLARITY_HIGH), | |
| 235 | + regs + TSADCV2_AUTO_CON); | |
| 236 | + else | |
| 237 | + writel_relaxed(0 | (TSADCV2_AUTO_TSHUT_POLARITY_LOW), | |
| 238 | + regs + TSADCV2_AUTO_CON); | |
| 239 | + | |
| 240 | + writel_relaxed(TSADCV2_AUTO_PERIOD_TIME, regs + TSADCV2_AUTO_PERIOD); | |
| 241 | + writel_relaxed(TSADCV2_HIGHT_INT_DEBOUNCE_COUNT, | |
| 242 | + regs + TSADCV2_HIGHT_INT_DEBOUNCE); | |
| 243 | + writel_relaxed(TSADCV2_AUTO_PERIOD_HT_TIME, | |
| 244 | + regs + TSADCV2_AUTO_PERIOD_HT); | |
| 245 | + writel_relaxed(TSADCV2_HIGHT_TSHUT_DEBOUNCE_COUNT, | |
| 246 | + regs + TSADCV2_HIGHT_TSHUT_DEBOUNCE); | |
| 247 | +} | |
| 248 | + | |
| 249 | +static void rk_tsadcv2_irq_ack(void __iomem *regs) | |
| 250 | +{ | |
| 251 | + u32 val; | |
| 252 | + | |
| 253 | + val = readl_relaxed(regs + TSADCV2_INT_PD); | |
| 254 | + writel_relaxed(val & TSADCV2_INT_PD_CLEAR, regs + TSADCV2_INT_PD); | |
| 255 | +} | |
| 256 | + | |
| 257 | +static void rk_tsadcv2_control(void __iomem *regs, bool enable) | |
| 258 | +{ | |
| 259 | + u32 val; | |
| 260 | + | |
| 261 | + val = readl_relaxed(regs + TSADCV2_AUTO_CON); | |
| 262 | + if (enable) | |
| 263 | + val |= TSADCV2_AUTO_EN; | |
| 264 | + else | |
| 265 | + val &= ~TSADCV2_AUTO_EN; | |
| 266 | + | |
| 267 | + writel_relaxed(val, regs + TSADCV2_AUTO_CON); | |
| 268 | +} | |
| 269 | + | |
| 270 | +static int rk_tsadcv2_get_temp(int chn, void __iomem *regs, long *temp) | |
| 271 | +{ | |
| 272 | + u32 val; | |
| 273 | + | |
| 274 | + /* the A/D value of the channel last conversion need some time */ | |
| 275 | + val = readl_relaxed(regs + TSADCV2_DATA(chn)); | |
| 276 | + if (val == 0) | |
| 277 | + return -EAGAIN; | |
| 278 | + | |
| 279 | + *temp = rk_tsadcv2_code_to_temp(val); | |
| 280 | + | |
| 281 | + return 0; | |
| 282 | +} | |
| 283 | + | |
| 284 | +static void rk_tsadcv2_tshut_temp(int chn, void __iomem *regs, long temp) | |
| 285 | +{ | |
| 286 | + u32 tshut_value, val; | |
| 287 | + | |
| 288 | + tshut_value = rk_tsadcv2_temp_to_code(temp); | |
| 289 | + writel_relaxed(tshut_value, regs + TSADCV2_COMP_SHUT(chn)); | |
| 290 | + | |
| 291 | + /* TSHUT will be valid */ | |
| 292 | + val = readl_relaxed(regs + TSADCV2_AUTO_CON); | |
| 293 | + writel_relaxed(val | TSADCV2_AUTO_SRC_EN(chn), regs + TSADCV2_AUTO_CON); | |
| 294 | +} | |
| 295 | + | |
| 296 | +static void rk_tsadcv2_tshut_mode(int chn, void __iomem *regs, | |
| 297 | + enum tshut_mode mode) | |
| 298 | +{ | |
| 299 | + u32 val; | |
| 300 | + | |
| 301 | + val = readl_relaxed(regs + TSADCV2_INT_EN); | |
| 302 | + if (mode == TSHUT_MODE_GPIO) { | |
| 303 | + val &= ~TSADCV2_SHUT_2CRU_SRC_EN(chn); | |
| 304 | + val |= TSADCV2_SHUT_2GPIO_SRC_EN(chn); | |
| 305 | + } else { | |
| 306 | + val &= ~TSADCV2_SHUT_2GPIO_SRC_EN(chn); | |
| 307 | + val |= TSADCV2_SHUT_2CRU_SRC_EN(chn); | |
| 308 | + } | |
| 309 | + | |
| 310 | + writel_relaxed(val, regs + TSADCV2_INT_EN); | |
| 311 | +} | |
| 312 | + | |
| 313 | +static const struct rockchip_tsadc_chip rk3288_tsadc_data = { | |
| 314 | + .tshut_mode = TSHUT_MODE_GPIO, /* default TSHUT via GPIO give PMIC */ | |
| 315 | + .tshut_polarity = TSHUT_LOW_ACTIVE, /* default TSHUT LOW ACTIVE */ | |
| 316 | + .tshut_temp = 95000, | |
| 317 | + | |
| 318 | + .initialize = rk_tsadcv2_initialize, | |
| 319 | + .irq_ack = rk_tsadcv2_irq_ack, | |
| 320 | + .control = rk_tsadcv2_control, | |
| 321 | + .get_temp = rk_tsadcv2_get_temp, | |
| 322 | + .set_tshut_temp = rk_tsadcv2_tshut_temp, | |
| 323 | + .set_tshut_mode = rk_tsadcv2_tshut_mode, | |
| 324 | +}; | |
| 325 | + | |
| 326 | +static const struct of_device_id of_rockchip_thermal_match[] = { | |
| 327 | + { | |
| 328 | + .compatible = "rockchip,rk3288-tsadc", | |
| 329 | + .data = (void *)&rk3288_tsadc_data, | |
| 330 | + }, | |
| 331 | + { /* end */ }, | |
| 332 | +}; | |
| 333 | +MODULE_DEVICE_TABLE(of, of_rockchip_thermal_match); | |
| 334 | + | |
| 335 | +static void | |
| 336 | +rockchip_thermal_toggle_sensor(struct rockchip_thermal_sensor *sensor, bool on) | |
| 337 | +{ | |
| 338 | + struct thermal_zone_device *tzd = sensor->tzd; | |
| 339 | + | |
| 340 | + tzd->ops->set_mode(tzd, | |
| 341 | + on ? THERMAL_DEVICE_ENABLED : THERMAL_DEVICE_DISABLED); | |
| 342 | +} | |
| 343 | + | |
| 344 | +static irqreturn_t rockchip_thermal_alarm_irq_thread(int irq, void *dev) | |
| 345 | +{ | |
| 346 | + struct rockchip_thermal_data *thermal = dev; | |
| 347 | + int i; | |
| 348 | + | |
| 349 | + dev_dbg(&thermal->pdev->dev, "thermal alarm\n"); | |
| 350 | + | |
| 351 | + thermal->chip->irq_ack(thermal->regs); | |
| 352 | + | |
| 353 | + for (i = 0; i < ARRAY_SIZE(thermal->sensors); i++) | |
| 354 | + thermal_zone_device_update(thermal->sensors[i].tzd); | |
| 355 | + | |
| 356 | + return IRQ_HANDLED; | |
| 357 | +} | |
| 358 | + | |
| 359 | +static int rockchip_thermal_get_temp(void *_sensor, long *out_temp) | |
| 360 | +{ | |
| 361 | + struct rockchip_thermal_sensor *sensor = _sensor; | |
| 362 | + struct rockchip_thermal_data *thermal = sensor->thermal; | |
| 363 | + const struct rockchip_tsadc_chip *tsadc = sensor->thermal->chip; | |
| 364 | + int retval; | |
| 365 | + | |
| 366 | + retval = tsadc->get_temp(sensor->id, thermal->regs, out_temp); | |
| 367 | + dev_dbg(&thermal->pdev->dev, "sensor %d - temp: %ld, retval: %d\n", | |
| 368 | + sensor->id, *out_temp, retval); | |
| 369 | + | |
| 370 | + return retval; | |
| 371 | +} | |
| 372 | + | |
| 373 | +static const struct thermal_zone_of_device_ops rockchip_of_thermal_ops = { | |
| 374 | + .get_temp = rockchip_thermal_get_temp, | |
| 375 | +}; | |
| 376 | + | |
| 377 | +static int rockchip_configure_from_dt(struct device *dev, | |
| 378 | + struct device_node *np, | |
| 379 | + struct rockchip_thermal_data *thermal) | |
| 380 | +{ | |
| 381 | + u32 shut_temp, tshut_mode, tshut_polarity; | |
| 382 | + | |
| 383 | + if (of_property_read_u32(np, "rockchip,hw-tshut-temp", &shut_temp)) { | |
| 384 | + dev_warn(dev, | |
| 385 | + "Missing tshut temp property, using default %ld\n", | |
| 386 | + thermal->chip->tshut_temp); | |
| 387 | + thermal->tshut_temp = thermal->chip->tshut_temp; | |
| 388 | + } else { | |
| 389 | + thermal->tshut_temp = shut_temp; | |
| 390 | + } | |
| 391 | + | |
| 392 | + if (thermal->tshut_temp > INT_MAX) { | |
| 393 | + dev_err(dev, "Invalid tshut temperature specified: %ld\n", | |
| 394 | + thermal->tshut_temp); | |
| 395 | + return -ERANGE; | |
| 396 | + } | |
| 397 | + | |
| 398 | + if (of_property_read_u32(np, "rockchip,hw-tshut-mode", &tshut_mode)) { | |
| 399 | + dev_warn(dev, | |
| 400 | + "Missing tshut mode property, using default (%s)\n", | |
| 401 | + thermal->chip->tshut_mode == TSHUT_MODE_GPIO ? | |
| 402 | + "gpio" : "cru"); | |
| 403 | + thermal->tshut_mode = thermal->chip->tshut_mode; | |
| 404 | + } else { | |
| 405 | + thermal->tshut_mode = tshut_mode; | |
| 406 | + } | |
| 407 | + | |
| 408 | + if (thermal->tshut_mode > 1) { | |
| 409 | + dev_err(dev, "Invalid tshut mode specified: %d\n", | |
| 410 | + thermal->tshut_mode); | |
| 411 | + return -EINVAL; | |
| 412 | + } | |
| 413 | + | |
| 414 | + if (of_property_read_u32(np, "rockchip,hw-tshut-polarity", | |
| 415 | + &tshut_polarity)) { | |
| 416 | + dev_warn(dev, | |
| 417 | + "Missing tshut-polarity property, using default (%s)\n", | |
| 418 | + thermal->chip->tshut_polarity == TSHUT_LOW_ACTIVE ? | |
| 419 | + "low" : "high"); | |
| 420 | + thermal->tshut_polarity = thermal->chip->tshut_polarity; | |
| 421 | + } else { | |
| 422 | + thermal->tshut_polarity = tshut_polarity; | |
| 423 | + } | |
| 424 | + | |
| 425 | + if (thermal->tshut_polarity > 1) { | |
| 426 | + dev_err(dev, "Invalid tshut-polarity specified: %d\n", | |
| 427 | + thermal->tshut_polarity); | |
| 428 | + return -EINVAL; | |
| 429 | + } | |
| 430 | + | |
| 431 | + return 0; | |
| 432 | +} | |
| 433 | + | |
| 434 | +static int | |
| 435 | +rockchip_thermal_register_sensor(struct platform_device *pdev, | |
| 436 | + struct rockchip_thermal_data *thermal, | |
| 437 | + struct rockchip_thermal_sensor *sensor, | |
| 438 | + enum sensor_id id) | |
| 439 | +{ | |
| 440 | + const struct rockchip_tsadc_chip *tsadc = thermal->chip; | |
| 441 | + int error; | |
| 442 | + | |
| 443 | + tsadc->set_tshut_mode(id, thermal->regs, thermal->tshut_mode); | |
| 444 | + tsadc->set_tshut_temp(id, thermal->regs, thermal->tshut_temp); | |
| 445 | + | |
| 446 | + sensor->thermal = thermal; | |
| 447 | + sensor->id = id; | |
| 448 | + sensor->tzd = thermal_zone_of_sensor_register(&pdev->dev, id, sensor, | |
| 449 | + &rockchip_of_thermal_ops); | |
| 450 | + if (IS_ERR(sensor->tzd)) { | |
| 451 | + error = PTR_ERR(sensor->tzd); | |
| 452 | + dev_err(&pdev->dev, "failed to register sensor %d: %d\n", | |
| 453 | + id, error); | |
| 454 | + return error; | |
| 455 | + } | |
| 456 | + | |
| 457 | + return 0; | |
| 458 | +} | |
| 459 | + | |
| 460 | +/* | |
| 461 | + * Reset TSADC Controller, reset all tsadc registers. | |
| 462 | + */ | |
| 463 | +static void rockchip_thermal_reset_controller(struct reset_control *reset) | |
| 464 | +{ | |
| 465 | + reset_control_assert(reset); | |
| 466 | + usleep_range(10, 20); | |
| 467 | + reset_control_deassert(reset); | |
| 468 | +} | |
| 469 | + | |
| 470 | +static int rockchip_thermal_probe(struct platform_device *pdev) | |
| 471 | +{ | |
| 472 | + struct device_node *np = pdev->dev.of_node; | |
| 473 | + struct rockchip_thermal_data *thermal; | |
| 474 | + const struct of_device_id *match; | |
| 475 | + struct resource *res; | |
| 476 | + int irq; | |
| 477 | + int i; | |
| 478 | + int error; | |
| 479 | + | |
| 480 | + match = of_match_node(of_rockchip_thermal_match, np); | |
| 481 | + if (!match) | |
| 482 | + return -ENXIO; | |
| 483 | + | |
| 484 | + irq = platform_get_irq(pdev, 0); | |
| 485 | + if (irq < 0) { | |
| 486 | + dev_err(&pdev->dev, "no irq resource?\n"); | |
| 487 | + return -EINVAL; | |
| 488 | + } | |
| 489 | + | |
| 490 | + thermal = devm_kzalloc(&pdev->dev, sizeof(struct rockchip_thermal_data), | |
| 491 | + GFP_KERNEL); | |
| 492 | + if (!thermal) | |
| 493 | + return -ENOMEM; | |
| 494 | + | |
| 495 | + thermal->pdev = pdev; | |
| 496 | + | |
| 497 | + thermal->chip = (const struct rockchip_tsadc_chip *)match->data; | |
| 498 | + if (!thermal->chip) | |
| 499 | + return -EINVAL; | |
| 500 | + | |
| 501 | + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
| 502 | + thermal->regs = devm_ioremap_resource(&pdev->dev, res); | |
| 503 | + if (IS_ERR(thermal->regs)) | |
| 504 | + return PTR_ERR(thermal->regs); | |
| 505 | + | |
| 506 | + thermal->reset = devm_reset_control_get(&pdev->dev, "tsadc-apb"); | |
| 507 | + if (IS_ERR(thermal->reset)) { | |
| 508 | + error = PTR_ERR(thermal->reset); | |
| 509 | + dev_err(&pdev->dev, "failed to get tsadc reset: %d\n", error); | |
| 510 | + return error; | |
| 511 | + } | |
| 512 | + | |
| 513 | + thermal->clk = devm_clk_get(&pdev->dev, "tsadc"); | |
| 514 | + if (IS_ERR(thermal->clk)) { | |
| 515 | + error = PTR_ERR(thermal->clk); | |
| 516 | + dev_err(&pdev->dev, "failed to get tsadc clock: %d\n", error); | |
| 517 | + return error; | |
| 518 | + } | |
| 519 | + | |
| 520 | + thermal->pclk = devm_clk_get(&pdev->dev, "apb_pclk"); | |
| 521 | + if (IS_ERR(thermal->pclk)) { | |
| 522 | + error = PTR_ERR(thermal->clk); | |
| 523 | + dev_err(&pdev->dev, "failed to get apb_pclk clock: %d\n", | |
| 524 | + error); | |
| 525 | + return error; | |
| 526 | + } | |
| 527 | + | |
| 528 | + error = clk_prepare_enable(thermal->clk); | |
| 529 | + if (error) { | |
| 530 | + dev_err(&pdev->dev, "failed to enable converter clock: %d\n", | |
| 531 | + error); | |
| 532 | + return error; | |
| 533 | + } | |
| 534 | + | |
| 535 | + error = clk_prepare_enable(thermal->pclk); | |
| 536 | + if (error) { | |
| 537 | + dev_err(&pdev->dev, "failed to enable pclk: %d\n", error); | |
| 538 | + goto err_disable_clk; | |
| 539 | + } | |
| 540 | + | |
| 541 | + rockchip_thermal_reset_controller(thermal->reset); | |
| 542 | + | |
| 543 | + error = rockchip_configure_from_dt(&pdev->dev, np, thermal); | |
| 544 | + if (error) { | |
| 545 | + dev_err(&pdev->dev, "failed to parse device tree data: %d\n", | |
| 546 | + error); | |
| 547 | + goto err_disable_pclk; | |
| 548 | + } | |
| 549 | + | |
| 550 | + thermal->chip->initialize(thermal->regs, thermal->tshut_polarity); | |
| 551 | + | |
| 552 | + error = rockchip_thermal_register_sensor(pdev, thermal, | |
| 553 | + &thermal->sensors[0], | |
| 554 | + SENSOR_CPU); | |
| 555 | + if (error) { | |
| 556 | + dev_err(&pdev->dev, | |
| 557 | + "failed to register CPU thermal sensor: %d\n", error); | |
| 558 | + goto err_disable_pclk; | |
| 559 | + } | |
| 560 | + | |
| 561 | + error = rockchip_thermal_register_sensor(pdev, thermal, | |
| 562 | + &thermal->sensors[1], | |
| 563 | + SENSOR_GPU); | |
| 564 | + if (error) { | |
| 565 | + dev_err(&pdev->dev, | |
| 566 | + "failed to register GPU thermal sensor: %d\n", error); | |
| 567 | + goto err_unregister_cpu_sensor; | |
| 568 | + } | |
| 569 | + | |
| 570 | + error = devm_request_threaded_irq(&pdev->dev, irq, NULL, | |
| 571 | + &rockchip_thermal_alarm_irq_thread, | |
| 572 | + IRQF_ONESHOT, | |
| 573 | + "rockchip_thermal", thermal); | |
| 574 | + if (error) { | |
| 575 | + dev_err(&pdev->dev, | |
| 576 | + "failed to request tsadc irq: %d\n", error); | |
| 577 | + goto err_unregister_gpu_sensor; | |
| 578 | + } | |
| 579 | + | |
| 580 | + thermal->chip->control(thermal->regs, true); | |
| 581 | + | |
| 582 | + for (i = 0; i < ARRAY_SIZE(thermal->sensors); i++) | |
| 583 | + rockchip_thermal_toggle_sensor(&thermal->sensors[i], true); | |
| 584 | + | |
| 585 | + platform_set_drvdata(pdev, thermal); | |
| 586 | + | |
| 587 | + return 0; | |
| 588 | + | |
| 589 | +err_unregister_gpu_sensor: | |
| 590 | + thermal_zone_of_sensor_unregister(&pdev->dev, thermal->sensors[1].tzd); | |
| 591 | +err_unregister_cpu_sensor: | |
| 592 | + thermal_zone_of_sensor_unregister(&pdev->dev, thermal->sensors[0].tzd); | |
| 593 | +err_disable_pclk: | |
| 594 | + clk_disable_unprepare(thermal->pclk); | |
| 595 | +err_disable_clk: | |
| 596 | + clk_disable_unprepare(thermal->clk); | |
| 597 | + | |
| 598 | + return error; | |
| 599 | +} | |
| 600 | + | |
| 601 | +static int rockchip_thermal_remove(struct platform_device *pdev) | |
| 602 | +{ | |
| 603 | + struct rockchip_thermal_data *thermal = platform_get_drvdata(pdev); | |
| 604 | + int i; | |
| 605 | + | |
| 606 | + for (i = 0; i < ARRAY_SIZE(thermal->sensors); i++) { | |
| 607 | + struct rockchip_thermal_sensor *sensor = &thermal->sensors[i]; | |
| 608 | + | |
| 609 | + rockchip_thermal_toggle_sensor(sensor, false); | |
| 610 | + thermal_zone_of_sensor_unregister(&pdev->dev, sensor->tzd); | |
| 611 | + } | |
| 612 | + | |
| 613 | + thermal->chip->control(thermal->regs, false); | |
| 614 | + | |
| 615 | + clk_disable_unprepare(thermal->pclk); | |
| 616 | + clk_disable_unprepare(thermal->clk); | |
| 617 | + | |
| 618 | + return 0; | |
| 619 | +} | |
| 620 | + | |
| 621 | +static int __maybe_unused rockchip_thermal_suspend(struct device *dev) | |
| 622 | +{ | |
| 623 | + struct platform_device *pdev = to_platform_device(dev); | |
| 624 | + struct rockchip_thermal_data *thermal = platform_get_drvdata(pdev); | |
| 625 | + int i; | |
| 626 | + | |
| 627 | + for (i = 0; i < ARRAY_SIZE(thermal->sensors); i++) | |
| 628 | + rockchip_thermal_toggle_sensor(&thermal->sensors[i], false); | |
| 629 | + | |
| 630 | + thermal->chip->control(thermal->regs, false); | |
| 631 | + | |
| 632 | + clk_disable(thermal->pclk); | |
| 633 | + clk_disable(thermal->clk); | |
| 634 | + | |
| 635 | + return 0; | |
| 636 | +} | |
| 637 | + | |
| 638 | +static int __maybe_unused rockchip_thermal_resume(struct device *dev) | |
| 639 | +{ | |
| 640 | + struct platform_device *pdev = to_platform_device(dev); | |
| 641 | + struct rockchip_thermal_data *thermal = platform_get_drvdata(pdev); | |
| 642 | + int i; | |
| 643 | + int error; | |
| 644 | + | |
| 645 | + error = clk_enable(thermal->clk); | |
| 646 | + if (error) | |
| 647 | + return error; | |
| 648 | + | |
| 649 | + error = clk_enable(thermal->pclk); | |
| 650 | + if (error) | |
| 651 | + return error; | |
| 652 | + | |
| 653 | + rockchip_thermal_reset_controller(thermal->reset); | |
| 654 | + | |
| 655 | + thermal->chip->initialize(thermal->regs, thermal->tshut_polarity); | |
| 656 | + | |
| 657 | + for (i = 0; i < ARRAY_SIZE(thermal->sensors); i++) { | |
| 658 | + enum sensor_id id = thermal->sensors[i].id; | |
| 659 | + | |
| 660 | + thermal->chip->set_tshut_mode(id, thermal->regs, | |
| 661 | + thermal->tshut_mode); | |
| 662 | + thermal->chip->set_tshut_temp(id, thermal->regs, | |
| 663 | + thermal->tshut_temp); | |
| 664 | + } | |
| 665 | + | |
| 666 | + thermal->chip->control(thermal->regs, true); | |
| 667 | + | |
| 668 | + for (i = 0; i < ARRAY_SIZE(thermal->sensors); i++) | |
| 669 | + rockchip_thermal_toggle_sensor(&thermal->sensors[i], true); | |
| 670 | + | |
| 671 | + return 0; | |
| 672 | +} | |
| 673 | + | |
| 674 | +static SIMPLE_DEV_PM_OPS(rockchip_thermal_pm_ops, | |
| 675 | + rockchip_thermal_suspend, rockchip_thermal_resume); | |
| 676 | + | |
| 677 | +static struct platform_driver rockchip_thermal_driver = { | |
| 678 | + .driver = { | |
| 679 | + .name = "rockchip-thermal", | |
| 680 | + .owner = THIS_MODULE, | |
| 681 | + .pm = &rockchip_thermal_pm_ops, | |
| 682 | + .of_match_table = of_rockchip_thermal_match, | |
| 683 | + }, | |
| 684 | + .probe = rockchip_thermal_probe, | |
| 685 | + .remove = rockchip_thermal_remove, | |
| 686 | +}; | |
| 687 | + | |
| 688 | +module_platform_driver(rockchip_thermal_driver); | |
| 689 | + | |
| 690 | +MODULE_DESCRIPTION("ROCKCHIP THERMAL Driver"); | |
| 691 | +MODULE_AUTHOR("Rockchip, Inc."); | |
| 692 | +MODULE_LICENSE("GPL v2"); | |
| 693 | +MODULE_ALIAS("platform:rockchip-thermal"); | |
| @@ -27,7 +27,6 @@ · 26 unmodified lines | ||
| 27 | 27 | #define SENSOR_NAME_LEN 16 |
| 28 | 28 | #define MAX_TRIP_COUNT 8 |
| 29 | 29 | #define MAX_COOLING_DEVICE 4 |
| 30 | −#define MAX_TRIMINFO_CTRL_REG 2 | |
| 31 | 30 | |
| 32 | 31 | #define ACTIVE_INTERVAL 500 |
| 33 | 32 | #define IDLE_INTERVAL 10000 |
| @@ -33,7 +33,87 @@ · 32 unmodified lines | ||
| 33 | 33 | |
| 34 | 34 | #include "exynos_thermal_common.h" |
| 35 | 35 | #include "exynos_tmu.h" |
| 36 | −#include "exynos_tmu_data.h" | |
| 36 | + | |
| 37 | +/* Exynos generic registers */ | |
| 38 | +#define EXYNOS_TMU_REG_TRIMINFO 0x0 | |
| 39 | +#define EXYNOS_TMU_REG_CONTROL 0x20 | |
| 40 | +#define EXYNOS_TMU_REG_STATUS 0x28 | |
| 41 | +#define EXYNOS_TMU_REG_CURRENT_TEMP 0x40 | |
| 42 | +#define EXYNOS_TMU_REG_INTEN 0x70 | |
| 43 | +#define EXYNOS_TMU_REG_INTSTAT 0x74 | |
| 44 | +#define EXYNOS_TMU_REG_INTCLEAR 0x78 | |
| 45 | + | |
| 46 | +#define EXYNOS_TMU_TEMP_MASK 0xff | |
| 47 | +#define EXYNOS_TMU_REF_VOLTAGE_SHIFT 24 | |
| 48 | +#define EXYNOS_TMU_REF_VOLTAGE_MASK 0x1f | |
| 49 | +#define EXYNOS_TMU_BUF_SLOPE_SEL_MASK 0xf | |
| 50 | +#define EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT 8 | |
| 51 | +#define EXYNOS_TMU_CORE_EN_SHIFT 0 | |
| 52 | + | |
| 53 | +/* Exynos3250 specific registers */ | |
| 54 | +#define EXYNOS_TMU_TRIMINFO_CON1 0x10 | |
| 55 | + | |
| 56 | +/* Exynos4210 specific registers */ | |
| 57 | +#define EXYNOS4210_TMU_REG_THRESHOLD_TEMP 0x44 | |
| 58 | +#define EXYNOS4210_TMU_REG_TRIG_LEVEL0 0x50 | |
| 59 | + | |
| 60 | +/* Exynos5250, Exynos4412, Exynos3250 specific registers */ | |
| 61 | +#define EXYNOS_TMU_TRIMINFO_CON2 0x14 | |
| 62 | +#define EXYNOS_THD_TEMP_RISE 0x50 | |
| 63 | +#define EXYNOS_THD_TEMP_FALL 0x54 | |
| 64 | +#define EXYNOS_EMUL_CON 0x80 | |
| 65 | + | |
| 66 | +#define EXYNOS_TRIMINFO_RELOAD_ENABLE 1 | |
| 67 | +#define EXYNOS_TRIMINFO_25_SHIFT 0 | |
| 68 | +#define EXYNOS_TRIMINFO_85_SHIFT 8 | |
| 69 | +#define EXYNOS_TMU_TRIP_MODE_SHIFT 13 | |
| 70 | +#define EXYNOS_TMU_TRIP_MODE_MASK 0x7 | |
| 71 | +#define EXYNOS_TMU_THERM_TRIP_EN_SHIFT 12 | |
| 72 | + | |
| 73 | +#define EXYNOS_TMU_INTEN_RISE0_SHIFT 0 | |
| 74 | +#define EXYNOS_TMU_INTEN_RISE1_SHIFT 4 | |
| 75 | +#define EXYNOS_TMU_INTEN_RISE2_SHIFT 8 | |
| 76 | +#define EXYNOS_TMU_INTEN_RISE3_SHIFT 12 | |
| 77 | +#define EXYNOS_TMU_INTEN_FALL0_SHIFT 16 | |
| 78 | + | |
| 79 | +#define EXYNOS_EMUL_TIME 0x57F0 | |
| 80 | +#define EXYNOS_EMUL_TIME_MASK 0xffff | |
| 81 | +#define EXYNOS_EMUL_TIME_SHIFT 16 | |
| 82 | +#define EXYNOS_EMUL_DATA_SHIFT 8 | |
| 83 | +#define EXYNOS_EMUL_DATA_MASK 0xFF | |
| 84 | +#define EXYNOS_EMUL_ENABLE 0x1 | |
| 85 | + | |
| 86 | +/* Exynos5260 specific */ | |
| 87 | +#define EXYNOS5260_TMU_REG_INTEN 0xC0 | |
| 88 | +#define EXYNOS5260_TMU_REG_INTSTAT 0xC4 | |
| 89 | +#define EXYNOS5260_TMU_REG_INTCLEAR 0xC8 | |
| 90 | +#define EXYNOS5260_EMUL_CON 0x100 | |
| 91 | + | |
| 92 | +/* Exynos4412 specific */ | |
| 93 | +#define EXYNOS4412_MUX_ADDR_VALUE 6 | |
| 94 | +#define EXYNOS4412_MUX_ADDR_SHIFT 20 | |
| 95 | + | |
| 96 | +/*exynos5440 specific registers*/ | |
| 97 | +#define EXYNOS5440_TMU_S0_7_TRIM 0x000 | |
| 98 | +#define EXYNOS5440_TMU_S0_7_CTRL 0x020 | |
| 99 | +#define EXYNOS5440_TMU_S0_7_DEBUG 0x040 | |
| 100 | +#define EXYNOS5440_TMU_S0_7_TEMP 0x0f0 | |
| 101 | +#define EXYNOS5440_TMU_S0_7_TH0 0x110 | |
| 102 | +#define EXYNOS5440_TMU_S0_7_TH1 0x130 | |
| 103 | +#define EXYNOS5440_TMU_S0_7_TH2 0x150 | |
| 104 | +#define EXYNOS5440_TMU_S0_7_IRQEN 0x210 | |
| 105 | +#define EXYNOS5440_TMU_S0_7_IRQ 0x230 | |
| 106 | +/* exynos5440 common registers */ | |
| 107 | +#define EXYNOS5440_TMU_IRQ_STATUS 0x000 | |
| 108 | +#define EXYNOS5440_TMU_PMIN 0x004 | |
| 109 | + | |
| 110 | +#define EXYNOS5440_TMU_INTEN_RISE0_SHIFT 0 | |
| 111 | +#define EXYNOS5440_TMU_INTEN_RISE1_SHIFT 1 | |
| 112 | +#define EXYNOS5440_TMU_INTEN_RISE2_SHIFT 2 | |
| 113 | +#define EXYNOS5440_TMU_INTEN_RISE3_SHIFT 3 | |
| 114 | +#define EXYNOS5440_TMU_INTEN_FALL0_SHIFT 4 | |
| 115 | +#define EXYNOS5440_TMU_TH_RISE4_SHIFT 24 | |
| 116 | +#define EXYNOS5440_EFUSE_SWAP_OFFSET 8 | |
| 37 | 117 | |
| 38 | 118 | /** |
| 39 | 119 | * struct exynos_tmu_data : A structure to hold the private data of the TMU |
| @@ -52,6 +132,11 @@ · 12 unmodified lines | ||
| 52 | 132 | * @temp_error2: fused value of the second point trim. |
| 53 | 133 | * @regulator: pointer to the TMU regulator structure. |
| 54 | 134 | * @reg_conf: pointer to structure to register with core thermal. |
| 135 | + * @tmu_initialize: SoC specific TMU initialization method | |
| 136 | + * @tmu_control: SoC specific TMU control method | |
| 137 | + * @tmu_read: SoC specific TMU temperature read method | |
| 138 | + * @tmu_set_emulation: SoC specific TMU emulation setting method | |
| 139 | + * @tmu_clear_irqs: SoC specific TMU interrupts clearing method | |
| 55 | 140 | */ |
| 56 | 141 | struct exynos_tmu_data { |
| 57 | 142 | int id; |
| @@ -66,6 +151,12 @@ struct exynos_tmu_data { · 8 unmodified lines | ||
| 66 | 151 | u8 temp_error1, temp_error2; |
| 67 | 152 | struct regulator *regulator; |
| 68 | 153 | struct thermal_sensor_conf *reg_conf; |
| 154 | + int (*tmu_initialize)(struct platform_device *pdev); | |
| 155 | + void (*tmu_control)(struct platform_device *pdev, bool on); | |
| 156 | + int (*tmu_read)(struct exynos_tmu_data *data); | |
| 157 | + void (*tmu_set_emulation)(struct exynos_tmu_data *data, | |
| 158 | + unsigned long temp); | |
| 159 | + void (*tmu_clear_irqs)(struct exynos_tmu_data *data); | |
| 69 | 160 | }; |
| 70 | 161 | |
| 71 | 162 | /* |
| @@ -122,83 +213,10 @@ static int code_to_temp(struct exynos_tmu_data *data, u8 temp_code) · 50 unmodified lines | ||
| 122 | 213 | return temp; |
| 123 | 214 | } |
| 124 | 215 | |
| 125 | −static void exynos_tmu_clear_irqs(struct exynos_tmu_data *data) | |
| 126 | −{ | |
| 127 | − const struct exynos_tmu_registers *reg = data->pdata->registers; | |
| 128 | − unsigned int val_irq; | |
| 129 | − | |
| 130 | − val_irq = readl(data->base + reg->tmu_intstat); | |
| 131 | − /* | |
| 132 | − * Clear the interrupts. Please note that the documentation for | |
| 133 | − * Exynos3250, Exynos4412, Exynos5250 and Exynos5260 incorrectly | |
| 134 | − * states that INTCLEAR register has a different placing of bits | |
| 135 | − * responsible for FALL IRQs than INTSTAT register. Exynos5420 | |
| 136 | − * and Exynos5440 documentation is correct (Exynos4210 doesn't | |
| 137 | − * support FALL IRQs at all). | |
| 138 | − */ | |
| 139 | − writel(val_irq, data->base + reg->tmu_intclear); | |
| 140 | −} | |
| 141 | − | |
| 142 | −static int exynos_tmu_initialize(struct platform_device *pdev) | |
| 216 | +static void sanitize_temp_error(struct exynos_tmu_data *data, u32 trim_info) | |
| 143 | 217 | { |
| 144 | − struct exynos_tmu_data *data = platform_get_drvdata(pdev); | |
| 145 | 218 | struct exynos_tmu_platform_data *pdata = data->pdata; |
| 146 | − const struct exynos_tmu_registers *reg = pdata->registers; | |
| 147 | − unsigned int status, trim_info = 0, con, ctrl; | |
| 148 | − unsigned int rising_threshold = 0, falling_threshold = 0; | |
| 149 | − int ret = 0, threshold_code, i; | |
| 150 | − | |
| 151 | − mutex_lock(&data->lock); | |
| 152 | − clk_enable(data->clk); | |
| 153 | − if (!IS_ERR(data->clk_sec)) | |
| 154 | − clk_enable(data->clk_sec); | |
| 155 | 219 | |
| 156 | − if (TMU_SUPPORTS(pdata, READY_STATUS)) { | |
| 157 | − status = readb(data->base + reg->tmu_status); | |
| 158 | − if (!status) { | |
| 159 | − ret = -EBUSY; | |
| 160 | − goto out; | |
| 161 | − } | |
| 162 | − } | |
| 163 | − | |
| 164 | − if (TMU_SUPPORTS(pdata, TRIM_RELOAD)) { | |
| 165 | − for (i = 0; i < reg->triminfo_ctrl_count; i++) { | |
| 166 | − if (pdata->triminfo_reload[i]) { | |
| 167 | − ctrl = readl(data->base + | |
| 168 | − reg->triminfo_ctrl[i]); | |
| 169 | − ctrl |= pdata->triminfo_reload[i]; | |
| 170 | − writel(ctrl, data->base + | |
| 171 | − reg->triminfo_ctrl[i]); | |
| 172 | − } | |
| 173 | − } | |
| 174 | − } | |
| 175 | − | |
| 176 | − /* Save trimming info in order to perform calibration */ | |
| 177 | − if (data->soc == SOC_ARCH_EXYNOS5440) { | |
| 178 | − /* | |
| 179 | − * For exynos5440 soc triminfo value is swapped between TMU0 and | |
| 180 | − * TMU2, so the below logic is needed. | |
| 181 | − */ | |
| 182 | − switch (data->id) { | |
| 183 | − case 0: | |
| 184 | − trim_info = readl(data->base + | |
| 185 | − EXYNOS5440_EFUSE_SWAP_OFFSET + reg->triminfo_data); | |
| 186 | − break; | |
| 187 | − case 1: | |
| 188 | − trim_info = readl(data->base + reg->triminfo_data); | |
| 189 | − break; | |
| 190 | − case 2: | |
| 191 | − trim_info = readl(data->base - | |
| 192 | − EXYNOS5440_EFUSE_SWAP_OFFSET + reg->triminfo_data); | |
| 193 | − } | |
| 194 | − } else { | |
| 195 | − /* On exynos5420 the triminfo register is in the shared space */ | |
| 196 | − if (data->soc == SOC_ARCH_EXYNOS5420_TRIMINFO) | |
| 197 | − trim_info = readl(data->base_second + | |
| 198 | − reg->triminfo_data); | |
| 199 | − else | |
| 200 | − trim_info = readl(data->base + reg->triminfo_data); | |
| 201 | − } | |
| 202 | 220 | data->temp_error1 = trim_info & EXYNOS_TMU_TEMP_MASK; |
| 203 | 221 | data->temp_error2 = ((trim_info >> EXYNOS_TRIMINFO_85_SHIFT) & |
| 204 | 222 | EXYNOS_TMU_TEMP_MASK); |
| @@ -212,69 +230,37 @@ static int exynos_tmu_initialize(struct platform_device *pdev) · 7 unmodified lines | ||
| 212 | 230 | data->temp_error2 = |
| 213 | 231 | (pdata->efuse_value >> EXYNOS_TRIMINFO_85_SHIFT) & |
| 214 | 232 | EXYNOS_TMU_TEMP_MASK; |
| 233 | +} | |
| 215 | 234 | |
| 216 | − rising_threshold = readl(data->base + reg->threshold_th0); | |
| 235 | +static u32 get_th_reg(struct exynos_tmu_data *data, u32 threshold, bool falling) | |
| 236 | +{ | |
| 237 | + struct exynos_tmu_platform_data *pdata = data->pdata; | |
| 238 | + int i; | |
| 217 | 239 | |
| 218 | − if (data->soc == SOC_ARCH_EXYNOS4210) { | |
| 219 | − /* Write temperature code for threshold */ | |
| 220 | − threshold_code = temp_to_code(data, pdata->threshold); | |
| 221 | − writeb(threshold_code, | |
| 222 | − data->base + reg->threshold_temp); | |
| 223 | − for (i = 0; i < pdata->non_hw_trigger_levels; i++) | |
| 224 | − writeb(pdata->trigger_levels[i], data->base + | |
| 225 | − reg->threshold_th0 + i * sizeof(reg->threshold_th0)); | |
| 240 | + for (i = 0; i < pdata->non_hw_trigger_levels; i++) { | |
| 241 | + u8 temp = pdata->trigger_levels[i]; | |
| 226 | 242 | |
| 227 | − exynos_tmu_clear_irqs(data); | |
| 228 | − } else { | |
| 229 | − /* Write temperature code for rising and falling threshold */ | |
| 230 | − for (i = 0; i < pdata->non_hw_trigger_levels; i++) { | |
| 231 | − threshold_code = temp_to_code(data, | |
| 232 | − pdata->trigger_levels[i]); | |
| 233 | − rising_threshold &= ~(0xff << 8 * i); | |
| 234 | − rising_threshold |= threshold_code << 8 * i; | |
| 235 | − if (pdata->threshold_falling) { | |
| 236 | − threshold_code = temp_to_code(data, | |
| 237 | − pdata->trigger_levels[i] - | |
| 238 | − pdata->threshold_falling); | |
| 239 | − falling_threshold |= threshold_code << 8 * i; | |
| 240 | − } | |
| 241 | − } | |
| 243 | + if (falling) | |
| 244 | + temp -= pdata->threshold_falling; | |
| 245 | + else | |
| 246 | + threshold &= ~(0xff << 8 * i); | |
| 242 | 247 | |
| 243 | − writel(rising_threshold, | |
| 244 | − data->base + reg->threshold_th0); | |
| 245 | − writel(falling_threshold, | |
| 246 | − data->base + reg->threshold_th1); | |
| 247 | − | |
| 248 | − exynos_tmu_clear_irqs(data); | |
| 249 | − | |
| 250 | − /* if last threshold limit is also present */ | |
| 251 | − i = pdata->max_trigger_level - 1; | |
| 252 | − if (pdata->trigger_levels[i] && | |
| 253 | − (pdata->trigger_type[i] == HW_TRIP)) { | |
| 254 | − threshold_code = temp_to_code(data, | |
| 255 | − pdata->trigger_levels[i]); | |
| 256 | − if (i == EXYNOS_MAX_TRIGGER_PER_REG - 1) { | |
| 257 | − /* 1-4 level to be assigned in th0 reg */ | |
| 258 | − rising_threshold &= ~(0xff << 8 * i); | |
| 259 | − rising_threshold |= threshold_code << 8 * i; | |
| 260 | − writel(rising_threshold, | |
| 261 | − data->base + reg->threshold_th0); | |
| 262 | − } else if (i == EXYNOS_MAX_TRIGGER_PER_REG) { | |
| 263 | − /* 5th level to be assigned in th2 reg */ | |
| 264 | − rising_threshold = | |
| 265 | − threshold_code << reg->threshold_th3_l0_shift; | |
| 266 | − writel(rising_threshold, | |
| 267 | − data->base + reg->threshold_th2); | |
| 268 | − } | |
| 269 | − con = readl(data->base + reg->tmu_ctrl); | |
| 270 | − con |= (1 << reg->therm_trip_en_shift); | |
| 271 | − writel(con, data->base + reg->tmu_ctrl); | |
| 272 | − } | |
| 248 | + threshold |= temp_to_code(data, temp) << 8 * i; | |
| 273 | 249 | } |
| 274 | − /*Clear the PMIN in the common TMU register*/ | |
| 275 | − if (reg->tmu_pmin && !data->id) | |
| 276 | − writel(0, data->base_second + reg->tmu_pmin); | |
| 277 | −out: | |
| 250 | + | |
| 251 | + return threshold; | |
| 252 | +} | |
| 253 | + | |
| 254 | +static int exynos_tmu_initialize(struct platform_device *pdev) | |
| 255 | +{ | |
| 256 | + struct exynos_tmu_data *data = platform_get_drvdata(pdev); | |
| 257 | + int ret; | |
| 258 | + | |
| 259 | + mutex_lock(&data->lock); | |
| 260 | + clk_enable(data->clk); | |
| 261 | + if (!IS_ERR(data->clk_sec)) | |
| 262 | + clk_enable(data->clk_sec); | |
| 263 | + ret = data->tmu_initialize(pdev); | |
| 278 | 264 | clk_disable(data->clk); |
| 279 | 265 | mutex_unlock(&data->lock); |
| 280 | 266 | if (!IS_ERR(data->clk_sec)) |
| @@ -283,20 +269,13 @@ out: · 2 unmodified lines | ||
| 283 | 269 | return ret; |
| 284 | 270 | } |
| 285 | 271 | |
| 286 | −static void exynos_tmu_control(struct platform_device *pdev, bool on) | |
| 272 | +static u32 get_con_reg(struct exynos_tmu_data *data, u32 con) | |
| 287 | 273 | { |
| 288 | − struct exynos_tmu_data *data = platform_get_drvdata(pdev); | |
| 289 | 274 | struct exynos_tmu_platform_data *pdata = data->pdata; |
| 290 | − const struct exynos_tmu_registers *reg = pdata->registers; | |
| 291 | − unsigned int con, interrupt_en; | |
| 292 | 275 | |
| 293 | − mutex_lock(&data->lock); | |
| 294 | − clk_enable(data->clk); | |
| 295 | − | |
| 296 | − con = readl(data->base + reg->tmu_ctrl); | |
| 297 | − | |
| 298 | − if (pdata->test_mux) | |
| 299 | − con |= (pdata->test_mux << reg->test_mux_addr_shift); | |
| 276 | + if (data->soc == SOC_ARCH_EXYNOS4412 || | |
| 277 | + data->soc == SOC_ARCH_EXYNOS3250) | |
| 278 | + con |= (EXYNOS4412_MUX_ADDR_VALUE << EXYNOS4412_MUX_ADDR_SHIFT); | |
| 300 | 279 | |
| 301 | 280 | con &= ~(EXYNOS_TMU_REF_VOLTAGE_MASK << EXYNOS_TMU_REF_VOLTAGE_SHIFT); |
| 302 | 281 | con |= pdata->reference_voltage << EXYNOS_TMU_REF_VOLTAGE_SHIFT; |
| @@ -305,95 +284,287 @@ static void exynos_tmu_control(struct platform_device *pdev, bool on) · 2 unmodified lines | ||
| 305 | 284 | con |= (pdata->gain << EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT); |
| 306 | 285 | |
| 307 | 286 | if (pdata->noise_cancel_mode) { |
| 308 | − con &= ~(reg->therm_trip_mode_mask << | |
| 309 | − reg->therm_trip_mode_shift); | |
| 310 | − con |= (pdata->noise_cancel_mode << reg->therm_trip_mode_shift); | |
| 287 | + con &= ~(EXYNOS_TMU_TRIP_MODE_MASK << EXYNOS_TMU_TRIP_MODE_SHIFT); | |
| 288 | + con |= (pdata->noise_cancel_mode << EXYNOS_TMU_TRIP_MODE_SHIFT); | |
| 311 | 289 | } |
| 312 | 290 | |
| 313 | − if (on) { | |
| 314 | − con |= (1 << EXYNOS_TMU_CORE_EN_SHIFT); | |
| 315 | − interrupt_en = | |
| 316 | − pdata->trigger_enable[3] << reg->inten_rise3_shift | | |
| 317 | − pdata->trigger_enable[2] << reg->inten_rise2_shift | | |
| 318 | − pdata->trigger_enable[1] << reg->inten_rise1_shift | | |
| 319 | − pdata->trigger_enable[0] << reg->inten_rise0_shift; | |
| 320 | − if (TMU_SUPPORTS(pdata, FALLING_TRIP)) | |
| 321 | − interrupt_en |= | |
| 322 | − interrupt_en << reg->inten_fall0_shift; | |
| 323 | − } else { | |
| 324 | − con &= ~(1 << EXYNOS_TMU_CORE_EN_SHIFT); | |
| 325 | − interrupt_en = 0; /* Disable all interrupts */ | |
| 326 | − } | |
| 327 | − writel(interrupt_en, data->base + reg->tmu_inten); | |
| 328 | − writel(con, data->base + reg->tmu_ctrl); | |
| 291 | + return con; | |
| 292 | +} | |
| 293 | + | |
| 294 | +static void exynos_tmu_control(struct platform_device *pdev, bool on) | |
| 295 | +{ | |
| 296 | + struct exynos_tmu_data *data = platform_get_drvdata(pdev); | |
| 329 | 297 | |
| 298 | + mutex_lock(&data->lock); | |
| 299 | + clk_enable(data->clk); | |
| 300 | + data->tmu_control(pdev, on); | |
| 330 | 301 | clk_disable(data->clk); |
| 331 | 302 | mutex_unlock(&data->lock); |
| 332 | 303 | } |
| 333 | 304 | |
| 334 | −static int exynos_tmu_read(struct exynos_tmu_data *data) | |
| 305 | +static int exynos4210_tmu_initialize(struct platform_device *pdev) | |
| 335 | 306 | { |
| 307 | + struct exynos_tmu_data *data = platform_get_drvdata(pdev); | |
| 336 | 308 | struct exynos_tmu_platform_data *pdata = data->pdata; |
| 337 | − const struct exynos_tmu_registers *reg = pdata->registers; | |
| 338 | − u8 temp_code; | |
| 339 | − int temp; | |
| 309 | + unsigned int status; | |
| 310 | + int ret = 0, threshold_code, i; | |
| 340 | 311 | |
| 341 | − mutex_lock(&data->lock); | |
| 342 | − clk_enable(data->clk); | |
| 312 | + status = readb(data->base + EXYNOS_TMU_REG_STATUS); | |
| 313 | + if (!status) { | |
| 314 | + ret = -EBUSY; | |
| 315 | + goto out; | |
| 316 | + } | |
| 343 | 317 | |
| 344 | − temp_code = readb(data->base + reg->tmu_cur_temp); | |
| 318 | + sanitize_temp_error(data, readl(data->base + EXYNOS_TMU_REG_TRIMINFO)); | |
| 345 | 319 | |
| 346 | − if (data->soc == SOC_ARCH_EXYNOS4210) | |
| 347 | − /* temp_code should range between 75 and 175 */ | |
| 348 | − if (temp_code < 75 || temp_code > 175) { | |
| 349 | − temp = -ENODATA; | |
| 350 | − goto out; | |
| 320 | + /* Write temperature code for threshold */ | |
| 321 | + threshold_code = temp_to_code(data, pdata->threshold); | |
| 322 | + writeb(threshold_code, data->base + EXYNOS4210_TMU_REG_THRESHOLD_TEMP); | |
| 323 | + | |
| 324 | + for (i = 0; i < pdata->non_hw_trigger_levels; i++) | |
| 325 | + writeb(pdata->trigger_levels[i], data->base + | |
| 326 | + EXYNOS4210_TMU_REG_TRIG_LEVEL0 + i * 4); | |
| 327 | + | |
| 328 | + data->tmu_clear_irqs(data); | |
| 329 | +out: | |
| 330 | + return ret; | |
| 331 | +} | |
| 332 | + | |
| 333 | +static int exynos4412_tmu_initialize(struct platform_device *pdev) | |
| 334 | +{ | |
| 335 | + struct exynos_tmu_data *data = platform_get_drvdata(pdev); | |
| 336 | + struct exynos_tmu_platform_data *pdata = data->pdata; | |
| 337 | + unsigned int status, trim_info, con, ctrl, rising_threshold; | |
| 338 | + int ret = 0, threshold_code, i; | |
| 339 | + | |
| 340 | + status = readb(data->base + EXYNOS_TMU_REG_STATUS); | |
| 341 | + if (!status) { | |
| 342 | + ret = -EBUSY; | |
| 343 | + goto out; | |
| 344 | + } | |
| 345 | + | |
| 346 | + if (data->soc == SOC_ARCH_EXYNOS3250 || | |
| 347 | + data->soc == SOC_ARCH_EXYNOS4412 || | |
| 348 | + data->soc == SOC_ARCH_EXYNOS5250) { | |
| 349 | + if (data->soc == SOC_ARCH_EXYNOS3250) { | |
| 350 | + ctrl = readl(data->base + EXYNOS_TMU_TRIMINFO_CON1); | |
| 351 | + ctrl |= EXYNOS_TRIMINFO_RELOAD_ENABLE; | |
| 352 | + writel(ctrl, data->base + EXYNOS_TMU_TRIMINFO_CON1); | |
| 351 | 353 | } |
| 354 | + ctrl = readl(data->base + EXYNOS_TMU_TRIMINFO_CON2); | |
| 355 | + ctrl |= EXYNOS_TRIMINFO_RELOAD_ENABLE; | |
| 356 | + writel(ctrl, data->base + EXYNOS_TMU_TRIMINFO_CON2); | |
| 357 | + } | |
| 352 | 358 | |
| 353 | − temp = code_to_temp(data, temp_code); | |
| 359 | + /* On exynos5420 the triminfo register is in the shared space */ | |
| 360 | + if (data->soc == SOC_ARCH_EXYNOS5420_TRIMINFO) | |
| 361 | + trim_info = readl(data->base_second + EXYNOS_TMU_REG_TRIMINFO); | |
| 362 | + else | |
| 363 | + trim_info = readl(data->base + EXYNOS_TMU_REG_TRIMINFO); | |
| 364 | + | |
| 365 | + sanitize_temp_error(data, trim_info); | |
| 366 | + | |
| 367 | + /* Write temperature code for rising and falling threshold */ | |
| 368 | + rising_threshold = readl(data->base + EXYNOS_THD_TEMP_RISE); | |
| 369 | + rising_threshold = get_th_reg(data, rising_threshold, false); | |
| 370 | + writel(rising_threshold, data->base + EXYNOS_THD_TEMP_RISE); | |
| 371 | + writel(get_th_reg(data, 0, true), data->base + EXYNOS_THD_TEMP_FALL); | |
| 372 | + | |
| 373 | + data->tmu_clear_irqs(data); | |
| 374 | + | |
| 375 | + /* if last threshold limit is also present */ | |
| 376 | + i = pdata->max_trigger_level - 1; | |
| 377 | + if (pdata->trigger_levels[i] && pdata->trigger_type[i] == HW_TRIP) { | |
| 378 | + threshold_code = temp_to_code(data, pdata->trigger_levels[i]); | |
| 379 | + /* 1-4 level to be assigned in th0 reg */ | |
| 380 | + rising_threshold &= ~(0xff << 8 * i); | |
| 381 | + rising_threshold |= threshold_code << 8 * i; | |
| 382 | + writel(rising_threshold, data->base + EXYNOS_THD_TEMP_RISE); | |
| 383 | + con = readl(data->base + EXYNOS_TMU_REG_CONTROL); | |
| 384 | + con |= (1 << EXYNOS_TMU_THERM_TRIP_EN_SHIFT); | |
| 385 | + writel(con, data->base + EXYNOS_TMU_REG_CONTROL); | |
| 386 | + } | |
| 354 | 387 | out: |
| 355 | − clk_disable(data->clk); | |
| 356 | − mutex_unlock(&data->lock); | |
| 388 | + return ret; | |
| 389 | +} | |
| 357 | 390 | |
| 358 | − return temp; | |
| 391 | +static int exynos5440_tmu_initialize(struct platform_device *pdev) | |
| 392 | +{ | |
| 393 | + struct exynos_tmu_data *data = platform_get_drvdata(pdev); | |
| 394 | + struct exynos_tmu_platform_data *pdata = data->pdata; | |
| 395 | + unsigned int trim_info = 0, con, rising_threshold; | |
| 396 | + int ret = 0, threshold_code, i; | |
| 397 | + | |
| 398 | + /* | |
| 399 | + * For exynos5440 soc triminfo value is swapped between TMU0 and | |
| 400 | + * TMU2, so the below logic is needed. | |
| 401 | + */ | |
| 402 | + switch (data->id) { | |
| 403 | + case 0: | |
| 404 | + trim_info = readl(data->base + EXYNOS5440_EFUSE_SWAP_OFFSET + | |
| 405 | + EXYNOS5440_TMU_S0_7_TRIM); | |
| 406 | + break; | |
| 407 | + case 1: | |
| 408 | + trim_info = readl(data->base + EXYNOS5440_TMU_S0_7_TRIM); | |
| 409 | + break; | |
| 410 | + case 2: | |
| 411 | + trim_info = readl(data->base - EXYNOS5440_EFUSE_SWAP_OFFSET + | |
| 412 | + EXYNOS5440_TMU_S0_7_TRIM); | |
| 413 | + } | |
| 414 | + sanitize_temp_error(data, trim_info); | |
| 415 | + | |
| 416 | + /* Write temperature code for rising and falling threshold */ | |
| 417 | + rising_threshold = readl(data->base + EXYNOS5440_TMU_S0_7_TH0); | |
| 418 | + rising_threshold = get_th_reg(data, rising_threshold, false); | |
| 419 | + writel(rising_threshold, data->base + EXYNOS5440_TMU_S0_7_TH0); | |
| 420 | + writel(0, data->base + EXYNOS5440_TMU_S0_7_TH1); | |
| 421 | + | |
| 422 | + data->tmu_clear_irqs(data); | |
| 423 | + | |
| 424 | + /* if last threshold limit is also present */ | |
| 425 | + i = pdata->max_trigger_level - 1; | |
| 426 | + if (pdata->trigger_levels[i] && pdata->trigger_type[i] == HW_TRIP) { | |
| 427 | + threshold_code = temp_to_code(data, pdata->trigger_levels[i]); | |
| 428 | + /* 5th level to be assigned in th2 reg */ | |
| 429 | + rising_threshold = | |
| 430 | + threshold_code << EXYNOS5440_TMU_TH_RISE4_SHIFT; | |
| 431 | + writel(rising_threshold, data->base + EXYNOS5440_TMU_S0_7_TH2); | |
| 432 | + con = readl(data->base + EXYNOS5440_TMU_S0_7_CTRL); | |
| 433 | + con |= (1 << EXYNOS_TMU_THERM_TRIP_EN_SHIFT); | |
| 434 | + writel(con, data->base + EXYNOS5440_TMU_S0_7_CTRL); | |
| 435 | + } | |
| 436 | + /* Clear the PMIN in the common TMU register */ | |
| 437 | + if (!data->id) | |
| 438 | + writel(0, data->base_second + EXYNOS5440_TMU_PMIN); | |
| 439 | + return ret; | |
| 359 | 440 | } |
| 360 | 441 | |
| 361 | −#ifdef CONFIG_THERMAL_EMULATION | |
| 362 | −static int exynos_tmu_set_emulation(void *drv_data, unsigned long temp) | |
| 442 | +static void exynos4210_tmu_control(struct platform_device *pdev, bool on) | |
| 363 | 443 | { |
| 364 | − struct exynos_tmu_data *data = drv_data; | |
| 444 | + struct exynos_tmu_data *data = platform_get_drvdata(pdev); | |
| 365 | 445 | struct exynos_tmu_platform_data *pdata = data->pdata; |
| 366 | − const struct exynos_tmu_registers *reg = pdata->registers; | |
| 367 | − unsigned int val; | |
| 368 | − int ret = -EINVAL; | |
| 446 | + unsigned int con, interrupt_en; | |
| 369 | 447 | |
| 370 | − if (!TMU_SUPPORTS(pdata, EMULATION)) | |
| 371 | − goto out; | |
| 448 | + con = get_con_reg(data, readl(data->base + EXYNOS_TMU_REG_CONTROL)); | |
| 372 | 449 | |
| 373 | − if (temp && temp < MCELSIUS) | |
| 374 | − goto out; | |
| 450 | + if (on) { | |
| 451 | + con |= (1 << EXYNOS_TMU_CORE_EN_SHIFT); | |
| 452 | + interrupt_en = | |
| 453 | + pdata->trigger_enable[3] << EXYNOS_TMU_INTEN_RISE3_SHIFT | | |
| 454 | + pdata->trigger_enable[2] << EXYNOS_TMU_INTEN_RISE2_SHIFT | | |
| 455 | + pdata->trigger_enable[1] << EXYNOS_TMU_INTEN_RISE1_SHIFT | | |
| 456 | + pdata->trigger_enable[0] << EXYNOS_TMU_INTEN_RISE0_SHIFT; | |
| 457 | + if (data->soc != SOC_ARCH_EXYNOS4210) | |
| 458 | + interrupt_en |= | |
| 459 | + interrupt_en << EXYNOS_TMU_INTEN_FALL0_SHIFT; | |
| 460 | + } else { | |
| 461 | + con &= ~(1 << EXYNOS_TMU_CORE_EN_SHIFT); | |
| 462 | + interrupt_en = 0; /* Disable all interrupts */ | |
| 463 | + } | |
| 464 | + writel(interrupt_en, data->base + EXYNOS_TMU_REG_INTEN); | |
| 465 | + writel(con, data->base + EXYNOS_TMU_REG_CONTROL); | |
| 466 | +} | |
| 467 | + | |
| 468 | +static void exynos5440_tmu_control(struct platform_device *pdev, bool on) | |
| 469 | +{ | |
| 470 | + struct exynos_tmu_data *data = platform_get_drvdata(pdev); | |
| 471 | + struct exynos_tmu_platform_data *pdata = data->pdata; | |
| 472 | + unsigned int con, interrupt_en; | |
| 473 | + | |
| 474 | + con = get_con_reg(data, readl(data->base + EXYNOS5440_TMU_S0_7_CTRL)); | |
| 475 | + | |
| 476 | + if (on) { | |
| 477 | + con |= (1 << EXYNOS_TMU_CORE_EN_SHIFT); | |
| 478 | + interrupt_en = | |
| 479 | + pdata->trigger_enable[3] << EXYNOS5440_TMU_INTEN_RISE3_SHIFT | | |
| 480 | + pdata->trigger_enable[2] << EXYNOS5440_TMU_INTEN_RISE2_SHIFT | | |
| 481 | + pdata->trigger_enable[1] << EXYNOS5440_TMU_INTEN_RISE1_SHIFT | | |
| 482 | + pdata->trigger_enable[0] << EXYNOS5440_TMU_INTEN_RISE0_SHIFT; | |
| 483 | + interrupt_en |= interrupt_en << EXYNOS5440_TMU_INTEN_FALL0_SHIFT; | |
| 484 | + } else { | |
| 485 | + con &= ~(1 << EXYNOS_TMU_CORE_EN_SHIFT); | |
| 486 | + interrupt_en = 0; /* Disable all interrupts */ | |
| 487 | + } | |
| 488 | + writel(interrupt_en, data->base + EXYNOS5440_TMU_S0_7_IRQEN); | |
| 489 | + writel(con, data->base + EXYNOS5440_TMU_S0_7_CTRL); | |
| 490 | +} | |
| 491 | + | |
| 492 | +static int exynos_tmu_read(struct exynos_tmu_data *data) | |
| 493 | +{ | |
| 494 | + int ret; | |
| 375 | 495 | |
| 376 | 496 | mutex_lock(&data->lock); |
| 377 | 497 | clk_enable(data->clk); |
| 498 | + ret = data->tmu_read(data); | |
| 499 | + if (ret >= 0) | |
| 500 | + ret = code_to_temp(data, ret); | |
| 501 | + clk_disable(data->clk); | |
| 502 | + mutex_unlock(&data->lock); | |
| 378 | 503 | |
| 379 | − val = readl(data->base + reg->emul_con); | |
| 504 | + return ret; | |
| 505 | +} | |
| 380 | 506 | |
| 507 | +#ifdef CONFIG_THERMAL_EMULATION | |
| 508 | +static u32 get_emul_con_reg(struct exynos_tmu_data *data, unsigned int val, | |
| 509 | + unsigned long temp) | |
| 510 | +{ | |
| 381 | 511 | if (temp) { |
| 382 | 512 | temp /= MCELSIUS; |
| 383 | 513 | |
| 384 | − if (TMU_SUPPORTS(pdata, EMUL_TIME)) { | |
| 385 | − val &= ~(EXYNOS_EMUL_TIME_MASK << reg->emul_time_shift); | |
| 386 | − val |= (EXYNOS_EMUL_TIME << reg->emul_time_shift); | |
| 514 | + if (data->soc != SOC_ARCH_EXYNOS5440) { | |
| 515 | + val &= ~(EXYNOS_EMUL_TIME_MASK << EXYNOS_EMUL_TIME_SHIFT); | |
| 516 | + val |= (EXYNOS_EMUL_TIME << EXYNOS_EMUL_TIME_SHIFT); | |
| 387 | 517 | } |
| 388 | − val &= ~(EXYNOS_EMUL_DATA_MASK << reg->emul_temp_shift); | |
| 389 | − val |= (temp_to_code(data, temp) << reg->emul_temp_shift) | | |
| 518 | + val &= ~(EXYNOS_EMUL_DATA_MASK << EXYNOS_EMUL_DATA_SHIFT); | |
| 519 | + val |= (temp_to_code(data, temp) << EXYNOS_EMUL_DATA_SHIFT) | | |
| 390 | 520 | EXYNOS_EMUL_ENABLE; |
| 391 | 521 | } else { |
| 392 | 522 | val &= ~EXYNOS_EMUL_ENABLE; |
| 393 | 523 | } |
| 394 | 524 | |
| 395 | − writel(val, data->base + reg->emul_con); | |
| 525 | + return val; | |
| 526 | +} | |
| 527 | + | |
| 528 | +static void exynos4412_tmu_set_emulation(struct exynos_tmu_data *data, | |
| 529 | + unsigned long temp) | |
| 530 | +{ | |
| 531 | + unsigned int val; | |
| 532 | + u32 emul_con; | |
| 533 | + | |
| 534 | + if (data->soc == SOC_ARCH_EXYNOS5260) | |
| 535 | + emul_con = EXYNOS5260_EMUL_CON; | |
| 536 | + else | |
| 537 | + emul_con = EXYNOS_EMUL_CON; | |
| 538 | + | |
| 539 | + val = readl(data->base + emul_con); | |
| 540 | + val = get_emul_con_reg(data, val, temp); | |
| 541 | + writel(val, data->base + emul_con); | |
| 542 | +} | |
| 543 | + | |
| 544 | +static void exynos5440_tmu_set_emulation(struct exynos_tmu_data *data, | |
| 545 | + unsigned long temp) | |
| 546 | +{ | |
| 547 | + unsigned int val; | |
| 548 | + | |
| 549 | + val = readl(data->base + EXYNOS5440_TMU_S0_7_DEBUG); | |
| 550 | + val = get_emul_con_reg(data, val, temp); | |
| 551 | + writel(val, data->base + EXYNOS5440_TMU_S0_7_DEBUG); | |
| 552 | +} | |
| 553 | + | |
| 554 | +static int exynos_tmu_set_emulation(void *drv_data, unsigned long temp) | |
| 555 | +{ | |
| 556 | + struct exynos_tmu_data *data = drv_data; | |
| 557 | + int ret = -EINVAL; | |
| 558 | + | |
| 559 | + if (data->soc == SOC_ARCH_EXYNOS4210) | |
| 560 | + goto out; | |
| 396 | 561 | |
| 562 | + if (temp && temp < MCELSIUS) | |
| 563 | + goto out; | |
| 564 | + | |
| 565 | + mutex_lock(&data->lock); | |
| 566 | + clk_enable(data->clk); | |
| 567 | + data->tmu_set_emulation(data, temp); | |
| 397 | 568 | clk_disable(data->clk); |
| 398 | 569 | mutex_unlock(&data->lock); |
| 399 | 570 | return 0; |
| @@ -401,23 +572,41 @@ out: · 1 unmodified line | ||
| 401 | 572 | return ret; |
| 402 | 573 | } |
| 403 | 574 | #else |
| 575 | +#define exynos4412_tmu_set_emulation NULL | |
| 576 | +#define exynos5440_tmu_set_emulation NULL | |
| 404 | 577 | static int exynos_tmu_set_emulation(void *drv_data, unsigned long temp) |
| 405 | 578 | { return -EINVAL; } |
| 406 | 579 | #endif/*CONFIG_THERMAL_EMULATION*/ |
| 407 | 580 | |
| 581 | +static int exynos4210_tmu_read(struct exynos_tmu_data *data) | |
| 582 | +{ | |
| 583 | + int ret = readb(data->base + EXYNOS_TMU_REG_CURRENT_TEMP); | |
| 584 | + | |
| 585 | + /* "temp_code" should range between 75 and 175 */ | |
| 586 | + return (ret < 75 || ret > 175) ? -ENODATA : ret; | |
| 587 | +} | |
| 588 | + | |
| 589 | +static int exynos4412_tmu_read(struct exynos_tmu_data *data) | |
| 590 | +{ | |
| 591 | + return readb(data->base + EXYNOS_TMU_REG_CURRENT_TEMP); | |
| 592 | +} | |
| 593 | + | |
| 594 | +static int exynos5440_tmu_read(struct exynos_tmu_data *data) | |
| 595 | +{ | |
| 596 | + return readb(data->base + EXYNOS5440_TMU_S0_7_TEMP); | |
| 597 | +} | |
| 598 | + | |
| 408 | 599 | static void exynos_tmu_work(struct work_struct *work) |
| 409 | 600 | { |
| 410 | 601 | struct exynos_tmu_data *data = container_of(work, |
| 411 | 602 | struct exynos_tmu_data, irq_work); |
| 412 | − struct exynos_tmu_platform_data *pdata = data->pdata; | |
| 413 | − const struct exynos_tmu_registers *reg = pdata->registers; | |
| 414 | 603 | unsigned int val_type; |
| 415 | 604 | |
| 416 | 605 | if (!IS_ERR(data->clk_sec)) |
| 417 | 606 | clk_enable(data->clk_sec); |
| 418 | 607 | /* Find which sensor generated this interrupt */ |
| 419 | − if (reg->tmu_irqstatus) { | |
| 420 | − val_type = readl(data->base_second + reg->tmu_irqstatus); | |
| 608 | + if (data->soc == SOC_ARCH_EXYNOS5440) { | |
| 609 | + val_type = readl(data->base_second + EXYNOS5440_TMU_IRQ_STATUS); | |
| 421 | 610 | if (!((val_type >> data->id) & 0x1)) |
| 422 | 611 | goto out; |
| 423 | 612 | } |
| @@ -429,7 +618,7 @@ static void exynos_tmu_work(struct work_struct *work) · 5 unmodified lines | ||
| 429 | 618 | clk_enable(data->clk); |
| 430 | 619 | |
| 431 | 620 | /* TODO: take action based on particular interrupt */ |
| 432 | − exynos_tmu_clear_irqs(data); | |
| 621 | + data->tmu_clear_irqs(data); | |
| 433 | 622 | |
| 434 | 623 | clk_disable(data->clk); |
| 435 | 624 | mutex_unlock(&data->lock); |
| @@ -437,6 +626,40 @@ out: · 1 unmodified line | ||
| 437 | 626 | enable_irq(data->irq); |
| 438 | 627 | } |
| 439 | 628 | |
| 629 | +static void exynos4210_tmu_clear_irqs(struct exynos_tmu_data *data) | |
| 630 | +{ | |
| 631 | + unsigned int val_irq; | |
| 632 | + u32 tmu_intstat, tmu_intclear; | |
| 633 | + | |
| 634 | + if (data->soc == SOC_ARCH_EXYNOS5260) { | |
| 635 | + tmu_intstat = EXYNOS5260_TMU_REG_INTSTAT; | |
| 636 | + tmu_intclear = EXYNOS5260_TMU_REG_INTCLEAR; | |
| 637 | + } else { | |
| 638 | + tmu_intstat = EXYNOS_TMU_REG_INTSTAT; | |
| 639 | + tmu_intclear = EXYNOS_TMU_REG_INTCLEAR; | |
| 640 | + } | |
| 641 | + | |
| 642 | + val_irq = readl(data->base + tmu_intstat); | |
| 643 | + /* | |
| 644 | + * Clear the interrupts. Please note that the documentation for | |
| 645 | + * Exynos3250, Exynos4412, Exynos5250 and Exynos5260 incorrectly | |
| 646 | + * states that INTCLEAR register has a different placing of bits | |
| 647 | + * responsible for FALL IRQs than INTSTAT register. Exynos5420 | |
| 648 | + * and Exynos5440 documentation is correct (Exynos4210 doesn't | |
| 649 | + * support FALL IRQs at all). | |
| 650 | + */ | |
| 651 | + writel(val_irq, data->base + tmu_intclear); | |
| 652 | +} | |
| 653 | + | |
| 654 | +static void exynos5440_tmu_clear_irqs(struct exynos_tmu_data *data) | |
| 655 | +{ | |
| 656 | + unsigned int val_irq; | |
| 657 | + | |
| 658 | + val_irq = readl(data->base + EXYNOS5440_TMU_S0_7_IRQ); | |
| 659 | + /* clear the interrupts */ | |
| 660 | + writel(val_irq, data->base + EXYNOS5440_TMU_S0_7_IRQ); | |
| 661 | +} | |
| 662 | + | |
| 440 | 663 | static irqreturn_t exynos_tmu_irq(int irq, void *id) |
| 441 | 664 | { |
| 442 | 665 | struct exynos_tmu_data *data = id; |
| @@ -450,35 +673,35 @@ static irqreturn_t exynos_tmu_irq(int irq, void *id) · 7 unmodified lines | ||
| 450 | 673 | static const struct of_device_id exynos_tmu_match[] = { |
| 451 | 674 | { |
| 452 | 675 | .compatible = "samsung,exynos3250-tmu", |
| 453 | − .data = (void *)EXYNOS3250_TMU_DRV_DATA, | |
| 676 | + .data = &exynos3250_default_tmu_data, | |
| 454 | 677 | }, |
| 455 | 678 | { |
| 456 | 679 | .compatible = "samsung,exynos4210-tmu", |
| 457 | − .data = (void *)EXYNOS4210_TMU_DRV_DATA, | |
| 680 | + .data = &exynos4210_default_tmu_data, | |
| 458 | 681 | }, |
| 459 | 682 | { |
| 460 | 683 | .compatible = "samsung,exynos4412-tmu", |
| 461 | − .data = (void *)EXYNOS4412_TMU_DRV_DATA, | |
| 684 | + .data = &exynos4412_default_tmu_data, | |
| 462 | 685 | }, |
| 463 | 686 | { |
| 464 | 687 | .compatible = "samsung,exynos5250-tmu", |
| 465 | − .data = (void *)EXYNOS5250_TMU_DRV_DATA, | |
| 688 | + .data = &exynos5250_default_tmu_data, | |
| 466 | 689 | }, |
| 467 | 690 | { |
| 468 | 691 | .compatible = "samsung,exynos5260-tmu", |
| 469 | − .data = (void *)EXYNOS5260_TMU_DRV_DATA, | |
| 692 | + .data = &exynos5260_default_tmu_data, | |
| 470 | 693 | }, |
| 471 | 694 | { |
| 472 | 695 | .compatible = "samsung,exynos5420-tmu", |
| 473 | − .data = (void *)EXYNOS5420_TMU_DRV_DATA, | |
| 696 | + .data = &exynos5420_default_tmu_data, | |
| 474 | 697 | }, |
| 475 | 698 | { |
| 476 | 699 | .compatible = "samsung,exynos5420-tmu-ext-triminfo", |
| 477 | − .data = (void *)EXYNOS5420_TMU_DRV_DATA, | |
| 700 | + .data = &exynos5420_default_tmu_data, | |
| 478 | 701 | }, |
| 479 | 702 | { |
| 480 | 703 | .compatible = "samsung,exynos5440-tmu", |
| 481 | − .data = (void *)EXYNOS5440_TMU_DRV_DATA, | |
| 704 | + .data = &exynos5440_default_tmu_data, | |
| 482 | 705 | }, |
| 483 | 706 | {}, |
| 484 | 707 | }; |
| @@ -553,12 +776,47 @@ static int exynos_map_dt_data(struct platform_device *pdev) · 68 unmodified lines | ||
| 553 | 776 | dev_err(&pdev->dev, "No platform init data supplied.\n"); |
| 554 | 777 | return -ENODEV; |
| 555 | 778 | } |
| 779 | + | |
| 556 | 780 | data->pdata = pdata; |
| 781 | + data->soc = pdata->type; | |
| 782 | + | |
| 783 | + switch (data->soc) { | |
| 784 | + case SOC_ARCH_EXYNOS4210: | |
| 785 | + data->tmu_initialize = exynos4210_tmu_initialize; | |
| 786 | + data->tmu_control = exynos4210_tmu_control; | |
| 787 | + data->tmu_read = exynos4210_tmu_read; | |
| 788 | + data->tmu_clear_irqs = exynos4210_tmu_clear_irqs; | |
| 789 | + break; | |
| 790 | + case SOC_ARCH_EXYNOS3250: | |
| 791 | + case SOC_ARCH_EXYNOS4412: | |
| 792 | + case SOC_ARCH_EXYNOS5250: | |
| 793 | + case SOC_ARCH_EXYNOS5260: | |
| 794 | + case SOC_ARCH_EXYNOS5420: | |
| 795 | + case SOC_ARCH_EXYNOS5420_TRIMINFO: | |
| 796 | + data->tmu_initialize = exynos4412_tmu_initialize; | |
| 797 | + data->tmu_control = exynos4210_tmu_control; | |
| 798 | + data->tmu_read = exynos4412_tmu_read; | |
| 799 | + data->tmu_set_emulation = exynos4412_tmu_set_emulation; | |
| 800 | + data->tmu_clear_irqs = exynos4210_tmu_clear_irqs; | |
| 801 | + break; | |
| 802 | + case SOC_ARCH_EXYNOS5440: | |
| 803 | + data->tmu_initialize = exynos5440_tmu_initialize; | |
| 804 | + data->tmu_control = exynos5440_tmu_control; | |
| 805 | + data->tmu_read = exynos5440_tmu_read; | |
| 806 | + data->tmu_set_emulation = exynos5440_tmu_set_emulation; | |
| 807 | + data->tmu_clear_irqs = exynos5440_tmu_clear_irqs; | |
| 808 | + break; | |
| 809 | + default: | |
| 810 | + dev_err(&pdev->dev, "Platform not supported\n"); | |
| 811 | + return -EINVAL; | |
| 812 | + } | |
| 813 | + | |
| 557 | 814 | /* |
| 558 | 815 | * Check if the TMU shares some registers and then try to map the |
| 559 | 816 | * memory of common registers. |
| 560 | 817 | */ |
| 561 | − if (!TMU_SUPPORTS(pdata, ADDRESS_MULTIPLE)) | |
| 818 | + if (data->soc != SOC_ARCH_EXYNOS5420_TRIMINFO && | |
| 819 | + data->soc != SOC_ARCH_EXYNOS5440) | |
| 562 | 820 | return 0; |
| 563 | 821 | |
| 564 | 822 | if (of_address_to_resource(pdev->dev.of_node, 1, &res)) { |
| @@ -625,20 +883,6 @@ static int exynos_tmu_probe(struct platform_device *pdev) · 60 unmodified lines | ||
| 625 | 883 | goto err_clk_sec; |
| 626 | 884 | } |
| 627 | 885 | |
| 628 | − if (pdata->type == SOC_ARCH_EXYNOS3250 || | |
| 629 | − pdata->type == SOC_ARCH_EXYNOS4210 || | |
| 630 | − pdata->type == SOC_ARCH_EXYNOS4412 || | |
| 631 | − pdata->type == SOC_ARCH_EXYNOS5250 || | |
| 632 | − pdata->type == SOC_ARCH_EXYNOS5260 || | |
| 633 | − pdata->type == SOC_ARCH_EXYNOS5420_TRIMINFO || | |
| 634 | − pdata->type == SOC_ARCH_EXYNOS5440) | |
| 635 | − data->soc = pdata->type; | |
| 636 | − else { | |
| 637 | − ret = -EINVAL; | |
| 638 | − dev_err(&pdev->dev, "Platform not supported\n"); | |
| 639 | − goto err_clk; | |
| 640 | − } | |
| 641 | − | |
| 642 | 886 | ret = exynos_tmu_initialize(pdev); |
| 643 | 887 | if (ret) { |
| 644 | 888 | dev_err(&pdev->dev, "Failed to initialize TMU\n"); |
| @@ -40,114 +40,11 @@ enum soc_type { · 39 unmodified lines | ||
| 40 | 40 | SOC_ARCH_EXYNOS4412, |
| 41 | 41 | SOC_ARCH_EXYNOS5250, |
| 42 | 42 | SOC_ARCH_EXYNOS5260, |
| 43 | + SOC_ARCH_EXYNOS5420, | |
| 43 | 44 | SOC_ARCH_EXYNOS5420_TRIMINFO, |
| 44 | 45 | SOC_ARCH_EXYNOS5440, |
| 45 | 46 | }; |
| 46 | 47 | |
| 47 | −/** | |
| 48 | − * EXYNOS TMU supported features. | |
| 49 | − * TMU_SUPPORT_EMULATION - This features is used to set user defined | |
| 50 | − * temperature to the TMU controller. | |
| 51 | − * TMU_SUPPORT_MULTI_INST - This features denotes that the soc | |
| 52 | − * has many instances of TMU. | |
| 53 | − * TMU_SUPPORT_TRIM_RELOAD - This features shows that trimming can | |
| 54 | − * be reloaded. | |
| 55 | − * TMU_SUPPORT_FALLING_TRIP - This features shows that interrupt can | |
| 56 | − * be registered for falling trips also. | |
| 57 | − * TMU_SUPPORT_READY_STATUS - This feature tells that the TMU current | |
| 58 | − * state(active/idle) can be checked. | |
| 59 | − * TMU_SUPPORT_EMUL_TIME - This features allows to set next temp emulation | |
| 60 | − * sample time. | |
| 61 | − * TMU_SUPPORT_ADDRESS_MULTIPLE - This feature tells that the different TMU | |
| 62 | − * sensors shares some common registers. | |
| 63 | − * TMU_SUPPORT - macro to compare the above features with the supplied. | |
| 64 | − */ | |
| 65 | −#define TMU_SUPPORT_EMULATION BIT(0) | |
| 66 | −#define TMU_SUPPORT_MULTI_INST BIT(1) | |
| 67 | −#define TMU_SUPPORT_TRIM_RELOAD BIT(2) | |
| 68 | −#define TMU_SUPPORT_FALLING_TRIP BIT(3) | |
| 69 | −#define TMU_SUPPORT_READY_STATUS BIT(4) | |
| 70 | −#define TMU_SUPPORT_EMUL_TIME BIT(5) | |
| 71 | −#define TMU_SUPPORT_ADDRESS_MULTIPLE BIT(6) | |
| 72 | − | |
| 73 | −#define TMU_SUPPORTS(a, b) (a->features & TMU_SUPPORT_ ## b) | |
| 74 | − | |
| 75 | −/** | |
| 76 | − * struct exynos_tmu_register - register descriptors to access registers and | |
| 77 | − * bitfields. The register validity, offsets and bitfield values may vary | |
| 78 | − * slightly across different exynos SOC's. | |
| 79 | − * @triminfo_data: register containing 2 pont trimming data | |
| 80 | − * @triminfo_ctrl: trim info controller register. | |
| 81 | − * @triminfo_ctrl_count: the number of trim info controller register. | |
| 82 | − * @tmu_ctrl: TMU main controller register. | |
| 83 | − * @test_mux_addr_shift: shift bits of test mux address. | |
| 84 | − * @therm_trip_mode_shift: shift bits of tripping mode in tmu_ctrl register. | |
| 85 | − * @therm_trip_mode_mask: mask bits of tripping mode in tmu_ctrl register. | |
| 86 | − * @therm_trip_en_shift: shift bits of tripping enable in tmu_ctrl register. | |
| 87 | − * @tmu_status: register drescribing the TMU status. | |
| 88 | − * @tmu_cur_temp: register containing the current temperature of the TMU. | |
| 89 | − * @threshold_temp: register containing the base threshold level. | |
| 90 | − * @threshold_th0: Register containing first set of rising levels. | |
| 91 | − * @threshold_th1: Register containing second set of rising levels. | |
| 92 | − * @threshold_th2: Register containing third set of rising levels. | |
| 93 | − * @threshold_th3_l0_shift: shift bits of level0 threshold temperature. | |
| 94 | − * @tmu_inten: register containing the different threshold interrupt | |
| 95 | − enable bits. | |
| 96 | − * @inten_rise0_shift: shift bits of rising 0 interrupt bits. | |
| 97 | − * @inten_rise1_shift: shift bits of rising 1 interrupt bits. | |
| 98 | − * @inten_rise2_shift: shift bits of rising 2 interrupt bits. | |
| 99 | − * @inten_rise3_shift: shift bits of rising 3 interrupt bits. | |
| 100 | − * @inten_fall0_shift: shift bits of falling 0 interrupt bits. | |
| 101 | − * @tmu_intstat: Register containing the interrupt status values. | |
| 102 | − * @tmu_intclear: Register for clearing the raised interrupt status. | |
| 103 | − * @emul_con: TMU emulation controller register. | |
| 104 | − * @emul_temp_shift: shift bits of emulation temperature. | |
| 105 | − * @emul_time_shift: shift bits of emulation time. | |
| 106 | − * @tmu_irqstatus: register to find which TMU generated interrupts. | |
| 107 | − * @tmu_pmin: register to get/set the Pmin value. | |
| 108 | − */ | |
| 109 | −struct exynos_tmu_registers { | |
| 110 | − u32 triminfo_data; | |
| 111 | − | |
| 112 | − u32 triminfo_ctrl[MAX_TRIMINFO_CTRL_REG]; | |
| 113 | − u32 triminfo_ctrl_count; | |
| 114 | − | |
| 115 | − u32 tmu_ctrl; | |
| 116 | − u32 test_mux_addr_shift; | |
| 117 | − u32 therm_trip_mode_shift; | |
| 118 | − u32 therm_trip_mode_mask; | |
| 119 | − u32 therm_trip_en_shift; | |
| 120 | − | |
| 121 | − u32 tmu_status; | |
| 122 | − | |
| 123 | − u32 tmu_cur_temp; | |
| 124 | − | |
| 125 | − u32 threshold_temp; | |
| 126 | − | |
| 127 | − u32 threshold_th0; | |
| 128 | − u32 threshold_th1; | |
| 129 | − u32 threshold_th2; | |
| 130 | − u32 threshold_th3_l0_shift; | |
| 131 | − | |
| 132 | − u32 tmu_inten; | |
| 133 | − u32 inten_rise0_shift; | |
| 134 | − u32 inten_rise1_shift; | |
| 135 | − u32 inten_rise2_shift; | |
| 136 | − u32 inten_rise3_shift; | |
| 137 | − u32 inten_fall0_shift; | |
| 138 | − | |
| 139 | − u32 tmu_intstat; | |
| 140 | − | |
| 141 | − u32 tmu_intclear; | |
| 142 | − | |
| 143 | − u32 emul_con; | |
| 144 | − u32 emul_temp_shift; | |
| 145 | − u32 emul_time_shift; | |
| 146 | − | |
| 147 | − u32 tmu_irqstatus; | |
| 148 | − u32 tmu_pmin; | |
| 149 | −}; | |
| 150 | − | |
| 151 | 48 | /** |
| 152 | 49 | * struct exynos_tmu_platform_data |
| 153 | 50 | * @threshold: basic temperature for generating interrupt |
| @@ -192,16 +89,10 @@ struct exynos_tmu_registers { · 38 unmodified lines | ||
| 192 | 89 | * @first_point_trim: temp value of the first point trimming |
| 193 | 90 | * @second_point_trim: temp value of the second point trimming |
| 194 | 91 | * @default_temp_offset: default temperature offset in case of no trimming |
| 195 | − * @test_mux; information if SoC supports test MUX | |
| 196 | − * @triminfo_reload: reload value to read TRIMINFO register | |
| 197 | 92 | * @cal_type: calibration type for temperature |
| 198 | 93 | * @freq_clip_table: Table representing frequency reduction percentage. |
| 199 | 94 | * @freq_tab_count: Count of the above table as frequency reduction may |
| 200 | 95 | * applicable to only some of the trigger levels. |
| 201 | − * @registers: Pointer to structure containing all the TMU controller registers | |
| 202 | − * and bitfields shifts and masks. | |
| 203 | − * @features: a bitfield value indicating the features supported in SOC like | |
| 204 | − * emulation, multi instance etc | |
| 205 | 96 | * |
| 206 | 97 | * This structure is required for configuration of exynos_tmu driver. |
| 207 | 98 | */ |
| @@ -223,15 +114,11 @@ struct exynos_tmu_platform_data { · 15 unmodified lines | ||
| 223 | 114 | u8 first_point_trim; |
| 224 | 115 | u8 second_point_trim; |
| 225 | 116 | u8 default_temp_offset; |
| 226 | − u8 test_mux; | |
| 227 | − u8 triminfo_reload[MAX_TRIMINFO_CTRL_REG]; | |
| 228 | 117 | |
| 229 | 118 | enum calibration_type cal_type; |
| 230 | 119 | enum soc_type type; |
| 231 | 120 | struct freq_clip_table freq_tab[4]; |
| 232 | 121 | unsigned int freq_tab_count; |
| 233 | − const struct exynos_tmu_registers *registers; | |
| 234 | − unsigned int features; | |
| 235 | 122 | }; |
| 236 | 123 | |
| 237 | 124 | /** |
| @@ -246,4 +133,12 @@ struct exynos_tmu_init_data { · 8 unmodified lines | ||
| 246 | 133 | struct exynos_tmu_platform_data tmu_data[]; |
| 247 | 134 | }; |
| 248 | 135 | |
| 136 | +extern struct exynos_tmu_init_data const exynos3250_default_tmu_data; | |
| 137 | +extern struct exynos_tmu_init_data const exynos4210_default_tmu_data; | |
| 138 | +extern struct exynos_tmu_init_data const exynos4412_default_tmu_data; | |
| 139 | +extern struct exynos_tmu_init_data const exynos5250_default_tmu_data; | |
| 140 | +extern struct exynos_tmu_init_data const exynos5260_default_tmu_data; | |
| 141 | +extern struct exynos_tmu_init_data const exynos5420_default_tmu_data; | |
| 142 | +extern struct exynos_tmu_init_data const exynos5440_default_tmu_data; | |
| 143 | + | |
| 249 | 144 | #endif /* _EXYNOS_TMU_H */ |
| @@ -22,24 +22,6 @@ · 21 unmodified lines | ||
| 22 | 22 | |
| 23 | 23 | #include "exynos_thermal_common.h" |
| 24 | 24 | #include "exynos_tmu.h" |
| 25 | −#include "exynos_tmu_data.h" | |
| 26 | − | |
| 27 | −#if defined(CONFIG_CPU_EXYNOS4210) | |
| 28 | −static const struct exynos_tmu_registers exynos4210_tmu_registers = { | |
| 29 | − .triminfo_data = EXYNOS_TMU_REG_TRIMINFO, | |
| 30 | − .tmu_ctrl = EXYNOS_TMU_REG_CONTROL, | |
| 31 | − .tmu_status = EXYNOS_TMU_REG_STATUS, | |
| 32 | − .tmu_cur_temp = EXYNOS_TMU_REG_CURRENT_TEMP, | |
| 33 | − .threshold_temp = EXYNOS4210_TMU_REG_THRESHOLD_TEMP, | |
| 34 | − .threshold_th0 = EXYNOS4210_TMU_REG_TRIG_LEVEL0, | |
| 35 | − .tmu_inten = EXYNOS_TMU_REG_INTEN, | |
| 36 | − .inten_rise0_shift = EXYNOS_TMU_INTEN_RISE0_SHIFT, | |
| 37 | − .inten_rise1_shift = EXYNOS_TMU_INTEN_RISE1_SHIFT, | |
| 38 | − .inten_rise2_shift = EXYNOS_TMU_INTEN_RISE2_SHIFT, | |
| 39 | − .inten_rise3_shift = EXYNOS_TMU_INTEN_RISE3_SHIFT, | |
| 40 | − .tmu_intstat = EXYNOS_TMU_REG_INTSTAT, | |
| 41 | − .tmu_intclear = EXYNOS_TMU_REG_INTCLEAR, | |
| 42 | −}; | |
| 43 | 25 | |
| 44 | 26 | struct exynos_tmu_init_data const exynos4210_default_tmu_data = { |
| 45 | 27 | .tmu_data = { |
| @@ -75,40 +57,10 @@ struct exynos_tmu_init_data const exynos4210_default_tmu_data = { · 29 unmodified lines | ||
| 75 | 57 | }, |
| 76 | 58 | .freq_tab_count = 2, |
| 77 | 59 | .type = SOC_ARCH_EXYNOS4210, |
| 78 | − .registers = &exynos4210_tmu_registers, | |
| 79 | − .features = TMU_SUPPORT_READY_STATUS, | |
| 80 | 60 | }, |
| 81 | 61 | }, |
| 82 | 62 | .tmu_count = 1, |
| 83 | 63 | }; |
| 84 | −#endif | |
| 85 | − | |
| 86 | −#if defined(CONFIG_SOC_EXYNOS3250) | |
| 87 | −static const struct exynos_tmu_registers exynos3250_tmu_registers = { | |
| 88 | − .triminfo_data = EXYNOS_TMU_REG_TRIMINFO, | |
| 89 | − .triminfo_ctrl[0] = EXYNOS_TMU_TRIMINFO_CON1, | |
| 90 | − .triminfo_ctrl[1] = EXYNOS_TMU_TRIMINFO_CON2, | |
| 91 | − .triminfo_ctrl_count = 2, | |
| 92 | − .tmu_ctrl = EXYNOS_TMU_REG_CONTROL, | |
| 93 | − .test_mux_addr_shift = EXYNOS4412_MUX_ADDR_SHIFT, | |
| 94 | − .therm_trip_mode_shift = EXYNOS_TMU_TRIP_MODE_SHIFT, | |
| 95 | − .therm_trip_mode_mask = EXYNOS_TMU_TRIP_MODE_MASK, | |
| 96 | − .therm_trip_en_shift = EXYNOS_TMU_THERM_TRIP_EN_SHIFT, | |
| 97 | − .tmu_status = EXYNOS_TMU_REG_STATUS, | |
| 98 | − .tmu_cur_temp = EXYNOS_TMU_REG_CURRENT_TEMP, | |
| 99 | − .threshold_th0 = EXYNOS_THD_TEMP_RISE, | |
| 100 | − .threshold_th1 = EXYNOS_THD_TEMP_FALL, | |
| 101 | − .tmu_inten = EXYNOS_TMU_REG_INTEN, | |
| 102 | − .inten_rise0_shift = EXYNOS_TMU_INTEN_RISE0_SHIFT, | |
| 103 | − .inten_rise1_shift = EXYNOS_TMU_INTEN_RISE1_SHIFT, | |
| 104 | − .inten_rise2_shift = EXYNOS_TMU_INTEN_RISE2_SHIFT, | |
| 105 | − .inten_fall0_shift = EXYNOS_TMU_INTEN_FALL0_SHIFT, | |
| 106 | − .tmu_intstat = EXYNOS_TMU_REG_INTSTAT, | |
| 107 | − .tmu_intclear = EXYNOS_TMU_REG_INTCLEAR, | |
| 108 | − .emul_con = EXYNOS_EMUL_CON, | |
| 109 | − .emul_temp_shift = EXYNOS_EMUL_DATA_SHIFT, | |
| 110 | − .emul_time_shift = EXYNOS_EMUL_TIME_SHIFT, | |
| 111 | −}; | |
| 112 | 64 | |
| 113 | 65 | #define EXYNOS3250_TMU_DATA \ |
| 114 | 66 | .threshold_falling = 10, \ |
| @@ -144,54 +96,17 @@ static const struct exynos_tmu_registers exynos3250_tmu_registers = { · 29 unmodified lines | ||
| 144 | 96 | .freq_clip_max = 400 * 1000, \ |
| 145 | 97 | .temp_level = 95, \ |
| 146 | 98 | }, \ |
| 147 | − .freq_tab_count = 2, \ | |
| 148 | − .triminfo_reload[0] = EXYNOS_TRIMINFO_RELOAD_ENABLE, \ | |
| 149 | − .triminfo_reload[1] = EXYNOS_TRIMINFO_RELOAD_ENABLE, \ | |
| 150 | − .registers = &exynos3250_tmu_registers, \ | |
| 151 | − .features = (TMU_SUPPORT_EMULATION | TMU_SUPPORT_TRIM_RELOAD | \ | |
| 152 | − TMU_SUPPORT_FALLING_TRIP | TMU_SUPPORT_READY_STATUS | \ | |
| 153 | − TMU_SUPPORT_EMUL_TIME) | |
| 154 | −#endif | |
| 99 | + .freq_tab_count = 2 | |
| 155 | 100 | |
| 156 | −#if defined(CONFIG_SOC_EXYNOS3250) | |
| 157 | 101 | struct exynos_tmu_init_data const exynos3250_default_tmu_data = { |
| 158 | 102 | .tmu_data = { |
| 159 | 103 | { |
| 160 | 104 | EXYNOS3250_TMU_DATA, |
| 161 | 105 | .type = SOC_ARCH_EXYNOS3250, |
| 162 | − .test_mux = EXYNOS4412_MUX_ADDR_VALUE, | |
| 163 | 106 | }, |
| 164 | 107 | }, |
| 165 | 108 | .tmu_count = 1, |
| 166 | 109 | }; |
| 167 | −#endif | |
| 168 | − | |
| 169 | −#if defined(CONFIG_SOC_EXYNOS4412) || defined(CONFIG_SOC_EXYNOS5250) | |
| 170 | −static const struct exynos_tmu_registers exynos4412_tmu_registers = { | |
| 171 | − .triminfo_data = EXYNOS_TMU_REG_TRIMINFO, | |
| 172 | − .triminfo_ctrl[0] = EXYNOS_TMU_TRIMINFO_CON2, | |
| 173 | − .triminfo_ctrl_count = 1, | |
| 174 | − .tmu_ctrl = EXYNOS_TMU_REG_CONTROL, | |
| 175 | − .test_mux_addr_shift = EXYNOS4412_MUX_ADDR_SHIFT, | |
| 176 | − .therm_trip_mode_shift = EXYNOS_TMU_TRIP_MODE_SHIFT, | |
| 177 | − .therm_trip_mode_mask = EXYNOS_TMU_TRIP_MODE_MASK, | |
| 178 | − .therm_trip_en_shift = EXYNOS_TMU_THERM_TRIP_EN_SHIFT, | |
| 179 | − .tmu_status = EXYNOS_TMU_REG_STATUS, | |
| 180 | − .tmu_cur_temp = EXYNOS_TMU_REG_CURRENT_TEMP, | |
| 181 | − .threshold_th0 = EXYNOS_THD_TEMP_RISE, | |
| 182 | − .threshold_th1 = EXYNOS_THD_TEMP_FALL, | |
| 183 | − .tmu_inten = EXYNOS_TMU_REG_INTEN, | |
| 184 | − .inten_rise0_shift = EXYNOS_TMU_INTEN_RISE0_SHIFT, | |
| 185 | − .inten_rise1_shift = EXYNOS_TMU_INTEN_RISE1_SHIFT, | |
| 186 | − .inten_rise2_shift = EXYNOS_TMU_INTEN_RISE2_SHIFT, | |
| 187 | − .inten_rise3_shift = EXYNOS_TMU_INTEN_RISE3_SHIFT, | |
| 188 | − .inten_fall0_shift = EXYNOS_TMU_INTEN_FALL0_SHIFT, | |
| 189 | − .tmu_intstat = EXYNOS_TMU_REG_INTSTAT, | |
| 190 | − .tmu_intclear = EXYNOS_TMU_REG_INTCLEAR, | |
| 191 | − .emul_con = EXYNOS_EMUL_CON, | |
| 192 | − .emul_temp_shift = EXYNOS_EMUL_DATA_SHIFT, | |
| 193 | − .emul_time_shift = EXYNOS_EMUL_TIME_SHIFT, | |
| 194 | −}; | |
| 195 | 110 | |
| 196 | 111 | #define EXYNOS4412_TMU_DATA \ |
| 197 | 112 | .threshold_falling = 10, \ |
| @@ -227,28 +142,18 @@ static const struct exynos_tmu_registers exynos4412_tmu_registers = { · 29 unmodified lines | ||
| 227 | 142 | .freq_clip_max = 400 * 1000, \ |
| 228 | 143 | .temp_level = 95, \ |
| 229 | 144 | }, \ |
| 230 | − .freq_tab_count = 2, \ | |
| 231 | − .triminfo_reload[0] = EXYNOS_TRIMINFO_RELOAD_ENABLE, \ | |
| 232 | − .registers = &exynos4412_tmu_registers, \ | |
| 233 | − .features = (TMU_SUPPORT_EMULATION | TMU_SUPPORT_TRIM_RELOAD | \ | |
| 234 | − TMU_SUPPORT_FALLING_TRIP | TMU_SUPPORT_READY_STATUS | \ | |
| 235 | − TMU_SUPPORT_EMUL_TIME) | |
| 236 | −#endif | |
| 145 | + .freq_tab_count = 2 | |
| 237 | 146 | |
| 238 | −#if defined(CONFIG_SOC_EXYNOS4412) | |
| 239 | 147 | struct exynos_tmu_init_data const exynos4412_default_tmu_data = { |
| 240 | 148 | .tmu_data = { |
| 241 | 149 | { |
| 242 | 150 | EXYNOS4412_TMU_DATA, |
| 243 | 151 | .type = SOC_ARCH_EXYNOS4412, |
| 244 | − .test_mux = EXYNOS4412_MUX_ADDR_VALUE, | |
| 245 | 152 | }, |
| 246 | 153 | }, |
| 247 | 154 | .tmu_count = 1, |
| 248 | 155 | }; |
| 249 | −#endif | |
| 250 | 156 | |
| 251 | −#if defined(CONFIG_SOC_EXYNOS5250) | |
| 252 | 157 | struct exynos_tmu_init_data const exynos5250_default_tmu_data = { |
| 253 | 158 | .tmu_data = { |
| 254 | 159 | { |
| @@ -258,31 +163,6 @@ struct exynos_tmu_init_data const exynos5250_default_tmu_data = { · 3 unmodified lines | ||
| 258 | 163 | }, |
| 259 | 164 | .tmu_count = 1, |
| 260 | 165 | }; |
| 261 | −#endif | |
| 262 | − | |
| 263 | −#if defined(CONFIG_SOC_EXYNOS5260) | |
| 264 | −static const struct exynos_tmu_registers exynos5260_tmu_registers = { | |
| 265 | − .triminfo_data = EXYNOS_TMU_REG_TRIMINFO, | |
| 266 | − .tmu_ctrl = EXYNOS_TMU_REG_CONTROL, | |
| 267 | − .therm_trip_mode_shift = EXYNOS_TMU_TRIP_MODE_SHIFT, | |
| 268 | − .therm_trip_mode_mask = EXYNOS_TMU_TRIP_MODE_MASK, | |
| 269 | − .therm_trip_en_shift = EXYNOS_TMU_THERM_TRIP_EN_SHIFT, | |
| 270 | − .tmu_status = EXYNOS_TMU_REG_STATUS, | |
| 271 | − .tmu_cur_temp = EXYNOS_TMU_REG_CURRENT_TEMP, | |
| 272 | − .threshold_th0 = EXYNOS_THD_TEMP_RISE, | |
| 273 | − .threshold_th1 = EXYNOS_THD_TEMP_FALL, | |
| 274 | − .tmu_inten = EXYNOS5260_TMU_REG_INTEN, | |
| 275 | − .inten_rise0_shift = EXYNOS_TMU_INTEN_RISE0_SHIFT, | |
| 276 | − .inten_rise1_shift = EXYNOS_TMU_INTEN_RISE1_SHIFT, | |
| 277 | − .inten_rise2_shift = EXYNOS_TMU_INTEN_RISE2_SHIFT, | |
| 278 | − .inten_rise3_shift = EXYNOS_TMU_INTEN_RISE3_SHIFT, | |
| 279 | − .inten_fall0_shift = EXYNOS_TMU_INTEN_FALL0_SHIFT, | |
| 280 | − .tmu_intstat = EXYNOS5260_TMU_REG_INTSTAT, | |
| 281 | − .tmu_intclear = EXYNOS5260_TMU_REG_INTCLEAR, | |
| 282 | − .emul_con = EXYNOS5260_EMUL_CON, | |
| 283 | − .emul_temp_shift = EXYNOS_EMUL_DATA_SHIFT, | |
| 284 | − .emul_time_shift = EXYNOS_EMUL_TIME_SHIFT, | |
| 285 | −}; | |
| 286 | 166 | |
| 287 | 167 | #define __EXYNOS5260_TMU_DATA \ |
| 288 | 168 | .threshold_falling = 10, \ |
| @@ -319,13 +199,10 @@ static const struct exynos_tmu_registers exynos5260_tmu_registers = { · 30 unmodified lines | ||
| 319 | 199 | .temp_level = 103, \ |
| 320 | 200 | }, \ |
| 321 | 201 | .freq_tab_count = 2, \ |
| 322 | − .registers = &exynos5260_tmu_registers, \ | |
| 323 | 202 | |
| 324 | 203 | #define EXYNOS5260_TMU_DATA \ |
| 325 | 204 | __EXYNOS5260_TMU_DATA \ |
| 326 | − .type = SOC_ARCH_EXYNOS5260, \ | |
| 327 | − .features = (TMU_SUPPORT_EMULATION | TMU_SUPPORT_FALLING_TRIP | \ | |
| 328 | − TMU_SUPPORT_READY_STATUS | TMU_SUPPORT_EMUL_TIME) | |
| 205 | + .type = SOC_ARCH_EXYNOS5260 | |
| 329 | 206 | |
| 330 | 207 | struct exynos_tmu_init_data const exynos5260_default_tmu_data = { |
| 331 | 208 | .tmu_data = { |
| @@ -337,82 +214,14 @@ struct exynos_tmu_init_data const exynos5260_default_tmu_data = { · 5 unmodified lines | ||
| 337 | 214 | }, |
| 338 | 215 | .tmu_count = 5, |
| 339 | 216 | }; |
| 340 | −#endif | |
| 341 | − | |
| 342 | −#if defined(CONFIG_SOC_EXYNOS5420) | |
| 343 | −static const struct exynos_tmu_registers exynos5420_tmu_registers = { | |
| 344 | − .triminfo_data = EXYNOS_TMU_REG_TRIMINFO, | |
| 345 | − .tmu_ctrl = EXYNOS_TMU_REG_CONTROL, | |
| 346 | − .therm_trip_mode_shift = EXYNOS_TMU_TRIP_MODE_SHIFT, | |
| 347 | − .therm_trip_mode_mask = EXYNOS_TMU_TRIP_MODE_MASK, | |
| 348 | − .therm_trip_en_shift = EXYNOS_TMU_THERM_TRIP_EN_SHIFT, | |
| 349 | − .tmu_status = EXYNOS_TMU_REG_STATUS, | |
| 350 | − .tmu_cur_temp = EXYNOS_TMU_REG_CURRENT_TEMP, | |
| 351 | − .threshold_th0 = EXYNOS_THD_TEMP_RISE, | |
| 352 | − .threshold_th1 = EXYNOS_THD_TEMP_FALL, | |
| 353 | − .tmu_inten = EXYNOS_TMU_REG_INTEN, | |
| 354 | − .inten_rise0_shift = EXYNOS_TMU_INTEN_RISE0_SHIFT, | |
| 355 | − .inten_rise1_shift = EXYNOS_TMU_INTEN_RISE1_SHIFT, | |
| 356 | − .inten_rise2_shift = EXYNOS_TMU_INTEN_RISE2_SHIFT, | |
| 357 | − /* INTEN_RISE3 Not availble in exynos5420 */ | |
| 358 | − .inten_rise3_shift = EXYNOS_TMU_INTEN_RISE3_SHIFT, | |
| 359 | − .inten_fall0_shift = EXYNOS_TMU_INTEN_FALL0_SHIFT, | |
| 360 | − .tmu_intstat = EXYNOS_TMU_REG_INTSTAT, | |
| 361 | − .tmu_intclear = EXYNOS_TMU_REG_INTCLEAR, | |
| 362 | − .emul_con = EXYNOS_EMUL_CON, | |
| 363 | − .emul_temp_shift = EXYNOS_EMUL_DATA_SHIFT, | |
| 364 | − .emul_time_shift = EXYNOS_EMUL_TIME_SHIFT, | |
| 365 | −}; | |
| 366 | − | |
| 367 | −#define __EXYNOS5420_TMU_DATA \ | |
| 368 | − .threshold_falling = 10, \ | |
| 369 | − .trigger_levels[0] = 85, \ | |
| 370 | − .trigger_levels[1] = 103, \ | |
| 371 | − .trigger_levels[2] = 110, \ | |
| 372 | − .trigger_levels[3] = 120, \ | |
| 373 | − .trigger_enable[0] = true, \ | |
| 374 | − .trigger_enable[1] = true, \ | |
| 375 | − .trigger_enable[2] = true, \ | |
| 376 | − .trigger_enable[3] = false, \ | |
| 377 | − .trigger_type[0] = THROTTLE_ACTIVE, \ | |
| 378 | − .trigger_type[1] = THROTTLE_ACTIVE, \ | |
| 379 | − .trigger_type[2] = SW_TRIP, \ | |
| 380 | − .trigger_type[3] = HW_TRIP, \ | |
| 381 | − .max_trigger_level = 4, \ | |
| 382 | − .non_hw_trigger_levels = 3, \ | |
| 383 | − .gain = 8, \ | |
| 384 | − .reference_voltage = 16, \ | |
| 385 | − .noise_cancel_mode = 4, \ | |
| 386 | − .cal_type = TYPE_ONE_POINT_TRIMMING, \ | |
| 387 | − .efuse_value = 55, \ | |
| 388 | − .min_efuse_value = 40, \ | |
| 389 | − .max_efuse_value = 100, \ | |
| 390 | − .first_point_trim = 25, \ | |
| 391 | − .second_point_trim = 85, \ | |
| 392 | − .default_temp_offset = 50, \ | |
| 393 | − .freq_tab[0] = { \ | |
| 394 | − .freq_clip_max = 800 * 1000, \ | |
| 395 | − .temp_level = 85, \ | |
| 396 | − }, \ | |
| 397 | − .freq_tab[1] = { \ | |
| 398 | − .freq_clip_max = 200 * 1000, \ | |
| 399 | − .temp_level = 103, \ | |
| 400 | − }, \ | |
| 401 | − .freq_tab_count = 2, \ | |
| 402 | − .registers = &exynos5420_tmu_registers, \ | |
| 403 | 217 | |
| 404 | 218 | #define EXYNOS5420_TMU_DATA \ |
| 405 | − __EXYNOS5420_TMU_DATA \ | |
| 406 | − .type = SOC_ARCH_EXYNOS5250, \ | |
| 407 | − .features = (TMU_SUPPORT_EMULATION | TMU_SUPPORT_FALLING_TRIP | \ | |
| 408 | − TMU_SUPPORT_READY_STATUS | TMU_SUPPORT_EMUL_TIME) | |
| 219 | + __EXYNOS5260_TMU_DATA \ | |
| 220 | + .type = SOC_ARCH_EXYNOS5420 | |
| 409 | 221 | |
| 410 | 222 | #define EXYNOS5420_TMU_DATA_SHARED \ |
| 411 | − __EXYNOS5420_TMU_DATA \ | |
| 412 | − .type = SOC_ARCH_EXYNOS5420_TRIMINFO, \ | |
| 413 | − .features = (TMU_SUPPORT_EMULATION | TMU_SUPPORT_FALLING_TRIP | \ | |
| 414 | − TMU_SUPPORT_READY_STATUS | TMU_SUPPORT_EMUL_TIME | \ | |
| 415 | − TMU_SUPPORT_ADDRESS_MULTIPLE) | |
| 223 | + __EXYNOS5260_TMU_DATA \ | |
| 224 | + .type = SOC_ARCH_EXYNOS5420_TRIMINFO | |
| 416 | 225 | |
| 417 | 226 | struct exynos_tmu_init_data const exynos5420_default_tmu_data = { |
| 418 | 227 | .tmu_data = { |
| @@ -424,34 +233,6 @@ struct exynos_tmu_init_data const exynos5420_default_tmu_data = { · 5 unmodified lines | ||
| 424 | 233 | }, |
| 425 | 234 | .tmu_count = 5, |
| 426 | 235 | }; |
| 427 | −#endif | |
| 428 | − | |
| 429 | −#if defined(CONFIG_SOC_EXYNOS5440) | |
| 430 | −static const struct exynos_tmu_registers exynos5440_tmu_registers = { | |
| 431 | − .triminfo_data = EXYNOS5440_TMU_S0_7_TRIM, | |
| 432 | − .tmu_ctrl = EXYNOS5440_TMU_S0_7_CTRL, | |
| 433 | − .therm_trip_mode_shift = EXYNOS_TMU_TRIP_MODE_SHIFT, | |
| 434 | − .therm_trip_mode_mask = EXYNOS_TMU_TRIP_MODE_MASK, | |
| 435 | − .therm_trip_en_shift = EXYNOS_TMU_THERM_TRIP_EN_SHIFT, | |
| 436 | − .tmu_status = EXYNOS5440_TMU_S0_7_STATUS, | |
| 437 | − .tmu_cur_temp = EXYNOS5440_TMU_S0_7_TEMP, | |
| 438 | − .threshold_th0 = EXYNOS5440_TMU_S0_7_TH0, | |
| 439 | − .threshold_th1 = EXYNOS5440_TMU_S0_7_TH1, | |
| 440 | − .threshold_th2 = EXYNOS5440_TMU_S0_7_TH2, | |
| 441 | − .threshold_th3_l0_shift = EXYNOS5440_TMU_TH_RISE4_SHIFT, | |
| 442 | − .tmu_inten = EXYNOS5440_TMU_S0_7_IRQEN, | |
| 443 | − .inten_rise0_shift = EXYNOS5440_TMU_INTEN_RISE0_SHIFT, | |
| 444 | − .inten_rise1_shift = EXYNOS5440_TMU_INTEN_RISE1_SHIFT, | |
| 445 | − .inten_rise2_shift = EXYNOS5440_TMU_INTEN_RISE2_SHIFT, | |
| 446 | − .inten_rise3_shift = EXYNOS5440_TMU_INTEN_RISE3_SHIFT, | |
| 447 | − .inten_fall0_shift = EXYNOS5440_TMU_INTEN_FALL0_SHIFT, | |
| 448 | − .tmu_intstat = EXYNOS5440_TMU_S0_7_IRQ, | |
| 449 | − .tmu_intclear = EXYNOS5440_TMU_S0_7_IRQ, | |
| 450 | − .tmu_irqstatus = EXYNOS5440_TMU_IRQ_STATUS, | |
| 451 | − .emul_con = EXYNOS5440_TMU_S0_7_DEBUG, | |
| 452 | − .emul_temp_shift = EXYNOS_EMUL_DATA_SHIFT, | |
| 453 | − .tmu_pmin = EXYNOS5440_TMU_PMIN, | |
| 454 | −}; | |
| 455 | 236 | |
| 456 | 237 | #define EXYNOS5440_TMU_DATA \ |
| 457 | 238 | .trigger_levels[0] = 100, \ |
| @@ -471,10 +252,7 @@ static const struct exynos_tmu_registers exynos5440_tmu_registers = { · 13 unmodified lines | ||
| 471 | 252 | .first_point_trim = 25, \ |
| 472 | 253 | .second_point_trim = 70, \ |
| 473 | 254 | .default_temp_offset = 25, \ |
| 474 | − .type = SOC_ARCH_EXYNOS5440, \ | |
| 475 | − .registers = &exynos5440_tmu_registers, \ | |
| 476 | − .features = (TMU_SUPPORT_EMULATION | TMU_SUPPORT_FALLING_TRIP | \ | |
| 477 | − TMU_SUPPORT_MULTI_INST | TMU_SUPPORT_ADDRESS_MULTIPLE), | |
| 255 | + .type = SOC_ARCH_EXYNOS5440 | |
| 478 | 256 | |
| 479 | 257 | struct exynos_tmu_init_data const exynos5440_default_tmu_data = { |
| 480 | 258 | .tmu_data = { |
| @@ -484,4 +262,3 @@ struct exynos_tmu_init_data const exynos5440_default_tmu_data = { · 3 unmodified lines | ||
| 484 | 262 | }, |
| 485 | 263 | .tmu_count = 3, |
| 486 | 264 | }; |
| 487 | −#endif | |
| @@ -1,159 +0,0 @@ | ||
| 1 | −/* | |
| 2 | − * exynos_tmu_data.h - Samsung EXYNOS tmu data header file | |
| 3 | − * | |
| 4 | − * Copyright (C) 2013 Samsung Electronics | |
| 5 | − * Amit Daniel Kachhap <amit.daniel@samsung.com> | |
| 6 | − * | |
| 7 | − * This program is free software; you can redistribute it and/or modify | |
| 8 | − * it under the terms of the GNU General Public License as published by | |
| 9 | − * the Free Software Foundation; either version 2 of the License, or | |
| 10 | − * (at your option) any later version. | |
| 11 | − * | |
| 12 | − * This program is distributed in the hope that it will be useful, | |
| 13 | − * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 14 | − * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 15 | − * GNU General Public License for more details. | |
| 16 | − * | |
| 17 | − * You should have received a copy of the GNU General Public License | |
| 18 | − * along with this program; if not, write to the Free Software | |
| 19 | − * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
| 20 | − * | |
| 21 | − */ | |
| 22 | − | |
| 23 | −#ifndef _EXYNOS_TMU_DATA_H | |
| 24 | −#define _EXYNOS_TMU_DATA_H | |
| 25 | − | |
| 26 | −/* Exynos generic registers */ | |
| 27 | −#define EXYNOS_TMU_REG_TRIMINFO 0x0 | |
| 28 | −#define EXYNOS_TMU_REG_CONTROL 0x20 | |
| 29 | −#define EXYNOS_TMU_REG_STATUS 0x28 | |
| 30 | −#define EXYNOS_TMU_REG_CURRENT_TEMP 0x40 | |
| 31 | −#define EXYNOS_TMU_REG_INTEN 0x70 | |
| 32 | −#define EXYNOS_TMU_REG_INTSTAT 0x74 | |
| 33 | −#define EXYNOS_TMU_REG_INTCLEAR 0x78 | |
| 34 | − | |
| 35 | −#define EXYNOS_TMU_TEMP_MASK 0xff | |
| 36 | −#define EXYNOS_TMU_REF_VOLTAGE_SHIFT 24 | |
| 37 | −#define EXYNOS_TMU_REF_VOLTAGE_MASK 0x1f | |
| 38 | −#define EXYNOS_TMU_BUF_SLOPE_SEL_MASK 0xf | |
| 39 | −#define EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT 8 | |
| 40 | −#define EXYNOS_TMU_CORE_EN_SHIFT 0 | |
| 41 | − | |
| 42 | −/* Exynos3250 specific registers */ | |
| 43 | −#define EXYNOS_TMU_TRIMINFO_CON1 0x10 | |
| 44 | − | |
| 45 | −/* Exynos4210 specific registers */ | |
| 46 | −#define EXYNOS4210_TMU_REG_THRESHOLD_TEMP 0x44 | |
| 47 | −#define EXYNOS4210_TMU_REG_TRIG_LEVEL0 0x50 | |
| 48 | − | |
| 49 | −/* Exynos5250, Exynos4412, Exynos3250 specific registers */ | |
| 50 | −#define EXYNOS_TMU_TRIMINFO_CON2 0x14 | |
| 51 | −#define EXYNOS_THD_TEMP_RISE 0x50 | |
| 52 | −#define EXYNOS_THD_TEMP_FALL 0x54 | |
| 53 | −#define EXYNOS_EMUL_CON 0x80 | |
| 54 | − | |
| 55 | −#define EXYNOS_TRIMINFO_RELOAD_ENABLE 1 | |
| 56 | −#define EXYNOS_TRIMINFO_25_SHIFT 0 | |
| 57 | −#define EXYNOS_TRIMINFO_85_SHIFT 8 | |
| 58 | −#define EXYNOS_TMU_TRIP_MODE_SHIFT 13 | |
| 59 | −#define EXYNOS_TMU_TRIP_MODE_MASK 0x7 | |
| 60 | −#define EXYNOS_TMU_THERM_TRIP_EN_SHIFT 12 | |
| 61 | − | |
| 62 | −#define EXYNOS_TMU_INTEN_RISE0_SHIFT 0 | |
| 63 | −#define EXYNOS_TMU_INTEN_RISE1_SHIFT 4 | |
| 64 | −#define EXYNOS_TMU_INTEN_RISE2_SHIFT 8 | |
| 65 | −#define EXYNOS_TMU_INTEN_RISE3_SHIFT 12 | |
| 66 | −#define EXYNOS_TMU_INTEN_FALL0_SHIFT 16 | |
| 67 | − | |
| 68 | −#define EXYNOS_EMUL_TIME 0x57F0 | |
| 69 | −#define EXYNOS_EMUL_TIME_MASK 0xffff | |
| 70 | −#define EXYNOS_EMUL_TIME_SHIFT 16 | |
| 71 | −#define EXYNOS_EMUL_DATA_SHIFT 8 | |
| 72 | −#define EXYNOS_EMUL_DATA_MASK 0xFF | |
| 73 | −#define EXYNOS_EMUL_ENABLE 0x1 | |
| 74 | − | |
| 75 | −#define EXYNOS_MAX_TRIGGER_PER_REG 4 | |
| 76 | − | |
| 77 | −/* Exynos5260 specific */ | |
| 78 | −#define EXYNOS5260_TMU_REG_INTEN 0xC0 | |
| 79 | −#define EXYNOS5260_TMU_REG_INTSTAT 0xC4 | |
| 80 | −#define EXYNOS5260_TMU_REG_INTCLEAR 0xC8 | |
| 81 | −#define EXYNOS5260_EMUL_CON 0x100 | |
| 82 | − | |
| 83 | −/* Exynos4412 specific */ | |
| 84 | −#define EXYNOS4412_MUX_ADDR_VALUE 6 | |
| 85 | −#define EXYNOS4412_MUX_ADDR_SHIFT 20 | |
| 86 | − | |
| 87 | −/*exynos5440 specific registers*/ | |
| 88 | −#define EXYNOS5440_TMU_S0_7_TRIM 0x000 | |
| 89 | −#define EXYNOS5440_TMU_S0_7_CTRL 0x020 | |
| 90 | −#define EXYNOS5440_TMU_S0_7_DEBUG 0x040 | |
| 91 | −#define EXYNOS5440_TMU_S0_7_STATUS 0x060 | |
| 92 | −#define EXYNOS5440_TMU_S0_7_TEMP 0x0f0 | |
| 93 | −#define EXYNOS5440_TMU_S0_7_TH0 0x110 | |
| 94 | −#define EXYNOS5440_TMU_S0_7_TH1 0x130 | |
| 95 | −#define EXYNOS5440_TMU_S0_7_TH2 0x150 | |
| 96 | −#define EXYNOS5440_TMU_S0_7_IRQEN 0x210 | |
| 97 | −#define EXYNOS5440_TMU_S0_7_IRQ 0x230 | |
| 98 | −/* exynos5440 common registers */ | |
| 99 | −#define EXYNOS5440_TMU_IRQ_STATUS 0x000 | |
| 100 | −#define EXYNOS5440_TMU_PMIN 0x004 | |
| 101 | − | |
| 102 | −#define EXYNOS5440_TMU_INTEN_RISE0_SHIFT 0 | |
| 103 | −#define EXYNOS5440_TMU_INTEN_RISE1_SHIFT 1 | |
| 104 | −#define EXYNOS5440_TMU_INTEN_RISE2_SHIFT 2 | |
| 105 | −#define EXYNOS5440_TMU_INTEN_RISE3_SHIFT 3 | |
| 106 | −#define EXYNOS5440_TMU_INTEN_FALL0_SHIFT 4 | |
| 107 | −#define EXYNOS5440_TMU_TH_RISE4_SHIFT 24 | |
| 108 | −#define EXYNOS5440_EFUSE_SWAP_OFFSET 8 | |
| 109 | − | |
| 110 | −#if defined(CONFIG_SOC_EXYNOS3250) | |
| 111 | −extern struct exynos_tmu_init_data const exynos3250_default_tmu_data; | |
| 112 | −#define EXYNOS3250_TMU_DRV_DATA (&exynos3250_default_tmu_data) | |
| 113 | −#else | |
| 114 | −#define EXYNOS3250_TMU_DRV_DATA (NULL) | |
| 115 | −#endif | |
| 116 | − | |
| 117 | −#if defined(CONFIG_CPU_EXYNOS4210) | |
| 118 | −extern struct exynos_tmu_init_data const exynos4210_default_tmu_data; | |
| 119 | −#define EXYNOS4210_TMU_DRV_DATA (&exynos4210_default_tmu_data) | |
| 120 | −#else | |
| 121 | −#define EXYNOS4210_TMU_DRV_DATA (NULL) | |
| 122 | −#endif | |
| 123 | − | |
| 124 | −#if defined(CONFIG_SOC_EXYNOS4412) | |
| 125 | −extern struct exynos_tmu_init_data const exynos4412_default_tmu_data; | |
| 126 | −#define EXYNOS4412_TMU_DRV_DATA (&exynos4412_default_tmu_data) | |
| 127 | −#else | |
| 128 | −#define EXYNOS4412_TMU_DRV_DATA (NULL) | |
| 129 | −#endif | |
| 130 | − | |
| 131 | −#if defined(CONFIG_SOC_EXYNOS5250) | |
| 132 | −extern struct exynos_tmu_init_data const exynos5250_default_tmu_data; | |
| 133 | −#define EXYNOS5250_TMU_DRV_DATA (&exynos5250_default_tmu_data) | |
| 134 | −#else | |
| 135 | −#define EXYNOS5250_TMU_DRV_DATA (NULL) | |
| 136 | −#endif | |
| 137 | − | |
| 138 | −#if defined(CONFIG_SOC_EXYNOS5260) | |
| 139 | −extern struct exynos_tmu_init_data const exynos5260_default_tmu_data; | |
| 140 | −#define EXYNOS5260_TMU_DRV_DATA (&exynos5260_default_tmu_data) | |
| 141 | −#else | |
| 142 | −#define EXYNOS5260_TMU_DRV_DATA (NULL) | |
| 143 | −#endif | |
| 144 | − | |
| 145 | −#if defined(CONFIG_SOC_EXYNOS5420) | |
| 146 | −extern struct exynos_tmu_init_data const exynos5420_default_tmu_data; | |
| 147 | −#define EXYNOS5420_TMU_DRV_DATA (&exynos5420_default_tmu_data) | |
| 148 | −#else | |
| 149 | −#define EXYNOS5420_TMU_DRV_DATA (NULL) | |
| 150 | −#endif | |
| 151 | − | |
| 152 | −#if defined(CONFIG_SOC_EXYNOS5440) | |
| 153 | −extern struct exynos_tmu_init_data const exynos5440_default_tmu_data; | |
| 154 | −#define EXYNOS5440_TMU_DRV_DATA (&exynos5440_default_tmu_data) | |
| 155 | −#else | |
| 156 | −#define EXYNOS5440_TMU_DRV_DATA (NULL) | |
| 157 | −#endif | |
| 158 | − | |
| 159 | −#endif /*_EXYNOS_TMU_DATA_H*/ | |
| @@ -0,0 +1,476 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved. | |
| 3 | + * | |
| 4 | + * Author: | |
| 5 | + * Mikko Perttunen <mperttunen@nvidia.com> | |
| 6 | + * | |
| 7 | + * This software is licensed under the terms of the GNU General Public | |
| 8 | + * License version 2, as published by the Free Software Foundation, and | |
| 9 | + * may be copied, distributed, and modified under those terms. | |
| 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 | + | |
| 18 | +#include <linux/bitops.h> | |
| 19 | +#include <linux/clk.h> | |
| 20 | +#include <linux/delay.h> | |
| 21 | +#include <linux/err.h> | |
| 22 | +#include <linux/interrupt.h> | |
| 23 | +#include <linux/io.h> | |
| 24 | +#include <linux/module.h> | |
| 25 | +#include <linux/of.h> | |
| 26 | +#include <linux/platform_device.h> | |
| 27 | +#include <linux/reset.h> | |
| 28 | +#include <linux/thermal.h> | |
| 29 | + | |
| 30 | +#include <soc/tegra/fuse.h> | |
| 31 | + | |
| 32 | +#define SENSOR_CONFIG0 0 | |
| 33 | +#define SENSOR_CONFIG0_STOP BIT(0) | |
| 34 | +#define SENSOR_CONFIG0_TALL_SHIFT 8 | |
| 35 | +#define SENSOR_CONFIG0_TCALC_OVER BIT(4) | |
| 36 | +#define SENSOR_CONFIG0_OVER BIT(3) | |
| 37 | +#define SENSOR_CONFIG0_CPTR_OVER BIT(2) | |
| 38 | + | |
| 39 | +#define SENSOR_CONFIG1 4 | |
| 40 | +#define SENSOR_CONFIG1_TSAMPLE_SHIFT 0 | |
| 41 | +#define SENSOR_CONFIG1_TIDDQ_EN_SHIFT 15 | |
| 42 | +#define SENSOR_CONFIG1_TEN_COUNT_SHIFT 24 | |
| 43 | +#define SENSOR_CONFIG1_TEMP_ENABLE BIT(31) | |
| 44 | + | |
| 45 | +#define SENSOR_CONFIG2 8 | |
| 46 | +#define SENSOR_CONFIG2_THERMA_SHIFT 16 | |
| 47 | +#define SENSOR_CONFIG2_THERMB_SHIFT 0 | |
| 48 | + | |
| 49 | +#define SENSOR_PDIV 0x1c0 | |
| 50 | +#define SENSOR_PDIV_T124 0x8888 | |
| 51 | +#define SENSOR_HOTSPOT_OFF 0x1c4 | |
| 52 | +#define SENSOR_HOTSPOT_OFF_T124 0x00060600 | |
| 53 | +#define SENSOR_TEMP1 0x1c8 | |
| 54 | +#define SENSOR_TEMP2 0x1cc | |
| 55 | + | |
| 56 | +#define SENSOR_TEMP_MASK 0xffff | |
| 57 | +#define READBACK_VALUE_MASK 0xff00 | |
| 58 | +#define READBACK_VALUE_SHIFT 8 | |
| 59 | +#define READBACK_ADD_HALF BIT(7) | |
| 60 | +#define READBACK_NEGATE BIT(1) | |
| 61 | + | |
| 62 | +#define FUSE_TSENSOR8_CALIB 0x180 | |
| 63 | +#define FUSE_SPARE_REALIGNMENT_REG_0 0x1fc | |
| 64 | + | |
| 65 | +#define FUSE_TSENSOR_CALIB_CP_TS_BASE_MASK 0x1fff | |
| 66 | +#define FUSE_TSENSOR_CALIB_FT_TS_BASE_MASK (0x1fff << 13) | |
| 67 | +#define FUSE_TSENSOR_CALIB_FT_TS_BASE_SHIFT 13 | |
| 68 | + | |
| 69 | +#define FUSE_TSENSOR8_CALIB_CP_TS_BASE_MASK 0x3ff | |
| 70 | +#define FUSE_TSENSOR8_CALIB_FT_TS_BASE_MASK (0x7ff << 10) | |
| 71 | +#define FUSE_TSENSOR8_CALIB_FT_TS_BASE_SHIFT 10 | |
| 72 | + | |
| 73 | +#define FUSE_SPARE_REALIGNMENT_REG_SHIFT_CP_MASK 0x3f | |
| 74 | +#define FUSE_SPARE_REALIGNMENT_REG_SHIFT_FT_MASK (0x1f << 21) | |
| 75 | +#define FUSE_SPARE_REALIGNMENT_REG_SHIFT_FT_SHIFT 21 | |
| 76 | + | |
| 77 | +#define NOMINAL_CALIB_FT_T124 105 | |
| 78 | +#define NOMINAL_CALIB_CP_T124 25 | |
| 79 | + | |
| 80 | +struct tegra_tsensor_configuration { | |
| 81 | + u32 tall, tsample, tiddq_en, ten_count, pdiv, tsample_ate, pdiv_ate; | |
| 82 | +}; | |
| 83 | + | |
| 84 | +struct tegra_tsensor { | |
| 85 | + const struct tegra_tsensor_configuration *config; | |
| 86 | + u32 base, calib_fuse_offset; | |
| 87 | + /* Correction values used to modify values read from calibration fuses */ | |
| 88 | + s32 fuse_corr_alpha, fuse_corr_beta; | |
| 89 | +}; | |
| 90 | + | |
| 91 | +struct tegra_thermctl_zone { | |
| 92 | + void __iomem *reg; | |
| 93 | + unsigned int shift; | |
| 94 | +}; | |
| 95 | + | |
| 96 | +static const struct tegra_tsensor_configuration t124_tsensor_config = { | |
| 97 | + .tall = 16300, | |
| 98 | + .tsample = 120, | |
| 99 | + .tiddq_en = 1, | |
| 100 | + .ten_count = 1, | |
| 101 | + .pdiv = 8, | |
| 102 | + .tsample_ate = 480, | |
| 103 | + .pdiv_ate = 8 | |
| 104 | +}; | |
| 105 | + | |
| 106 | +static const struct tegra_tsensor t124_tsensors[] = { | |
| 107 | + { | |
| 108 | + .config = &t124_tsensor_config, | |
| 109 | + .base = 0xc0, | |
| 110 | + .calib_fuse_offset = 0x098, | |
| 111 | + .fuse_corr_alpha = 1135400, | |
| 112 | + .fuse_corr_beta = -6266900, | |
| 113 | + }, | |
| 114 | + { | |
| 115 | + .config = &t124_tsensor_config, | |
| 116 | + .base = 0xe0, | |
| 117 | + .calib_fuse_offset = 0x084, | |
| 118 | + .fuse_corr_alpha = 1122220, | |
| 119 | + .fuse_corr_beta = -5700700, | |
| 120 | + }, | |
| 121 | + { | |
| 122 | + .config = &t124_tsensor_config, | |
| 123 | + .base = 0x100, | |
| 124 | + .calib_fuse_offset = 0x088, | |
| 125 | + .fuse_corr_alpha = 1127000, | |
| 126 | + .fuse_corr_beta = -6768200, | |
| 127 | + }, | |
| 128 | + { | |
| 129 | + .config = &t124_tsensor_config, | |
| 130 | + .base = 0x120, | |
| 131 | + .calib_fuse_offset = 0x12c, | |
| 132 | + .fuse_corr_alpha = 1110900, | |
| 133 | + .fuse_corr_beta = -6232000, | |
| 134 | + }, | |
| 135 | + { | |
| 136 | + .config = &t124_tsensor_config, | |
| 137 | + .base = 0x140, | |
| 138 | + .calib_fuse_offset = 0x158, | |
| 139 | + .fuse_corr_alpha = 1122300, | |
| 140 | + .fuse_corr_beta = -5936400, | |
| 141 | + }, | |
| 142 | + { | |
| 143 | + .config = &t124_tsensor_config, | |
| 144 | + .base = 0x160, | |
| 145 | + .calib_fuse_offset = 0x15c, | |
| 146 | + .fuse_corr_alpha = 1145700, | |
| 147 | + .fuse_corr_beta = -7124600, | |
| 148 | + }, | |
| 149 | + { | |
| 150 | + .config = &t124_tsensor_config, | |
| 151 | + .base = 0x180, | |
| 152 | + .calib_fuse_offset = 0x154, | |
| 153 | + .fuse_corr_alpha = 1120100, | |
| 154 | + .fuse_corr_beta = -6000500, | |
| 155 | + }, | |
| 156 | + { | |
| 157 | + .config = &t124_tsensor_config, | |
| 158 | + .base = 0x1a0, | |
| 159 | + .calib_fuse_offset = 0x160, | |
| 160 | + .fuse_corr_alpha = 1106500, | |
| 161 | + .fuse_corr_beta = -6729300, | |
| 162 | + }, | |
| 163 | +}; | |
| 164 | + | |
| 165 | +struct tegra_soctherm { | |
| 166 | + struct reset_control *reset; | |
| 167 | + struct clk *clock_tsensor; | |
| 168 | + struct clk *clock_soctherm; | |
| 169 | + void __iomem *regs; | |
| 170 | + | |
| 171 | + struct thermal_zone_device *thermctl_tzs[4]; | |
| 172 | +}; | |
| 173 | + | |
| 174 | +struct tsensor_shared_calibration { | |
| 175 | + u32 base_cp, base_ft; | |
| 176 | + u32 actual_temp_cp, actual_temp_ft; | |
| 177 | +}; | |
| 178 | + | |
| 179 | +static int calculate_shared_calibration(struct tsensor_shared_calibration *r) | |
| 180 | +{ | |
| 181 | + u32 val, shifted_cp, shifted_ft; | |
| 182 | + int err; | |
| 183 | + | |
| 184 | + err = tegra_fuse_readl(FUSE_TSENSOR8_CALIB, &val); | |
| 185 | + if (err) | |
| 186 | + return err; | |
| 187 | + r->base_cp = val & FUSE_TSENSOR8_CALIB_CP_TS_BASE_MASK; | |
| 188 | + r->base_ft = (val & FUSE_TSENSOR8_CALIB_FT_TS_BASE_MASK) | |
| 189 | + >> FUSE_TSENSOR8_CALIB_FT_TS_BASE_SHIFT; | |
| 190 | + val = ((val & FUSE_SPARE_REALIGNMENT_REG_SHIFT_FT_MASK) | |
| 191 | + >> FUSE_SPARE_REALIGNMENT_REG_SHIFT_FT_SHIFT); | |
| 192 | + shifted_ft = sign_extend32(val, 4); | |
| 193 | + | |
| 194 | + err = tegra_fuse_readl(FUSE_SPARE_REALIGNMENT_REG_0, &val); | |
| 195 | + if (err) | |
| 196 | + return err; | |
| 197 | + shifted_cp = sign_extend32(val, 5); | |
| 198 | + | |
| 199 | + r->actual_temp_cp = 2 * NOMINAL_CALIB_CP_T124 + shifted_cp; | |
| 200 | + r->actual_temp_ft = 2 * NOMINAL_CALIB_FT_T124 + shifted_ft; | |
| 201 | + | |
| 202 | + return 0; | |
| 203 | +} | |
| 204 | + | |
| 205 | +static s64 div64_s64_precise(s64 a, s64 b) | |
| 206 | +{ | |
| 207 | + s64 r, al; | |
| 208 | + | |
| 209 | + /* Scale up for increased precision division */ | |
| 210 | + al = a << 16; | |
| 211 | + | |
| 212 | + r = div64_s64(al * 2 + 1, 2 * b); | |
| 213 | + return r >> 16; | |
| 214 | +} | |
| 215 | + | |
| 216 | +static int | |
| 217 | +calculate_tsensor_calibration(const struct tegra_tsensor *sensor, | |
| 218 | + const struct tsensor_shared_calibration *shared, | |
| 219 | + u32 *calib) | |
| 220 | +{ | |
| 221 | + u32 val; | |
| 222 | + s32 actual_tsensor_ft, actual_tsensor_cp, delta_sens, delta_temp, | |
| 223 | + mult, div; | |
| 224 | + s16 therma, thermb; | |
| 225 | + s64 tmp; | |
| 226 | + int err; | |
| 227 | + | |
| 228 | + err = tegra_fuse_readl(sensor->calib_fuse_offset, &val); | |
| 229 | + if (err) | |
| 230 | + return err; | |
| 231 | + | |
| 232 | + actual_tsensor_cp = (shared->base_cp * 64) + sign_extend32(val, 12); | |
| 233 | + val = (val & FUSE_TSENSOR_CALIB_FT_TS_BASE_MASK) | |
| 234 | + >> FUSE_TSENSOR_CALIB_FT_TS_BASE_SHIFT; | |
| 235 | + actual_tsensor_ft = (shared->base_ft * 32) + sign_extend32(val, 12); | |
| 236 | + | |
| 237 | + delta_sens = actual_tsensor_ft - actual_tsensor_cp; | |
| 238 | + delta_temp = shared->actual_temp_ft - shared->actual_temp_cp; | |
| 239 | + | |
| 240 | + mult = sensor->config->pdiv * sensor->config->tsample_ate; | |
| 241 | + div = sensor->config->tsample * sensor->config->pdiv_ate; | |
| 242 | + | |
| 243 | + therma = div64_s64_precise((s64) delta_temp * (1LL << 13) * mult, | |
| 244 | + (s64) delta_sens * div); | |
| 245 | + | |
| 246 | + tmp = (s64)actual_tsensor_ft * shared->actual_temp_cp - | |
| 247 | + (s64)actual_tsensor_cp * shared->actual_temp_ft; | |
| 248 | + thermb = div64_s64_precise(tmp, (s64)delta_sens); | |
| 249 | + | |
| 250 | + therma = div64_s64_precise((s64)therma * sensor->fuse_corr_alpha, | |
| 251 | + (s64)1000000LL); | |
| 252 | + thermb = div64_s64_precise((s64)thermb * sensor->fuse_corr_alpha + | |
| 253 | + sensor->fuse_corr_beta, (s64)1000000LL); | |
| 254 | + | |
| 255 | + *calib = ((u16)therma << SENSOR_CONFIG2_THERMA_SHIFT) | | |
| 256 | + ((u16)thermb << SENSOR_CONFIG2_THERMB_SHIFT); | |
| 257 | + | |
| 258 | + return 0; | |
| 259 | +} | |
| 260 | + | |
| 261 | +static int enable_tsensor(struct tegra_soctherm *tegra, | |
| 262 | + const struct tegra_tsensor *sensor, | |
| 263 | + const struct tsensor_shared_calibration *shared) | |
| 264 | +{ | |
| 265 | + void __iomem *base = tegra->regs + sensor->base; | |
| 266 | + unsigned int val; | |
| 267 | + u32 calib; | |
| 268 | + int err; | |
| 269 | + | |
| 270 | + err = calculate_tsensor_calibration(sensor, shared, &calib); | |
| 271 | + if (err) | |
| 272 | + return err; | |
| 273 | + | |
| 274 | + val = sensor->config->tall << SENSOR_CONFIG0_TALL_SHIFT; | |
| 275 | + writel(val, base + SENSOR_CONFIG0); | |
| 276 | + | |
| 277 | + val = (sensor->config->tsample - 1) << SENSOR_CONFIG1_TSAMPLE_SHIFT; | |
| 278 | + val |= sensor->config->tiddq_en << SENSOR_CONFIG1_TIDDQ_EN_SHIFT; | |
| 279 | + val |= sensor->config->ten_count << SENSOR_CONFIG1_TEN_COUNT_SHIFT; | |
| 280 | + val |= SENSOR_CONFIG1_TEMP_ENABLE; | |
| 281 | + writel(val, base + SENSOR_CONFIG1); | |
| 282 | + | |
| 283 | + writel(calib, base + SENSOR_CONFIG2); | |
| 284 | + | |
| 285 | + return 0; | |
| 286 | +} | |
| 287 | + | |
| 288 | +/* | |
| 289 | + * Translate from soctherm readback format to millicelsius. | |
| 290 | + * The soctherm readback format in bits is as follows: | |
| 291 | + * TTTTTTTT H______N | |
| 292 | + * where T's contain the temperature in Celsius, | |
| 293 | + * H denotes an addition of 0.5 Celsius and N denotes negation | |
| 294 | + * of the final value. | |
| 295 | + */ | |
| 296 | +static long translate_temp(u16 val) | |
| 297 | +{ | |
| 298 | + long t; | |
| 299 | + | |
| 300 | + t = ((val & READBACK_VALUE_MASK) >> READBACK_VALUE_SHIFT) * 1000; | |
| 301 | + if (val & READBACK_ADD_HALF) | |
| 302 | + t += 500; | |
| 303 | + if (val & READBACK_NEGATE) | |
| 304 | + t *= -1; | |
| 305 | + | |
| 306 | + return t; | |
| 307 | +} | |
| 308 | + | |
| 309 | +static int tegra_thermctl_get_temp(void *data, long *out_temp) | |
| 310 | +{ | |
| 311 | + struct tegra_thermctl_zone *zone = data; | |
| 312 | + u32 val; | |
| 313 | + | |
| 314 | + val = (readl(zone->reg) >> zone->shift) & SENSOR_TEMP_MASK; | |
| 315 | + *out_temp = translate_temp(val); | |
| 316 | + | |
| 317 | + return 0; | |
| 318 | +} | |
| 319 | + | |
| 320 | +static const struct thermal_zone_of_device_ops tegra_of_thermal_ops = { | |
| 321 | + .get_temp = tegra_thermctl_get_temp, | |
| 322 | +}; | |
| 323 | + | |
| 324 | +static const struct of_device_id tegra_soctherm_of_match[] = { | |
| 325 | + { .compatible = "nvidia,tegra124-soctherm" }, | |
| 326 | + { }, | |
| 327 | +}; | |
| 328 | +MODULE_DEVICE_TABLE(of, tegra_soctherm_of_match); | |
| 329 | + | |
| 330 | +struct thermctl_zone_desc { | |
| 331 | + unsigned int offset; | |
| 332 | + unsigned int shift; | |
| 333 | +}; | |
| 334 | + | |
| 335 | +static const struct thermctl_zone_desc t124_thermctl_temp_zones[] = { | |
| 336 | + { SENSOR_TEMP1, 16 }, | |
| 337 | + { SENSOR_TEMP2, 16 }, | |
| 338 | + { SENSOR_TEMP1, 0 }, | |
| 339 | + { SENSOR_TEMP2, 0 } | |
| 340 | +}; | |
| 341 | + | |
| 342 | +static int tegra_soctherm_probe(struct platform_device *pdev) | |
| 343 | +{ | |
| 344 | + struct tegra_soctherm *tegra; | |
| 345 | + struct thermal_zone_device *tz; | |
| 346 | + struct tsensor_shared_calibration shared_calib; | |
| 347 | + struct resource *res; | |
| 348 | + unsigned int i; | |
| 349 | + int err; | |
| 350 | + | |
| 351 | + const struct tegra_tsensor *tsensors = t124_tsensors; | |
| 352 | + | |
| 353 | + tegra = devm_kzalloc(&pdev->dev, sizeof(*tegra), GFP_KERNEL); | |
| 354 | + if (!tegra) | |
| 355 | + return -ENOMEM; | |
| 356 | + | |
| 357 | + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
| 358 | + tegra->regs = devm_ioremap_resource(&pdev->dev, res); | |
| 359 | + if (IS_ERR(tegra->regs)) | |
| 360 | + return PTR_ERR(tegra->regs); | |
| 361 | + | |
| 362 | + tegra->reset = devm_reset_control_get(&pdev->dev, "soctherm"); | |
| 363 | + if (IS_ERR(tegra->reset)) { | |
| 364 | + dev_err(&pdev->dev, "can't get soctherm reset\n"); | |
| 365 | + return PTR_ERR(tegra->reset); | |
| 366 | + } | |
| 367 | + | |
| 368 | + tegra->clock_tsensor = devm_clk_get(&pdev->dev, "tsensor"); | |
| 369 | + if (IS_ERR(tegra->clock_tsensor)) { | |
| 370 | + dev_err(&pdev->dev, "can't get tsensor clock\n"); | |
| 371 | + return PTR_ERR(tegra->clock_tsensor); | |
| 372 | + } | |
| 373 | + | |
| 374 | + tegra->clock_soctherm = devm_clk_get(&pdev->dev, "soctherm"); | |
| 375 | + if (IS_ERR(tegra->clock_soctherm)) { | |
| 376 | + dev_err(&pdev->dev, "can't get soctherm clock\n"); | |
| 377 | + return PTR_ERR(tegra->clock_soctherm); | |
| 378 | + } | |
| 379 | + | |
| 380 | + reset_control_assert(tegra->reset); | |
| 381 | + | |
| 382 | + err = clk_prepare_enable(tegra->clock_soctherm); | |
| 383 | + if (err) | |
| 384 | + return err; | |
| 385 | + | |
| 386 | + err = clk_prepare_enable(tegra->clock_tsensor); | |
| 387 | + if (err) { | |
| 388 | + clk_disable_unprepare(tegra->clock_soctherm); | |
| 389 | + return err; | |
| 390 | + } | |
| 391 | + | |
| 392 | + reset_control_deassert(tegra->reset); | |
| 393 | + | |
| 394 | + /* Initialize raw sensors */ | |
| 395 | + | |
| 396 | + err = calculate_shared_calibration(&shared_calib); | |
| 397 | + if (err) | |
| 398 | + goto disable_clocks; | |
| 399 | + | |
| 400 | + for (i = 0; i < ARRAY_SIZE(t124_tsensors); ++i) { | |
| 401 | + err = enable_tsensor(tegra, tsensors + i, &shared_calib); | |
| 402 | + if (err) | |
| 403 | + goto disable_clocks; | |
| 404 | + } | |
| 405 | + | |
| 406 | + writel(SENSOR_PDIV_T124, tegra->regs + SENSOR_PDIV); | |
| 407 | + writel(SENSOR_HOTSPOT_OFF_T124, tegra->regs + SENSOR_HOTSPOT_OFF); | |
| 408 | + | |
| 409 | + /* Initialize thermctl sensors */ | |
| 410 | + | |
| 411 | + for (i = 0; i < ARRAY_SIZE(tegra->thermctl_tzs); ++i) { | |
| 412 | + struct tegra_thermctl_zone *zone = | |
| 413 | + devm_kzalloc(&pdev->dev, sizeof(*zone), GFP_KERNEL); | |
| 414 | + if (!zone) { | |
| 415 | + err = -ENOMEM; | |
| 416 | + goto unregister_tzs; | |
| 417 | + } | |
| 418 | + | |
| 419 | + zone->reg = tegra->regs + t124_thermctl_temp_zones[i].offset; | |
| 420 | + zone->shift = t124_thermctl_temp_zones[i].shift; | |
| 421 | + | |
| 422 | + tz = thermal_zone_of_sensor_register(&pdev->dev, i, zone, | |
| 423 | + &tegra_of_thermal_ops); | |
| 424 | + if (IS_ERR(tz)) { | |
| 425 | + err = PTR_ERR(tz); | |
| 426 | + dev_err(&pdev->dev, "failed to register sensor: %d\n", | |
| 427 | + err); | |
| 428 | + goto unregister_tzs; | |
| 429 | + } | |
| 430 | + | |
| 431 | + tegra->thermctl_tzs[i] = tz; | |
| 432 | + } | |
| 433 | + | |
| 434 | + return 0; | |
| 435 | + | |
| 436 | +unregister_tzs: | |
| 437 | + while (i--) | |
| 438 | + thermal_zone_of_sensor_unregister(&pdev->dev, | |
| 439 | + tegra->thermctl_tzs[i]); | |
| 440 | + | |
| 441 | +disable_clocks: | |
| 442 | + clk_disable_unprepare(tegra->clock_tsensor); | |
| 443 | + clk_disable_unprepare(tegra->clock_soctherm); | |
| 444 | + | |
| 445 | + return err; | |
| 446 | +} | |
| 447 | + | |
| 448 | +static int tegra_soctherm_remove(struct platform_device *pdev) | |
| 449 | +{ | |
| 450 | + struct tegra_soctherm *tegra = platform_get_drvdata(pdev); | |
| 451 | + unsigned int i; | |
| 452 | + | |
| 453 | + for (i = 0; i < ARRAY_SIZE(tegra->thermctl_tzs); ++i) { | |
| 454 | + thermal_zone_of_sensor_unregister(&pdev->dev, | |
| 455 | + tegra->thermctl_tzs[i]); | |
| 456 | + } | |
| 457 | + | |
| 458 | + clk_disable_unprepare(tegra->clock_tsensor); | |
| 459 | + clk_disable_unprepare(tegra->clock_soctherm); | |
| 460 | + | |
| 461 | + return 0; | |
| 462 | +} | |
| 463 | + | |
| 464 | +static struct platform_driver tegra_soctherm_driver = { | |
| 465 | + .probe = tegra_soctherm_probe, | |
| 466 | + .remove = tegra_soctherm_remove, | |
| 467 | + .driver = { | |
| 468 | + .name = "tegra-soctherm", | |
| 469 | + .of_match_table = tegra_soctherm_of_match, | |
| 470 | + }, | |
| 471 | +}; | |
| 472 | +module_platform_driver(tegra_soctherm_driver); | |
| 473 | + | |
| 474 | +MODULE_AUTHOR("Mikko Perttunen <mperttunen@nvidia.com>"); | |
| 475 | +MODULE_DESCRIPTION("NVIDIA Tegra SOCTHERM thermal management driver"); | |
| 476 | +MODULE_LICENSE("GPL v2"); | |
| @@ -368,7 +368,7 @@ static void handle_critical_trips(struct thermal_zone_device *tz, · 367 unmodified lines | ||
| 368 | 368 | tz->ops->get_trip_temp(tz, trip, &trip_temp); |
| 369 | 369 | |
| 370 | 370 | /* If we have not crossed the trip_temp, we do not care. */ |
| 371 | − if (tz->temperature < trip_temp) | |
| 371 | + if (trip_temp <= 0 || tz->temperature < trip_temp) | |
| 372 | 372 | return; |
| 373 | 373 | |
| 374 | 374 | trace_thermal_zone_trip(tz, trip, trip_type); |
| @@ -757,6 +757,7 @@ policy_store(struct device *dev, struct device_attribute *attr, · 382 unmodified lines | ||
| 757 | 757 | snprintf(name, sizeof(name), "%s", buf); |
| 758 | 758 | |
| 759 | 759 | mutex_lock(&thermal_governor_lock); |
| 760 | + mutex_lock(&tz->lock); | |
| 760 | 761 | |
| 761 | 762 | gov = __find_governor(strim(name)); |
| 762 | 763 | if (!gov) |
| @@ -766,6 +767,7 @@ policy_store(struct device *dev, struct device_attribute *attr, · 3 unmodified lines | ||
| 766 | 767 | ret = count; |
| 767 | 768 | |
| 768 | 769 | exit: |
| 770 | + mutex_unlock(&tz->lock); | |
| 769 | 771 | mutex_unlock(&thermal_governor_lock); |
| 770 | 772 | return ret; |
| 771 | 773 | } |
| @@ -1835,10 +1837,10 @@ static int __init thermal_init(void) · 1063 unmodified lines | ||
| 1835 | 1837 | |
| 1836 | 1838 | exit_netlink: |
| 1837 | 1839 | genetlink_exit(); |
| 1838 | −unregister_governors: | |
| 1839 | − thermal_unregister_governors(); | |
| 1840 | 1840 | unregister_class: |
| 1841 | 1841 | class_unregister(&thermal_class); |
| 1842 | +unregister_governors: | |
| 1843 | + thermal_unregister_governors(); | |
| 1842 | 1844 | error: |
| 1843 | 1845 | idr_destroy(&thermal_tz_idr); |
| 1844 | 1846 | idr_destroy(&thermal_cdev_idr); |
| @@ -89,9 +89,27 @@ static inline void thermal_gov_user_space_unregister(void) {} · 88 unmodified lines | ||
| 89 | 89 | #ifdef CONFIG_THERMAL_OF |
| 90 | 90 | int of_parse_thermal_zones(void); |
| 91 | 91 | void of_thermal_destroy_zones(void); |
| 92 | +int of_thermal_get_ntrips(struct thermal_zone_device *); | |
| 93 | +bool of_thermal_is_trip_valid(struct thermal_zone_device *, int); | |
| 94 | +const struct thermal_trip * const | |
| 95 | +of_thermal_get_trip_points(struct thermal_zone_device *); | |
| 92 | 96 | #else |
| 93 | 97 | static inline int of_parse_thermal_zones(void) { return 0; } |
| 94 | 98 | static inline void of_thermal_destroy_zones(void) { } |
| 99 | +static inline int of_thermal_get_ntrips(struct thermal_zone_device *tz) | |
| 100 | +{ | |
| 101 | + return 0; | |
| 102 | +} | |
| 103 | +static inline bool of_thermal_is_trip_valid(struct thermal_zone_device *tz, | |
| 104 | + int trip) | |
| 105 | +{ | |
| 106 | + return 0; | |
| 107 | +} | |
| 108 | +static inline const struct thermal_trip * const | |
| 109 | +of_thermal_get_trip_points(struct thermal_zone_device *tz) | |
| 110 | +{ | |
| 111 | + return NULL; | |
| 112 | +} | |
| 95 | 113 | #endif |
| 96 | 114 | |
| 97 | 115 | #endif /* __THERMAL_CORE_H__ */ |
| @@ -286,6 +286,11 @@ static int ti_thermal_get_crit_temp(struct thermal_zone_device *thermal, · 285 unmodified lines | ||
| 286 | 286 | return ti_thermal_get_trip_temp(thermal, OMAP_TRIP_NUMBER - 1, temp); |
| 287 | 287 | } |
| 288 | 288 | |
| 289 | +static const struct thermal_zone_of_device_ops ti_of_thermal_ops = { | |
| 290 | + .get_temp = __ti_thermal_get_temp, | |
| 291 | + .get_trend = __ti_thermal_get_trend, | |
| 292 | +}; | |
| 293 | + | |
| 289 | 294 | static struct thermal_zone_device_ops ti_thermal_ops = { |
| 290 | 295 | .get_temp = ti_thermal_get_temp, |
| 291 | 296 | .get_trend = ti_thermal_get_trend, |
| @@ -333,8 +338,7 @@ int ti_thermal_expose_sensor(struct ti_bandgap *bgp, int id, · 41 unmodified lines | ||
| 333 | 338 | |
| 334 | 339 | /* in case this is specified by DT */ |
| 335 | 340 | data->ti_thermal = thermal_zone_of_sensor_register(bgp->dev, id, |
| 336 | − data, __ti_thermal_get_temp, | |
| 337 | − __ti_thermal_get_trend); | |
| 341 | + data, &ti_of_thermal_ops); | |
| 338 | 342 | if (IS_ERR(data->ti_thermal)) { |
| 339 | 343 | /* Create thermal zone */ |
| 340 | 344 | data->ti_thermal = thermal_zone_device_register(domain, |
| @@ -0,0 +1,13 @@ | ||
| 1 | +/* | |
| 2 | + * This header provides constants for binding nvidia,tegra124-soctherm. | |
| 3 | + */ | |
| 4 | + | |
| 5 | +#ifndef _DT_BINDINGS_THERMAL_TEGRA124_SOCTHERM_H | |
| 6 | +#define _DT_BINDINGS_THERMAL_TEGRA124_SOCTHERM_H | |
| 7 | + | |
| 8 | +#define TEGRA124_SOCTHERM_SENSOR_CPU 0 | |
| 9 | +#define TEGRA124_SOCTHERM_SENSOR_MEM 1 | |
| 10 | +#define TEGRA124_SOCTHERM_SENSOR_GPU 2 | |
| 11 | +#define TEGRA124_SOCTHERM_SENSOR_PLLX 3 | |
| 12 | + | |
| 13 | +#endif | |
| @@ -0,0 +1,65 @@ | ||
| 1 | +/* | |
| 2 | + * linux/include/linux/clock_cooling.h | |
| 3 | + * | |
| 4 | + * Copyright (C) 2014 Eduardo Valentin <edubezval@gmail.com> | |
| 5 | + * | |
| 6 | + * Copyright (C) 2013 Texas Instruments Inc. | |
| 7 | + * Contact: Eduardo Valentin <eduardo.valentin@ti.com> | |
| 8 | + * | |
| 9 | + * Highly based on cpu_cooling.c. | |
| 10 | + * Copyright (C) 2012 Samsung Electronics Co., Ltd(http://www.samsung.com) | |
| 11 | + * Copyright (C) 2012 Amit Daniel <amit.kachhap@linaro.org> | |
| 12 | + * | |
| 13 | + * This program is free software; you can redistribute it and/or modify | |
| 14 | + * it under the terms of the GNU General Public License as published by | |
| 15 | + * the Free Software Foundation; version 2 of the License. | |
| 16 | + * | |
| 17 | + * This program is distributed in the hope that it will be useful, but | |
| 18 | + * WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 19 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| 20 | + * General Public License for more details. | |
| 21 | + */ | |
| 22 | + | |
| 23 | +#ifndef __CPU_COOLING_H__ | |
| 24 | +#define __CPU_COOLING_H__ | |
| 25 | + | |
| 26 | +#include <linux/of.h> | |
| 27 | +#include <linux/thermal.h> | |
| 28 | +#include <linux/cpumask.h> | |
| 29 | + | |
| 30 | +#ifdef CONFIG_CLOCK_THERMAL | |
| 31 | +/** | |
| 32 | + * clock_cooling_register - function to create clock cooling device. | |
| 33 | + * @dev: struct device pointer to the device used as clock cooling device. | |
| 34 | + * @clock_name: string containing the clock used as cooling mechanism. | |
| 35 | + */ | |
| 36 | +struct thermal_cooling_device * | |
| 37 | +clock_cooling_register(struct device *dev, const char *clock_name); | |
| 38 | + | |
| 39 | +/** | |
| 40 | + * clock_cooling_unregister - function to remove clock cooling device. | |
| 41 | + * @cdev: thermal cooling device pointer. | |
| 42 | + */ | |
| 43 | +void clock_cooling_unregister(struct thermal_cooling_device *cdev); | |
| 44 | + | |
| 45 | +unsigned long clock_cooling_get_level(struct thermal_cooling_device *cdev, | |
| 46 | + unsigned long freq); | |
| 47 | +#else /* !CONFIG_CLOCK_THERMAL */ | |
| 48 | +static inline struct thermal_cooling_device * | |
| 49 | +clock_cooling_register(struct device *dev, const char *clock_name) | |
| 50 | +{ | |
| 51 | + return NULL; | |
| 52 | +} | |
| 53 | +static inline | |
| 54 | +void clock_cooling_unregister(struct thermal_cooling_device *cdev) | |
| 55 | +{ | |
| 56 | +} | |
| 57 | +static inline | |
| 58 | +unsigned long clock_cooling_get_level(struct thermal_cooling_device *cdev, | |
| 59 | + unsigned long freq) | |
| 60 | +{ | |
| 61 | + return THERMAL_CSTATE_INVALID; | |
| 62 | +} | |
| 63 | +#endif /* CONFIG_CLOCK_THERMAL */ | |
| 64 | + | |
| 65 | +#endif /* __CPU_COOLING_H__ */ | |
| @@ -29,10 +29,10 @@ · 28 unmodified lines | ||
| 29 | 29 | #include <linux/idr.h> |
| 30 | 30 | #include <linux/device.h> |
| 31 | 31 | #include <linux/workqueue.h> |
| 32 | +#include <uapi/linux/thermal.h> | |
| 32 | 33 | |
| 33 | 34 | #define THERMAL_TRIPS_NONE -1 |
| 34 | 35 | #define THERMAL_MAX_TRIPS 12 |
| 35 | −#define THERMAL_NAME_LENGTH 20 | |
| 36 | 36 | |
| 37 | 37 | /* invalid cooling state */ |
| 38 | 38 | #define THERMAL_CSTATE_INVALID -1UL |
| @@ -49,11 +49,6 @@ · 10 unmodified lines | ||
| 49 | 49 | #define MILLICELSIUS_TO_DECI_KELVIN_WITH_OFFSET(t, off) (((t) / 100) + (off)) |
| 50 | 50 | #define MILLICELSIUS_TO_DECI_KELVIN(t) MILLICELSIUS_TO_DECI_KELVIN_WITH_OFFSET(t, 2732) |
| 51 | 51 | |
| 52 | −/* Adding event notification support elements */ | |
| 53 | −#define THERMAL_GENL_FAMILY_NAME "thermal_event" | |
| 54 | −#define THERMAL_GENL_VERSION 0x01 | |
| 55 | −#define THERMAL_GENL_MCAST_GROUP_NAME "thermal_mc_grp" | |
| 56 | − | |
| 57 | 52 | /* Default Thermal Governor */ |
| 58 | 53 | #if defined(CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE) |
| 59 | 54 | #define DEFAULT_THERMAL_GOVERNOR "step_wise" |
| @@ -86,30 +81,6 @@ enum thermal_trend { · 26 unmodified lines | ||
| 86 | 81 | THERMAL_TREND_DROP_FULL, /* apply lowest cooling action */ |
| 87 | 82 | }; |
| 88 | 83 | |
| 89 | −/* Events supported by Thermal Netlink */ | |
| 90 | −enum events { | |
| 91 | − THERMAL_AUX0, | |
| 92 | − THERMAL_AUX1, | |
| 93 | − THERMAL_CRITICAL, | |
| 94 | − THERMAL_DEV_FAULT, | |
| 95 | −}; | |
| 96 | − | |
| 97 | −/* attributes of thermal_genl_family */ | |
| 98 | −enum { | |
| 99 | − THERMAL_GENL_ATTR_UNSPEC, | |
| 100 | − THERMAL_GENL_ATTR_EVENT, | |
| 101 | − __THERMAL_GENL_ATTR_MAX, | |
| 102 | −}; | |
| 103 | −#define THERMAL_GENL_ATTR_MAX (__THERMAL_GENL_ATTR_MAX - 1) | |
| 104 | − | |
| 105 | −/* commands supported by the thermal_genl_family */ | |
| 106 | −enum { | |
| 107 | − THERMAL_GENL_CMD_UNSPEC, | |
| 108 | − THERMAL_GENL_CMD_EVENT, | |
| 109 | − __THERMAL_GENL_CMD_MAX, | |
| 110 | −}; | |
| 111 | −#define THERMAL_GENL_CMD_MAX (__THERMAL_GENL_CMD_MAX - 1) | |
| 112 | − | |
| 113 | 84 | struct thermal_zone_device_ops { |
| 114 | 85 | int (*bind) (struct thermal_zone_device *, |
| 115 | 86 | struct thermal_cooling_device *); |
| @@ -289,19 +260,49 @@ struct thermal_genl_event { · 173 unmodified lines | ||
| 289 | 260 | enum events event; |
| 290 | 261 | }; |
| 291 | 262 | |
| 263 | +/** | |
| 264 | + * struct thermal_zone_of_device_ops - scallbacks for handling DT based zones | |
| 265 | + * | |
| 266 | + * Mandatory: | |
| 267 | + * @get_temp: a pointer to a function that reads the sensor temperature. | |
| 268 | + * | |
| 269 | + * Optional: | |
| 270 | + * @get_trend: a pointer to a function that reads the sensor temperature trend. | |
| 271 | + * @set_emul_temp: a pointer to a function that sets sensor emulated | |
| 272 | + * temperature. | |
| 273 | + */ | |
| 274 | +struct thermal_zone_of_device_ops { | |
| 275 | + int (*get_temp)(void *, long *); | |
| 276 | + int (*get_trend)(void *, long *); | |
| 277 | + int (*set_emul_temp)(void *, unsigned long); | |
| 278 | +}; | |
| 279 | + | |
| 280 | +/** | |
| 281 | + * struct thermal_trip - representation of a point in temperature domain | |
| 282 | + * @np: pointer to struct device_node that this trip point was created from | |
| 283 | + * @temperature: temperature value in miliCelsius | |
| 284 | + * @hysteresis: relative hysteresis in miliCelsius | |
| 285 | + * @type: trip point type | |
| 286 | + */ | |
| 287 | + | |
| 288 | +struct thermal_trip { | |
| 289 | + struct device_node *np; | |
| 290 | + unsigned long int temperature; | |
| 291 | + unsigned long int hysteresis; | |
| 292 | + enum thermal_trip_type type; | |
| 293 | +}; | |
| 294 | + | |
| 292 | 295 | /* Function declarations */ |
| 293 | 296 | #ifdef CONFIG_THERMAL_OF |
| 294 | 297 | struct thermal_zone_device * |
| 295 | −thermal_zone_of_sensor_register(struct device *dev, int id, | |
| 296 | − void *data, int (*get_temp)(void *, long *), | |
| 297 | − int (*get_trend)(void *, long *)); | |
| 298 | +thermal_zone_of_sensor_register(struct device *dev, int id, void *data, | |
| 299 | + const struct thermal_zone_of_device_ops *ops); | |
| 298 | 300 | void thermal_zone_of_sensor_unregister(struct device *dev, |
| 299 | 301 | struct thermal_zone_device *tz); |
| 300 | 302 | #else |
| 301 | 303 | static inline struct thermal_zone_device * |
| 302 | −thermal_zone_of_sensor_register(struct device *dev, int id, | |
| 303 | − void *data, int (*get_temp)(void *, long *), | |
| 304 | − int (*get_trend)(void *, long *)) | |
| 304 | +thermal_zone_of_sensor_register(struct device *dev, int id, void *data, | |
| 305 | + const struct thermal_zone_of_device_ops *ops) | |
| 305 | 306 | { |
| 306 | 307 | return NULL; |
| 307 | 308 | } |
| @@ -387,6 +387,7 @@ header-y += tcp.h · 386 unmodified lines | ||
| 387 | 387 | header-y += tcp_metrics.h |
| 388 | 388 | header-y += telephony.h |
| 389 | 389 | header-y += termios.h |
| 390 | +header-y += thermal.h | |
| 390 | 391 | header-y += time.h |
| 391 | 392 | header-y += times.h |
| 392 | 393 | header-y += timex.h |
| @@ -0,0 +1,35 @@ | ||
| 1 | +#ifndef _UAPI_LINUX_THERMAL_H | |
| 2 | +#define _UAPI_LINUX_THERMAL_H | |
| 3 | + | |
| 4 | +#define THERMAL_NAME_LENGTH 20 | |
| 5 | + | |
| 6 | +/* Adding event notification support elements */ | |
| 7 | +#define THERMAL_GENL_FAMILY_NAME "thermal_event" | |
| 8 | +#define THERMAL_GENL_VERSION 0x01 | |
| 9 | +#define THERMAL_GENL_MCAST_GROUP_NAME "thermal_mc_grp" | |
| 10 | + | |
| 11 | +/* Events supported by Thermal Netlink */ | |
| 12 | +enum events { | |
| 13 | + THERMAL_AUX0, | |
| 14 | + THERMAL_AUX1, | |
| 15 | + THERMAL_CRITICAL, | |
| 16 | + THERMAL_DEV_FAULT, | |
| 17 | +}; | |
| 18 | + | |
| 19 | +/* attributes of thermal_genl_family */ | |
| 20 | +enum { | |
| 21 | + THERMAL_GENL_ATTR_UNSPEC, | |
| 22 | + THERMAL_GENL_ATTR_EVENT, | |
| 23 | + __THERMAL_GENL_ATTR_MAX, | |
| 24 | +}; | |
| 25 | +#define THERMAL_GENL_ATTR_MAX (__THERMAL_GENL_ATTR_MAX - 1) | |
| 26 | + | |
| 27 | +/* commands supported by the thermal_genl_family */ | |
| 28 | +enum { | |
| 29 | + THERMAL_GENL_CMD_UNSPEC, | |
| 30 | + THERMAL_GENL_CMD_EVENT, | |
| 31 | + __THERMAL_GENL_CMD_MAX, | |
| 32 | +}; | |
| 33 | +#define THERMAL_GENL_CMD_MAX (__THERMAL_GENL_CMD_MAX - 1) | |
| 34 | + | |
| 35 | +#endif /* _UAPI_LINUX_THERMAL_H */ | |