cc1aeedb98
The infrastructure includes parsing of the firmware image, initialising FW-side structures, handling the kernel and firmware command ringbuffers and starting & stopping the firmware processor. This patch also adds the necessary support code for the META firmware processor. Changes since v8: - Fix documentation for pvr_fwccb_process() - Corrected license identifiers Changes since v6: - Add a minimum retry count to pvr_kccb_reserve_slot_sync() Changes since v5: - Add workaround for BRN 71242 - Attempt to recover GPU on MMU flush command failure Changes since v4: - Remove use of drm_gem_shmem_get_pages() - Remove interrupt resource name Changes since v3: - Hard reset FW processor on watchdog timeout - Switch to threaded IRQ - Rework FW object creation/initialisation to aid hard reset - Added MODULE_FIRMWARE() - Use drm_dev_{enter,exit} Signed-off-by: Sarah Walker <sarah.walker@imgtec.com> Signed-off-by: Donald Robson <donald.robson@imgtec.com> Link: https://lore.kernel.org/r/bb52a8dc84f296b37dc6668dfe8fbaf2ba551139.1700668843.git.donald.robson@imgtec.com Signed-off-by: Maxime Ripard <mripard@kernel.org>
307 lines
9.1 KiB
C
307 lines
9.1 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);
|
|
}
|
|
|
|
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 */
|
|
}
|
|
|
|
/* 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);
|
|
|
|
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;
|
|
}
|