diff --git a/Documentation/devicetree/bindings/arm/arm_gpu.txt b/Documentation/devicetree/bindings/arm/arm_gpu.txt new file mode 100644 index 000000000000..00e2c28cfd56 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/arm_gpu.txt @@ -0,0 +1,10 @@ +ARM Mali GPU device tree bindings +------------------------------------ + +Boards with Mali-450 GPU shall have the following properties: + Required root node property: + compatible: "arm,mali-450" + +Boards with Mali-t82x GPU shall have the following properties: + Required root node property: + compatible: "arm,malit602", "arm,malit60x", "arm,malit6xx", "arm,mali-midgard" diff --git a/MAINTAINERS b/MAINTAINERS index 6c4065abb988..8a3c6bc1bcc8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13464,6 +13464,10 @@ AMLOGIC reboot M: Jianxin Pan F: drivers/amlogic/reboot/* +AMLOGIC driver for thermal +M: Tao Zeng +F: drivers/amlogic/thermal/* + HDMITX OUTPUT DRIVER M: Zongdong Jiao S: Maintained diff --git a/arch/arm64/boot/dts/amlogic/gxl_p212_1g.dts b/arch/arm64/boot/dts/amlogic/gxl_p212_1g.dts index 16c8a6e7390b..cd7e149975b2 100644 --- a/arch/arm64/boot/dts/amlogic/gxl_p212_1g.dts +++ b/arch/arm64/boot/dts/amlogic/gxl_p212_1g.dts @@ -302,6 +302,109 @@ internal_phy=<1>; }; + aml_sensor0: aml-sensor@0 { + compatible = "amlogic, aml-thermal"; + device_name = "thermal"; + #thermal-sensor-cells = <1>; + cooling_devices { + cpufreq_cool_cluster0 { + min_state = <1000000>; + dyn_coeff = <140>; + cluster_id = <0>; + node_name = "cpufreq_cool0"; + device_type = "cpufreq"; + }; + cpucore_cool_cluster0 { + min_state = <1>; + dyn_coeff = <0>; + cluster_id = <0>; + node_name = "cpucore_cool0"; + device_type = "cpucore"; + }; + gpufreq_cool { + min_state = <400>; + dyn_coeff = <437>; + cluster_id = <0>; + node_name = "gpufreq_cool0"; + device_type = "gpufreq"; + }; + gpucore_cool { + min_state = <1>; + dyn_coeff = <0>; + cluster_id = <0>; + node_name = "gpucore_cool0"; + device_type = "gpucore"; + }; + }; + cpufreq_cool0:cpufreq_cool0 { + #cooling-cells = <2>; /* min followed by max */ + }; + cpucore_cool0:cpucore_cool0 { + #cooling-cells = <2>; /* min followed by max */ + }; + gpufreq_cool0:gpufreq_cool0 { + #cooling-cells = <2>; /* min followed by max */ + }; + gpucore_cool0:gpucore_cool0 { + #cooling-cells = <2>; /* min followed by max */ + }; + }; + thermal-zones { + soc_thermal { + polling-delay = <1000>; + polling-delay-passive = <100>; + sustainable-power = <2150>; + + thermal-sensors = <&aml_sensor0 3>; + + trips { + switch_on: trip-point@0 { + temperature = <70000>; + hysteresis = <1000>; + type = "passive"; + }; + control: trip-point@1 { + temperature = <80000>; + hysteresis = <1000>; + type = "passive"; + }; + hot: trip-point@2 { + temperature = <85000>; + hysteresis = <5000>; + type = "hot"; + }; + critical: trip-point@3 { + temperature = <260000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + + cooling-maps { + cpufreq_cooling_map { + trip = <&control>; + cooling-device = <&cpufreq_cool0 0 4>; + contribution = <1024>; + }; + cpucore_cooling_map { + trip = <&control>; + cooling-device = <&cpucore_cool0 0 3>; + contribution = <1024>; + }; + gpufreq_cooling_map { + trip = <&control>; + cooling-device = <&gpufreq_cool0 0 4>; + contribution = <1024>; + }; + gpucore_cooling_map { + trip = <&control>; + cooling-device = <&gpucore_cool0 0 2>; + contribution = <1024>; + }; + }; + }; + }; + dwc3: dwc3@c9000000 { compatible = "synopsys, dwc3"; reg = <0x0 0xc9000000 0x0 0x100000>; diff --git a/arch/arm64/boot/dts/amlogic/gxl_p212_2g.dts b/arch/arm64/boot/dts/amlogic/gxl_p212_2g.dts index 0bf441714aef..476e6dae3cca 100644 --- a/arch/arm64/boot/dts/amlogic/gxl_p212_2g.dts +++ b/arch/arm64/boot/dts/amlogic/gxl_p212_2g.dts @@ -377,6 +377,108 @@ "mailbox_2"; }; + aml_sensor0: aml-sensor@0 { + compatible = "amlogic, aml-thermal"; + device_name = "thermal"; + #thermal-sensor-cells = <1>; + cooling_devices { + cpufreq_cool_cluster0 { + min_state = <1000000>; + dyn_coeff = <140>; + cluster_id = <0>; + node_name = "cpufreq_cool0"; + device_type = "cpufreq"; + }; + cpucore_cool_cluster0 { + min_state = <1>; + dyn_coeff = <0>; + cluster_id = <0>; + node_name = "cpucore_cool0"; + device_type = "cpucore"; + }; + gpufreq_cool { + min_state = <400>; + dyn_coeff = <437>; + cluster_id = <0>; + node_name = "gpufreq_cool0"; + device_type = "gpufreq"; + }; + gpucore_cool { + min_state = <1>; + dyn_coeff = <0>; + cluster_id = <0>; + node_name = "gpucore_cool0"; + device_type = "gpucore"; + }; + }; + cpufreq_cool0:cpufreq_cool0 { + #cooling-cells = <2>; /* min followed by max */ + }; + cpucore_cool0:cpucore_cool0 { + #cooling-cells = <2>; /* min followed by max */ + }; + gpufreq_cool0:gpufreq_cool0 { + #cooling-cells = <2>; /* min followed by max */ + }; + gpucore_cool0:gpucore_cool0 { + #cooling-cells = <2>; /* min followed by max */ + }; + }; + thermal-zones { + soc_thermal { + polling-delay = <1000>; + polling-delay-passive = <100>; + sustainable-power = <2150>; + + thermal-sensors = <&aml_sensor0 3>; + + trips { + switch_on: trip-point@0 { + temperature = <70000>; + hysteresis = <1000>; + type = "passive"; + }; + control: trip-point@1 { + temperature = <80000>; + hysteresis = <1000>; + type = "passive"; + }; + hot: trip-point@2 { + temperature = <85000>; + hysteresis = <5000>; + type = "hot"; + }; + critical: trip-point@3 { + temperature = <260000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + + cooling-maps { + cpufreq_cooling_map { + trip = <&control>; + cooling-device = <&cpufreq_cool0 0 4>; + contribution = <1024>; + }; + cpucore_cooling_map { + trip = <&control>; + cooling-device = <&cpucore_cool0 0 3>; + contribution = <1024>; + }; + gpufreq_cooling_map { + trip = <&control>; + cooling-device = <&gpufreq_cool0 0 4>; + contribution = <1024>; + }; + gpucore_cooling_map { + trip = <&control>; + cooling-device = <&gpucore_cool0 0 2>; + contribution = <1024>; + }; + }; + }; + }; dwc3: dwc3@c9000000 { compatible = "synopsys, dwc3"; diff --git a/arch/arm64/boot/dts/amlogic/gxm_q200_2g.dts b/arch/arm64/boot/dts/amlogic/gxm_q200_2g.dts index 6320ea62e601..839a441cbff5 100644 --- a/arch/arm64/boot/dts/amlogic/gxm_q200_2g.dts +++ b/arch/arm64/boot/dts/amlogic/gxm_q200_2g.dts @@ -379,6 +379,139 @@ "mailbox_2"; }; + aml_sensor0: aml-sensor@0 { + compatible = "amlogic, aml-thermal"; + device_name = "thermal"; + #thermal-sensor-cells = <1>; + cooling_devices { + cpufreq_cool_cluster0 { + min_state = <1000000>; + dyn_coeff = <140>; + cluster_id = <0>; + node_name = "cpufreq_cool0"; + device_type = "cpufreq"; + }; + cpufreq_cool_cluster1 { + min_state = <500000>; + dyn_coeff = <140>; + cluster_id = <1>; + node_name = "cpufreq_cool1"; + device_type = "cpufreq"; + }; + cpucore_cool_cluster0 { + min_state = <1>; + dyn_coeff = <0>; + cluster_id = <0>; + node_name = "cpucore_cool0"; + device_type = "cpucore"; + }; + cpucore_cool_cluster1 { + min_state = <1>; + dyn_coeff = <0>; + cluster_id = <1>; + node_name = "cpucore_cool1"; + device_type = "cpucore"; + }; + gpufreq_cool { + min_state = <400>; + dyn_coeff = <437>; + cluster_id = <0>; + node_name = "gpufreq_cool0"; + device_type = "gpufreq"; + }; + gpucore_cool { + min_state = <1>; + dyn_coeff = <0>; + cluster_id = <0>; + node_name = "gpucore_cool0"; + device_type = "gpucore"; + }; + }; + cpufreq_cool0:cpufreq_cool0 { + #cooling-cells = <2>; /* min followed by max */ + }; + cpufreq_cool1:cpufreq_cool1 { + #cooling-cells = <2>; /* min followed by max */ + }; + cpucore_cool0:cpucore_cool0 { + #cooling-cells = <2>; /* min followed by max */ + }; + cpucore_cool1:cpucore_cool1 { + #cooling-cells = <2>; /* min followed by max */ + }; + gpufreq_cool0:gpufreq_cool0 { + #cooling-cells = <2>; /* min followed by max */ + }; + gpucore_cool0:gpucore_cool0 { + #cooling-cells = <2>; /* min followed by max */ + }; + }; + thermal-zones { + soc_thermal { + polling-delay = <1000>; + polling-delay-passive = <100>; + sustainable-power = <2450>; + + thermal-sensors = <&aml_sensor0 3>; + + trips { + switch_on: trip-point@0 { + temperature = <70000>; + hysteresis = <1000>; + type = "passive"; + }; + control: trip-point@1 { + temperature = <80000>; + hysteresis = <1000>; + type = "passive"; + }; + hot: trip-point@2 { + temperature = <85000>; + hysteresis = <5000>; + type = "hot"; + }; + critical: trip-point@3 { + temperature = <260000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + + cooling-maps { + cpufreq_cooling_map0 { + trip = <&control>; + cooling-device = <&cpufreq_cool0 0 4>; + contribution = <1024>; + }; + cpufreq_cooling_map1 { + trip = <&control>; + cooling-device = <&cpufreq_cool1 0 4>; + contribution = <1024>; + }; + cpucore_cooling_map0 { + trip = <&control>; + cooling-device = <&cpucore_cool0 0 3>; + contribution = <1024>; + }; + cpucore_cooling_map1 { + trip = <&control>; + cooling-device = <&cpucore_cool1 0 3>; + contribution = <1024>; + }; + gpufreq_cooling_map { + trip = <&control>; + cooling-device = <&gpufreq_cool0 0 4>; + contribution = <1024>; + }; + gpucore_cooling_map { + trip = <&control>; + cooling-device = <&gpucore_cool0 0 2>; + contribution = <1024>; + }; + }; + }; + }; + dwc3: dwc3@c9000000 { compatible = "synopsys, dwc3"; reg = <0x0 0xc9000000 0x0 0x100000>; diff --git a/arch/arm64/boot/dts/amlogic/gxm_skt.dts b/arch/arm64/boot/dts/amlogic/gxm_skt.dts index e35d27e32965..f4a401060149 100644 --- a/arch/arm64/boot/dts/amlogic/gxm_skt.dts +++ b/arch/arm64/boot/dts/amlogic/gxm_skt.dts @@ -358,6 +358,139 @@ "mailbox_2"; }; + aml_sensor0: aml-sensor@0 { + compatible = "amlogic, aml-thermal"; + device_name = "thermal"; + #thermal-sensor-cells = <1>; + cooling_devices { + cpufreq_cool_cluster0 { + min_state = <1000000>; + dyn_coeff = <140>; + cluster_id = <0>; + node_name = "cpufreq_cool0"; + device_type = "cpufreq"; + }; + cpufreq_cool_cluster1 { + min_state = <500000>; + dyn_coeff = <140>; + cluster_id = <1>; + node_name = "cpufreq_cool1"; + device_type = "cpufreq"; + }; + cpucore_cool_cluster0 { + min_state = <1>; + dyn_coeff = <0>; + cluster_id = <0>; + node_name = "cpucore_cool0"; + device_type = "cpucore"; + }; + cpucore_cool_cluster1 { + min_state = <1>; + dyn_coeff = <0>; + cluster_id = <1>; + node_name = "cpucore_cool1"; + device_type = "cpucore"; + }; + gpufreq_cool { + min_state = <400>; + dyn_coeff = <437>; + cluster_id = <0>; + node_name = "gpufreq_cool0"; + device_type = "gpufreq"; + }; + gpucore_cool { + min_state = <1>; + dyn_coeff = <0>; + cluster_id = <0>; + node_name = "gpucore_cool0"; + device_type = "gpucore"; + }; + }; + cpufreq_cool0:cpufreq_cool0 { + #cooling-cells = <2>; /* min followed by max */ + }; + cpufreq_cool1:cpufreq_cool1 { + #cooling-cells = <2>; /* min followed by max */ + }; + cpucore_cool0:cpucore_cool0 { + #cooling-cells = <2>; /* min followed by max */ + }; + cpucore_cool1:cpucore_cool1 { + #cooling-cells = <2>; /* min followed by max */ + }; + gpufreq_cool0:gpufreq_cool0 { + #cooling-cells = <2>; /* min followed by max */ + }; + gpucore_cool0:gpucore_cool0 { + #cooling-cells = <2>; /* min followed by max */ + }; + }; + thermal-zones { + soc_thermal { + polling-delay = <1000>; + polling-delay-passive = <100>; + sustainable-power = <2450>; + + thermal-sensors = <&aml_sensor0 3>; + + trips { + switch_on: trip-point@0 { + temperature = <70000>; + hysteresis = <1000>; + type = "passive"; + }; + control: trip-point@1 { + temperature = <80000>; + hysteresis = <1000>; + type = "passive"; + }; + hot: trip-point@2 { + temperature = <85000>; + hysteresis = <5000>; + type = "hot"; + }; + critical: trip-point@3 { + temperature = <260000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + + cooling-maps { + cpufreq_cooling_map0 { + trip = <&control>; + cooling-device = <&cpufreq_cool0 0 4>; + contribution = <1024>; + }; + cpufreq_cooling_map1 { + trip = <&control>; + cooling-device = <&cpufreq_cool1 0 4>; + contribution = <1024>; + }; + cpucore_cooling_map0 { + trip = <&control>; + cooling-device = <&cpucore_cool0 0 3>; + contribution = <1024>; + }; + cpucore_cooling_map1 { + trip = <&control>; + cooling-device = <&cpucore_cool1 0 3>; + contribution = <1024>; + }; + gpufreq_cooling_map { + trip = <&control>; + cooling-device = <&gpufreq_cool0 0 4>; + contribution = <1024>; + }; + gpucore_cooling_map { + trip = <&control>; + cooling-device = <&gpucore_cool0 0 2>; + contribution = <1024>; + }; + }; + }; + }; + vout { compatible = "amlogic, vout"; dev_name = "vout"; diff --git a/arch/arm64/boot/dts/amlogic/mesongxl.dtsi b/arch/arm64/boot/dts/amlogic/mesongxl.dtsi index db97b498b87a..f16b2486e7fc 100644 --- a/arch/arm64/boot/dts/amlogic/mesongxl.dtsi +++ b/arch/arm64/boot/dts/amlogic/mesongxl.dtsi @@ -31,7 +31,6 @@ cpu-map { cluster0:cluster0 { - #cooling-cells = <2>; /* min followed by max */ core0 { cpu = <&CPU0>; }; diff --git a/arch/arm64/boot/dts/amlogic/mesongxm.dtsi b/arch/arm64/boot/dts/amlogic/mesongxm.dtsi index 2a0a6592b610..b575b6873d28 100644 --- a/arch/arm64/boot/dts/amlogic/mesongxm.dtsi +++ b/arch/arm64/boot/dts/amlogic/mesongxm.dtsi @@ -31,7 +31,6 @@ cpu-map { cluster0:cluster0 { - #cooling-cells = <2>; /* min followed by max */ core0 { cpu = <&CPU0>; }; @@ -46,7 +45,6 @@ }; }; cluster1:cluster1 { - #cooling-cells = <2>; /* min followed by max */ core0 { cpu = <&CPU4>; }; diff --git a/arch/arm64/configs/meson64_defconfig b/arch/arm64/configs/meson64_defconfig index 9496ef593df1..132236a2370f 100644 --- a/arch/arm64/configs/meson64_defconfig +++ b/arch/arm64/configs/meson64_defconfig @@ -7,11 +7,11 @@ CONFIG_BSD_PROCESS_ACCT=y CONFIG_IKCONFIG=y CONFIG_LOG_BUF_SHIFT=19 CONFIG_CGROUPS=y -CONFIG_CGROUP_SCHED=y -CONFIG_RT_GROUP_SCHED=y +CONFIG_CGROUP_DEBUG=y CONFIG_CGROUP_FREEZER=y CONFIG_CGROUP_CPUACCT=y -CONFIG_CGROUP_DEBUG=y +CONFIG_CGROUP_SCHED=y +CONFIG_RT_GROUP_SCHED=y CONFIG_BLK_DEV_INITRD=y CONFIG_EMBEDDED=y # CONFIG_COMPAT_BRK is not set @@ -187,7 +187,6 @@ CONFIG_AMLOGIC_MESON_REMOTE=y CONFIG_AMLOGIC_EFUSE=y CONFIG_AMLOGIC_REBOOT=y CONFIG_AMLOGIC_INTERNAL_PHY=y -CONFIG_AMLOGIC_AO_CEC=y CONFIG_AMLOGIC_CPU_HOTPLUG=y CONFIG_AMLOGIC_PWM=y CONFIG_AMLOGIC_MEDIA_ENABLE=y @@ -218,8 +217,13 @@ CONFIG_AMLOGIC_MEDIA_VIUIN=y CONFIG_AMLOGIC_MMC=y CONFIG_AMLOGIC_VRTC=y CONFIG_AMLOGIC_SMARTCARD=y +CONFIG_AMLOGIC_AO_CEC=y CONFIG_AMLOGIC_SECURITY_KEY=y CONFIG_AMLOGIC_KEY_MANAGE=y +CONFIG_AMLOGIC_TEMP_SENSOR=y +CONFIG_AMLOGIC_CPUCORE_THERMAL=y +CONFIG_AMLOGIC_GPU_THERMAL=y +CONFIG_AMLOGIC_GPUCORE_THERMAL=y CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" CONFIG_DEVTMPFS=y CONFIG_DEVTMPFS_MOUNT=y diff --git a/arch/arm64/include/asm/topology.h b/arch/arm64/include/asm/topology.h index 7ec84d0191c8..3f38a025ed64 100644 --- a/arch/arm64/include/asm/topology.h +++ b/arch/arm64/include/asm/topology.h @@ -18,6 +18,7 @@ extern struct cpu_topology cpu_topology[NR_CPUS]; #define topology_core_cpumask(cpu) (&cpu_topology[cpu].core_sibling) #define topology_sibling_cpumask(cpu) (&cpu_topology[cpu].thread_sibling) +#define mc_capable() (cpu_topology[0].cluster_id != -1) void init_cpu_topology(void); void store_cpu_topology(unsigned int cpuid); const struct cpumask *cpu_coregroup_mask(int cpu); diff --git a/drivers/amlogic/Kconfig b/drivers/amlogic/Kconfig index 3fb882ab4e6e..5881b72f1ae0 100644 --- a/drivers/amlogic/Kconfig +++ b/drivers/amlogic/Kconfig @@ -63,5 +63,7 @@ source "drivers/amlogic/securitykey/Kconfig" source "drivers/amlogic/key_manage/Kconfig" +source "drivers/amlogic/thermal/Kconfig" + endmenu endif diff --git a/drivers/amlogic/Makefile b/drivers/amlogic/Makefile index ec2f084bf7c0..720c537fd1cc 100644 --- a/drivers/amlogic/Makefile +++ b/drivers/amlogic/Makefile @@ -59,3 +59,5 @@ obj-$(CONFIG_AMLOGIC_SECURITY_KEY) += securitykey/ obj-$(CONFIG_AMLOGIC_KEY_MANAGE) += key_manage/ +obj-$(CONFIG_AMLOGIC_TEMP_SENSOR) += thermal/ + diff --git a/drivers/amlogic/thermal/Kconfig b/drivers/amlogic/thermal/Kconfig new file mode 100644 index 000000000000..861cf1777e68 --- /dev/null +++ b/drivers/amlogic/thermal/Kconfig @@ -0,0 +1,52 @@ +# +# Amlogic temperature sensor configuration +# + +menu "Amlogic temperature sensor" + +config AMLOGIC_TEMP_SENSOR + bool "Amlgoic temperature sensor Support" + default n + ---help--- + Thermal sensor low level support for thermal + This driver parse thermal config data from device tree files. + And register each cooling device for thermal framework. + + If you wan this driver, selest it. + +config AMLOGIC_CPUCORE_THERMAL + bool "generic cpu core cooling support" + depends on AMLOGIC_TEMP_SENSOR + default n + help + This implements the generic cpu cooling mechanism through reduce cpu core numbers. + This will be useful for platforms using the generic thermal interface + and not the ACPI interface. + + If you want this support, you should say Y here. + +config AMLOGIC_GPU_THERMAL + bool "generic gpu cooling support" + depends on AMLOGIC_TEMP_SENSOR + default n + help + This implements the generic gpu cooling mechanism through frequency + reduction. An ACPI version of this already exists + (drivers/acpi/processor_thermal.c). + This will be useful for platforms using the generic thermal interface + and not the ACPI interface. + + If you want this support, you should say Y here. + +config AMLOGIC_GPUCORE_THERMAL + bool "generic gpu core cooling support" + depends on AMLOGIC_TEMP_SENSOR + default n + help + This implements the generic cpu cooling mechanism through reduce cpu core numbers. + This will be useful for platforms using the generic thermal interface + and not the ACPI interface. + + If you want this support, you should say Y here. + +endmenu diff --git a/drivers/amlogic/thermal/Makefile b/drivers/amlogic/thermal/Makefile new file mode 100644 index 000000000000..79cd77a8991e --- /dev/null +++ b/drivers/amlogic/thermal/Makefile @@ -0,0 +1,6 @@ + +obj-$(CONFIG_AMLOGIC_TEMP_SENSOR) += aml_thermal_hw.o +obj-$(CONFIG_AMLOGIC_CPUCORE_THERMAL) += cpucore_cooling.o +obj-$(CONFIG_AMLOGIC_GPU_THERMAL) += gpu_cooling.o +obj-$(CONFIG_AMLOGIC_GPUCORE_THERMAL) += gpucore_cooling.o + diff --git a/drivers/amlogic/thermal/aml_thermal_hw.c b/drivers/amlogic/thermal/aml_thermal_hw.c new file mode 100644 index 000000000000..673fe0915e0a --- /dev/null +++ b/drivers/amlogic/thermal/aml_thermal_hw.c @@ -0,0 +1,406 @@ +/* + * drivers/amlogic/thermal/aml_thermal_hw.c + * + * Copyright (C) 2016 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define NOT_WRITE_EFUSE 0x0 +#define EFUEE_PRIVATE 0x4 +#define EFUSE_OPS 0xa + +enum cluster_type { + CLUSTER_BIG = 0, + CLUSTER_LITTLE, + NUM_CLUSTERS +}; + +enum cool_dev_type { + COOL_DEV_TYPE_CPU_FREQ = 0, + COOL_DEV_TYPE_CPU_CORE, + COOL_DEV_TYPE_GPU_FREQ, + COOL_DEV_TYPE_GPU_CORE, + COOL_DEV_TYPE_MAX +}; + +struct cool_dev { + int min_state; + int coeff; + int cluster_id; + char *device_type; + struct device_node *np; + struct thermal_cooling_device *cooling_dev; +}; + +struct aml_thermal_sensor { + int chip_trimmed; + int cool_dev_num; + int min_exist; + struct cpumask mask[NUM_CLUSTERS]; + struct cool_dev *cool_devs; + struct thermal_zone_device *tzd; +}; + +static struct aml_thermal_sensor soc_sensor; + +int thermal_firmware_init(void) +{ + int ret; + + ret = scpi_get_sensor("aml_thermal"); + soc_sensor.chip_trimmed = ret < 0 ? 0 : 1; + return ret; +} +EXPORT_SYMBOL(thermal_firmware_init); + +int get_cpu_temp(void) +{ + unsigned int val = 0; + + if (soc_sensor.chip_trimmed) { /* only supported trimmed chips */ + if (scpi_get_sensor_value(0, &val) < 0) + return -1000; + return val; + } else { + return -1000; + } +} +EXPORT_SYMBOL(get_cpu_temp); + +static int get_cur_temp(void *data, int *temp) +{ + int val; + + val = get_cpu_temp(); + if (val == -1000) + return -EINVAL; + + *temp = val * 1000; + + return 0; +} + +static int get_cool_dev_type(char *type) +{ + if (!strcmp(type, "cpufreq")) + return COOL_DEV_TYPE_CPU_FREQ; + if (!strcmp(type, "cpucore")) + return COOL_DEV_TYPE_CPU_CORE; + if (!strcmp(type, "gpufreq")) + return COOL_DEV_TYPE_GPU_FREQ; + if (!strcmp(type, "gpucore")) + return COOL_DEV_TYPE_GPU_CORE; + return COOL_DEV_TYPE_MAX; +} + +static struct cool_dev *get_cool_dev_by_node(struct device_node *np) +{ + int i; + struct cool_dev *dev; + + if (!np) + return NULL; + for (i = 0; i < soc_sensor.cool_dev_num; i++) { + dev = &soc_sensor.cool_devs[i]; + if (dev->np == np) + return dev; + } + return NULL; +} + +int aml_thermal_min_update(struct thermal_cooling_device *cdev) +{ + struct gpufreq_cooling_device *gf_cdev; + struct gpucore_cooling_device *gc_cdev; + struct cool_dev *cool; + long min_state; + int i; + int cpu, c_id; + + cool = get_cool_dev_by_node(cdev->np); + if (!cool) + return -ENODEV; + + if (cool->cooling_dev == NULL) + cool->cooling_dev = cdev; + + if (cool->min_state == 0) + return 0; + + switch (get_cool_dev_type(cool->device_type)) { + case COOL_DEV_TYPE_CPU_CORE: + /* TODO: cluster ID */ + cool->cooling_dev->ops->get_max_state(cdev, &min_state); + min_state = min_state - cool->min_state; + break; + + case COOL_DEV_TYPE_CPU_FREQ: + for_each_possible_cpu(cpu) { + if (mc_capable()) + c_id = topology_physical_package_id(cpu); + else + c_id = 0; /* force cluster 0 if no MC */ + if (c_id == cool->cluster_id) + break; + } + min_state = cpufreq_cooling_get_level(cpu, cool->min_state); + break; + + case COOL_DEV_TYPE_GPU_CORE: + gc_cdev = (struct gpucore_cooling_device *)cdev->devdata; + cdev->ops->get_max_state(cdev, &min_state); + min_state = min_state - cool->min_state; + break; + + case COOL_DEV_TYPE_GPU_FREQ: + gf_cdev = (struct gpufreq_cooling_device *)cdev->devdata; + min_state = gf_cdev->get_gpu_freq_level(cool->min_state); + break; + + default: + return -EINVAL; + } + + for (i = 0; i < soc_sensor.tzd->trips; i++) + thermal_set_upper(soc_sensor.tzd, cdev, i, min_state); + + return 0; +} +EXPORT_SYMBOL(aml_thermal_min_update); + +int set_cur_mode(struct thermal_zone_device *tzd, enum thermal_device_mode mode) +{ + int i, ret = 0; + struct thermal_cooling_device *cdev; + + /* + * each cooling device should return to max state if thermal is disalbed + */ + if (mode != THERMAL_DEVICE_DISABLED) + return 0; + + for (i = 0; i < soc_sensor.cool_dev_num; i++) { + cdev = soc_sensor.cool_devs[i].cooling_dev; + if (cdev) + ret |= cdev->ops->set_cur_state(cdev, 0); + } + return ret; +} + +static struct thermal_zone_of_device_ops aml_thermal_ops = { + .get_temp = get_cur_temp, + .set_mode = set_cur_mode, +}; + +static int register_cool_dev(struct cool_dev *cool) +{ + int pp; + int id = cool->cluster_id; + struct cpumask *mask; + + switch (get_cool_dev_type(cool->device_type)) { + case COOL_DEV_TYPE_CPU_CORE: + cool->cooling_dev = cpucore_cooling_register(cool->np, + cool->cluster_id); + break; + + case COOL_DEV_TYPE_CPU_FREQ: + mask = &soc_sensor.mask[id]; + cool->cooling_dev = of_cpufreq_power_cooling_register(cool->np, + mask, + cool->coeff, + NULL); + break; + + /* GPU is KO, just save these parameters */ + case COOL_DEV_TYPE_GPU_FREQ: + if (of_property_read_u32(cool->np, "num_of_pp", &pp)) + pr_err("thermal: read num_of_pp failed\n"); + save_gpu_cool_para(cool->coeff, cool->np, pp); + return 0; + + case COOL_DEV_TYPE_GPU_CORE: + save_gpucore_thermal_para(cool->np); + return 0; + + default: + pr_err("thermal: unknown type:%s\n", cool->device_type); + return -EINVAL; + } + + if (IS_ERR(cool->cooling_dev)) { + pr_err("thermal: register %s failed\n", cool->device_type); + return -EINVAL; + } + return 0; +} + +static int parse_cool_device(struct device_node *np) +{ + int i, temp, ret = 0; + struct cool_dev *cool; + struct device_node *node, *child; + const char *str; + + child = of_get_next_child(np, NULL); + for (i = 0; i < soc_sensor.cool_dev_num; i++) { + cool = &soc_sensor.cool_devs[i]; + if (child == NULL) + break; + if (of_property_read_u32(child, "min_state", &temp)) + pr_err("thermal: read min_state failed\n"); + else + cool->min_state = temp; + + if (of_property_read_u32(child, "dyn_coeff", &temp)) + pr_err("thermal: read dyn_coeff failed\n"); + else + cool->coeff = temp; + + if (of_property_read_u32(child, "cluster_id", &temp)) + pr_err("thermal: read cluster_id failed\n"); + else + cool->cluster_id = temp; + + if (of_property_read_string(child, "device_type", &str)) + pr_err("thermal: read device_type failed\n"); + else + cool->device_type = (char *)str; + + if (of_property_read_string(child, "node_name", &str)) + pr_err("thermal: read node_name failed\n"); + else { + node = of_find_node_by_name(NULL, str); + if (!node) + pr_err("thermal: can't find node\n"); + cool->np = node; + } + if (cool->np) + ret += register_cool_dev(cool); + child = of_get_next_child(np, child); + } + return ret; +} + +static int aml_thermal_probe(struct platform_device *pdev) +{ + int cpu, i, c_id; + struct device_node *np, *child; + struct cool_dev *cool; + struct cpufreq_policy *policy; + + memset(&soc_sensor, 0, sizeof(struct aml_thermal_sensor)); + policy = cpufreq_cpu_get(0); + if (!policy || !policy->freq_table) { + dev_info(&pdev->dev, + "Frequency policy not init. Deferring probe...\n"); + return -EPROBE_DEFER; + } + + if (thermal_firmware_init() < 0) { + dev_err(&pdev->dev, "chip is not trimmed, disable thermal\n"); + return -EINVAL; + } + + for_each_possible_cpu(cpu) { + if (mc_capable()) + c_id = topology_physical_package_id(cpu); + else + c_id = CLUSTER_BIG; /* Always cluster 0 if no mc */ + if (c_id > NUM_CLUSTERS) { + pr_err("Cluster id: %d > %d\n", c_id, NUM_CLUSTERS); + return -EINVAL; + } + cpumask_set_cpu(cpu, &soc_sensor.mask[c_id]); + } + + np = pdev->dev.of_node; + child = of_get_child_by_name(np, "cooling_devices"); + if (child == NULL) { + pr_err("thermal: can't found cooling_devices\n"); + return -EINVAL; + } + soc_sensor.cool_dev_num = of_get_child_count(child); + i = sizeof(struct cool_dev) * soc_sensor.cool_dev_num; + soc_sensor.cool_devs = kzalloc(i, GFP_KERNEL); + if (soc_sensor.cool_devs == NULL) { + pr_err("thermal: alloc mem failed\n"); + return -ENOMEM; + } + + if (parse_cool_device(child)) + return -EINVAL; + + soc_sensor.tzd = thermal_zone_of_sensor_register(&pdev->dev, + 3, + &soc_sensor, + &aml_thermal_ops); + + if (IS_ERR(soc_sensor.tzd)) { + dev_warn(&pdev->dev, "Error registering sensor: %p\n", + soc_sensor.tzd); + return PTR_ERR(soc_sensor.tzd); + } + + /* update min state for each device */ + for (i = 0; i < soc_sensor.cool_dev_num; i++) { + cool = &soc_sensor.cool_devs[i]; + if (cool->cooling_dev) + aml_thermal_min_update(cool->cooling_dev); + } + thermal_zone_device_update(soc_sensor.tzd, THERMAL_EVENT_UNSPECIFIED); + + return 0; +} + +static int aml_thermal_remove(struct platform_device *pdev) +{ + kfree(soc_sensor.cool_devs); + return 0; +} + +static const struct of_device_id aml_thermal_of_match[] = { + { .compatible = "amlogic, aml-thermal" }, + {}, +}; + +static struct platform_driver aml_thermal_platdrv = { + .driver = { + .name = "aml-thermal", + .owner = THIS_MODULE, + .of_match_table = aml_thermal_of_match, + }, + .probe = aml_thermal_probe, + .remove = aml_thermal_remove, +}; + + +static int __init aml_thermal_platdrv_init(void) +{ + return platform_driver_register(&(aml_thermal_platdrv)); +} +late_initcall(aml_thermal_platdrv_init); diff --git a/drivers/amlogic/thermal/cpucore_cooling.c b/drivers/amlogic/thermal/cpucore_cooling.c new file mode 100644 index 000000000000..93d4b4fa2f43 --- /dev/null +++ b/drivers/amlogic/thermal/cpucore_cooling.c @@ -0,0 +1,314 @@ +/* + * drivers/amlogic/thermal/cpucore_cooling.c + * + * Copyright (C) 2016 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * struct cpucore_cooling_device - data for cooling device with cpucore + * @id: unique integer value corresponding to each cpucore_cooling_device + * registered. + * @cool_dev: thermal_cooling_device pointer to keep track of the + * registered cooling device. + * @cpucore_state: integer value representing the current state of cpucore + * cooling devices. + * @cpucore_val: integer value representing the absolute value of the clipped + * frequency. + * @allowed_cpus: all the cpus involved for this cpucore_cooling_device. + * + * This structure is required for keeping information of each + * cpucore_cooling_device registered. In order to prevent corruption of this a + * mutex lock cooling_cpucore_lock is used. + */ + +static DEFINE_IDR(cpucore_idr); +static DEFINE_MUTEX(cooling_cpucore_lock); +static LIST_HEAD(cpucore_dev_list); + +/* notify_table passes value to the cpucore_ADJUST callback function. */ +#define NOTIFY_INVALID NULL + +/** + * get_idr - function to get a unique id. + * @idr: struct idr * handle used to create a id. + * @id: int * value generated by this function. + * + * This function will populate @id with an unique + * id, using the idr API. + * + * Return: 0 on success, an error code on failure. + */ +static int get_idr(struct idr *idr, int *id) +{ + int ret; + + mutex_lock(&cooling_cpucore_lock); + ret = idr_alloc(idr, NULL, 0, 0, GFP_KERNEL); + mutex_unlock(&cooling_cpucore_lock); + if (unlikely(ret < 0)) + return ret; + *id = ret; + + return 0; +} + +/** + * release_idr - function to free the unique id. + * @idr: struct idr * handle used for creating the id. + * @id: int value representing the unique id. + */ +static void release_idr(struct idr *idr, int id) +{ + mutex_lock(&cooling_cpucore_lock); + idr_remove(idr, id); + mutex_unlock(&cooling_cpucore_lock); +} + +/* cpucore cooling device callback functions are defined below */ + +/** + * cpucore_get_max_state - callback function to get the max cooling state. + * @cdev: thermal cooling device pointer. + * @state: fill this variable with the max cooling state. + * + * Callback for the thermal cooling device to return the cpucore + * max cooling state. + * + * Return: 0 on success, an error code otherwise. + */ +static int cpucore_get_max_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + struct cpucore_cooling_device *cpucore_device = cdev->devdata; + *state = cpucore_device->max_cpu_core_num; + pr_debug("max cpu core=%ld\n", *state); + return 0; +} + +/** + * cpucore_get_cur_state - callback function to get the current cooling state. + * @cdev: thermal cooling device pointer. + * @state: fill this variable with the current cooling state. + * + * Callback for the thermal cooling device to return the cpucore + * current cooling state. + * + * Return: 0 on success, an error code otherwise. + */ +static int cpucore_get_cur_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + struct cpucore_cooling_device *cpucore_device = cdev->devdata; + *state = cpucore_device->cpucore_state; + pr_debug("current state=%ld\n", *state); + return 0; +} + +/** + * cpucore_set_cur_state - callback function to set the current cooling state. + * @cdev: thermal cooling device pointer. + * @state: set this variable to the current cooling state. + * + * Callback for the thermal cooling device to change the cpucore + * current cooling state. + * + * Return: 0 on success, an error code otherwise. + */ +static int cpucore_set_cur_state(struct thermal_cooling_device *cdev, + unsigned long state) +{ + struct cpucore_cooling_device *cpucore_device = cdev->devdata; + int set_max_num, id; + + mutex_lock(&cooling_cpucore_lock); + if (cpucore_device->stop_flag) { + mutex_unlock(&cooling_cpucore_lock); + return 0; + } + if ((state & CPU_STOP) == CPU_STOP) { + cpucore_device->stop_flag = 1; + state = state&(~CPU_STOP); + } + mutex_unlock(&cooling_cpucore_lock); + if (cpucore_device->max_cpu_core_num - state > 0) { + cpucore_device->cpucore_state = state; + set_max_num = cpucore_device->max_cpu_core_num - state; + id = cpucore_device->cluster_id; + pr_debug("set max cpu num=%d,state=%ld\n", set_max_num, state); + cpufreq_set_max_cpu_num(set_max_num, id); + } + + return 0; +} + +/* + * Simple mathematics model for cpu core power: + * just for ipa hook, nothing to do; + */ +static int cpucore_get_requested_power(struct thermal_cooling_device *cdev, + struct thermal_zone_device *tz, + u32 *power) +{ + *power = 0; + + return 0; +} + +static int cpucore_state2power(struct thermal_cooling_device *cdev, + struct thermal_zone_device *tz, + unsigned long state, u32 *power) +{ + *power = 0; + + return 0; +} + +static int cpucore_power2state(struct thermal_cooling_device *cdev, + struct thermal_zone_device *tz, u32 power, + unsigned long *state) +{ + cdev->ops->get_cur_state(cdev, state); + return 0; +} + +static int cpucore_notify_state(struct thermal_cooling_device *cdev, + struct thermal_zone_device *tz, + enum thermal_trip_type type) +{ + unsigned long cur_state, ins_upper; + long upper = -1; + int i; + + switch (type) { + case THERMAL_TRIP_HOT: + for (i = 0; i < tz->trips; i++) { + ins_upper = thermal_get_upper(tz, cdev, i); + if (ins_upper > upper) + upper = ins_upper; + } + cur_state = tz->hot_step; + /* do not exceed levels */ + if (upper != -1 && cur_state > upper) + cur_state = upper; + if (cur_state < 0) + cur_state = 0; + pr_debug("%s, cur_state:%ld, upper:%ld, step:%d\n", + __func__, cur_state, upper, tz->hot_step); + cdev->ops->set_cur_state(cdev, cur_state); + break; + default: + break; + } + return 0; +} + + +/* Bind cpucore callbacks to thermal cooling device ops */ +static struct thermal_cooling_device_ops const cpucore_cooling_ops = { + .get_max_state = cpucore_get_max_state, + .get_cur_state = cpucore_get_cur_state, + .set_cur_state = cpucore_set_cur_state, + .state2power = cpucore_state2power, + .power2state = cpucore_power2state, + .notify_state = cpucore_notify_state, + .get_requested_power = cpucore_get_requested_power, +}; + +/** + * cpucore_cooling_register - function to create cpucore cooling device. + * + * This interface function registers the cpucore cooling device with the name + * "thermal-cpucore-%x". This api can support multiple instances of cpucore + * cooling devices. + * + * Return: a valid struct thermal_cooling_device pointer on success, + * on failure, it returns a corresponding ERR_PTR(). + */ +struct thermal_cooling_device * +cpucore_cooling_register(struct device_node *np, int cluster_id) +{ + struct thermal_cooling_device *cool_dev; + struct cpucore_cooling_device *cpucore_dev = NULL; + char dev_name[THERMAL_NAME_LENGTH]; + int ret = 0, cpu; + int cores = 0; + + cpucore_dev = kzalloc(sizeof(struct cpucore_cooling_device), + GFP_KERNEL); + if (!cpucore_dev) + return ERR_PTR(-ENOMEM); + + ret = get_idr(&cpucore_idr, &cpucore_dev->id); + if (ret) { + kfree(cpucore_dev); + return ERR_PTR(-EINVAL); + } + + if (mc_capable()) { + for_each_possible_cpu(cpu) { + if (topology_physical_package_id(cpu) == cluster_id) + cores++; + } + } else { + cores = num_possible_cpus(); + } + cpucore_dev->max_cpu_core_num = cores; + pr_info("%s, max_cpu_core_num:%d\n", __func__, cores); + snprintf(dev_name, sizeof(dev_name), "thermal-cpucore-%d", + cpucore_dev->id); + + cool_dev = thermal_of_cooling_device_register(np, dev_name, cpucore_dev, + &cpucore_cooling_ops); + if (!cool_dev) { + release_idr(&cpucore_idr, cpucore_dev->id); + kfree(cpucore_dev); + return ERR_PTR(-EINVAL); + } + cpucore_dev->cool_dev = cool_dev; + cpucore_dev->cpucore_state = 0; + cpucore_dev->cluster_id = cluster_id; + return cool_dev; +} +EXPORT_SYMBOL_GPL(cpucore_cooling_register); + +/** + * cpucore_cooling_unregister - function to remove cpucore cooling device. + * @cdev: thermal cooling device pointer. + * + * This interface function unregisters the "thermal-cpucore-%x" cooling device. + */ +void cpucore_cooling_unregister(struct thermal_cooling_device *cdev) +{ + struct cpucore_cooling_device *cpucore_dev; + + if (!cdev) + return; + + cpucore_dev = cdev->devdata; + + thermal_cooling_device_unregister(cpucore_dev->cool_dev); + release_idr(&cpucore_idr, cpucore_dev->id); + kfree(cpucore_dev); +} +EXPORT_SYMBOL_GPL(cpucore_cooling_unregister); diff --git a/drivers/amlogic/thermal/gpu_cooling.c b/drivers/amlogic/thermal/gpu_cooling.c new file mode 100644 index 000000000000..cabcb8860b76 --- /dev/null +++ b/drivers/amlogic/thermal/gpu_cooling.c @@ -0,0 +1,342 @@ +/* + * drivers/amlogic/thermal/gpu_cooling.c + * + * Copyright (C) 2016 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include + + +static DEFINE_IDR(gpufreq_idr); +static DEFINE_MUTEX(cooling_gpufreq_lock); +static int dyn_coef = -1; +static int max_pp; +static struct device_node *np; + +/* notify_table passes value to the gpuFREQ_ADJUST callback function. */ +#define NOTIFY_INVALID NULL + +void save_gpu_cool_para(int coef, struct device_node *n, int pp) +{ + if (dyn_coef == -1 && np == NULL) { + dyn_coef = coef; + np = n; + max_pp = pp; + } +} + +/** + * get_idr - function to get a unique id. + * @idr: struct idr * handle used to create a id. + * @id: int * value generated by this function. + * + * This function will populate @id with an unique + * id, using the idr API. + * + * Return: 0 on success, an error code on failure. + */ +static int get_idr(struct idr *idr, int *id) +{ + int ret; + + mutex_lock(&cooling_gpufreq_lock); + ret = idr_alloc(idr, NULL, 0, 0, GFP_KERNEL); + mutex_unlock(&cooling_gpufreq_lock); + if (unlikely(ret < 0)) + return ret; + *id = ret; + + return 0; +} + +/** + * release_idr - function to free the unique id. + * @idr: struct idr * handle used for creating the id. + * @id: int value representing the unique id. + */ +static void release_idr(struct idr *idr, int id) +{ + mutex_lock(&cooling_gpufreq_lock); + idr_remove(idr, id); + mutex_unlock(&cooling_gpufreq_lock); +} + + +/* gpufreq cooling device callback functions are defined below */ + +/** + * gpufreq_get_max_state - callback function to get the max cooling state. + * @cdev: thermal cooling device pointer. + * @state: fill this variable with the max cooling state. + * + * Callback for the thermal cooling device to return the gpufreq + * max cooling state. + * + * Return: 0 on success, an error code otherwise. + */ +static int gpufreq_get_max_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + struct gpufreq_cooling_device *gpufreq_device = cdev->devdata; + + if (gpufreq_device->get_gpu_max_level) + *state = (unsigned long)(gpufreq_device->get_gpu_max_level()); + pr_debug("default max state=%ld\n", *state); + return 0; +} + +/** + * gpufreq_get_cur_state - callback function to get the current cooling state. + * @cdev: thermal cooling device pointer. + * @state: fill this variable with the current cooling state. + * + * Callback for the thermal cooling device to return the gpufreq + * current cooling state. + * + * Return: 0 on success, an error code otherwise. + */ +static int gpufreq_get_cur_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + struct gpufreq_cooling_device *gpufreq_device = cdev->devdata; + unsigned long max_state = 0, temp = 0; + + /* *state = gpufreq_device->gpufreq_state; */ + gpufreq_get_max_state(cdev, &max_state); + if (gpufreq_device->get_gpu_current_max_level) { + temp = gpufreq_device->get_gpu_current_max_level(); + *state = ((max_state - 1) - temp); + gpufreq_device->gpufreq_state = *state; + pr_debug("current max state=%ld\n", *state); + } else + return -EINVAL; + return 0; +} + +/** + * gpufreq_set_cur_state - callback function to set the current cooling state. + * @cdev: thermal cooling device pointer. + * @state: set this variable to the current cooling state. + * + * Callback for the thermal cooling device to change the gpufreq + * current cooling state. + * + * Return: 0 on success, an error code otherwise. + */ +static int gpufreq_set_cur_state(struct thermal_cooling_device *cdev, + unsigned long state) +{ + struct gpufreq_cooling_device *gpufreq_device = cdev->devdata; + unsigned long max_state = 0; + int ret; + + pr_debug("state=%ld,gpufreq_device->gpufreq_state=%d\n", + state, gpufreq_device->gpufreq_state); + /* if (gpufreq_device->gpufreq_state == state) */ + /* return 0; */ + gpufreq_device->gpufreq_state = state; + ret = gpufreq_get_max_state(cdev, &max_state); + state = max_state-1-state; + + pr_debug("state=%ld,gpufreq_device->gpufreq_state=%d\n", + state, gpufreq_device->gpufreq_state); + if (state >= 0 && state <= max_state) { + if (gpufreq_device->set_gpu_freq_idx) + gpufreq_device->set_gpu_freq_idx((unsigned int)state); + } + return 0; + + +} + +/* + * Simple mathematics model for gpu freq power: + * power is linear with frequent with coefficient t_c, each GPU pp has + * same frequent + * We set: online PP to n_c; + * temperature coefficient to t_c; + * power to p_c; + * current runnint frequent to F(MHz) + * We have: + * Power = n_c * t_c * F + */ +static int gpufreq_get_requested_power(struct thermal_cooling_device *cdev, + struct thermal_zone_device *tz, + u32 *power) +{ + struct gpufreq_cooling_device *gf_dev = cdev->devdata; + int freq, coef, pp; + long freq_state, max_state = 0; + int load; + + gpufreq_get_max_state(cdev, &max_state); + freq_state = (max_state - 1 - gf_dev->gpufreq_state); + freq = gf_dev->get_gpu_freq(freq_state); + pp = gf_dev->get_online_pp(); + coef = gf_dev->dyn_coeff; + load = gf_dev->get_gpu_loading(); + + *power = (freq * coef * pp) * load / 102400; + + return 0; +} + +static int gpufreq_state2power(struct thermal_cooling_device *cdev, + struct thermal_zone_device *tz, + unsigned long state, u32 *power) +{ + struct gpufreq_cooling_device *gf_dev = cdev->devdata; + int freq; + int coef = gf_dev->dyn_coeff; + int pp; + int full_power; + long max_state = 0; + + /* assume max pp */ + gpufreq_get_max_state(cdev, &max_state); + freq = gf_dev->get_gpu_freq(max_state - 1 - state); + pp = gf_dev->max_pp; + full_power = freq * coef * pp; + + /* round up */ + *power = full_power / 1024 + ((full_power & 0x3ff) ? 1 : 0); + + return 0; +} + +static int gpufreq_power2state(struct thermal_cooling_device *cdev, + struct thermal_zone_device *tz, u32 power, + unsigned long *state) +{ + struct gpufreq_cooling_device *gf_dev = cdev->devdata; + int freq; + int coef; + int pp; + long max_state = 0; + + gpufreq_get_max_state(cdev, &max_state); + pp = gf_dev->max_pp; + coef = gf_dev->dyn_coeff; + freq = (power * 1024) / (coef * pp); + + *state = gf_dev->get_gpu_freq_level(freq); + return 0; +} +/* Bind gpufreq callbacks to thermal cooling device ops */ +static struct thermal_cooling_device_ops const gpufreq_cooling_ops = { + .get_max_state = gpufreq_get_max_state, + .get_cur_state = gpufreq_get_cur_state, + .set_cur_state = gpufreq_set_cur_state, + .state2power = gpufreq_state2power, + .power2state = gpufreq_power2state, + .get_requested_power = gpufreq_get_requested_power, +}; + + +/** + * gpufreq_cooling_register - function to create gpufreq cooling device. + * @clip_gpus: gpumask of gpus where the frequency constraints will happen. + * + * This interface function registers the gpufreq cooling device with the name + * "thermal-gpufreq-%x". This api can support multiple instances of gpufreq + * cooling devices. + * + * Return: a valid struct thermal_cooling_device pointer on success, + * on failure, it returns a corresponding ERR_PTR(). + */ +struct gpufreq_cooling_device *gpufreq_cooling_alloc(void) +{ + struct gpufreq_cooling_device *gcdev; + + gcdev = kzalloc(sizeof(struct gpufreq_cooling_device), GFP_KERNEL); + if (!gcdev) + return ERR_PTR(-ENOMEM); + memset(gcdev, 0, sizeof(*gcdev)); + if (np) { + gcdev->np = np; + gcdev->dyn_coeff = dyn_coef; + gcdev->max_pp = max_pp; + } + return gcdev; +} +EXPORT_SYMBOL_GPL(gpufreq_cooling_alloc); + +int gpufreq_cooling_register(struct gpufreq_cooling_device *gpufreq_dev) +{ + struct thermal_cooling_device *cool_dev; + char dev_name[THERMAL_NAME_LENGTH]; + int ret = 0; + + ret = get_idr(&gpufreq_idr, &gpufreq_dev->id); + if (ret) { + kfree(gpufreq_dev); + return -EINVAL; + } + + snprintf(dev_name, sizeof(dev_name), "thermal-gpufreq-%d", + gpufreq_dev->id); + gpufreq_dev->gpufreq_state = 0; + + cool_dev = thermal_of_cooling_device_register(gpufreq_dev->np, + dev_name, gpufreq_dev, + &gpufreq_cooling_ops); + if (!cool_dev) { + release_idr(&gpufreq_idr, gpufreq_dev->id); + kfree(gpufreq_dev); + return -EINVAL; + } + gpufreq_dev->cool_dev = cool_dev; + + return 0; +} +EXPORT_SYMBOL_GPL(gpufreq_cooling_register); + +/** + * gpufreq_cooling_unregister - function to remove gpufreq cooling device. + * @cdev: thermal cooling device pointer. + * + * This interface function unregisters the "thermal-gpufreq-%x" cooling device. + */ +void gpufreq_cooling_unregister(struct thermal_cooling_device *cdev) +{ + struct gpufreq_cooling_device *gpufreq_dev; + + if (!cdev) + return; + + gpufreq_dev = cdev->devdata; + + thermal_cooling_device_unregister(gpufreq_dev->cool_dev); + release_idr(&gpufreq_idr, gpufreq_dev->id); + kfree(gpufreq_dev); +} +EXPORT_SYMBOL_GPL(gpufreq_cooling_unregister); + +unsigned int (*gpu_freq_callback)(void) = NULL; +EXPORT_SYMBOL(gpu_freq_callback); + +int register_gpu_freq_info(unsigned int (*fun)(void)) +{ + if (fun) + gpu_freq_callback = fun; + + return 0; +} +EXPORT_SYMBOL(register_gpu_freq_info); diff --git a/drivers/amlogic/thermal/gpucore_cooling.c b/drivers/amlogic/thermal/gpucore_cooling.c new file mode 100644 index 000000000000..fd378441c1ca --- /dev/null +++ b/drivers/amlogic/thermal/gpucore_cooling.c @@ -0,0 +1,322 @@ +/* + * drivers/amlogic/thermal/gpucore_cooling.c + * + * Copyright (C) 2016 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * struct gpucore_cooling_device - data for cooling device with gpucore + * @id: unique integer value corresponding to each gpucore_cooling_device + * registered. + * @cool_dev: thermal_cooling_device pointer to keep track of the + * registered cooling device. + * @gpucore_state: integer value representing the current state of gpucore + * cooling devices. + * @gpucore_val: integer value representing the absolute value of the clipped + * frequency. + * @allowed_cpus: all the cpus involved for this gpucore_cooling_device. + * + * This structure is required for keeping information of each + * gpucore_cooling_device registered. In order to prevent corruption of this a + * mutex lock cooling_gpucore_lock is used. + */ + +static DEFINE_IDR(gpucore_idr); +static DEFINE_MUTEX(cooling_gpucore_lock); + +/* notify_table passes value to the gpucore_ADJUST callback function. */ +#define NOTIFY_INVALID NULL + +static struct device_node *np; +static int save_flag = -1; + +void save_gpucore_thermal_para(struct device_node *n) +{ + if (!n) + return; + + if (save_flag == -1) { + save_flag = 1; + np = n; + } +} + +/** + * get_idr - function to get a unique id. + * @idr: struct idr * handle used to create a id. + * @id: int * value generated by this function. + * + * This function will populate @id with an unique + * id, using the idr API. + * + * Return: 0 on success, an error code on failure. + */ +static int get_idr(struct idr *idr, int *id) +{ + int ret; + + mutex_lock(&cooling_gpucore_lock); + ret = idr_alloc(idr, NULL, 0, 0, GFP_KERNEL); + mutex_unlock(&cooling_gpucore_lock); + if (unlikely(ret < 0)) + return ret; + *id = ret; + + return 0; +} + +/** + * release_idr - function to free the unique id. + * @idr: struct idr * handle used for creating the id. + * @id: int value representing the unique id. + */ +static void release_idr(struct idr *idr, int id) +{ + mutex_lock(&cooling_gpucore_lock); + idr_remove(idr, id); + mutex_unlock(&cooling_gpucore_lock); +} + +/* gpucore cooling device callback functions are defined below */ + +/** + * gpucore_get_max_state - callback function to get the max cooling state. + * @cdev: thermal cooling device pointer. + * @state: fill this variable with the max cooling state. + * + * Callback for the thermal cooling device to return the gpucore + * max cooling state. + * + * Return: 0 on success, an error code otherwise. + */ +static int gpucore_get_max_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + struct gpucore_cooling_device *gpucore_device = cdev->devdata; + *state = gpucore_device->max_gpu_core_num; + pr_debug("max Gpu core=%ld\n", *state); + return 0; +} + +/** + * gpucore_get_cur_state - callback function to get the current cooling state. + * @cdev: thermal cooling device pointer. + * @state: fill this variable with the current cooling state. + * + * Callback for the thermal cooling device to return the gpucore + * current cooling state. + * + * Return: 0 on success, an error code otherwise. + */ +static int gpucore_get_cur_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + struct gpucore_cooling_device *gpucore_device = cdev->devdata; + + *state = gpucore_device->gpucore_state; + pr_debug("current state=%ld\n", *state); + return 0; +} + +/** + * gpucore_set_cur_state - callback function to set the current cooling state. + * @cdev: thermal cooling device pointer. + * @state: set this variable to the current cooling state. + * + * Callback for the thermal cooling device to change the gpucore + * current cooling state. + * + * Return: 0 on success, an error code otherwise. + */ +static int gpucore_set_cur_state(struct thermal_cooling_device *cdev, + unsigned long state) +{ + struct gpucore_cooling_device *gpucore_device = cdev->devdata; + int set_max_num; + + mutex_lock(&cooling_gpucore_lock); + if (gpucore_device->stop_flag) { + mutex_unlock(&cooling_gpucore_lock); + return 0; + } + if ((state & GPU_STOP) == GPU_STOP) { + gpucore_device->stop_flag = 1; + state = state&(~GPU_STOP); + } + mutex_unlock(&cooling_gpucore_lock); + set_max_num = gpucore_device->max_gpu_core_num - state; + /* pp should not be 0 */ + if (!set_max_num) + return -EINVAL; + + gpucore_device->gpucore_state = state; + gpucore_device->set_max_pp_num((unsigned int)set_max_num); + pr_debug("need set max gpu num=%d,state=%ld\n", set_max_num, state); + return 0; +} + +/* + * Simple mathematics model for gpu core power: + * just for ipa hook + */ +static int gpucore_get_requested_power(struct thermal_cooling_device *cdev, + struct thermal_zone_device *tz, + u32 *power) +{ + *power = 0; + + return 0; +} + +static int gpucore_state2power(struct thermal_cooling_device *cdev, + struct thermal_zone_device *tz, + unsigned long state, u32 *power) +{ + *power = 0; + + return 0; +} + +static int gpucore_power2state(struct thermal_cooling_device *cdev, + struct thermal_zone_device *tz, u32 power, + unsigned long *state) +{ + cdev->ops->get_cur_state(cdev, state); + return 0; +} + +static int gpucore_notify_state(struct thermal_cooling_device *cdev, + struct thermal_zone_device *tz, + enum thermal_trip_type type) +{ + unsigned long cur_state, ins_upper; + long upper = -1; + int i; + + switch (type) { + case THERMAL_TRIP_HOT: + for (i = 0; i < tz->trips; i++) { + ins_upper = thermal_get_upper(tz, cdev, i); + if (ins_upper > upper) + upper = ins_upper; + } + cur_state = tz->hot_step; + /* do not exceed levels */ + if (upper != -1 && cur_state > upper) + cur_state = upper; + if (cur_state < 0) + cur_state = 0; + pr_debug("%s, cur_state:%ld, upper:%ld, step:%d\n", + __func__, cur_state, upper, tz->hot_step); + cdev->ops->set_cur_state(cdev, cur_state); + break; + default: + break; + } + return 0; +} + +/* Bind gpucore callbacks to thermal cooling device ops */ +static struct thermal_cooling_device_ops const gpucore_cooling_ops = { + .get_max_state = gpucore_get_max_state, + .get_cur_state = gpucore_get_cur_state, + .set_cur_state = gpucore_set_cur_state, + .state2power = gpucore_state2power, + .power2state = gpucore_power2state, + .notify_state = gpucore_notify_state, + .get_requested_power = gpucore_get_requested_power, +}; + +/** + * gpucore_cooling_register - function to create gpucore cooling device. + * @clip_cpus: cpumask of cpus where the frequency constraints will happen. + * + * This interface function registers the gpucore cooling device with the name + * "thermal-gpucore-%x". This api can support multiple instances of gpucore + * cooling devices. + * + * Return: a valid struct thermal_cooling_device pointer on success, + * on failure, it returns a corresponding ERR_PTR(). + */ +struct gpucore_cooling_device *gpucore_cooling_alloc(void) +{ + struct gpucore_cooling_device *gcdev; + + gcdev = kzalloc(sizeof(struct gpucore_cooling_device), GFP_KERNEL); + if (!gcdev) + return ERR_PTR(-ENOMEM); + memset(gcdev, 0, sizeof(*gcdev)); + if (save_flag == 1) + gcdev->np = np; + return gcdev; +} +EXPORT_SYMBOL_GPL(gpucore_cooling_alloc); + +int gpucore_cooling_register(struct gpucore_cooling_device *gpucore_dev) +{ + struct thermal_cooling_device *cool_dev; + char dev_name[THERMAL_NAME_LENGTH]; + int ret = 0; + + ret = get_idr(&gpucore_idr, &gpucore_dev->id); + if (ret) { + kfree(gpucore_dev); + return -EINVAL; + } + + snprintf(dev_name, sizeof(dev_name), "thermal-gpucore-%d", + gpucore_dev->id); + + gpucore_dev->gpucore_state = 0; + cool_dev = thermal_of_cooling_device_register(gpucore_dev->np, + dev_name, gpucore_dev, &gpucore_cooling_ops); + if (!cool_dev) { + release_idr(&gpucore_idr, gpucore_dev->id); + kfree(gpucore_dev); + return -EINVAL; + } + gpucore_dev->cool_dev = cool_dev; + return 0; +} +EXPORT_SYMBOL_GPL(gpucore_cooling_register); + +/** + * gpucore_cooling_unregister - function to remove gpucore cooling device. + * @cdev: thermal cooling device pointer. + * + * This interface function unregisters the "thermal-gpucore-%x" cooling device. + */ +void gpucore_cooling_unregister(struct thermal_cooling_device *cdev) +{ + struct gpucore_cooling_device *gpucore_dev; + + if (!cdev) + return; + + gpucore_dev = cdev->devdata; + + thermal_cooling_device_unregister(gpucore_dev->cool_dev); + release_idr(&gpucore_idr, gpucore_dev->id); + kfree(gpucore_dev); +} +EXPORT_SYMBOL_GPL(gpucore_cooling_unregister); diff --git a/drivers/thermal/of-thermal.c b/drivers/thermal/of-thermal.c index d04ec3b9e5ff..e92c8863acba 100644 --- a/drivers/thermal/of-thermal.c +++ b/drivers/thermal/of-thermal.c @@ -278,11 +278,28 @@ static int of_thermal_set_mode(struct thermal_zone_device *tz, mutex_lock(&tz->lock); +#ifdef CONFIG_AMLOGIC_TEMP_SENSOR + /* passive_delay should be cleared if disabled */ + if (mode == THERMAL_DEVICE_ENABLED) { + tz->polling_delay = data->polling_delay; + tz->passive_delay = data->passive_delay; + } else { + tz->polling_delay = 0; + tz->passive_delay = 0; + } + + /* + * give opportunity that theraml device can + * do something when mode change + */ + if (data->ops && data->ops->set_mode) + data->ops->set_mode(tz, mode); +#else if (mode == THERMAL_DEVICE_ENABLED) tz->polling_delay = data->polling_delay; else tz->polling_delay = 0; - +#endif mutex_unlock(&tz->lock); data->mode = mode; @@ -381,6 +398,23 @@ static int of_thermal_get_crit_temp(struct thermal_zone_device *tz, return -EINVAL; } +#ifdef CONFIG_AMLOGIC_TEMP_SENSOR +static int of_thermal_notify(struct thermal_zone_device *tz, int trip, + enum thermal_trip_type type) +{ + struct thermal_instance *instance; + struct thermal_cooling_device *cdev; + int ret = 0; + + list_for_each_entry(instance, &tz->thermal_instances, tz_node) { + cdev = instance->cdev; + if (cdev->ops && cdev->ops->notify_state) + ret += cdev->ops->notify_state(cdev, tz, type); + } + return ret; +} +#endif + static struct thermal_zone_device_ops of_thermal_ops = { .get_mode = of_thermal_get_mode, .set_mode = of_thermal_set_mode, @@ -394,6 +428,9 @@ static struct thermal_zone_device_ops of_thermal_ops = { .bind = of_thermal_bind, .unbind = of_thermal_unbind, +#ifdef CONFIG_AMLOGIC_TEMP_SENSOR + .notify = of_thermal_notify, +#endif }; /*** sensor API ***/ @@ -973,6 +1010,9 @@ int __init of_parse_thermal_zones(void) for_each_available_child_of_node(np, child) { struct thermal_zone_device *zone; struct thermal_zone_params *tzp; + #ifdef CONFIG_AMLOGIC_TEMP_SENSOR + const char *str; + #endif int i, mask = 0; u32 prop; @@ -1000,6 +1040,11 @@ int __init of_parse_thermal_zones(void) if (!of_property_read_u32(child, "sustainable-power", &prop)) tzp->sustainable_power = prop; + #ifdef CONFIG_AMLOGIC_TEMP_SENSOR + if (!of_property_read_string(child, "policy", &str)) + strncpy(tzp->governor_name, str, THERMAL_NAME_LENGTH); + #endif + for (i = 0; i < tz->ntrips; i++) mask |= 1 << i; diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 226b0b4aced6..bacbd44245a8 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -271,6 +271,33 @@ struct thermal_instance *get_thermal_instance(struct thermal_zone_device *tz, } EXPORT_SYMBOL(get_thermal_instance); +#ifdef CONFIG_AMLOGIC_TEMP_SENSOR +void thermal_set_upper(struct thermal_zone_device *tz, + struct thermal_cooling_device *cdev, + int trip, unsigned long upper) +{ + struct thermal_instance *ins; + + ins = get_thermal_instance(tz, cdev, trip); + if (ins != NULL) + ins->upper = upper; +} +EXPORT_SYMBOL(thermal_set_upper); + +unsigned long thermal_get_upper(struct thermal_zone_device *tz, + struct thermal_cooling_device *cdev, int trip) +{ + struct thermal_instance *ins; + + ins = get_thermal_instance(tz, cdev, trip); + if (ins != NULL) + return ins->upper; + + return (-1UL); +} +EXPORT_SYMBOL(thermal_get_upper); +#endif + static void print_bind_err_msg(struct thermal_zone_device *tz, struct thermal_cooling_device *cdev, int ret) { @@ -426,11 +453,64 @@ static void handle_non_critical_trips(struct thermal_zone_device *tz, def_governor->throttle(tz, trip); } +#ifdef CONFIG_AMLOGIC_TEMP_SENSOR +static bool can_notify(struct thermal_zone_device *tz, + int trip, enum thermal_trip_type trip_type, + long trip_temp) +{ + unsigned int hyst = 0; + + if (trip_type != THERMAL_TRIP_HOT) + return false; + tz->ops->get_trip_hyst(tz, trip, &hyst); + + if (tz->temperature < 0) + return false; + + /* increase each hyst step */ + if (tz->temperature >= (trip_temp + tz->hot_step * hyst)) { + tz->hot_step++; + dev_info(&tz->device, + "temp:%d increase, hyst:%d, trip_temp:%ld, hot:%x\n", + tz->temperature, hyst, trip_temp, tz->hot_step); + return true; + } + /* reserve a step gap */ + if (tz->temperature <= (trip_temp + (tz->hot_step - 2) * hyst) && + tz->hot_step) { + tz->hot_step--; + dev_info(&tz->device, + "temp:%d decrease, hyst:%d, trip_temp:%ld, hot:%x\n", + tz->temperature, hyst, trip_temp, tz->hot_step); + return true; + } + return false; +} +#endif + static void handle_critical_trips(struct thermal_zone_device *tz, int trip, enum thermal_trip_type trip_type) { int trip_temp; +#ifdef CONFIG_AMLOGIC_TEMP_SENSOR + tz->ops->get_trip_temp(tz, trip, &trip_temp); + + trace_thermal_zone_trip(tz, trip, trip_type); + + if (can_notify(tz, trip, trip_type, trip_temp)) { + if (tz->ops->notify) + tz->ops->notify(tz, trip, trip_type); + } + + if ((trip_type == THERMAL_TRIP_CRITICAL) && + (tz->temperature >= trip_temp)) { + dev_emerg(&tz->device, + "critical temperature reached(%d C),shutting down\n", + tz->temperature / 1000); + orderly_poweroff(true); + } +#else tz->ops->get_trip_temp(tz, trip, &trip_temp); /* If we have not crossed the trip_temp, we do not care. */ @@ -448,6 +528,7 @@ static void handle_critical_trips(struct thermal_zone_device *tz, tz->temperature / 1000); orderly_poweroff(true); } +#endif } static void handle_thermal_trip(struct thermal_zone_device *tz, int trip) @@ -1150,6 +1231,10 @@ int power_actor_set_power(struct thermal_cooling_device *cdev, if (ret) return ret; +#ifdef CONFIG_AMLOGIC_TEMP_SENSOR + if (state > instance->upper) + state = instance->upper; +#endif instance->target = state; mutex_lock(&cdev->lock); cdev->updated = false; diff --git a/include/linux/amlogic/aml_thermal_hw.h b/include/linux/amlogic/aml_thermal_hw.h new file mode 100644 index 000000000000..4fc18da001cd --- /dev/null +++ b/include/linux/amlogic/aml_thermal_hw.h @@ -0,0 +1,24 @@ +/* + * include/linux/amlogic/aml_thermal_hw.h + * + * Copyright (C) 2016 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#ifndef ARCH__THERMAL_H__ +#define ARCH__THERMAL_H__ +struct thermal_cooling_device; +extern int thermal_firmware_init(void); +extern int get_cpu_temp(void); +extern int aml_thermal_min_update(struct thermal_cooling_device *cdev); +#endif diff --git a/include/linux/amlogic/cpucore_cooling.h b/include/linux/amlogic/cpucore_cooling.h new file mode 100644 index 000000000000..9039455b0d55 --- /dev/null +++ b/include/linux/amlogic/cpucore_cooling.h @@ -0,0 +1,62 @@ +/* + * include/linux/amlogic/cpucore_cooling.h + * + * Copyright (C) 2016 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#ifndef __CPUCORE_COOLING_H__ +#define __CPUCORE_COOLING_H__ + +#include +#include +struct cpucore_cooling_device { + int id; + struct thermal_cooling_device *cool_dev; + unsigned int cpucore_state; + unsigned int cpucore_val; + struct list_head node; + int max_cpu_core_num; + int cluster_id; + int stop_flag; +}; +#define CPU_STOP 0x80000000 +#ifdef CONFIG_AMLOGIC_CPUCORE_THERMAL + +/** + * cpucore_cooling_register - function to create cpucore cooling device. + * @clip_cpus: cpumask of cpus where the frequency constraints will happen + */ +struct thermal_cooling_device *cpucore_cooling_register(struct device_node *, + int); + +/** + * cpucore_cooling_unregister - function to remove cpucore cooling device. + * @cdev: thermal cooling device pointer. + */ +void cpucore_cooling_unregister(struct thermal_cooling_device *cdev); + + +#else +static inline struct thermal_cooling_device * +cpucore_cooling_register(struct device_node *np) +{ + return NULL; +} +static inline +void cpucore_cooling_unregister(struct thermal_cooling_device *cdev) +{ +} +#endif + +#endif /* __CPU_COOLING_H__ */ diff --git a/include/linux/amlogic/gpu_cooling.h b/include/linux/amlogic/gpu_cooling.h new file mode 100644 index 000000000000..9d5f7a8390b9 --- /dev/null +++ b/include/linux/amlogic/gpu_cooling.h @@ -0,0 +1,106 @@ +/* + * include/linux/amlogic/gpu_cooling.h + * + * Copyright (C) 2016 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#ifndef __gpu_COOLING_H__ +#define __gpu_COOLING_H__ + +#include +#include + +#ifdef CONFIG_AMLOGIC_GPU_THERMAL + +/** + * gpufreq_cooling_register - function to create gpufreq cooling device. + * @clip_gpus: gpumask of gpus where the frequency constraints will happen + */ +/** + * struct gpufreq_cooling_device - data for cooling device with gpufreq + * @id: unique integer value corresponding to each gpufreq_cooling_device + * registered. + * @cool_dev: thermal_cooling_device pointer to keep track of the + * registered cooling device. + * @gpufreq_state: integer value representing the current state of gpufreq + * cooling devices. + * @gpufreq_val: integer value representing the absolute value of the clipped + * frequency. + * @allowed_gpus: all the gpus involved for this gpufreq_cooling_device. + * + * This structure is required for keeping information of each + * gpufreq_cooling_device registered. In order to prevent corruption of this a + * mutex lock cooling_gpufreq_lock is used. + */ +struct gpufreq_cooling_device { + int id; + struct thermal_cooling_device *cool_dev; + unsigned int gpufreq_state; + unsigned int gpufreq_val; + int (*get_gpu_freq_level)(int freq); + unsigned int (*get_gpu_max_level)(void); + unsigned int (*get_gpu_current_max_level)(void); + void (*set_gpu_freq_idx)(unsigned int idx); + unsigned int (*get_online_pp)(void); + unsigned int (*get_gpu_loading)(void); + unsigned int (*get_gpu_freq)(unsigned int idx); + unsigned int *gpu_freq_tbl; + unsigned int dyn_coeff; + int max_pp; + struct device_node *np; +}; +int gpufreq_cooling_register(struct gpufreq_cooling_device *gpufreq_dev); +struct gpufreq_cooling_device *gpufreq_cooling_alloc(void); + +/** + * gpufreq_cooling_unregister - function to remove gpufreq cooling device. + * @cdev: thermal cooling device pointer. + */ +void gpufreq_cooling_unregister(struct thermal_cooling_device *cdev); +int register_gpu_freq_info(unsigned int (*fun)(void)); + +unsigned long gpufreq_cooling_get_level(unsigned int gpu, unsigned int freq); +void save_gpu_cool_para(int i, struct device_node *d, int j); +#else +static inline void save_gpu_cool_para(unsigned int coef, + struct device_node *n, int pp) +{ + +} + +struct gpufreq_cooling_device *gpufreq_cooling_alloc(void) +{ + return NULL; +} + +int gpufreq_cooling_register(struct gpufreq_cooling_device *gpufreq_dev) +{ + return 0; +} +static inline +void gpufreq_cooling_unregister(struct thermal_cooling_device *cdev) +{ +} +static inline +unsigned long gpufreq_cooling_get_level(unsigned int gpu, unsigned int freq) +{ + return THERMAL_CSTATE_INVALID; +} +static inline int register_gpu_freq_info(unsigned int (*fun)(void)) +{ + return 0; +} +#endif + +#endif /* __GPU_COOLING_H__ */ diff --git a/include/linux/amlogic/gpucore_cooling.h b/include/linux/amlogic/gpucore_cooling.h new file mode 100644 index 000000000000..7ab8101f4165 --- /dev/null +++ b/include/linux/amlogic/gpucore_cooling.h @@ -0,0 +1,69 @@ +/* + * include/linux/amlogic/gpucore_cooling.h + * + * Copyright (C) 2016 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#ifndef __GPUCORE_COOLING_H__ +#define __GPUCORE_COOLING_H__ + +#include +#include +struct gpucore_cooling_device { + int id; + struct thermal_cooling_device *cool_dev; + unsigned int gpucore_state; + unsigned int gpucore_val; + int max_gpu_core_num; + unsigned int (*set_max_pp_num)(unsigned int pp); + struct device_node *np; + int stop_flag; +}; +#define GPU_STOP 0x80000000 + +#ifdef CONFIG_AMLOGIC_GPUCORE_THERMAL + +/** + * gpucore_cooling_register - function to create gpucore cooling device. + * @clip_cpus: cpumask of cpus where the frequency constraints will happen + */ +int gpucore_cooling_register(struct gpucore_cooling_device *gcd); + +/** + * gpucore_cooling_unregister - function to remove gpucore cooling device. + * @cdev: thermal cooling device pointer. + */ +void gpucore_cooling_unregister(struct thermal_cooling_device *cdev); +struct gpucore_cooling_device *gpucore_cooling_alloc(void); +void save_gpucore_thermal_para(struct device_node *node); + +#else +inline struct gpucore_cooling_device *gpucore_cooling_alloc(void) +{ + return NULL; +} + +inline int gpucore_cooling_register(struct gpucore_cooling_device *gcd) +{ + return 0; +} +inline void gpucore_cooling_unregister(struct thermal_cooling_device *cdev) +{ +} +inline void save_gpucore_thermal_para(struct device_node *n) +{ +} +#endif + +#endif /* __CPU_COOLING_H__ */ diff --git a/include/linux/thermal.h b/include/linux/thermal.h index 511182a88e76..b8c222296002 100644 --- a/include/linux/thermal.h +++ b/include/linux/thermal.h @@ -138,6 +138,11 @@ struct thermal_cooling_device_ops { struct thermal_zone_device *, unsigned long, u32 *); int (*power2state)(struct thermal_cooling_device *, struct thermal_zone_device *, u32, unsigned long *); +#ifdef CONFIG_AMLOGIC_TEMP_SENSOR + int (*notify_state)(struct thermal_cooling_device *, + struct thermal_zone_device *, + enum thermal_trip_type); +#endif }; struct thermal_cooling_device { @@ -216,6 +221,9 @@ struct thermal_zone_device { int last_temperature; int emul_temperature; int passive; +#ifdef CONFIG_AMLOGIC_TEMP_SENSOR + int hot_step; +#endif int prev_low_trip; int prev_high_trip; unsigned int forced_passive; @@ -365,6 +373,9 @@ struct thermal_zone_of_device_ops { int (*get_temp)(void *, int *); int (*get_trend)(void *, int, enum thermal_trend *); int (*set_trips)(void *, int, int); +#ifdef CONFIG_AMLOGIC_TEMP_SENSOR + int (*set_mode)(struct thermal_zone_device*, enum thermal_device_mode); +#endif int (*set_emul_temp)(void *, int); int (*set_trip_temp)(void *, int, int); }; @@ -469,6 +480,13 @@ struct thermal_instance *get_thermal_instance(struct thermal_zone_device *, struct thermal_cooling_device *, int); void thermal_cdev_update(struct thermal_cooling_device *); void thermal_notify_framework(struct thermal_zone_device *, int); +#ifdef CONFIG_AMLOGIC_TEMP_SENSOR +void thermal_set_upper(struct thermal_zone_device *tzd, + struct thermal_cooling_device *tcd, + int trip, unsigned long upper); +unsigned long thermal_get_upper(struct thermal_zone_device *tzd, + struct thermal_cooling_device *tcd, int trip); +#endif #else static inline bool cdev_is_power_actor(struct thermal_cooling_device *cdev) { return false; } @@ -540,6 +558,15 @@ static inline void thermal_cdev_update(struct thermal_cooling_device *cdev) static inline void thermal_notify_framework(struct thermal_zone_device *tz, int trip) { } +#ifdef CONFIG_AMLOGIC_TEMP_SENSOR +static inline void thermal_set_upper(struct thermal_zone_device *tz, + struct thermal_cooling_device *cdev, + int trip, unsigned long upper) +{ } +static inline unsigned long thermal_get_upper(struct thermal_zone_device *tz, + struct thermal_cooling_device *cdev, int trip) +{ return -1UL; } +#endif #endif /* CONFIG_THERMAL */ #if defined(CONFIG_NET) && IS_ENABLED(CONFIG_THERMAL)