[PATCH] drm/etnaviv: add etnaviv cooling device
Lucas Stach
l.stach at pengutronix.de
Wed Mar 15 13:03:09 UTC 2017
Am Sonntag, den 12.03.2017, 19:00 +0000 schrieb Russell King:
> Each Vivante GPU contains a clock divider which can divide the GPU clock
> by 2^n, which can lower the power dissipation from the GPU. It has been
> suggested that the GC600 on Dove is responsible for 20-30% of the power
> dissipation from the SoC, so lowering the GPU clock rate provides a way
> to throttle the power dissiptation, and reduce the temperature when the
> SoC gets hot.
>
> This patch hooks the Etnaviv driver into the kernel's thermal management
> to allow the GPUs to be throttled when necessary, allowing a reduction in
> GPU clock rate from /1 to /64 in power of 2 steps.
Are those power of 2 steps a hardware limitation, or is it something you
implemented this way to get a smaller number of steps, with a more
meaningful difference in clock speed?
My understanding was that the FSCALE value is just a regular divider
with all steps values in the range of 1-64 being usable.
Regards,
Lucas
>
> Signed-off-by: Russell King <rmk+kernel at armlinux.org.uk>
> ---
> drivers/gpu/drm/etnaviv/etnaviv_gpu.c | 84 ++++++++++++++++++++++++++++-------
> drivers/gpu/drm/etnaviv/etnaviv_gpu.h | 2 +
> 2 files changed, 71 insertions(+), 15 deletions(-)
>
> diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c
> index 130d7d517a19..bd95182d0852 100644
> --- a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c
> +++ b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c
> @@ -18,6 +18,7 @@
> #include <linux/dma-fence.h>
> #include <linux/moduleparam.h>
> #include <linux/of_device.h>
> +#include <linux/thermal.h>
>
> #include "etnaviv_cmdbuf.h"
> #include "etnaviv_dump.h"
> @@ -409,6 +410,17 @@ static void etnaviv_gpu_load_clock(struct etnaviv_gpu *gpu, u32 clock)
> gpu_write(gpu, VIVS_HI_CLOCK_CONTROL, clock);
> }
>
> +static void etnaviv_gpu_update_clock(struct etnaviv_gpu *gpu)
> +{
> + unsigned int fscale = 1 << (6 - gpu->freq_scale);
> + u32 clock;
> +
> + clock = VIVS_HI_CLOCK_CONTROL_DISABLE_DEBUG_REGISTERS |
> + VIVS_HI_CLOCK_CONTROL_FSCALE_VAL(fscale);
> +
> + etnaviv_gpu_load_clock(gpu, clock);
> +}
> +
> static int etnaviv_hw_reset(struct etnaviv_gpu *gpu)
> {
> u32 control, idle;
> @@ -426,11 +438,10 @@ static int etnaviv_hw_reset(struct etnaviv_gpu *gpu)
> timeout = jiffies + msecs_to_jiffies(1000);
>
> while (time_is_after_jiffies(timeout)) {
> - control = VIVS_HI_CLOCK_CONTROL_DISABLE_DEBUG_REGISTERS |
> - VIVS_HI_CLOCK_CONTROL_FSCALE_VAL(0x40);
> -
> /* enable clock */
> - etnaviv_gpu_load_clock(gpu, control);
> + etnaviv_gpu_update_clock(gpu);
> +
> + control = gpu_read(gpu, VIVS_HI_CLOCK_CONTROL);
>
> /* Wait for stable clock. Vivante's code waited for 1ms */
> usleep_range(1000, 10000);
> @@ -490,11 +501,7 @@ static int etnaviv_hw_reset(struct etnaviv_gpu *gpu)
> }
>
> /* We rely on the GPU running, so program the clock */
> - control = VIVS_HI_CLOCK_CONTROL_DISABLE_DEBUG_REGISTERS |
> - VIVS_HI_CLOCK_CONTROL_FSCALE_VAL(0x40);
> -
> - /* enable clock */
> - etnaviv_gpu_load_clock(gpu, control);
> + etnaviv_gpu_update_clock(gpu);
>
> return 0;
> }
> @@ -1526,17 +1533,13 @@ static int etnaviv_gpu_hw_suspend(struct etnaviv_gpu *gpu)
> #ifdef CONFIG_PM
> static int etnaviv_gpu_hw_resume(struct etnaviv_gpu *gpu)
> {
> - u32 clock;
> int ret;
>
> ret = mutex_lock_killable(&gpu->lock);
> if (ret)
> return ret;
>
> - clock = VIVS_HI_CLOCK_CONTROL_DISABLE_DEBUG_REGISTERS |
> - VIVS_HI_CLOCK_CONTROL_FSCALE_VAL(0x40);
> -
> - etnaviv_gpu_load_clock(gpu, clock);
> + etnaviv_gpu_update_clock(gpu);
> etnaviv_gpu_hw_init(gpu);
>
> gpu->switch_context = true;
> @@ -1548,6 +1551,47 @@ static int etnaviv_gpu_hw_resume(struct etnaviv_gpu *gpu)
> }
> #endif
>
> +static int
> +etnaviv_gpu_cooling_get_max_state(struct thermal_cooling_device *cdev,
> + unsigned long *state)
> +{
> + *state = 6;
> +
> + return 0;
> +}
> +
> +static int
> +etnaviv_gpu_cooling_get_cur_state(struct thermal_cooling_device *cdev,
> + unsigned long *state)
> +{
> + struct etnaviv_gpu *gpu = cdev->devdata;
> +
> + *state = gpu->freq_scale;
> +
> + return 0;
> +}
> +
> +static int
> +etnaviv_gpu_cooling_set_cur_state(struct thermal_cooling_device *cdev,
> + unsigned long state)
> +{
> + struct etnaviv_gpu *gpu = cdev->devdata;
> +
> + mutex_lock(&gpu->lock);
> + gpu->freq_scale = state;
> + if (!pm_runtime_suspended(gpu->dev))
> + etnaviv_gpu_update_clock(gpu);
> + mutex_unlock(&gpu->lock);
> +
> + return 0;
> +}
> +
> +static struct thermal_cooling_device_ops cooling_ops = {
> + .get_max_state = etnaviv_gpu_cooling_get_max_state,
> + .get_cur_state = etnaviv_gpu_cooling_get_cur_state,
> + .set_cur_state = etnaviv_gpu_cooling_set_cur_state,
> +};
> +
> static int etnaviv_gpu_bind(struct device *dev, struct device *master,
> void *data)
> {
> @@ -1556,13 +1600,20 @@ static int etnaviv_gpu_bind(struct device *dev, struct device *master,
> struct etnaviv_gpu *gpu = dev_get_drvdata(dev);
> int ret;
>
> + gpu->cooling = thermal_of_cooling_device_register(dev->of_node,
> + (char *)dev_name(dev), gpu, &cooling_ops);
> + if (IS_ERR(gpu->cooling))
> + return PTR_ERR(gpu->cooling);
> +
> #ifdef CONFIG_PM
> ret = pm_runtime_get_sync(gpu->dev);
> #else
> ret = etnaviv_gpu_clk_enable(gpu);
> #endif
> - if (ret < 0)
> + if (ret < 0) {
> + thermal_cooling_device_unregister(gpu->cooling);
> return ret;
> + }
>
> gpu->drm = drm;
> gpu->fence_context = dma_fence_context_alloc(1);
> @@ -1616,6 +1667,9 @@ static void etnaviv_gpu_unbind(struct device *dev, struct device *master,
> }
>
> gpu->drm = NULL;
> +
> + thermal_cooling_device_unregister(gpu->cooling);
> + gpu->cooling = NULL;
> }
>
> static const struct component_ops gpu_ops = {
> diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gpu.h b/drivers/gpu/drm/etnaviv/etnaviv_gpu.h
> index 1c0606ea7d5e..6a1e68eec24c 100644
> --- a/drivers/gpu/drm/etnaviv/etnaviv_gpu.h
> +++ b/drivers/gpu/drm/etnaviv/etnaviv_gpu.h
> @@ -97,6 +97,7 @@ struct etnaviv_cmdbuf;
>
> struct etnaviv_gpu {
> struct drm_device *drm;
> + struct thermal_cooling_device *cooling;
> struct device *dev;
> struct mutex lock;
> struct etnaviv_chip_identity identity;
> @@ -150,6 +151,7 @@ struct etnaviv_gpu {
> u32 hangcheck_fence;
> u32 hangcheck_dma_addr;
> struct work_struct recover_work;
> + unsigned int freq_scale;
> };
>
> static inline void gpu_write(struct etnaviv_gpu *gpu, u32 reg, u32 data)
More information about the etnaviv
mailing list