thermal: initial add thermal driver

PD#138714: initial add thermal driver

1. Add amlogic thermal sensor driver
2. add cpu/gpu core cooling methord and gpu freq cooling devices;
3. device tree support of thermal for p212/q200
4. related dtsi/Makefiles/Kconfig/Headfiles update

Change-Id: I555fbb1e36a3cdc3187d92bdf5da6a720f2377d0
Signed-off-by: Tao Zeng <tao.zeng@amlogic.com>
This commit is contained in:
Tao Zeng
2017-03-14 10:30:38 +08:00
committed by Jianxin Pan
parent 42dcc2cf34
commit b56a98ab9a
25 changed files with 2359 additions and 8 deletions

View File

@@ -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"

View File

@@ -13464,6 +13464,10 @@ AMLOGIC reboot
M: Jianxin Pan <jianxin.pan@amlogic.com>
F: drivers/amlogic/reboot/*
AMLOGIC driver for thermal
M: Tao Zeng <tao.zeng@amlogic.com>
F: drivers/amlogic/thermal/*
HDMITX OUTPUT DRIVER
M: Zongdong Jiao <zongdong.jiao@amlogic.com>
S: Maintained

View File

@@ -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>;

View File

@@ -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";

View File

@@ -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>;

View File

@@ -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";

View File

@@ -31,7 +31,6 @@
cpu-map {
cluster0:cluster0 {
#cooling-cells = <2>; /* min followed by max */
core0 {
cpu = <&CPU0>;
};

View File

@@ -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>;
};

View File

@@ -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

View File

@@ -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);

View File

@@ -63,5 +63,7 @@ source "drivers/amlogic/securitykey/Kconfig"
source "drivers/amlogic/key_manage/Kconfig"
source "drivers/amlogic/thermal/Kconfig"
endmenu
endif

View File

@@ -59,3 +59,5 @@ obj-$(CONFIG_AMLOGIC_SECURITY_KEY) += securitykey/
obj-$(CONFIG_AMLOGIC_KEY_MANAGE) += key_manage/
obj-$(CONFIG_AMLOGIC_TEMP_SENSOR) += thermal/

View File

@@ -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

View File

@@ -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

View File

@@ -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 <linux/slab.h>
#include <linux/types.h>
#include <linux/amlogic/cpu_version.h>
#include <linux/amlogic/scpi_protocol.h>
#include <linux/printk.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/cpufreq.h>
#include <linux/cpu_cooling.h>
#include <linux/amlogic/cpucore_cooling.h>
#include <linux/amlogic/gpucore_cooling.h>
#include <linux/amlogic/gpu_cooling.h>
#include <linux/cpu.h>
#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);

View File

@@ -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 <linux/module.h>
#include <linux/thermal.h>
#include <linux/cpufreq.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/cpu.h>
#include <linux/amlogic/cpucore_cooling.h>
#include <linux/amlogic/cpu_hotplug.h>
#include <linux/cpumask.h>
/**
* 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);

View File

@@ -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 <linux/module.h>
#include <linux/thermal.h>
#include <linux/cpufreq.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/cpu.h>
#include <linux/amlogic/gpu_cooling.h>
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);

View File

@@ -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 <linux/module.h>
#include <linux/thermal.h>
#include <linux/cpufreq.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/cpu.h>
#include <linux/amlogic/gpucore_cooling.h>
#include <linux/device.h>
/**
* 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);

View File

@@ -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;

View File

@@ -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;

View File

@@ -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

View File

@@ -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 <linux/thermal.h>
#include <linux/cpumask.h>
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__ */

View File

@@ -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 <linux/thermal.h>
#include <linux/cpumask.h>
#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__ */

View File

@@ -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 <linux/thermal.h>
#include <linux/cpumask.h>
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__ */

View File

@@ -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)