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

Commit dbe31acf authored by Carter Cooper's avatar Carter Cooper
Browse files

msm: kgsl: Implement new method to load GMU firmware



The new combined GMU firmware image contains load regions
for both the TCMs as well as cached instruction memory.
Implement the procedure to load firmware into the right
regions (ITCM/DTCM/cached instructions region) based on
the load region start address and size information.

Change-Id: Iff0474bd830583b4169563d8b1718ab6795df214
Signed-off-by: default avatarCarter Cooper <ccooper@codeaurora.org>
parent cb16de5c
Loading
Loading
Loading
Loading
+79 −12
Original line number Diff line number Diff line
@@ -1941,6 +1941,79 @@ static int a6xx_rpmh_power_off_gpu(struct kgsl_device *device)
	return 0;
}

/*
 * Gmu FW header format:
 * <32-bit start addr> <32-bit size> <32-bit pad0> <32-bit pad1> <Payload>
 */
#define GMU_FW_HEADER_SIZE 4

#define NUM_LOAD_REGIONS  5

#define GMU_ITCM_VA_START 0x0
#define GMU_ITCM_VA_END   (GMU_ITCM_VA_START + 0x4000) /* 16 KB */

#define GMU_DTCM_VA_START 0x40000
#define GMU_DTCM_VA_END   (GMU_DTCM_VA_START + 0x4000) /* 16 KB */

#define GMU_ICACHE_VA_START 0x4000
#define GMU_ICACHE_VA_END   (GMU_ICACHE_VA_START + 0x3C000) /* 240 KB */

static int load_gmu_fw(struct kgsl_device *device)
{
	struct gmu_device *gmu = &device->gmu;
	uint32_t *fwptr = gmu->fw_image->hostptr;
	int i, j, ret;
	int start_addr, size_in_bytes, num_dwords, tcm_slot;

	/* Allocates & maps memory for GMU cached instructions range */
	ret = allocate_gmu_cached_fw(gmu);
	if (ret)
		return ret;

	for (i = 0; i < NUM_LOAD_REGIONS; i++) {
		start_addr = fwptr[0];
		size_in_bytes = fwptr[1];
		num_dwords = size_in_bytes / sizeof(uint32_t);
		fwptr += GMU_FW_HEADER_SIZE;

		if ((start_addr >= GMU_ITCM_VA_START) &&
				(start_addr < GMU_ITCM_VA_END)) {
			tcm_slot = start_addr / sizeof(uint32_t);

			for (j = 0; j < num_dwords; j++)
				kgsl_gmu_regwrite(device,
					A6XX_GMU_CM3_ITCM_START + tcm_slot + j,
					fwptr[j]);
		} else if ((start_addr >= GMU_DTCM_VA_START) &&
				(start_addr < GMU_DTCM_VA_END)) {
			tcm_slot = (start_addr - GMU_DTCM_VA_START)
				/ sizeof(uint32_t);

			for (j = 0; j < num_dwords; j++)
				kgsl_gmu_regwrite(device,
					A6XX_GMU_CM3_DTCM_START + tcm_slot + j,
					fwptr[j]);
		} else if ((start_addr >= GMU_ICACHE_VA_START) &&
				(start_addr < GMU_ICACHE_VA_END)) {
			if (!is_cached_fw_size_valid(size_in_bytes)) {
				dev_err(&gmu->pdev->dev,
						"GMU firmware size too big\n");
				return -EINVAL;

			}
			memcpy(gmu->cached_fw_image.hostptr,
					fwptr, size_in_bytes);
		}

		fwptr += num_dwords;
	}

	/* Proceed only after the FW is written */
	wmb();
	return 0;
}


/*
 * a6xx_gmu_fw_start() - set up GMU and start FW
 * @device: Pointer to KGSL device
@@ -1952,7 +2025,7 @@ static int a6xx_gmu_fw_start(struct kgsl_device *device,
	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
	struct gmu_device *gmu = &device->gmu;
	struct gmu_memdesc *mem_addr = gmu->hfi_mem;
	int ret, i;
	int ret;
	unsigned int chipid = 0;

	switch (boot_state) {
@@ -1972,15 +2045,9 @@ static int a6xx_gmu_fw_start(struct kgsl_device *device,

		if (gmu->load_mode == TCM_BOOT) {
			/* Load GMU image via AHB bus */
			for (i = 0; i < MAX_GMUFW_SIZE; i++) {
				uint32_t *data = gmu->fw_image.hostptr;

				kgsl_gmu_regwrite(device,
					A6XX_GMU_CM3_ITCM_START + i,
					data[i]);
		}
			/* Prevent leaving reset before the FW is written */
			wmb();
			ret = load_gmu_fw(device);
			if (ret)
				return ret;
		} else {
			dev_err(&gmu->pdev->dev, "Unsupported GMU load mode %d\n",
					gmu->load_mode);
@@ -2195,7 +2262,7 @@ static int _load_gmu_firmware(struct kgsl_device *device)
		return 0;

	/* GMU fw already saved and verified so do nothing new */
	if (gmu->fw_image.hostptr != 0)
	if (gmu->fw_image)
		return 0;

	if (gpucore->gpmufw_name == NULL)
@@ -2214,7 +2281,7 @@ static int _load_gmu_firmware(struct kgsl_device *device)

	/* load into shared memory with GMU */
	if (!ret)
		memcpy(gmu->fw_image.hostptr, fw->data, fw->size);
		memcpy(gmu->fw_image->hostptr, fw->data, fw->size);

	release_firmware(fw);

+60 −33
Original line number Diff line number Diff line
@@ -80,7 +80,7 @@ static const struct gmu_vma vma = {
	/* Cached data segment */
	0x44000, (SZ_256K-SZ_16K),
	/* Cached code segment */
	0x0, (SZ_256K-SZ_16K),
	0x4000, (SZ_256K-SZ_16K),
	/* FW image */
	0x0,
};
@@ -172,30 +172,6 @@ static int alloc_and_map(struct gmu_device *gmu, unsigned int ctx_id,
	return ret;
}

/*
 * allocate_gmu_image() - allocates & maps memory for FW image, the size
 * shall come from the loaded f/w file. Firmware image size shall be
 * less than code cache size. Otherwise, FW may experience performance issue.
 * @gmu: Pointer to GMU device
 * @size: Requested allocation size
 */
int allocate_gmu_image(struct gmu_device *gmu, unsigned int size)
{
	struct gmu_memdesc *md = &gmu->fw_image;

	if (size > vma.cached_csize) {
		dev_err(&gmu->pdev->dev,
			"GMU firmware size too big: %d\n", size);
		return -EINVAL;
	}

	md->size = size;
	md->gmuaddr = vma.image_start;
	md->attr = GMU_CACHED_CODE;

	return alloc_and_map(gmu, GMU_CONTEXT_KERNEL, md, IOMMU_READ);
}

/*
 * allocate_gmu_kmem() - allocates and maps GMU kernel shared memory
 * @gmu: Pointer to GMU device
@@ -242,6 +218,58 @@ static struct gmu_memdesc *allocate_gmu_kmem(struct gmu_device *gmu,
	return md;
}

/*
 * allocate_gmu_image() - allocates & maps memory for FW image, the size
 * shall come from the loaded f/w file.
 * @gmu: Pointer to GMU device
 * @size: Requested allocation size
 */
int allocate_gmu_image(struct gmu_device *gmu, unsigned int size)
{
	/* Allocates & maps memory for GMU FW */
	gmu->fw_image = allocate_gmu_kmem(gmu, size,
				(IOMMU_READ | IOMMU_PRIV));
	if (IS_ERR(gmu->fw_image)) {
		dev_err(&gmu->pdev->dev,
				"GMU firmware image allocation failed\n");
		return -EINVAL;
	}

	return 0;
}

/* Checks if cached fw code size falls within the cached code segment range */
bool is_cached_fw_size_valid(uint32_t size_in_bytes)
{
	if (size_in_bytes > vma.cached_csize)
		return false;

	return true;
}

/*
 * allocate_gmu_cached_fw() - Allocates & maps memory for the cached
 * GMU instructions range. This range has a specific size defined by
 * the GMU memory map. Cached firmware region size should be less than
 * cached code range size. Otherwise, FW may experience performance issues.
 * @gmu: Pointer to GMU device
 */
int allocate_gmu_cached_fw(struct gmu_device *gmu)
{
	struct gmu_memdesc *md = &gmu->cached_fw_image;

	if (gmu->cached_fw_image.hostptr != 0)
		return 0;

	/* Allocate and map memory for the GMU cached instructions range */
	md->size = vma.cached_csize;
	md->gmuaddr = vma.cached_cstart;
	md->attr = GMU_CACHED_CODE;

	return alloc_and_map(gmu, GMU_CONTEXT_KERNEL, md,
			IOMMU_READ | IOMMU_WRITE | IOMMU_PRIV);
}

static int gmu_iommu_cb_probe(struct gmu_device *gmu,
		struct gmu_iommu_context *ctx,
		struct device_node *node)
@@ -331,21 +359,20 @@ static int gmu_iommu_init(struct gmu_device *gmu, struct device_node *node)
static void gmu_kmem_close(struct gmu_device *gmu)
{
	int i;
	struct gmu_memdesc *md = &gmu->fw_image;
	struct gmu_memdesc *md;
	struct gmu_iommu_context *ctx = &gmu_ctx[GMU_CONTEXT_KERNEL];

	/* Free GMU image memory */
	free_gmu_mem(gmu, md);

	/* Unmap image memory */
	/* Unmap and free cached GMU image */
	md = &gmu->cached_fw_image;
	iommu_unmap(ctx->domain,
			gmu->fw_image.gmuaddr,
			gmu->fw_image.size);

			md->gmuaddr,
			md->size);
	free_gmu_mem(gmu, md);

	gmu->hfi_mem = NULL;
	gmu->bw_mem = NULL;
	gmu->dump_mem = NULL;
	gmu->fw_image = NULL;

	/* Unmap all memories in GMU kernel memory pool */
	for (i = 0; i < GMU_KERNEL_ENTRIES; i++) {
+4 −1
Original line number Diff line number Diff line
@@ -221,7 +221,8 @@ struct gmu_device {
	unsigned int gmu2gpu_offset;
	void __iomem *pdc_reg_virt;
	unsigned int gmu_interrupt_num;
	struct gmu_memdesc fw_image;
	struct gmu_memdesc cached_fw_image;
	struct gmu_memdesc *fw_image;
	struct gmu_memdesc *hfi_mem;
	struct gmu_memdesc *bw_mem;
	struct gmu_memdesc *dump_mem;
@@ -261,4 +262,6 @@ int gmu_start(struct kgsl_device *device);
void gmu_stop(struct kgsl_device *device);
int gmu_dcvs_set(struct gmu_device *gmu, unsigned int gpu_pwrlevel,
		unsigned int bus_level);
int allocate_gmu_cached_fw(struct gmu_device *gmu);
bool is_cached_fw_size_valid(uint32_t size_in_bytes);
#endif /* __KGSL_GMU_H */