Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 590c4413 authored by Lucille Sylvester's avatar Lucille Sylvester
Browse files

msm: kgsl: Add support for SP/TP power collapse



The A430 GPU will have the capability to do internal hw
block gating of the SP/TP.  Make sure the blocks are powered
on initially, then allow CP control of the blocks.  Check that
the blocks are off before considering the hw idle.  Check that
the blocks are on before updating performance counters.

Change-Id: Ibd5aebdd6c95463cd7afe9b65e5a4163df5678f8
Signed-off-by: default avatarLucille Sylvester <lsylvest@codeaurora.org>
parent c1732a1a
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -200,6 +200,7 @@ enum a4xx_rb_perfctr_rb_sel {
#define A4XX_RBBM_CFG_DEBBUS_CLRC		0x94
#define A4XX_RBBM_CFG_DEBBUS_LOADIVT		0x95

#define A4XX_RBBM_POWER_CNTL_IP			0x98
#define A4XX_RBBM_PERFCTR_CP_0_LO		0x9c
#define A4XX_RBBM_PERFCTR_CP_0_HI		0x9d
#define A4XX_RBBM_PERFCTR_CP_1_LO		0x9e
@@ -402,6 +403,7 @@ enum a4xx_rb_perfctr_rb_sel {
#define A4XX_RBBM_CFG_DEBBUS_TRACE_BUF4		0x1ad
#define A4XX_RBBM_CFG_DEBBUS_MISR0		0x1ae
#define A4XX_RBBM_CFG_DEBBUS_MISR1		0x1af
#define A4XX_RBBM_POWER_STATUS			0x1b0

/* CP registers */
#define A4XX_CP_RB_BASE			0x200
@@ -427,6 +429,7 @@ enum a4xx_rb_perfctr_rb_sel {
#define A4XX_CP_ME_RAM_DATA		0x227

#define A4XX_CP_DEBUG			0x22e
#define A4XX_CP_POWER_COLLAPSE_CNTL	0x234
/*
 * CP debug settings for A4xx cores
 * MIU_128BIT_WRITE_ENABLE [25] - Allow 128 bit writes to the VBIF
@@ -697,6 +700,7 @@ enum a4xx_pc_perfctr_pc_sel {

/* HLSQ registers */
#define A4XX_HLSQ_TIMEOUT_THRESHOLD     0xe00
#define A4XX_HLSQ_STATE_RESTORE_TRIGGER	0xe01
#define A4XX_HLSQ_PERFCTR_HLSQ_SEL_0	0xe06
#define A4XX_HLSQ_PERFCTR_HLSQ_SEL_1	0xe07
#define A4XX_HLSQ_PERFCTR_HLSQ_SEL_2	0xe08
+37 −5
Original line number Diff line number Diff line
@@ -1639,6 +1639,11 @@ static int adreno_init(struct kgsl_device *device)
	if (test_bit(ADRENO_DEVICE_INITIALIZED, &adreno_dev->priv))
		return 0;

	/* Identify the specific GPU */
	adreno_identify_gpu(adreno_dev);

	gpudev = ADRENO_GPU_DEVICE(adreno_dev);

	/* Power up the device */
	ret = kgsl_pwrctrl_enable(device);
	if (ret)
@@ -1647,11 +1652,6 @@ static int adreno_init(struct kgsl_device *device)
	/* Make a high priority workqueue for starting the GPU */
	adreno_wq = alloc_workqueue("adreno", 0, 1);

	/* Identify the specific GPU */
	adreno_identify_gpu(adreno_dev);

	gpudev = ADRENO_GPU_DEVICE(adreno_dev);

	/* Initialize coresight for the target */
	adreno_coresight_init(device);

@@ -2604,6 +2604,10 @@ bool adreno_hw_isidle(struct kgsl_device *device)
	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
	struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev);

	if (gpudev->is_sptp_idle)
		if (!gpudev->is_sptp_idle(adreno_dev))
			return false;

	adreno_readreg(adreno_dev, ADRENO_REG_RBBM_STATUS,
		&reg_rbbm_status);

@@ -3105,6 +3109,30 @@ static unsigned int adreno_gpuid(struct kgsl_device *device,
	return (0x0003 << 16) | ADRENO_GPUREV(adreno_dev);
}

static void adreno_enable_pc(struct kgsl_device *device)
{
	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
	struct adreno_gpudev *gpudev  = ADRENO_GPU_DEVICE(adreno_dev);
	if (gpudev->enable_pc)
		gpudev->enable_pc(adreno_dev);
}

static void adreno_disable_pc(struct kgsl_device *device)
{
	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
	struct adreno_gpudev *gpudev  = ADRENO_GPU_DEVICE(adreno_dev);
	if (gpudev->disable_pc)
		gpudev->disable_pc(adreno_dev);
}

static void adreno_regulator_enable(struct kgsl_device *device)
{
	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
	struct adreno_gpudev *gpudev  = ADRENO_GPU_DEVICE(adreno_dev);
	if (gpudev->regulator_enable)
		gpudev->regulator_enable(adreno_dev);
}

static const struct kgsl_functable adreno_functable = {
	/* Mandatory functions */
	.regread = adreno_regread,
@@ -3136,6 +3164,10 @@ static const struct kgsl_functable adreno_functable = {
	.setproperty_compat = adreno_setproperty_compat,
	.drawctxt_sched = adreno_drawctxt_sched,
	.resume = adreno_dispatcher_start,
	.enable_pc = adreno_enable_pc,
	.disable_pc = adreno_disable_pc,
	.regulator_enable = adreno_regulator_enable,

};

static struct platform_driver adreno_platform_driver = {
+5 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@
#include "adreno_profile.h"
#include "kgsl_iommu.h"
#include <linux/stat.h>
#include <linux/delay.h>

#ifdef CONFIG_MSM_OCMEM
#include <soc/qcom/ocmem.h>
@@ -598,6 +599,10 @@ struct adreno_gpudev {
	void (*perfcounter_write)(struct adreno_device *adreno_dev,
		unsigned int group, unsigned int counter);
	void (*soft_reset)(struct adreno_device *device);
	bool (*is_sptp_idle)(struct adreno_device *);
	void (*enable_pc)(struct adreno_device *);
	void (*disable_pc)(struct adreno_device *);
	void (*regulator_enable)(struct adreno_device *);
};

#define FT_DETECT_REGS_COUNT 14
+5 −0
Original line number Diff line number Diff line
@@ -1009,10 +1009,15 @@ int a3xx_perfcounter_enable(struct adreno_device *adreno_dev,
	}
	reg = &(counters->groups[group].regs[counter]);

	if (adreno_is_a4xx(adreno_dev))
		gpudev->disable_pc(adreno_dev);
	/* Select the desired perfcounter */
	kgsl_regwrite(&adreno_dev->dev, reg->select, countable);
	counters->groups[group].regs[counter].value = 0;

	if (adreno_is_a4xx(adreno_dev))
		gpudev->enable_pc(adreno_dev);

	return 0;
}

+96 −0
Original line number Diff line number Diff line
@@ -16,6 +16,11 @@
#include "adreno_a3xx.h"
#include "adreno_a4xx.h"
#include "adreno_cp_parser.h"
#include "adreno_trace.h"

#define SP_TP_PWR_COLLAPSE_MASK 0x6
#define SP_TP_PWR_ON_MASK BIT(0)
#define SP_TP_PWR_ON BIT(20)

/*
 * Set of registers to dump for A4XX on snapshot.
@@ -259,6 +264,92 @@ static const struct adreno_vbif_platform a4xx_vbif_platforms[] = {
	{ adreno_is_a420, a420_vbif },
};

/*
 * a4xx_is_sptp_idle() - A430 SP/TP should be off to be considered idle
 * @adreno_dev: The adreno device pointer
 */
static bool a4xx_is_sptp_idle(struct adreno_device *adreno_dev)
{
	unsigned int reg;
	struct kgsl_device *device = &adreno_dev->dev;
	if (adreno_is_a420(adreno_dev))
		return true;

	/* If SP/TP pc isn't enabled, don't worry about power */
	kgsl_regread(device, A4XX_CP_POWER_COLLAPSE_CNTL, &reg);
	if (!(reg & 0x10))
		return true;

	/* Check that SP/TP is off */
	kgsl_regread(device, A4XX_RBBM_POWER_STATUS, &reg);
	return !(reg & SP_TP_PWR_ON);
}

/*
 * a4xx_regulator_enable() - Enable any necessary HW regulators
 * @adreno_dev: The adreno device pointer
 *
 * Some HW blocks may need their regulators explicitly enabled
 * on a restart.  Clocks must be on during this call.
 */
static void a4xx_regulator_enable(struct adreno_device *adreno_dev)
{
	unsigned int reg;
	struct kgsl_device *device = &adreno_dev->dev;
	if (adreno_is_a420(adreno_dev))
		return;

	kgsl_regread(device, A4XX_RBBM_POWER_CNTL_IP, &reg);
	reg = (reg & ~SP_TP_PWR_ON_MASK);
	kgsl_regwrite(device, A4XX_RBBM_POWER_CNTL_IP, reg);
	do {
		udelay(5);
		kgsl_regread(device, A4XX_RBBM_POWER_STATUS, &reg);
	} while (!(reg & SP_TP_PWR_ON));
	trace_adreno_sp_tp((unsigned long) __builtin_return_address(0));
}

/*
 * a4xx_enable_pc() - Enable the SP/TP block power collapse
 * @adreno_dev: The adreno device pointer
 */
static void a4xx_enable_pc(struct adreno_device *adreno_dev)
{
	unsigned int reg;
	struct kgsl_device *device = &adreno_dev->dev;
	if (adreno_is_a420(adreno_dev))
		return;

	kgsl_regread(device, A4XX_RBBM_POWER_CNTL_IP, &reg);
	reg = (reg & ~SP_TP_PWR_COLLAPSE_MASK) | 0x2;
	kgsl_regwrite(device, A4XX_RBBM_POWER_CNTL_IP, reg);
	kgsl_regwrite(device, A4XX_CP_POWER_COLLAPSE_CNTL, 0x00400010);
	trace_adreno_sp_tp((unsigned long) __builtin_return_address(0));
};

/*
 * a4xx_disable_pc() - Disable the SP/TP block power collapse
 * @adreno_dev: The adreno device pointer
 */
static void a4xx_disable_pc(struct adreno_device *adreno_dev)
{
	unsigned int reg;
	struct kgsl_device *device = &adreno_dev->dev;
	if (adreno_is_a420(adreno_dev))
		return;

	/* remove hw control and use the sw override */
	kgsl_regread(device, A4XX_RBBM_POWER_CNTL_IP, &reg);
	reg = (reg & ~SP_TP_PWR_COLLAPSE_MASK) | 0x4;
	kgsl_regwrite(device, A4XX_RBBM_POWER_CNTL_IP, reg);

	/* turn the SP/TP on & restore it */
	a4xx_regulator_enable(adreno_dev);
	kgsl_regwrite(device, A4XX_HLSQ_STATE_RESTORE_TRIGGER, 0x1);
	trace_adreno_sp_tp((unsigned long) __builtin_return_address(0));
}


/*
 * a4xx_enable_hwcg() - Program the clock control registers
 * @device: The adreno device pointer
@@ -441,6 +532,7 @@ static void a4xx_start(struct adreno_device *adreno_dev)
			(adreno_is_a420(adreno_dev) ? (1 << 29) : 0));

	a4xx_enable_hwcg(device);
	a4xx_enable_pc(adreno_dev);
	/*
	 * For A420 set RBBM_CLOCK_DELAY_HLSQ.CGC_HLSQ_TP_EARLY_CYC >= 2
	 * due to timing issue with HLSQ_TP_CLK_EN
@@ -1251,4 +1343,8 @@ struct adreno_gpudev adreno_a4xx_gpudev = {
	.invalid_countables = a4xx_perfctr_invalid_countables,
	.soft_reset = a3xx_soft_reset,
	.snapshot = a4xx_snapshot,
	.is_sptp_idle = a4xx_is_sptp_idle,
	.enable_pc = a4xx_enable_pc,
	.disable_pc = a4xx_disable_pc,
	.regulator_enable = a4xx_regulator_enable,
};
Loading