1*171f378dSSarah Walker // SPDX-License-Identifier: GPL-2.0 OR MIT 2*171f378dSSarah Walker /* Copyright (c) 2024 Imagination Technologies Ltd. */ 3*171f378dSSarah Walker 4*171f378dSSarah Walker #include "pvr_device.h" 5*171f378dSSarah Walker #include "pvr_fw.h" 6*171f378dSSarah Walker #include "pvr_fw_info.h" 7*171f378dSSarah Walker #include "pvr_fw_mips.h" 8*171f378dSSarah Walker #include "pvr_gem.h" 9*171f378dSSarah Walker #include "pvr_rogue_cr_defs.h" 10*171f378dSSarah Walker #include "pvr_rogue_riscv.h" 11*171f378dSSarah Walker #include "pvr_vm.h" 12*171f378dSSarah Walker 13*171f378dSSarah Walker #include <linux/compiler.h> 14*171f378dSSarah Walker #include <linux/delay.h> 15*171f378dSSarah Walker #include <linux/firmware.h> 16*171f378dSSarah Walker #include <linux/ktime.h> 17*171f378dSSarah Walker #include <linux/types.h> 18*171f378dSSarah Walker 19*171f378dSSarah Walker #define ROGUE_FW_HEAP_RISCV_SHIFT 25 /* 32 MB */ 20*171f378dSSarah Walker #define ROGUE_FW_HEAP_RISCV_SIZE (1u << ROGUE_FW_HEAP_RISCV_SHIFT) 21*171f378dSSarah Walker 22*171f378dSSarah Walker static int 23*171f378dSSarah Walker pvr_riscv_wrapper_init(struct pvr_device *pvr_dev) 24*171f378dSSarah Walker { 25*171f378dSSarah Walker const u64 common_opts = 26*171f378dSSarah Walker ((u64)(ROGUE_FW_HEAP_RISCV_SIZE >> FWCORE_ADDR_REMAP_CONFIG0_SIZE_ALIGNSHIFT) 27*171f378dSSarah Walker << ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG0_SIZE_SHIFT) | 28*171f378dSSarah Walker ((u64)MMU_CONTEXT_MAPPING_FWPRIV 29*171f378dSSarah Walker << FWCORE_ADDR_REMAP_CONFIG0_MMU_CONTEXT_SHIFT); 30*171f378dSSarah Walker 31*171f378dSSarah Walker u64 code_addr = pvr_fw_obj_get_gpu_addr(pvr_dev->fw_dev.mem.code_obj); 32*171f378dSSarah Walker u64 data_addr = pvr_fw_obj_get_gpu_addr(pvr_dev->fw_dev.mem.data_obj); 33*171f378dSSarah Walker 34*171f378dSSarah Walker /* This condition allows us to OR the addresses into the register directly. */ 35*171f378dSSarah Walker static_assert(ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG1_DEVVADDR_SHIFT == 36*171f378dSSarah Walker ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG1_DEVVADDR_ALIGNSHIFT); 37*171f378dSSarah Walker 38*171f378dSSarah Walker WARN_ON(code_addr & ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG1_DEVVADDR_CLRMSK); 39*171f378dSSarah Walker WARN_ON(data_addr & ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG1_DEVVADDR_CLRMSK); 40*171f378dSSarah Walker 41*171f378dSSarah Walker pvr_cr_write64(pvr_dev, ROGUE_RISCVFW_REGION_REMAP_CR(BOOTLDR_CODE), 42*171f378dSSarah Walker code_addr | common_opts | ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG0_FETCH_EN_EN); 43*171f378dSSarah Walker 44*171f378dSSarah Walker pvr_cr_write64(pvr_dev, ROGUE_RISCVFW_REGION_REMAP_CR(BOOTLDR_DATA), 45*171f378dSSarah Walker data_addr | common_opts | 46*171f378dSSarah Walker ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG0_LOAD_STORE_EN_EN); 47*171f378dSSarah Walker 48*171f378dSSarah Walker /* Garten IDLE bit controlled by RISC-V. */ 49*171f378dSSarah Walker pvr_cr_write64(pvr_dev, ROGUE_CR_MTS_GARTEN_WRAPPER_CONFIG, 50*171f378dSSarah Walker ROGUE_CR_MTS_GARTEN_WRAPPER_CONFIG_IDLE_CTRL_META); 51*171f378dSSarah Walker 52*171f378dSSarah Walker return 0; 53*171f378dSSarah Walker } 54*171f378dSSarah Walker 55*171f378dSSarah Walker struct rogue_riscv_fw_boot_data { 56*171f378dSSarah Walker u64 coremem_code_dev_vaddr; 57*171f378dSSarah Walker u64 coremem_data_dev_vaddr; 58*171f378dSSarah Walker u32 coremem_code_fw_addr; 59*171f378dSSarah Walker u32 coremem_data_fw_addr; 60*171f378dSSarah Walker u32 coremem_code_size; 61*171f378dSSarah Walker u32 coremem_data_size; 62*171f378dSSarah Walker u32 flags; 63*171f378dSSarah Walker u32 reserved; 64*171f378dSSarah Walker }; 65*171f378dSSarah Walker 66*171f378dSSarah Walker static int 67*171f378dSSarah Walker pvr_riscv_fw_process(struct pvr_device *pvr_dev, const u8 *fw, 68*171f378dSSarah Walker u8 *fw_code_ptr, u8 *fw_data_ptr, u8 *fw_core_code_ptr, u8 *fw_core_data_ptr, 69*171f378dSSarah Walker u32 core_code_alloc_size) 70*171f378dSSarah Walker { 71*171f378dSSarah Walker struct pvr_fw_device *fw_dev = &pvr_dev->fw_dev; 72*171f378dSSarah Walker struct pvr_fw_mem *fw_mem = &fw_dev->mem; 73*171f378dSSarah Walker struct rogue_riscv_fw_boot_data *boot_data; 74*171f378dSSarah Walker int err; 75*171f378dSSarah Walker 76*171f378dSSarah Walker err = pvr_fw_process_elf_command_stream(pvr_dev, fw, fw_code_ptr, fw_data_ptr, 77*171f378dSSarah Walker fw_core_code_ptr, fw_core_data_ptr); 78*171f378dSSarah Walker if (err) 79*171f378dSSarah Walker goto err_out; 80*171f378dSSarah Walker 81*171f378dSSarah Walker boot_data = (struct rogue_riscv_fw_boot_data *)fw_data_ptr; 82*171f378dSSarah Walker 83*171f378dSSarah Walker if (fw_mem->core_code_obj) { 84*171f378dSSarah Walker boot_data->coremem_code_dev_vaddr = pvr_fw_obj_get_gpu_addr(fw_mem->core_code_obj); 85*171f378dSSarah Walker pvr_fw_object_get_fw_addr(fw_mem->core_code_obj, &boot_data->coremem_code_fw_addr); 86*171f378dSSarah Walker boot_data->coremem_code_size = pvr_fw_obj_get_object_size(fw_mem->core_code_obj); 87*171f378dSSarah Walker } 88*171f378dSSarah Walker 89*171f378dSSarah Walker if (fw_mem->core_data_obj) { 90*171f378dSSarah Walker boot_data->coremem_data_dev_vaddr = pvr_fw_obj_get_gpu_addr(fw_mem->core_data_obj); 91*171f378dSSarah Walker pvr_fw_object_get_fw_addr(fw_mem->core_data_obj, &boot_data->coremem_data_fw_addr); 92*171f378dSSarah Walker boot_data->coremem_data_size = pvr_fw_obj_get_object_size(fw_mem->core_data_obj); 93*171f378dSSarah Walker } 94*171f378dSSarah Walker 95*171f378dSSarah Walker return 0; 96*171f378dSSarah Walker 97*171f378dSSarah Walker err_out: 98*171f378dSSarah Walker return err; 99*171f378dSSarah Walker } 100*171f378dSSarah Walker 101*171f378dSSarah Walker static int 102*171f378dSSarah Walker pvr_riscv_init(struct pvr_device *pvr_dev) 103*171f378dSSarah Walker { 104*171f378dSSarah Walker pvr_fw_heap_info_init(pvr_dev, ROGUE_FW_HEAP_RISCV_SHIFT, 0); 105*171f378dSSarah Walker 106*171f378dSSarah Walker return 0; 107*171f378dSSarah Walker } 108*171f378dSSarah Walker 109*171f378dSSarah Walker static u32 110*171f378dSSarah Walker pvr_riscv_get_fw_addr_with_offset(struct pvr_fw_object *fw_obj, u32 offset) 111*171f378dSSarah Walker { 112*171f378dSSarah Walker u32 fw_addr = fw_obj->fw_addr_offset + offset; 113*171f378dSSarah Walker 114*171f378dSSarah Walker /* RISC-V cacheability is determined by address. */ 115*171f378dSSarah Walker if (fw_obj->gem->flags & PVR_BO_FW_FLAGS_DEVICE_UNCACHED) 116*171f378dSSarah Walker fw_addr |= ROGUE_RISCVFW_REGION_BASE(SHARED_UNCACHED_DATA); 117*171f378dSSarah Walker else 118*171f378dSSarah Walker fw_addr |= ROGUE_RISCVFW_REGION_BASE(SHARED_CACHED_DATA); 119*171f378dSSarah Walker 120*171f378dSSarah Walker return fw_addr; 121*171f378dSSarah Walker } 122*171f378dSSarah Walker 123*171f378dSSarah Walker static int 124*171f378dSSarah Walker pvr_riscv_vm_map(struct pvr_device *pvr_dev, struct pvr_fw_object *fw_obj) 125*171f378dSSarah Walker { 126*171f378dSSarah Walker struct pvr_gem_object *pvr_obj = fw_obj->gem; 127*171f378dSSarah Walker 128*171f378dSSarah Walker return pvr_vm_map(pvr_dev->kernel_vm_ctx, pvr_obj, 0, fw_obj->fw_mm_node.start, 129*171f378dSSarah Walker pvr_gem_object_size(pvr_obj)); 130*171f378dSSarah Walker } 131*171f378dSSarah Walker 132*171f378dSSarah Walker static void 133*171f378dSSarah Walker pvr_riscv_vm_unmap(struct pvr_device *pvr_dev, struct pvr_fw_object *fw_obj) 134*171f378dSSarah Walker { 135*171f378dSSarah Walker struct pvr_gem_object *pvr_obj = fw_obj->gem; 136*171f378dSSarah Walker 137*171f378dSSarah Walker pvr_vm_unmap_obj(pvr_dev->kernel_vm_ctx, pvr_obj, 138*171f378dSSarah Walker fw_obj->fw_mm_node.start, fw_obj->fw_mm_node.size); 139*171f378dSSarah Walker } 140*171f378dSSarah Walker 141*171f378dSSarah Walker static bool 142*171f378dSSarah Walker pvr_riscv_irq_pending(struct pvr_device *pvr_dev) 143*171f378dSSarah Walker { 144*171f378dSSarah Walker return pvr_cr_read32(pvr_dev, ROGUE_CR_IRQ_OS0_EVENT_STATUS) & 145*171f378dSSarah Walker ROGUE_CR_IRQ_OS0_EVENT_STATUS_SOURCE_EN; 146*171f378dSSarah Walker } 147*171f378dSSarah Walker 148*171f378dSSarah Walker static void 149*171f378dSSarah Walker pvr_riscv_irq_clear(struct pvr_device *pvr_dev) 150*171f378dSSarah Walker { 151*171f378dSSarah Walker pvr_cr_write32(pvr_dev, ROGUE_CR_IRQ_OS0_EVENT_CLEAR, 152*171f378dSSarah Walker ROGUE_CR_IRQ_OS0_EVENT_CLEAR_SOURCE_EN); 153*171f378dSSarah Walker } 154*171f378dSSarah Walker 155*171f378dSSarah Walker const struct pvr_fw_defs pvr_fw_defs_riscv = { 156*171f378dSSarah Walker .init = pvr_riscv_init, 157*171f378dSSarah Walker .fw_process = pvr_riscv_fw_process, 158*171f378dSSarah Walker .vm_map = pvr_riscv_vm_map, 159*171f378dSSarah Walker .vm_unmap = pvr_riscv_vm_unmap, 160*171f378dSSarah Walker .get_fw_addr_with_offset = pvr_riscv_get_fw_addr_with_offset, 161*171f378dSSarah Walker .wrapper_init = pvr_riscv_wrapper_init, 162*171f378dSSarah Walker .irq_pending = pvr_riscv_irq_pending, 163*171f378dSSarah Walker .irq_clear = pvr_riscv_irq_clear, 164*171f378dSSarah Walker .has_fixed_data_addr = false, 165*171f378dSSarah Walker }; 166