PM / devfreq: rockchip_dmc: add thermal control support

add rockchip_dmc as a cooling device in thermal control

Change-Id: Iefb046a104eb2205f2ef17dd80bb31fd75b42c90
Signed-off-by: Rocky Hao <rocky.hao@rock-chips.com>
This commit is contained in:
Rocky Hao 2017-12-26 10:01:00 +08:00 committed by Tao Huang
parent a993bfaf1a
commit e422847701
2 changed files with 156 additions and 0 deletions

View File

@ -28,6 +28,16 @@ Optional properties:
min-bandwidth: minimum ddr bandwidth in Mbyte/sec.
max-bandwidth: maximum ddr bandwidth in Mbyte/sec.
frequency: ddr frequency in KHz.
- #cooling-cells: This property indicates dmc can work as a cooling device
- ddr_power_model: Sets power model parameters.
- dynamic-power-coefficient: A u32 value that represents the running time dynamic
power coefficient in units of mW/MHz/uVolt^2. The coefficient can either be
calculated from power measurements or derived by analysis.
- static-power-coefficient: A u32 value that represents the static power
coefficient.
- ts: An array containing coefficients for the temperature scaling factor.
Used as : tsf = ts[3]*T^3 + ts[2]*T^2 + ts[1]*T + ts[0], where T = temperature
- thermal-zone: A string identifying the thermal zone used for the dmc
Example:
@ -90,5 +100,12 @@ Example:
center-supply = <&ppvar_centerlogic>;
upthreshold = <15>;
downdifferential = <10>;
#cooling-cells = <2>;
ddr_power_model: ddr_power_model {
dynamic-power-coefficient = <120>;
static-power-coefficient = <300>;
ts = <32000 4700 (-80) 2>;
thermal-zone = "soc-thermal";
}
status = "disabled";
};

View File

@ -23,6 +23,7 @@
#include <linux/cpufreq.h>
#include <linux/delay.h>
#include <linux/devfreq.h>
#include <linux/devfreq_cooling.h>
#include <linux/devfreq-event.h>
#include <linux/fb.h>
#include <linux/interrupt.h>
@ -37,6 +38,7 @@
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/suspend.h>
#include <linux/thermal.h>
#include <soc/rockchip/rkfb_dmc.h>
#include <soc/rockchip/rockchip_dmc.h>
@ -58,6 +60,8 @@
#define FIQ_NUM_FOR_DCF (143) /* NA irq map to fiq for dcf */
#define DTS_PAR_OFFSET (4096)
#define FALLBACK_STATIC_TEMPERATURE 55000
struct freq_map_table {
unsigned int min;
unsigned int max;
@ -296,6 +300,12 @@ struct rockchip_dmcfreq {
unsigned int last_refresh;
bool is_dualview;
struct thermal_cooling_device *devfreq_cooling;
u32 dynamic_coefficient;
u32 static_coefficient;
s32 ts[4];
struct thermal_zone_device *ddr_tz;
int (*set_auto_self_refresh)(u32 en);
};
@ -1721,6 +1731,120 @@ static struct devfreq_governor devfreq_dmc_ondemand = {
.event_handler = devfreq_dmc_ondemand_handler,
};
static unsigned long model_static_power(unsigned long voltage)
{
struct rockchip_dmcfreq *dmcfreq = rk_dmcfreq;
struct device *dev = dmcfreq->dev;
int temperature;
unsigned long temp;
unsigned long temp_squared, temp_cubed, temp_scaling_factor;
const unsigned long voltage_cubed = (voltage * voltage * voltage) >> 10;
if (!IS_ERR_OR_NULL(dmcfreq->ddr_tz) && dmcfreq->ddr_tz->ops->get_temp) {
int ret;
ret =
dmcfreq->ddr_tz->ops->get_temp(dmcfreq->ddr_tz,
&temperature);
if (ret) {
dev_warn_ratelimited(dev,
"failed to read temp for ddr thermal zone: %d\n",
ret);
temperature = FALLBACK_STATIC_TEMPERATURE;
}
} else {
temperature = FALLBACK_STATIC_TEMPERATURE;
}
/*
* Calculate the temperature scaling factor. To be applied to the
* voltage scaled power.
*/
temp = temperature / 1000;
temp_squared = temp * temp;
temp_cubed = temp_squared * temp;
temp_scaling_factor = (dmcfreq->ts[3] * temp_cubed)
+ (dmcfreq->ts[2] * temp_squared)
+ (dmcfreq->ts[1] * temp)
+ dmcfreq->ts[0];
return (((dmcfreq->static_coefficient * voltage_cubed) >> 20)
* temp_scaling_factor) / 1000000;
}
static unsigned long model_dynamic_power(unsigned long freq,
unsigned long voltage)
{
/*
* The inputs: freq (f) is in Hz, and voltage (v) in mV.
* The coefficient (c) is in mW/(MHz mV mV).
*
* This function calculates the dynamic power after this formula:
* Pdyn (mW) = c (mW/(MHz*mV*mV)) * v (mV) * v (mV) * f (MHz)
*/
struct rockchip_dmcfreq *dmcfreq = rk_dmcfreq;
const unsigned long v2 = (voltage * voltage) / 1000; /* m*(V*V) */
const unsigned long f_mhz = freq / 1000000; /* MHz */
return (dmcfreq->dynamic_coefficient * v2 * f_mhz) / 1000000; /* mW */
}
static struct devfreq_cooling_power ddr_power_model_simple_ops = {
.get_static_power = model_static_power,
.get_dynamic_power = model_dynamic_power,
};
static int ddr_power_model_simple_init(struct rockchip_dmcfreq *dmcfreq)
{
struct device_node *power_model_node;
const char *tz_name;
power_model_node = of_get_child_by_name(dmcfreq->dev->of_node,
"ddr_power_model");
if (!power_model_node) {
dev_err(dmcfreq->dev, "could not find power_model node\n");
return -ENODEV;
}
if (of_property_read_string(power_model_node, "thermal-zone", &tz_name)) {
dev_err(dmcfreq->dev, "ts in power_model not available\n");
return -EINVAL;
}
dmcfreq->ddr_tz = thermal_zone_get_zone_by_name(tz_name);
if (IS_ERR(dmcfreq->ddr_tz)) {
pr_warn_ratelimited
("Error getting ddr thermal zone (%ld), not yet ready?\n",
PTR_ERR(dmcfreq->ddr_tz));
dmcfreq->ddr_tz = NULL;
return -EPROBE_DEFER;
}
if (of_property_read_u32(power_model_node, "static-power-coefficient",
&dmcfreq->static_coefficient)) {
dev_err(dmcfreq->dev,
"static-power-coefficient not available\n");
return -EINVAL;
}
if (of_property_read_u32(power_model_node, "dynamic-power-coefficient",
&dmcfreq->dynamic_coefficient)) {
dev_err(dmcfreq->dev,
"dynamic-power-coefficient not available\n");
return -EINVAL;
}
if (of_property_read_u32_array
(power_model_node, "ts", (u32 *)dmcfreq->ts, 4)) {
dev_err(dmcfreq->dev, "ts in power_model not available\n");
return -EINVAL;
}
return 0;
}
static int rockchip_dmcfreq_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@ -1858,6 +1982,21 @@ static int rockchip_dmcfreq_probe(struct platform_device *pdev)
rk_dmcfreq = data;
ret = ddr_power_model_simple_init(data);
if (!ret) {
data->devfreq_cooling =
of_devfreq_cooling_register_power(data->dev->of_node,
data->devfreq,
&ddr_power_model_simple_ops);
if (IS_ERR_OR_NULL(data->devfreq_cooling)) {
ret = PTR_ERR(data->devfreq_cooling);
dev_err(data->dev,
"Failed to register cooling device (%d)\n",
ret);
}
}
return 0;
}