Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm/imagination: Add RISC-V firmware processor support

Newer PowerVR GPUs (such as the BXS-4-64 MC1) use a RISC-V firmware
processor instead of the previous MIPS or META.

The current version of this patch depends on a patch[1] which exists in
drm-misc-fixes, but has not yet made it back to drm-misc-next (the
target of this patch). That patch adds the function pvr_vm_unmap_obj()
which is used here.

[1]: https://lore.kernel.org/r/20250226-hold-drm_gem_gpuva-lock-for-unmap-v2-1-3fdacded227f@imgtec.com

Signed-off-by: Sarah Walker <sarah.walker@imgtec.com>
Reviewed-by: Frank Binns <frank.binns@imgtec.com>
Link: https://lore.kernel.org/r/20250410-sets-bxs-4-64-patch-v1-v6-14-eda620c5865f@imgtec.com
Signed-off-by: Matt Coster <matt.coster@imgtec.com>

authored by

Sarah Walker and committed by
Matt Coster
171f378d f48485ab

+244 -8
+1
drivers/gpu/drm/imagination/Makefile
··· 12 12 pvr_fw.o \ 13 13 pvr_fw_meta.o \ 14 14 pvr_fw_mips.o \ 15 + pvr_fw_riscv.o \ 15 16 pvr_fw_startstop.o \ 16 17 pvr_fw_trace.o \ 17 18 pvr_fw_util.o \
+10 -8
drivers/gpu/drm/imagination/pvr_fw.c
··· 941 941 static const struct pvr_fw_defs *fw_defs[PVR_FW_PROCESSOR_TYPE_COUNT] = { 942 942 [PVR_FW_PROCESSOR_TYPE_META] = &pvr_fw_defs_meta, 943 943 [PVR_FW_PROCESSOR_TYPE_MIPS] = &pvr_fw_defs_mips, 944 - [PVR_FW_PROCESSOR_TYPE_RISCV] = NULL, 944 + [PVR_FW_PROCESSOR_TYPE_RISCV] = &pvr_fw_defs_riscv, 945 945 }; 946 946 947 947 u32 kccb_size_log2 = ROGUE_FWIF_KCCB_NUMCMDS_LOG2_DEFAULT; ··· 953 953 return -EINVAL; 954 954 955 955 fw_dev->defs = fw_defs[fw_dev->processor_type]; 956 - 957 - /* 958 - * Not all firmware processor types are currently supported. 959 - * Once they are, this check can be removed. 960 - */ 961 - if (!fw_dev->defs) 962 - return -EINVAL; 963 956 964 957 err = fw_dev->defs->init(pvr_dev); 965 958 if (err) ··· 1457 1464 struct pvr_device *pvr_dev = to_pvr_device(gem_from_pvr_gem(pvr_obj)->dev); 1458 1465 1459 1466 *fw_addr_out = pvr_dev->fw_dev.defs->get_fw_addr_with_offset(fw_obj, offset); 1467 + } 1468 + 1469 + u64 1470 + pvr_fw_obj_get_gpu_addr(struct pvr_fw_object *fw_obj) 1471 + { 1472 + struct pvr_device *pvr_dev = to_pvr_device(gem_from_pvr_gem(fw_obj->gem)->dev); 1473 + struct pvr_fw_device *fw_dev = &pvr_dev->fw_dev; 1474 + 1475 + return fw_dev->fw_heap_info.gpu_addr + fw_obj->fw_addr_offset; 1460 1476 } 1461 1477 1462 1478 /*
+10
drivers/gpu/drm/imagination/pvr_fw.h
··· 392 392 393 393 extern const struct pvr_fw_defs pvr_fw_defs_meta; 394 394 extern const struct pvr_fw_defs pvr_fw_defs_mips; 395 + extern const struct pvr_fw_defs pvr_fw_defs_riscv; 395 396 396 397 int pvr_fw_validate_init_device_info(struct pvr_device *pvr_dev); 397 398 int pvr_fw_init(struct pvr_device *pvr_dev); ··· 477 476 pvr_fw_object_get_fw_addr(struct pvr_fw_object *fw_obj, u32 *fw_addr_out) 478 477 { 479 478 pvr_fw_object_get_fw_addr_offset(fw_obj, 0, fw_addr_out); 479 + } 480 + 481 + u64 482 + pvr_fw_obj_get_gpu_addr(struct pvr_fw_object *fw_obj); 483 + 484 + static __always_inline size_t 485 + pvr_fw_obj_get_object_size(struct pvr_fw_object *fw_obj) 486 + { 487 + return pvr_gem_object_size(fw_obj->gem); 480 488 } 481 489 482 490 /* Util functions defined in pvr_fw_util.c. These are intended for use in pvr_fw_<arch>.c files. */
+165
drivers/gpu/drm/imagination/pvr_fw_riscv.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 OR MIT 2 + /* Copyright (c) 2024 Imagination Technologies Ltd. */ 3 + 4 + #include "pvr_device.h" 5 + #include "pvr_fw.h" 6 + #include "pvr_fw_info.h" 7 + #include "pvr_fw_mips.h" 8 + #include "pvr_gem.h" 9 + #include "pvr_rogue_cr_defs.h" 10 + #include "pvr_rogue_riscv.h" 11 + #include "pvr_vm.h" 12 + 13 + #include <linux/compiler.h> 14 + #include <linux/delay.h> 15 + #include <linux/firmware.h> 16 + #include <linux/ktime.h> 17 + #include <linux/types.h> 18 + 19 + #define ROGUE_FW_HEAP_RISCV_SHIFT 25 /* 32 MB */ 20 + #define ROGUE_FW_HEAP_RISCV_SIZE (1u << ROGUE_FW_HEAP_RISCV_SHIFT) 21 + 22 + static int 23 + pvr_riscv_wrapper_init(struct pvr_device *pvr_dev) 24 + { 25 + const u64 common_opts = 26 + ((u64)(ROGUE_FW_HEAP_RISCV_SIZE >> FWCORE_ADDR_REMAP_CONFIG0_SIZE_ALIGNSHIFT) 27 + << ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG0_SIZE_SHIFT) | 28 + ((u64)MMU_CONTEXT_MAPPING_FWPRIV 29 + << FWCORE_ADDR_REMAP_CONFIG0_MMU_CONTEXT_SHIFT); 30 + 31 + u64 code_addr = pvr_fw_obj_get_gpu_addr(pvr_dev->fw_dev.mem.code_obj); 32 + u64 data_addr = pvr_fw_obj_get_gpu_addr(pvr_dev->fw_dev.mem.data_obj); 33 + 34 + /* This condition allows us to OR the addresses into the register directly. */ 35 + static_assert(ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG1_DEVVADDR_SHIFT == 36 + ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG1_DEVVADDR_ALIGNSHIFT); 37 + 38 + WARN_ON(code_addr & ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG1_DEVVADDR_CLRMSK); 39 + WARN_ON(data_addr & ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG1_DEVVADDR_CLRMSK); 40 + 41 + pvr_cr_write64(pvr_dev, ROGUE_RISCVFW_REGION_REMAP_CR(BOOTLDR_CODE), 42 + code_addr | common_opts | ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG0_FETCH_EN_EN); 43 + 44 + pvr_cr_write64(pvr_dev, ROGUE_RISCVFW_REGION_REMAP_CR(BOOTLDR_DATA), 45 + data_addr | common_opts | 46 + ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG0_LOAD_STORE_EN_EN); 47 + 48 + /* Garten IDLE bit controlled by RISC-V. */ 49 + pvr_cr_write64(pvr_dev, ROGUE_CR_MTS_GARTEN_WRAPPER_CONFIG, 50 + ROGUE_CR_MTS_GARTEN_WRAPPER_CONFIG_IDLE_CTRL_META); 51 + 52 + return 0; 53 + } 54 + 55 + struct rogue_riscv_fw_boot_data { 56 + u64 coremem_code_dev_vaddr; 57 + u64 coremem_data_dev_vaddr; 58 + u32 coremem_code_fw_addr; 59 + u32 coremem_data_fw_addr; 60 + u32 coremem_code_size; 61 + u32 coremem_data_size; 62 + u32 flags; 63 + u32 reserved; 64 + }; 65 + 66 + static int 67 + pvr_riscv_fw_process(struct pvr_device *pvr_dev, const u8 *fw, 68 + u8 *fw_code_ptr, u8 *fw_data_ptr, u8 *fw_core_code_ptr, u8 *fw_core_data_ptr, 69 + u32 core_code_alloc_size) 70 + { 71 + struct pvr_fw_device *fw_dev = &pvr_dev->fw_dev; 72 + struct pvr_fw_mem *fw_mem = &fw_dev->mem; 73 + struct rogue_riscv_fw_boot_data *boot_data; 74 + int err; 75 + 76 + err = pvr_fw_process_elf_command_stream(pvr_dev, fw, fw_code_ptr, fw_data_ptr, 77 + fw_core_code_ptr, fw_core_data_ptr); 78 + if (err) 79 + goto err_out; 80 + 81 + boot_data = (struct rogue_riscv_fw_boot_data *)fw_data_ptr; 82 + 83 + if (fw_mem->core_code_obj) { 84 + boot_data->coremem_code_dev_vaddr = pvr_fw_obj_get_gpu_addr(fw_mem->core_code_obj); 85 + pvr_fw_object_get_fw_addr(fw_mem->core_code_obj, &boot_data->coremem_code_fw_addr); 86 + boot_data->coremem_code_size = pvr_fw_obj_get_object_size(fw_mem->core_code_obj); 87 + } 88 + 89 + if (fw_mem->core_data_obj) { 90 + boot_data->coremem_data_dev_vaddr = pvr_fw_obj_get_gpu_addr(fw_mem->core_data_obj); 91 + pvr_fw_object_get_fw_addr(fw_mem->core_data_obj, &boot_data->coremem_data_fw_addr); 92 + boot_data->coremem_data_size = pvr_fw_obj_get_object_size(fw_mem->core_data_obj); 93 + } 94 + 95 + return 0; 96 + 97 + err_out: 98 + return err; 99 + } 100 + 101 + static int 102 + pvr_riscv_init(struct pvr_device *pvr_dev) 103 + { 104 + pvr_fw_heap_info_init(pvr_dev, ROGUE_FW_HEAP_RISCV_SHIFT, 0); 105 + 106 + return 0; 107 + } 108 + 109 + static u32 110 + pvr_riscv_get_fw_addr_with_offset(struct pvr_fw_object *fw_obj, u32 offset) 111 + { 112 + u32 fw_addr = fw_obj->fw_addr_offset + offset; 113 + 114 + /* RISC-V cacheability is determined by address. */ 115 + if (fw_obj->gem->flags & PVR_BO_FW_FLAGS_DEVICE_UNCACHED) 116 + fw_addr |= ROGUE_RISCVFW_REGION_BASE(SHARED_UNCACHED_DATA); 117 + else 118 + fw_addr |= ROGUE_RISCVFW_REGION_BASE(SHARED_CACHED_DATA); 119 + 120 + return fw_addr; 121 + } 122 + 123 + static int 124 + pvr_riscv_vm_map(struct pvr_device *pvr_dev, struct pvr_fw_object *fw_obj) 125 + { 126 + struct pvr_gem_object *pvr_obj = fw_obj->gem; 127 + 128 + return pvr_vm_map(pvr_dev->kernel_vm_ctx, pvr_obj, 0, fw_obj->fw_mm_node.start, 129 + pvr_gem_object_size(pvr_obj)); 130 + } 131 + 132 + static void 133 + pvr_riscv_vm_unmap(struct pvr_device *pvr_dev, struct pvr_fw_object *fw_obj) 134 + { 135 + struct pvr_gem_object *pvr_obj = fw_obj->gem; 136 + 137 + pvr_vm_unmap_obj(pvr_dev->kernel_vm_ctx, pvr_obj, 138 + fw_obj->fw_mm_node.start, fw_obj->fw_mm_node.size); 139 + } 140 + 141 + static bool 142 + pvr_riscv_irq_pending(struct pvr_device *pvr_dev) 143 + { 144 + return pvr_cr_read32(pvr_dev, ROGUE_CR_IRQ_OS0_EVENT_STATUS) & 145 + ROGUE_CR_IRQ_OS0_EVENT_STATUS_SOURCE_EN; 146 + } 147 + 148 + static void 149 + pvr_riscv_irq_clear(struct pvr_device *pvr_dev) 150 + { 151 + pvr_cr_write32(pvr_dev, ROGUE_CR_IRQ_OS0_EVENT_CLEAR, 152 + ROGUE_CR_IRQ_OS0_EVENT_CLEAR_SOURCE_EN); 153 + } 154 + 155 + const struct pvr_fw_defs pvr_fw_defs_riscv = { 156 + .init = pvr_riscv_init, 157 + .fw_process = pvr_riscv_fw_process, 158 + .vm_map = pvr_riscv_vm_map, 159 + .vm_unmap = pvr_riscv_vm_unmap, 160 + .get_fw_addr_with_offset = pvr_riscv_get_fw_addr_with_offset, 161 + .wrapper_init = pvr_riscv_wrapper_init, 162 + .irq_pending = pvr_riscv_irq_pending, 163 + .irq_clear = pvr_riscv_irq_clear, 164 + .has_fixed_data_addr = false, 165 + };
+17
drivers/gpu/drm/imagination/pvr_fw_startstop.c
··· 49 49 50 50 pvr_cr_write64(pvr_dev, BIF_CAT_BASEX(MMU_CONTEXT_MAPPING_FWPRIV), 51 51 pc_addr); 52 + 53 + if (pvr_dev->fw_dev.processor_type == PVR_FW_PROCESSOR_TYPE_RISCV) { 54 + pc_addr = (((u64)pc_dma_addr >> ROGUE_CR_FWCORE_MEM_CAT_BASE0_ADDR_ALIGNSHIFT) 55 + << ROGUE_CR_FWCORE_MEM_CAT_BASE0_ADDR_SHIFT) & 56 + ~ROGUE_CR_FWCORE_MEM_CAT_BASE0_ADDR_CLRMSK; 57 + 58 + pvr_cr_write64(pvr_dev, FWCORE_MEM_CAT_BASEX(MMU_CONTEXT_MAPPING_FWPRIV), pc_addr); 59 + } 52 60 } 53 61 54 62 static int ··· 122 114 (void)pvr_cr_read32(pvr_dev, ROGUE_CR_SYS_BUS_SECURE); /* Fence write */ 123 115 } 124 116 117 + if (pvr_dev->fw_dev.processor_type == PVR_FW_PROCESSOR_TYPE_RISCV) 118 + pvr_cr_write32(pvr_dev, ROGUE_CR_FWCORE_BOOT, 0); 119 + 125 120 /* Set Rogue in soft-reset. */ 126 121 pvr_cr_write64(pvr_dev, ROGUE_CR_SOFT_RESET, soft_reset_mask); 127 122 if (has_reset2) ··· 177 166 178 167 /* ... and afterwards. */ 179 168 udelay(3); 169 + 170 + if (pvr_dev->fw_dev.processor_type == PVR_FW_PROCESSOR_TYPE_RISCV) { 171 + /* Boot the FW. */ 172 + pvr_cr_write32(pvr_dev, ROGUE_CR_FWCORE_BOOT, 1); 173 + udelay(3); 174 + } 180 175 181 176 return 0; 182 177
+41
drivers/gpu/drm/imagination/pvr_rogue_riscv.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 OR MIT */ 2 + /* Copyright (c) 2024 Imagination Technologies Ltd. */ 3 + 4 + #ifndef PVR_ROGUE_RISCV_H 5 + #define PVR_ROGUE_RISCV_H 6 + 7 + #include "pvr_rogue_cr_defs.h" 8 + 9 + #include <linux/bitops.h> 10 + #include <linux/sizes.h> 11 + #include <linux/types.h> 12 + 13 + #define ROGUE_RISCVFW_REGION_SIZE SZ_256M 14 + #define ROGUE_RISCVFW_REGION_SHIFT __ffs(ROGUE_RISCVFW_REGION_SIZE) 15 + 16 + enum rogue_riscvfw_region { 17 + ROGUE_RISCV_REGION__RESERVED_0 = 0, 18 + ROGUE_RISCV_REGION__RESERVED_1, 19 + ROGUE_RISCV_REGION_SOCIF, 20 + ROGUE_RISCV_REGION__RESERVED_3, 21 + ROGUE_RISCV_REGION__RESERVED_4, 22 + ROGUE_RISCV_REGION_BOOTLDR_DATA, 23 + ROGUE_RISCV_REGION_SHARED_CACHED_DATA, 24 + ROGUE_RISCV_REGION__RESERVED_7, 25 + ROGUE_RISCV_REGION_COREMEM, 26 + ROGUE_RISCV_REGION__RESERVED_9, 27 + ROGUE_RISCV_REGION__RESERVED_A, 28 + ROGUE_RISCV_REGION__RESERVED_B, 29 + ROGUE_RISCV_REGION_BOOTLDR_CODE, 30 + ROGUE_RISCV_REGION_SHARED_UNCACHED_DATA, 31 + ROGUE_RISCV_REGION__RESERVED_E, 32 + ROGUE_RISCV_REGION__RESERVED_F, 33 + 34 + ROGUE_RISCV_REGION__COUNT, 35 + }; 36 + 37 + #define ROGUE_RISCVFW_REGION_BASE(r) ((u32)(ROGUE_RISCV_REGION_##r) << ROGUE_RISCVFW_REGION_SHIFT) 38 + #define ROGUE_RISCVFW_REGION_REMAP_CR(r) \ 39 + (ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG0 + (u32)(ROGUE_RISCV_REGION_##r) * 8U) 40 + 41 + #endif /* PVR_ROGUE_RISCV_H */