Skip to content

Commit

Permalink
drm/etnaviv: add etnaviv cooling device
Browse files Browse the repository at this point in the history
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.

Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
Reviewed-by: Lucas Stach <l.stach@pengutronix.de>
Signed-off-by: Lucas Stach <l.stach@pengutronix.de>
  • Loading branch information
Russell King authored and Lucas Stach committed Mar 29, 2017
1 parent fda8fa5 commit bcdfb5e
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 15 deletions.
84 changes: 69 additions & 15 deletions drivers/gpu/drm/etnaviv/etnaviv_gpu.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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;
Expand All @@ -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);
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -1532,17 +1539,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;
Expand All @@ -1554,6 +1557,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)
{
Expand All @@ -1562,13 +1606,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);
Expand Down Expand Up @@ -1622,6 +1673,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 = {
Expand Down
2 changes: 2 additions & 0 deletions drivers/gpu/drm/etnaviv/etnaviv_gpu.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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)
Expand Down

0 comments on commit bcdfb5e

Please sign in to comment.