mirror of
				https://github.com/torvalds/linux.git
				synced 2025-10-31 16:48:26 +02:00 
			
		
		
		
	 171f378d2a
			
		
	
	
		171f378d2a
		
			
		
	
	
	
	
		
			
			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>
		
			
				
	
	
		
			323 lines
		
	
	
	
		
			9.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			323 lines
		
	
	
	
		
			9.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // SPDX-License-Identifier: GPL-2.0-only OR MIT
 | |
| /* Copyright (c) 2023 Imagination Technologies Ltd. */
 | |
| 
 | |
| #include "pvr_device.h"
 | |
| #include "pvr_fw.h"
 | |
| #include "pvr_fw_meta.h"
 | |
| #include "pvr_fw_startstop.h"
 | |
| #include "pvr_rogue_cr_defs.h"
 | |
| #include "pvr_rogue_meta.h"
 | |
| #include "pvr_vm.h"
 | |
| 
 | |
| #include <linux/compiler.h>
 | |
| #include <linux/delay.h>
 | |
| #include <linux/ktime.h>
 | |
| #include <linux/types.h>
 | |
| 
 | |
| #define POLL_TIMEOUT_USEC 1000000
 | |
| 
 | |
| static void
 | |
| rogue_axi_ace_list_init(struct pvr_device *pvr_dev)
 | |
| {
 | |
| 	/* Setup AXI-ACE config. Set everything to outer cache. */
 | |
| 	u64 reg_val =
 | |
| 		(3U << ROGUE_CR_AXI_ACE_LITE_CONFIGURATION_AWDOMAIN_NON_SNOOPING_SHIFT) |
 | |
| 		(3U << ROGUE_CR_AXI_ACE_LITE_CONFIGURATION_ARDOMAIN_NON_SNOOPING_SHIFT) |
 | |
| 		(2U << ROGUE_CR_AXI_ACE_LITE_CONFIGURATION_ARDOMAIN_CACHE_MAINTENANCE_SHIFT) |
 | |
| 		(2U << ROGUE_CR_AXI_ACE_LITE_CONFIGURATION_AWDOMAIN_COHERENT_SHIFT) |
 | |
| 		(2U << ROGUE_CR_AXI_ACE_LITE_CONFIGURATION_ARDOMAIN_COHERENT_SHIFT) |
 | |
| 		(2U << ROGUE_CR_AXI_ACE_LITE_CONFIGURATION_AWCACHE_COHERENT_SHIFT) |
 | |
| 		(2U << ROGUE_CR_AXI_ACE_LITE_CONFIGURATION_ARCACHE_COHERENT_SHIFT) |
 | |
| 		(2U << ROGUE_CR_AXI_ACE_LITE_CONFIGURATION_ARCACHE_CACHE_MAINTENANCE_SHIFT);
 | |
| 
 | |
| 	pvr_cr_write64(pvr_dev, ROGUE_CR_AXI_ACE_LITE_CONFIGURATION, reg_val);
 | |
| }
 | |
| 
 | |
| static void
 | |
| rogue_bif_init(struct pvr_device *pvr_dev)
 | |
| {
 | |
| 	dma_addr_t pc_dma_addr;
 | |
| 	u64 pc_addr;
 | |
| 
 | |
| 	/* Acquire the address of the Kernel Page Catalogue. */
 | |
| 	pc_dma_addr = pvr_vm_get_page_table_root_addr(pvr_dev->kernel_vm_ctx);
 | |
| 
 | |
| 	/* Write the kernel catalogue base. */
 | |
| 	pc_addr = ((((u64)pc_dma_addr >> ROGUE_CR_BIF_CAT_BASE0_ADDR_ALIGNSHIFT)
 | |
| 		    << ROGUE_CR_BIF_CAT_BASE0_ADDR_SHIFT) &
 | |
| 		   ~ROGUE_CR_BIF_CAT_BASE0_ADDR_CLRMSK);
 | |
| 
 | |
| 	pvr_cr_write64(pvr_dev, BIF_CAT_BASEX(MMU_CONTEXT_MAPPING_FWPRIV),
 | |
| 		       pc_addr);
 | |
| 
 | |
| 	if (pvr_dev->fw_dev.processor_type == PVR_FW_PROCESSOR_TYPE_RISCV) {
 | |
| 		pc_addr = (((u64)pc_dma_addr >> ROGUE_CR_FWCORE_MEM_CAT_BASE0_ADDR_ALIGNSHIFT)
 | |
| 			   << ROGUE_CR_FWCORE_MEM_CAT_BASE0_ADDR_SHIFT) &
 | |
| 			  ~ROGUE_CR_FWCORE_MEM_CAT_BASE0_ADDR_CLRMSK;
 | |
| 
 | |
| 		pvr_cr_write64(pvr_dev, FWCORE_MEM_CAT_BASEX(MMU_CONTEXT_MAPPING_FWPRIV), pc_addr);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static int
 | |
| rogue_slc_init(struct pvr_device *pvr_dev)
 | |
| {
 | |
| 	u16 slc_cache_line_size_bits;
 | |
| 	u32 reg_val;
 | |
| 	int err;
 | |
| 
 | |
| 	/*
 | |
| 	 * SLC Misc control.
 | |
| 	 *
 | |
| 	 * Note: This is a 64bit register and we set only the lower 32bits
 | |
| 	 *       leaving the top 32bits (ROGUE_CR_SLC_CTRL_MISC_SCRAMBLE_BITS)
 | |
| 	 *       unchanged from the HW default.
 | |
| 	 */
 | |
| 	reg_val = (pvr_cr_read32(pvr_dev, ROGUE_CR_SLC_CTRL_MISC) &
 | |
| 		      ROGUE_CR_SLC_CTRL_MISC_ENABLE_PSG_HAZARD_CHECK_EN) |
 | |
| 		     ROGUE_CR_SLC_CTRL_MISC_ADDR_DECODE_MODE_PVR_HASH1;
 | |
| 
 | |
| 	err = PVR_FEATURE_VALUE(pvr_dev, slc_cache_line_size_bits, &slc_cache_line_size_bits);
 | |
| 	if (err)
 | |
| 		return err;
 | |
| 
 | |
| 	/* Bypass burst combiner if SLC line size is smaller than 1024 bits. */
 | |
| 	if (slc_cache_line_size_bits < 1024)
 | |
| 		reg_val |= ROGUE_CR_SLC_CTRL_MISC_BYPASS_BURST_COMBINER_EN;
 | |
| 
 | |
| 	if (PVR_HAS_QUIRK(pvr_dev, 71242) && !PVR_HAS_FEATURE(pvr_dev, gpu_multicore_support))
 | |
| 		reg_val |= ROGUE_CR_SLC_CTRL_MISC_LAZYWB_OVERRIDE_EN;
 | |
| 
 | |
| 	pvr_cr_write32(pvr_dev, ROGUE_CR_SLC_CTRL_MISC, reg_val);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * pvr_fw_start() - Start FW processor and boot firmware
 | |
|  * @pvr_dev: Target PowerVR device.
 | |
|  *
 | |
|  * Returns:
 | |
|  *  * 0 on success, or
 | |
|  *  * Any error returned by rogue_slc_init().
 | |
|  */
 | |
| int
 | |
| pvr_fw_start(struct pvr_device *pvr_dev)
 | |
| {
 | |
| 	bool has_reset2 = PVR_HAS_FEATURE(pvr_dev, xe_tpu2);
 | |
| 	u64 soft_reset_mask;
 | |
| 	int err;
 | |
| 
 | |
| 	if (PVR_HAS_FEATURE(pvr_dev, pbe2_in_xe))
 | |
| 		soft_reset_mask = ROGUE_CR_SOFT_RESET__PBE2_XE__MASKFULL;
 | |
| 	else
 | |
| 		soft_reset_mask = ROGUE_CR_SOFT_RESET_MASKFULL;
 | |
| 
 | |
| 	if (PVR_HAS_FEATURE(pvr_dev, sys_bus_secure_reset)) {
 | |
| 		/*
 | |
| 		 * Disable the default sys_bus_secure protection to perform
 | |
| 		 * minimal setup.
 | |
| 		 */
 | |
| 		pvr_cr_write32(pvr_dev, ROGUE_CR_SYS_BUS_SECURE, 0);
 | |
| 		(void)pvr_cr_read32(pvr_dev, ROGUE_CR_SYS_BUS_SECURE); /* Fence write */
 | |
| 	}
 | |
| 
 | |
| 	if (pvr_dev->fw_dev.processor_type == PVR_FW_PROCESSOR_TYPE_RISCV)
 | |
| 		pvr_cr_write32(pvr_dev, ROGUE_CR_FWCORE_BOOT, 0);
 | |
| 
 | |
| 	/* Set Rogue in soft-reset. */
 | |
| 	pvr_cr_write64(pvr_dev, ROGUE_CR_SOFT_RESET, soft_reset_mask);
 | |
| 	if (has_reset2)
 | |
| 		pvr_cr_write64(pvr_dev, ROGUE_CR_SOFT_RESET2, ROGUE_CR_SOFT_RESET2_MASKFULL);
 | |
| 
 | |
| 	/* Read soft-reset to fence previous write in order to clear the SOCIF pipeline. */
 | |
| 	(void)pvr_cr_read64(pvr_dev, ROGUE_CR_SOFT_RESET);
 | |
| 	if (has_reset2)
 | |
| 		(void)pvr_cr_read64(pvr_dev, ROGUE_CR_SOFT_RESET2);
 | |
| 
 | |
| 	/* Take Rascal and Dust out of reset. */
 | |
| 	pvr_cr_write64(pvr_dev, ROGUE_CR_SOFT_RESET,
 | |
| 		       soft_reset_mask ^ ROGUE_CR_SOFT_RESET_RASCALDUSTS_EN);
 | |
| 	if (has_reset2)
 | |
| 		pvr_cr_write64(pvr_dev, ROGUE_CR_SOFT_RESET2, 0);
 | |
| 
 | |
| 	(void)pvr_cr_read64(pvr_dev, ROGUE_CR_SOFT_RESET);
 | |
| 	if (has_reset2)
 | |
| 		(void)pvr_cr_read64(pvr_dev, ROGUE_CR_SOFT_RESET2);
 | |
| 
 | |
| 	/* Take everything out of reset but the FW processor. */
 | |
| 	pvr_cr_write64(pvr_dev, ROGUE_CR_SOFT_RESET, ROGUE_CR_SOFT_RESET_GARTEN_EN);
 | |
| 	if (has_reset2)
 | |
| 		pvr_cr_write64(pvr_dev, ROGUE_CR_SOFT_RESET2, 0);
 | |
| 
 | |
| 	(void)pvr_cr_read64(pvr_dev, ROGUE_CR_SOFT_RESET);
 | |
| 	if (has_reset2)
 | |
| 		(void)pvr_cr_read64(pvr_dev, ROGUE_CR_SOFT_RESET2);
 | |
| 
 | |
| 	err = rogue_slc_init(pvr_dev);
 | |
| 	if (err)
 | |
| 		goto err_reset;
 | |
| 
 | |
| 	/* Initialise Firmware wrapper. */
 | |
| 	pvr_dev->fw_dev.defs->wrapper_init(pvr_dev);
 | |
| 
 | |
| 	/* We must init the AXI-ACE interface before first BIF transaction. */
 | |
| 	rogue_axi_ace_list_init(pvr_dev);
 | |
| 
 | |
| 	if (pvr_dev->fw_dev.processor_type != PVR_FW_PROCESSOR_TYPE_MIPS) {
 | |
| 		/* Initialise BIF. */
 | |
| 		rogue_bif_init(pvr_dev);
 | |
| 	}
 | |
| 
 | |
| 	/* Need to wait for at least 16 cycles before taking the FW processor out of reset ... */
 | |
| 	udelay(3);
 | |
| 
 | |
| 	pvr_cr_write64(pvr_dev, ROGUE_CR_SOFT_RESET, 0x0);
 | |
| 	(void)pvr_cr_read64(pvr_dev, ROGUE_CR_SOFT_RESET);
 | |
| 
 | |
| 	/* ... and afterwards. */
 | |
| 	udelay(3);
 | |
| 
 | |
| 	if (pvr_dev->fw_dev.processor_type == PVR_FW_PROCESSOR_TYPE_RISCV) {
 | |
| 		/* Boot the FW. */
 | |
| 		pvr_cr_write32(pvr_dev, ROGUE_CR_FWCORE_BOOT, 1);
 | |
| 		udelay(3);
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| 
 | |
| err_reset:
 | |
| 	/* Put everything back into soft-reset. */
 | |
| 	pvr_cr_write64(pvr_dev, ROGUE_CR_SOFT_RESET, soft_reset_mask);
 | |
| 
 | |
| 	return err;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * pvr_fw_stop() - Stop FW processor
 | |
|  * @pvr_dev: Target PowerVR device.
 | |
|  *
 | |
|  * Returns:
 | |
|  *  * 0 on success, or
 | |
|  *  * Any error returned by pvr_cr_poll_reg32().
 | |
|  */
 | |
| int
 | |
| pvr_fw_stop(struct pvr_device *pvr_dev)
 | |
| {
 | |
| 	const u32 sidekick_idle_mask = ROGUE_CR_SIDEKICK_IDLE_MASKFULL &
 | |
| 				       ~(ROGUE_CR_SIDEKICK_IDLE_GARTEN_EN |
 | |
| 					 ROGUE_CR_SIDEKICK_IDLE_SOCIF_EN |
 | |
| 					 ROGUE_CR_SIDEKICK_IDLE_HOSTIF_EN);
 | |
| 	bool skip_garten_idle = false;
 | |
| 	u32 reg_value;
 | |
| 	int err;
 | |
| 
 | |
| 	/*
 | |
| 	 * Wait for Sidekick/Jones to signal IDLE except for the Garten Wrapper.
 | |
| 	 * For cores with the LAYOUT_MARS feature, SIDEKICK would have been
 | |
| 	 * powered down by the FW.
 | |
| 	 */
 | |
| 	err = pvr_cr_poll_reg32(pvr_dev, ROGUE_CR_SIDEKICK_IDLE, sidekick_idle_mask,
 | |
| 				sidekick_idle_mask, POLL_TIMEOUT_USEC);
 | |
| 	if (err)
 | |
| 		return err;
 | |
| 
 | |
| 	/* Unset MTS DM association with threads. */
 | |
| 	pvr_cr_write32(pvr_dev, ROGUE_CR_MTS_INTCTX_THREAD0_DM_ASSOC,
 | |
| 		       ROGUE_CR_MTS_INTCTX_THREAD0_DM_ASSOC_MASKFULL &
 | |
| 		       ROGUE_CR_MTS_INTCTX_THREAD0_DM_ASSOC_DM_ASSOC_CLRMSK);
 | |
| 	pvr_cr_write32(pvr_dev, ROGUE_CR_MTS_BGCTX_THREAD0_DM_ASSOC,
 | |
| 		       ROGUE_CR_MTS_BGCTX_THREAD0_DM_ASSOC_MASKFULL &
 | |
| 		       ROGUE_CR_MTS_BGCTX_THREAD0_DM_ASSOC_DM_ASSOC_CLRMSK);
 | |
| 	pvr_cr_write32(pvr_dev, ROGUE_CR_MTS_INTCTX_THREAD1_DM_ASSOC,
 | |
| 		       ROGUE_CR_MTS_INTCTX_THREAD1_DM_ASSOC_MASKFULL &
 | |
| 		       ROGUE_CR_MTS_INTCTX_THREAD1_DM_ASSOC_DM_ASSOC_CLRMSK);
 | |
| 	pvr_cr_write32(pvr_dev, ROGUE_CR_MTS_BGCTX_THREAD1_DM_ASSOC,
 | |
| 		       ROGUE_CR_MTS_BGCTX_THREAD1_DM_ASSOC_MASKFULL &
 | |
| 		       ROGUE_CR_MTS_BGCTX_THREAD1_DM_ASSOC_DM_ASSOC_CLRMSK);
 | |
| 
 | |
| 	/* Extra Idle checks. */
 | |
| 	err = pvr_cr_poll_reg32(pvr_dev, ROGUE_CR_BIF_STATUS_MMU, 0,
 | |
| 				ROGUE_CR_BIF_STATUS_MMU_MASKFULL,
 | |
| 				POLL_TIMEOUT_USEC);
 | |
| 	if (err)
 | |
| 		return err;
 | |
| 
 | |
| 	err = pvr_cr_poll_reg32(pvr_dev, ROGUE_CR_BIFPM_STATUS_MMU, 0,
 | |
| 				ROGUE_CR_BIFPM_STATUS_MMU_MASKFULL,
 | |
| 				POLL_TIMEOUT_USEC);
 | |
| 	if (err)
 | |
| 		return err;
 | |
| 
 | |
| 	if (!PVR_HAS_FEATURE(pvr_dev, xt_top_infrastructure)) {
 | |
| 		err = pvr_cr_poll_reg32(pvr_dev, ROGUE_CR_BIF_READS_EXT_STATUS, 0,
 | |
| 					ROGUE_CR_BIF_READS_EXT_STATUS_MASKFULL,
 | |
| 					POLL_TIMEOUT_USEC);
 | |
| 		if (err)
 | |
| 			return err;
 | |
| 	}
 | |
| 
 | |
| 	err = pvr_cr_poll_reg32(pvr_dev, ROGUE_CR_BIFPM_READS_EXT_STATUS, 0,
 | |
| 				ROGUE_CR_BIFPM_READS_EXT_STATUS_MASKFULL,
 | |
| 				POLL_TIMEOUT_USEC);
 | |
| 	if (err)
 | |
| 		return err;
 | |
| 
 | |
| 	err = pvr_cr_poll_reg64(pvr_dev, ROGUE_CR_SLC_STATUS1, 0,
 | |
| 				ROGUE_CR_SLC_STATUS1_MASKFULL,
 | |
| 				POLL_TIMEOUT_USEC);
 | |
| 	if (err)
 | |
| 		return err;
 | |
| 
 | |
| 	/*
 | |
| 	 * Wait for SLC to signal IDLE.
 | |
| 	 * For cores with the LAYOUT_MARS feature, SLC would have been powered
 | |
| 	 * down by the FW.
 | |
| 	 */
 | |
| 	err = pvr_cr_poll_reg32(pvr_dev, ROGUE_CR_SLC_IDLE,
 | |
| 				ROGUE_CR_SLC_IDLE_MASKFULL,
 | |
| 				ROGUE_CR_SLC_IDLE_MASKFULL, POLL_TIMEOUT_USEC);
 | |
| 	if (err)
 | |
| 		return err;
 | |
| 
 | |
| 	/*
 | |
| 	 * Wait for Sidekick/Jones to signal IDLE except for the Garten Wrapper.
 | |
| 	 * For cores with the LAYOUT_MARS feature, SIDEKICK would have been powered
 | |
| 	 * down by the FW.
 | |
| 	 */
 | |
| 	err = pvr_cr_poll_reg32(pvr_dev, ROGUE_CR_SIDEKICK_IDLE, sidekick_idle_mask,
 | |
| 				sidekick_idle_mask, POLL_TIMEOUT_USEC);
 | |
| 	if (err)
 | |
| 		return err;
 | |
| 
 | |
| 	if (pvr_dev->fw_dev.processor_type == PVR_FW_PROCESSOR_TYPE_META) {
 | |
| 		err = pvr_meta_cr_read32(pvr_dev, META_CR_TxVECINT_BHALT, ®_value);
 | |
| 		if (err)
 | |
| 			return err;
 | |
| 
 | |
| 		/*
 | |
| 		 * Wait for Sidekick/Jones to signal IDLE including the Garten
 | |
| 		 * Wrapper if there is no debugger attached (TxVECINT_BHALT =
 | |
| 		 * 0x0).
 | |
| 		 */
 | |
| 		if (reg_value)
 | |
| 			skip_garten_idle = true;
 | |
| 	}
 | |
| 
 | |
| 	if (!skip_garten_idle) {
 | |
| 		err = pvr_cr_poll_reg32(pvr_dev, ROGUE_CR_SIDEKICK_IDLE,
 | |
| 					ROGUE_CR_SIDEKICK_IDLE_GARTEN_EN,
 | |
| 					ROGUE_CR_SIDEKICK_IDLE_GARTEN_EN,
 | |
| 					POLL_TIMEOUT_USEC);
 | |
| 		if (err)
 | |
| 			return err;
 | |
| 	}
 | |
| 
 | |
| 	if (PVR_HAS_FEATURE(pvr_dev, pbe2_in_xe))
 | |
| 		pvr_cr_write64(pvr_dev, ROGUE_CR_SOFT_RESET,
 | |
| 			       ROGUE_CR_SOFT_RESET__PBE2_XE__MASKFULL);
 | |
| 	else
 | |
| 		pvr_cr_write64(pvr_dev, ROGUE_CR_SOFT_RESET, ROGUE_CR_SOFT_RESET_MASKFULL);
 | |
| 
 | |
| 	return 0;
 | |
| }
 |