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@xxxxxxxxxxxxxxx> > --- > 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) _______________________________________________ dri-devel mailing list dri-devel@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/dri-devel