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

Commit 1823783b authored by Carter Cooper's avatar Carter Cooper Committed by Tarun Karra
Browse files

msm: kgsl: Rework GPMU initialization on startup



GPMU initialization was doing a lot of things correctly to get the
new hardware block working. It wasn't however doing them quickly and
this was costing a lot of time when waking the device up. Streamline
this process to do things quicker to save power and make the GPU
available faster.

Change-Id: Ie06995e35c89e1c60f549e3fb4b6023203c99f89
Signed-off-by: default avatarCarter Cooper <ccooper@codeaurora.org>
parent cf55519e
Loading
Loading
Loading
Loading
+11 −25
Original line number Diff line number Diff line
@@ -1511,26 +1511,6 @@ static int _adreno_start(struct adreno_device *adreno_dev)
			goto error_mmu_off;
	}

	/* Set up LM before initializing the GPMU */
	if (gpudev->lm_init)
		gpudev->lm_init(adreno_dev);

	/* Enable h/w power collapse feature */
	if (gpudev->enable_pc)
		gpudev->enable_pc(adreno_dev);

	/* GPMU initialization and start */
	if (gpudev->gpmu_start)
		gpudev->gpmu_start(adreno_dev);

	/* Enable peak power detect feature */
	if (gpudev->enable_ppd)
		gpudev->enable_ppd(adreno_dev);

	/* Enable limits management */
	if (gpudev->lm_enable)
		gpudev->lm_enable(adreno_dev);

	/* Start the dispatcher */
	adreno_dispatcher_start(device);

@@ -2146,13 +2126,19 @@ static int adreno_soft_reset(struct kgsl_device *device)
		ret = adreno_ringbuffer_warm_start(adreno_dev);
	else
		ret = adreno_ringbuffer_cold_start(adreno_dev);
	if (ret)
		goto done;

	if (gpudev->hw_init)
		ret = gpudev->hw_init(adreno_dev);
	if (ret)
		goto done;

	if (!ret) {
	device->reset_counter++;
	/* device is back online */
	set_bit(ADRENO_DEVICE_STARTED, &adreno_dev->priv);
	}

done:
	return ret;
}

+4 −5
Original line number Diff line number Diff line
@@ -261,6 +261,8 @@ struct adreno_gpu_core {
 * @pm4_fw_size: Size of pm4 ucode buffer
 * @pm4_fw_version: Version of pm4 ucode
 * @pm4: Memory descriptor which holds pm4 ucode buffer info
 * @gpmu_cmds_size: Length of gpmu cmd stream
 * @gpmu_cmds: gpmu cmd stream
 * @ringbuffers: Array of pointers to adreno_ringbuffers
 * @num_ringbuffers: Number of ringbuffers for the GPU
 * @cur_rb: Pointer to the current ringbuffer
@@ -306,6 +308,8 @@ struct adreno_device {
	size_t pm4_fw_size;
	unsigned int pm4_fw_version;
	struct kgsl_memdesc pm4;
	size_t gpmu_cmds_size;
	unsigned int *gpmu_cmds;
	struct adreno_ringbuffer ringbuffers[ADRENO_PRIORITY_MAX_RB_LEVELS];
	int num_ringbuffers;
	struct adreno_ringbuffer *cur_rb;
@@ -655,13 +659,8 @@ struct adreno_gpudev {
	void (*perfcounter_close)(struct adreno_device *);
	void (*start)(struct adreno_device *);
	bool (*is_sptp_idle)(struct adreno_device *);
	void (*enable_pc)(struct adreno_device *);
	void (*enable_ppd)(struct adreno_device *);
	int (*regulator_enable)(struct adreno_device *);
	void (*regulator_disable)(struct adreno_device *);
	void (*gpmu_start)(struct adreno_device *);
	void (*lm_init)(struct adreno_device *);
	void (*lm_enable)(struct adreno_device *);
	void (*pwrlevel_change_settings)(struct adreno_device *,
				unsigned int prelevel, unsigned int postlevel,
				bool post);
+9 −2
Original line number Diff line number Diff line
@@ -1550,6 +1550,14 @@ int adreno_a4xx_pwron_fixup_init(struct adreno_device *adreno_dev)
	return 0;
}

static int a4xx_hw_init(struct adreno_device *adreno_dev)
{
	a4xx_enable_pc(adreno_dev);
	a4xx_enable_ppd(adreno_dev);

	return 0;
}

/*
 * a4xx_rb_init() - Initialize ringbuffer
 * @adreno_dev: Pointer to adreno device
@@ -2128,14 +2136,13 @@ struct adreno_gpudev adreno_a4xx_gpudev = {
	.perfcounter_init = a4xx_perfcounter_init,
	.perfcounter_close = a4xx_perfcounter_close,
	.rb_init = a4xx_rb_init,
	.hw_init = a4xx_hw_init,
	.microcode_read = a3xx_microcode_read,
	.microcode_load = a3xx_microcode_load,
	.coresight = &a4xx_coresight,
	.start = a4xx_start,
	.snapshot = a4xx_snapshot,
	.is_sptp_idle = a4xx_is_sptp_idle,
	.enable_pc = a4xx_enable_pc,
	.enable_ppd = a4xx_enable_ppd,
	.pwrlevel_change_settings = a4xx_pwrlevel_change_settings,
	.regulator_enable = a4xx_regulator_enable,
	.regulator_disable = a4xx_regulator_disable,
+249 −135
Original line number Diff line number Diff line
@@ -568,58 +568,68 @@ static void a5xx_enable_pc(struct adreno_device *adreno_dev)
	trace_adreno_sp_tp((unsigned long) __builtin_return_address(0));
};

/*
 * a5xx_gpmu_ucode_load() - Load the ucode into the GPMU RAM
 * @adreno_dev: Pointer to adreno device
static int _gpmu_create_load_cmds(struct adreno_device *adreno_dev,
	uint32_t *ucode, uint32_t size)
{
	uint32_t *start, *cmds;
	uint32_t offset = 0;
	uint32_t cmds_size = size + size / PM4_TYPE4_PKT_SIZE_MAX + 5;

 * GPMU firmare format (one dword per field):
 *	Header block length (length dword field not inclusive)
 *	Header block ID
 *	Header Field 1 tag
 *	Header Field 1 data
 *	Header Field 2 tag
 *	Header Field 2 data (two tag/data pairs minimum for major and minor)
 *	. . .
 *	Header Field M tag
 *	Header Field M data
 *	Microcode block length (length dword field not inclusive)
 *	Microcode block ID
 *	Microcode data Dword 1
 *	Microcode data Dword 2
 *	. . .
 *	Microcode data Dword N
	if (adreno_dev->gpmu_cmds != NULL)
		return 0;

	adreno_dev->gpmu_cmds = kmalloc(cmds_size << 2, GFP_KERNEL);
	if (adreno_dev->gpmu_cmds == NULL)
		return -ENOMEM;

	cmds = adreno_dev->gpmu_cmds;
	start = cmds;

	/* Turn CP protection OFF */
	*cmds++ = cp_type7_packet(CP_SET_PROTECTED_MODE, 1);
	*cmds++ = 0;

	/*
	 * Prebuild the cmd stream to send to the GPU to load
	 * the GPMU firmware
	 */
static int a5xx_gpmu_ucode_load(struct adreno_device *adreno_dev)
	while (size > 0) {
		int tmp_size = size;

		if (size >= PM4_TYPE4_PKT_SIZE_MAX - 1)
			tmp_size = PM4_TYPE4_PKT_SIZE_MAX - 1;

		*cmds++ = cp_type4_packet(
				A5XX_GPMU_INST_RAM_BASE + offset,
				tmp_size);

		memcpy(cmds, &ucode[offset], tmp_size << 2);

		cmds += tmp_size;
		offset += tmp_size;
		size -= tmp_size;
	}

	/* Turn CP protection ON */
	*cmds++ = cp_type7_packet(CP_SET_PROTECTED_MODE, 1);
	*cmds++ = 1;

	adreno_dev->gpmu_cmds_size = cmds - start;

	return 0;
}

static int _gpmu_verify_header(struct adreno_device *adreno_dev,
	uint32_t *header, uint32_t fw_size)
{
	static uint32_t *gpmu_fw_buffer;
	uint32_t *header, *gpmu_fw;
	unsigned int i;
	const struct firmware *fw = NULL;
	struct kgsl_device *device = &adreno_dev->dev;
	const struct adreno_gpu_core *gpucore = adreno_dev->gpucore;
	unsigned int gpmu_major_offset = 0, gpmu_minor_offset = 0;
	unsigned int gpmu_major, gpmu_minor;
	/* Dword size if not specified */
	uint32_t header_size, fw_size, size;
	int ret = -EIO;
	int result;
	uint32_t *cmds = NULL;
	uint32_t offset;
	uint32_t payload_size, payload_size_max = PM4_TYPE4_PKT_SIZE_MAX - 1;
	struct adreno_ringbuffer *rb = ADRENO_CURRENT_RINGBUFFER(adreno_dev);
	uint32_t header_size;

	if (gpucore->gpmufw_name == NULL)
		return -EINVAL;
	result = request_firmware(&fw, gpucore->gpmufw_name, device->dev);
	if (result) {
		KGSL_CORE_ERR("request_firmware (%s) failed: %d\n",
				gpucore->gpmufw_name, result);
		return result;
	}
	header_size = header[0];

	/* Read header block and verify versioning first */
	header = (uint32_t *)fw->data;
	fw_size = fw->size / sizeof(uint32_t);
	/*
	 * The minimum firmware binary size is eight dwords, one for each of
	 * the following fields: GPMU header block length, header block ID,
@@ -629,9 +639,9 @@ static int a5xx_gpmu_ucode_load(struct adreno_device *adreno_dev)
	if (fw_size < 8) {
		KGSL_CORE_ERR("GPMU firmware size is less than 8 dwords, actual size %d\n",
			fw_size);
		goto err;
		return -EIO;
	}
	header_size = header[0];

	/*
	 * The minimum firmware header size is five dwords, one for each of
	 * the following fields: GPMU header block ID, major tag, major value,
@@ -640,22 +650,26 @@ static int a5xx_gpmu_ucode_load(struct adreno_device *adreno_dev)
	if (header_size < 5) {
		KGSL_CORE_ERR("GPMU firmware header size is less than 5 dwords, actual size %d\n",
			header_size);
		goto err;
		return -EIO;
	}

	if (header_size >= fw_size) {
		KGSL_CORE_ERR("GPMU firmware header size (%d) is larger than firmware size (%d)\n",
				header_size, fw_size);
		goto err;
		return -EIO;
	}

	if (header[1] != GPMU_HEADER_ID) {
		KGSL_CORE_ERR("GPMU firmware header ID mis-match: expected 0x%X, actual 0x%X\n",
				GPMU_HEADER_ID, header[1]);
		goto err;
		return -EIO;
	}

	if ((header_size - 1) % 2 != 0) {
		KGSL_CORE_ERR("GPMU firmware header contains incompete (tag, value) pair\n");
		goto err;
		return -EIO;
	}

	for (i = 2; i < header_size; i += 2) {
		switch (header[i]) {
		case HEADER_MAJOR:
@@ -672,17 +686,20 @@ static int a5xx_gpmu_ucode_load(struct adreno_device *adreno_dev)
		default:
			KGSL_CORE_ERR("GPMU unknown header ID %d\n",
					header[i]);
			goto err;
			break;
		}
	}

	if (gpmu_major_offset == 0) {
		KGSL_CORE_ERR("GPMU major version number is missing\n");
		goto err;
		return -EIO;
	}

	if (gpmu_minor_offset == 0) {
		KGSL_CORE_ERR("GPMU minor version number is missing\n");
		goto err;
		return -EIO;
	}

	/*
	 * A value of 0 for both the Major and Minor version fields indicates
	 * the code is un-versioned, and should be allowed to pass all
@@ -693,8 +710,9 @@ static int a5xx_gpmu_ucode_load(struct adreno_device *adreno_dev)
			KGSL_CORE_ERR(
				"GPMU major version mis-match: expected %d, actual %d\n",
				gpucore->gpmu_major, gpmu_major);
			goto err;
			return -EIO;
		}

		/*
		 * Driver should be compatible for firmware with
		 * smaller minor version number.
@@ -703,84 +721,154 @@ static int a5xx_gpmu_ucode_load(struct adreno_device *adreno_dev)
			KGSL_CORE_ERR(
				"GPMU minor version mis-match: expected %d, actual %d\n",
				gpucore->gpmu_minor, gpmu_minor);
			goto err;
			return -EIO;
		}
	}

	/*
	 * Read in the firmware now, 1 dword for header block length field and
	 * header_size for the remaining.
	 */
	gpmu_fw = header + header_size + 1;
	size = *gpmu_fw++;
	if (size == 0 || size > GPMU_INST_RAM_SIZE) {
		KGSL_CORE_ERR("GPMU firmware block size is zero or larger than RAM size\n");
		goto err;
	return 0;
}

static int _gpmu_verify_ucode(struct adreno_device *adreno_dev,
	uint32_t *data, uint32_t fw_size)
{
	uint32_t header_size = data[0];
	uint32_t *block = &data[header_size + 1];
	uint32_t block_size = block[0];
	uint32_t block_id = block[1];

	if (block_size == 0) {
		KGSL_CORE_ERR("GPMU firmware block size is zero\n");
		return -EIO;
	}

	/*
	 * Deduct one (block length field) from the firmware block size to
	 * get the microcode size.
	 */
	size -= 1;
	block_size -= 1;

	if (block_size > GPMU_INST_RAM_SIZE) {
		KGSL_CORE_ERR(
		"GPMU firmware block size is larger than RAM size\n");
		return -EIO;
	}

	/*
	 * The actual microcode size is fw_size - header_size - 2, with
	 * the two block length dword fields deducted.
	 */
	if (size > fw_size - header_size - 2) {
	if (block_size > fw_size - header_size - 2) {
		KGSL_CORE_ERR("GPMU firmware microcode size (%d) larger than actual (%d)\n",
				size, fw_size - header_size - 2);
		goto err;
				block_size, fw_size - header_size - 2);
		return -EIO;
	}
	if (*gpmu_fw++ != GPMU_FIRMWARE_ID) {

	if (block_id != GPMU_FIRMWARE_ID) {
		KGSL_CORE_ERR("GPMU firmware id not mis-match: expected 0x%X, actual 0x%X\n",
			GPMU_FIRMWARE_ID,  header[header_size + 1 + 1]);
		goto err;
			GPMU_FIRMWARE_ID,  block_id);
		return -EIO;
	}

	if (gpmu_fw_buffer == NULL) {
		gpmu_fw_buffer = kmalloc((PM4_TYPE4_PKT_SIZE_MAX << 2),
					GFP_KERNEL);
		if (gpmu_fw_buffer == NULL) {
			KGSL_CORE_ERR("Memory allocation failed.\n");
			ret = -ENOMEM;
			goto err;
		}
	return 0;
}

	offset = 0;
	while (size > 0) {
		cmds = gpmu_fw_buffer;
		if (size >= payload_size_max) {
			*cmds++ = cp_type4_packet(
					A5XX_GPMU_INST_RAM_BASE + offset,
					payload_size_max);
			memcpy(cmds, gpmu_fw, payload_size_max << 2);
			gpmu_fw += payload_size_max;
			offset += payload_size_max;
			payload_size = payload_size_max;
		} else {
			*cmds++ = cp_type4_packet(
					A5XX_GPMU_INST_RAM_BASE + offset,
					size);
			memcpy(cmds, gpmu_fw, size << 2);
			payload_size = size;
/*
 * _load_gpmu_firmware() - Load the ucode into the GPMU RAM
 * @adreno_dev: Pointer to adreno device

 * GPMU firmare format (one dword per field):
 *	Header block length (length dword field not inclusive)
 *	Header block ID
 *	Header Field 1 tag
 *	Header Field 1 data
 *	Header Field 2 tag
 *	Header Field 2 data (two tag/data pairs minimum for major and minor)
 *	. . .
 *	Header Field M tag
 *	Header Field M data
 *	Microcode block length (length dword field not inclusive)
 *	Microcode block ID
 *	Microcode data Dword 1
 *	Microcode data Dword 2
 *	. . .
 *	Microcode data Dword N
 */
static int _load_gpmu_firmware(struct adreno_device *adreno_dev)
{
	uint32_t *data, *header, *block;
	const struct firmware *fw = NULL;
	struct kgsl_device *device = &adreno_dev->dev;
	const struct adreno_gpu_core *gpucore = adreno_dev->gpucore;
	uint32_t fw_size, header_size, block_size;
	int ret;

	/* gpmu fw already saved and verified so do nothing new */
	if (adreno_dev->gpmu_cmds_size != 0)
		return 0;

	if (gpucore->gpmufw_name == NULL)
		return 0;

	ret = request_firmware(&fw, gpucore->gpmufw_name, device->dev);
	if (ret || fw == NULL) {
		KGSL_CORE_ERR("request_firmware (%s) failed: %d\n",
				gpucore->gpmufw_name, ret);
		return ret;
	}
		size -= payload_size;

		ret = adreno_ringbuffer_issuecmds(rb, 0, gpmu_fw_buffer,
						payload_size + 1);
		if (ret) {
			KGSL_CORE_ERR("GPMU firmware loading failed (%d)\n",
					ret);
	data = (uint32_t *)fw->data;
	fw_size = fw->size / sizeof(uint32_t);

	/* Read header block and verify versioning first */
	ret = _gpmu_verify_header(adreno_dev, data, fw_size);
	if (ret)
		goto err;

	/* Now verify the block data */
	ret = _gpmu_verify_ucode(adreno_dev, data, fw_size);
	if (ret)
		goto err;

	header = &data[0];
	header_size = header[0];

	block = &data[header_size + 1];
	block_size = block[0];

	/* Everything is cool, so create some commands */
	ret = _gpmu_create_load_cmds(adreno_dev, &block[2], block_size - 1);
	if (ret)
		goto err;
		}
	}

err:
	if (fw)
		release_firmware(fw);

	return ret;
}

static int _gpmu_send_init_cmds(struct adreno_device *adreno_dev)
{
	struct adreno_ringbuffer *rb = adreno_dev->cur_rb;
	uint32_t *cmds;
	uint32_t size = adreno_dev->gpmu_cmds_size;

	if (size == 0 || adreno_dev->gpmu_cmds == NULL)
		return -EINVAL;

	cmds = adreno_ringbuffer_allocspace(rb, size);
	if (IS_ERR(cmds))
		return PTR_ERR(cmds);
	if (cmds == NULL)
		return -ENOSPC;

	/* Copy to the RB the predefined fw sequence cmds */
	memcpy(cmds, adreno_dev->gpmu_cmds, size << 2);
	adreno_ringbuffer_submit(rb, NULL);

	return adreno_spin_idle(&adreno_dev->dev);
}

/*
 * a5xx_gpmu_start() - Initialize and start the GPMU
 * @adreno_dev: Pointer to adreno device
@@ -788,18 +876,20 @@ err:
 * Load the GPMU microcode, set up any features such as hardware clock gating
 * or IFPC, and take the GPMU out of reset.
 */
static void a5xx_gpmu_start(struct adreno_device *adreno_dev)
static int a5xx_gpmu_start(struct adreno_device *adreno_dev)
{
	int ret;
	unsigned int reg, retry = GPMU_FW_INIT_RETRY;
	struct kgsl_device *device = &adreno_dev->dev;

	if (!ADRENO_FEATURE(adreno_dev, ADRENO_GPMU))
		return;
		return 0;

	ret = a5xx_gpmu_ucode_load(adreno_dev);
	if (ret)
		return;
	ret = _gpmu_send_init_cmds(adreno_dev);
	if (ret) {
		KGSL_CORE_ERR("Failed to program the GPMU: %d\n", ret);
		return ret;
	}

	/* GPMU clock gating setup */
	kgsl_regwrite(device, A5XX_GPMU_WFI_CONFIG, 0x00004014);
@@ -814,10 +904,14 @@ static void a5xx_gpmu_start(struct adreno_device *adreno_dev)
		udelay(1);
		kgsl_regread(device, A5XX_GPMU_GENERAL_0, &reg);
	} while ((reg != 0xBABEFACE) && retry--);
	if (reg != 0xBABEFACE)
	if (reg != 0xBABEFACE) {
		KGSL_CORE_ERR("GPMU firmware initialization timed out\n");
		ret = -ETIMEDOUT;
	}
	else
		set_bit(ADRENO_DEVICE_GPMU_INITIALIZED, &adreno_dev->priv);

	return ret;
}

struct kgsl_hwcg_reg {
@@ -1653,34 +1747,25 @@ static int _preemption_init(
	return cmds - cmds_orig;
}

/*
 * a5xx_hw_init() - Initialize GPU HW using PM4 cmds
 * @adreno_dev: Pointer to adreno device
 *
 * Submit PM4 commands for HW initialization,
 */
int a5xx_hw_init(struct adreno_device *adreno_dev)
static void a5xx_preemption_load(struct adreno_device *adreno_dev)
{
	unsigned int *cmds, *start;
	int status = 0;
	struct kgsl_device *device = &(adreno_dev->dev);
	struct adreno_ringbuffer *rb = adreno_dev->cur_rb;

	if (!adreno_is_preemption_enabled(adreno_dev))
		return;

	cmds = adreno_ringbuffer_allocspace(rb, 40);
	start = cmds;
	if (IS_ERR(cmds))
		return PTR_ERR(cmds);
	if (cmds == NULL)
		return -ENOSPC;
	if (IS_ERR_OR_NULL(cmds))
		return;

	if (adreno_is_preemption_enabled(adreno_dev))
	start = cmds;
	cmds += _preemption_init(adreno_dev, rb, cmds, 0);

	rb->wptr = rb->wptr - (40 - (cmds - start));

	if (cmds == start)
		return 0;

	adreno_ringbuffer_submit(rb, NULL);

	/* idle device to validate hw INIT */
@@ -1690,7 +1775,36 @@ int a5xx_hw_init(struct adreno_device *adreno_dev)
		"hw initialization failed to idle\n");
		kgsl_device_snapshot(device, NULL);
	}
	return status;
}

/*
 * a5xx_hw_init() - Initialize GPU HW using PM4 cmds
 * @adreno_dev: Pointer to adreno device
 *
 * Submit PM4 commands for HW initialization,
 */
static int a5xx_hw_init(struct adreno_device *adreno_dev)
{
	int ret;

	/* Set up LM before initializing the GPMU */
	a5xx_lm_init(adreno_dev);

	/* Program the GPMU */
	ret = a5xx_gpmu_start(adreno_dev);
	if (ret)
		return ret;

	/* Enable limits management */
	a5xx_lm_enable(adreno_dev);

	/* Program preemption */
	a5xx_preemption_load(adreno_dev);

	/* Enable GPMU based power collapse */
	a5xx_enable_pc(adreno_dev);

	return 0;
}

/*
@@ -1789,6 +1903,10 @@ int a5xx_microcode_read(struct adreno_device *adreno_dev)
	if (ret)
		return ret;

	ret = _load_gpmu_firmware(adreno_dev);
	if (ret)
		return ret;

	_load_regfile(adreno_dev);

	return ret;
@@ -3083,18 +3201,14 @@ struct adreno_gpudev adreno_a5xx_gpudev = {
	.perfcounters = &a5xx_perfcounters,
	.vbif_xin_halt_ctrl0_mask = A5XX_VBIF_XIN_HALT_CTRL0_MASK,
	.is_sptp_idle = a5xx_is_sptp_idle,
	.enable_pc = a5xx_enable_pc,
	.regulator_enable = a5xx_regulator_enable,
	.regulator_disable = a5xx_regulator_disable,
	.pwrlevel_change_settings = a5xx_pwrlevel_change_settings,
	.lm_init = a5xx_lm_init,
	.lm_enable = a5xx_lm_enable,
	.preemption_pre_ibsubmit = a5xx_preemption_pre_ibsubmit,
	.preemption_post_ibsubmit =
				a5xx_preemption_post_ibsubmit,
	.preemption_token = a5xx_preemption_token,
	.preemption_init = a5xx_preemption_init,
	.gpmu_start = a5xx_gpmu_start,
	.preemption_schedule = a5xx_preemption_schedule,
	.enable_64bit = a5xx_enable_64bit,
};
+2 −0
Original line number Diff line number Diff line
@@ -455,9 +455,11 @@ void adreno_ringbuffer_close(struct adreno_device *adreno_dev)

	kfree(adreno_dev->pfp_fw);
	kfree(adreno_dev->pm4_fw);
	kfree(adreno_dev->gpmu_cmds);

	adreno_dev->pfp_fw = NULL;
	adreno_dev->pm4_fw = NULL;
	adreno_dev->gpmu_cmds = NULL;

	kgsl_free_global(&adreno_dev->pm4);
	kgsl_free_global(&adreno_dev->pfp);