From e4228477012c19785488e4c22f288600cc8646d8 Mon Sep 17 00:00:00 2001 From: Rocky Hao Date: Tue, 26 Dec 2017 10:01:00 +0800 Subject: [PATCH] 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 --- .../bindings/devfreq/rockchip_dmc.txt | 17 +++ drivers/devfreq/rockchip_dmc.c | 139 ++++++++++++++++++ 2 files changed, 156 insertions(+) diff --git a/Documentation/devicetree/bindings/devfreq/rockchip_dmc.txt b/Documentation/devicetree/bindings/devfreq/rockchip_dmc.txt index c9d4830edbb6..f39f471d5c65 100644 --- a/Documentation/devicetree/bindings/devfreq/rockchip_dmc.txt +++ b/Documentation/devicetree/bindings/devfreq/rockchip_dmc.txt @@ -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"; }; diff --git a/drivers/devfreq/rockchip_dmc.c b/drivers/devfreq/rockchip_dmc.c index 98ed4f181726..07eacda1478c 100644 --- a/drivers/devfreq/rockchip_dmc.c +++ b/drivers/devfreq/rockchip_dmc.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -37,6 +38,7 @@ #include #include #include +#include #include #include @@ -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; }