mirror of
git://git.yoctoproject.org/linux-yocto.git
synced 2025-07-19 12:09:01 +02:00

When host system is under heavy load and the NPU is already running on the lowest frequency, PUNIT may request Duty Cycle Throttling (DCT). This will further reduce NPU power usage. PUNIT requests DCT mode using Survabilty IRQ and mailbox register. The driver then issues a JSM message to the FW that enables the DCT mode. If the NPU resets while in DCT mode, the driver request DCT mode during FW boot. Also add debugfs "dct" file that allows to set arbitrary DCT percentage, which is used by driver tests. Signed-off-by: Jacek Lawrynowicz <jacek.lawrynowicz@linux.intel.com> Reviewed-by: Wachowski, Karol <karol.wachowski@intel.com> Link: https://patchwork.freedesktop.org/patch/msgid/20240611120433.1012423-7-jacek.lawrynowicz@linux.intel.com
906 lines
26 KiB
C
906 lines
26 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* Copyright (C) 2020-2024 Intel Corporation
|
|
*/
|
|
|
|
#include "ivpu_drv.h"
|
|
#include "ivpu_hw.h"
|
|
#include "ivpu_hw_btrs.h"
|
|
#include "ivpu_hw_btrs_lnl_reg.h"
|
|
#include "ivpu_hw_btrs_mtl_reg.h"
|
|
#include "ivpu_hw_reg_io.h"
|
|
#include "ivpu_pm.h"
|
|
|
|
#define BTRS_MTL_IRQ_MASK ((REG_FLD(VPU_HW_BTRS_MTL_INTERRUPT_STAT, ATS_ERR)) | \
|
|
(REG_FLD(VPU_HW_BTRS_MTL_INTERRUPT_STAT, UFI_ERR)))
|
|
|
|
#define BTRS_LNL_IRQ_MASK ((REG_FLD(VPU_HW_BTRS_LNL_INTERRUPT_STAT, ATS_ERR)) | \
|
|
(REG_FLD(VPU_HW_BTRS_LNL_INTERRUPT_STAT, CFI0_ERR)) | \
|
|
(REG_FLD(VPU_HW_BTRS_LNL_INTERRUPT_STAT, CFI1_ERR)) | \
|
|
(REG_FLD(VPU_HW_BTRS_LNL_INTERRUPT_STAT, IMR0_ERR)) | \
|
|
(REG_FLD(VPU_HW_BTRS_LNL_INTERRUPT_STAT, IMR1_ERR)) | \
|
|
(REG_FLD(VPU_HW_BTRS_LNL_INTERRUPT_STAT, SURV_ERR)))
|
|
|
|
#define BTRS_MTL_ALL_IRQ_MASK (BTRS_MTL_IRQ_MASK | (REG_FLD(VPU_HW_BTRS_MTL_INTERRUPT_STAT, \
|
|
FREQ_CHANGE)))
|
|
|
|
#define BTRS_IRQ_DISABLE_MASK ((u32)-1)
|
|
|
|
#define BTRS_LNL_ALL_IRQ_MASK ((u32)-1)
|
|
|
|
#define BTRS_MTL_WP_CONFIG_1_TILE_5_3_RATIO WP_CONFIG(MTL_CONFIG_1_TILE, MTL_PLL_RATIO_5_3)
|
|
#define BTRS_MTL_WP_CONFIG_1_TILE_4_3_RATIO WP_CONFIG(MTL_CONFIG_1_TILE, MTL_PLL_RATIO_4_3)
|
|
#define BTRS_MTL_WP_CONFIG_2_TILE_5_3_RATIO WP_CONFIG(MTL_CONFIG_2_TILE, MTL_PLL_RATIO_5_3)
|
|
#define BTRS_MTL_WP_CONFIG_2_TILE_4_3_RATIO WP_CONFIG(MTL_CONFIG_2_TILE, MTL_PLL_RATIO_4_3)
|
|
#define BTRS_MTL_WP_CONFIG_0_TILE_PLL_OFF WP_CONFIG(0, 0)
|
|
|
|
#define PLL_CDYN_DEFAULT 0x80
|
|
#define PLL_EPP_DEFAULT 0x80
|
|
#define PLL_CONFIG_DEFAULT 0x0
|
|
#define PLL_SIMULATION_FREQ 10000000
|
|
#define PLL_REF_CLK_FREQ 50000000
|
|
#define PLL_TIMEOUT_US (1500 * USEC_PER_MSEC)
|
|
#define IDLE_TIMEOUT_US (5 * USEC_PER_MSEC)
|
|
#define TIMEOUT_US (150 * USEC_PER_MSEC)
|
|
|
|
/* Work point configuration values */
|
|
#define WP_CONFIG(tile, ratio) (((tile) << 8) | (ratio))
|
|
#define MTL_CONFIG_1_TILE 0x01
|
|
#define MTL_CONFIG_2_TILE 0x02
|
|
#define MTL_PLL_RATIO_5_3 0x01
|
|
#define MTL_PLL_RATIO_4_3 0x02
|
|
#define BTRS_MTL_TILE_FUSE_ENABLE_BOTH 0x0
|
|
#define BTRS_MTL_TILE_SKU_BOTH 0x3630
|
|
|
|
#define BTRS_LNL_TILE_MAX_NUM 6
|
|
#define BTRS_LNL_TILE_MAX_MASK 0x3f
|
|
|
|
#define WEIGHTS_DEFAULT 0xf711f711u
|
|
#define WEIGHTS_ATS_DEFAULT 0x0000f711u
|
|
|
|
#define DCT_REQ 0x2
|
|
#define DCT_ENABLE 0x1
|
|
#define DCT_DISABLE 0x0
|
|
|
|
int ivpu_hw_btrs_irqs_clear_with_0_mtl(struct ivpu_device *vdev)
|
|
{
|
|
REGB_WR32(VPU_HW_BTRS_MTL_INTERRUPT_STAT, BTRS_MTL_ALL_IRQ_MASK);
|
|
if (REGB_RD32(VPU_HW_BTRS_MTL_INTERRUPT_STAT) == BTRS_MTL_ALL_IRQ_MASK) {
|
|
/* Writing 1s does not clear the interrupt status register */
|
|
REGB_WR32(VPU_HW_BTRS_MTL_INTERRUPT_STAT, 0x0);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static void freq_ratios_init_mtl(struct ivpu_device *vdev)
|
|
{
|
|
struct ivpu_hw_info *hw = vdev->hw;
|
|
u32 fmin_fuse, fmax_fuse;
|
|
|
|
fmin_fuse = REGB_RD32(VPU_HW_BTRS_MTL_FMIN_FUSE);
|
|
hw->pll.min_ratio = REG_GET_FLD(VPU_HW_BTRS_MTL_FMIN_FUSE, MIN_RATIO, fmin_fuse);
|
|
hw->pll.pn_ratio = REG_GET_FLD(VPU_HW_BTRS_MTL_FMIN_FUSE, PN_RATIO, fmin_fuse);
|
|
|
|
fmax_fuse = REGB_RD32(VPU_HW_BTRS_MTL_FMAX_FUSE);
|
|
hw->pll.max_ratio = REG_GET_FLD(VPU_HW_BTRS_MTL_FMAX_FUSE, MAX_RATIO, fmax_fuse);
|
|
}
|
|
|
|
static void freq_ratios_init_lnl(struct ivpu_device *vdev)
|
|
{
|
|
struct ivpu_hw_info *hw = vdev->hw;
|
|
u32 fmin_fuse, fmax_fuse;
|
|
|
|
fmin_fuse = REGB_RD32(VPU_HW_BTRS_LNL_FMIN_FUSE);
|
|
hw->pll.min_ratio = REG_GET_FLD(VPU_HW_BTRS_LNL_FMIN_FUSE, MIN_RATIO, fmin_fuse);
|
|
hw->pll.pn_ratio = REG_GET_FLD(VPU_HW_BTRS_LNL_FMIN_FUSE, PN_RATIO, fmin_fuse);
|
|
|
|
fmax_fuse = REGB_RD32(VPU_HW_BTRS_LNL_FMAX_FUSE);
|
|
hw->pll.max_ratio = REG_GET_FLD(VPU_HW_BTRS_LNL_FMAX_FUSE, MAX_RATIO, fmax_fuse);
|
|
}
|
|
|
|
void ivpu_hw_btrs_freq_ratios_init(struct ivpu_device *vdev)
|
|
{
|
|
struct ivpu_hw_info *hw = vdev->hw;
|
|
|
|
if (ivpu_hw_btrs_gen(vdev) == IVPU_HW_BTRS_MTL)
|
|
freq_ratios_init_mtl(vdev);
|
|
else
|
|
freq_ratios_init_lnl(vdev);
|
|
|
|
hw->pll.min_ratio = clamp_t(u8, ivpu_pll_min_ratio, hw->pll.min_ratio, hw->pll.max_ratio);
|
|
hw->pll.max_ratio = clamp_t(u8, ivpu_pll_max_ratio, hw->pll.min_ratio, hw->pll.max_ratio);
|
|
hw->pll.pn_ratio = clamp_t(u8, hw->pll.pn_ratio, hw->pll.min_ratio, hw->pll.max_ratio);
|
|
}
|
|
|
|
static bool tile_disable_check(u32 config)
|
|
{
|
|
/* Allowed values: 0 or one bit from range 0-5 (6 tiles) */
|
|
if (config == 0)
|
|
return true;
|
|
|
|
if (config > BIT(BTRS_LNL_TILE_MAX_NUM - 1))
|
|
return false;
|
|
|
|
if ((config & (config - 1)) == 0)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
static int read_tile_config_fuse(struct ivpu_device *vdev, u32 *tile_fuse_config)
|
|
{
|
|
u32 fuse;
|
|
u32 config;
|
|
|
|
fuse = REGB_RD32(VPU_HW_BTRS_LNL_TILE_FUSE);
|
|
if (!REG_TEST_FLD(VPU_HW_BTRS_LNL_TILE_FUSE, VALID, fuse)) {
|
|
ivpu_err(vdev, "Fuse: invalid (0x%x)\n", fuse);
|
|
return -EIO;
|
|
}
|
|
|
|
config = REG_GET_FLD(VPU_HW_BTRS_LNL_TILE_FUSE, CONFIG, fuse);
|
|
if (!tile_disable_check(config)) {
|
|
ivpu_err(vdev, "Fuse: Invalid tile disable config (0x%x)\n", config);
|
|
return -EIO;
|
|
}
|
|
|
|
if (config)
|
|
ivpu_dbg(vdev, MISC, "Fuse: %d tiles enabled. Tile number %d disabled\n",
|
|
BTRS_LNL_TILE_MAX_NUM - 1, ffs(config) - 1);
|
|
else
|
|
ivpu_dbg(vdev, MISC, "Fuse: All %d tiles enabled\n", BTRS_LNL_TILE_MAX_NUM);
|
|
|
|
*tile_fuse_config = config;
|
|
return 0;
|
|
}
|
|
|
|
static int info_init_mtl(struct ivpu_device *vdev)
|
|
{
|
|
struct ivpu_hw_info *hw = vdev->hw;
|
|
|
|
hw->tile_fuse = BTRS_MTL_TILE_FUSE_ENABLE_BOTH;
|
|
hw->sku = BTRS_MTL_TILE_SKU_BOTH;
|
|
hw->config = BTRS_MTL_WP_CONFIG_2_TILE_4_3_RATIO;
|
|
hw->sched_mode = ivpu_sched_mode;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int info_init_lnl(struct ivpu_device *vdev)
|
|
{
|
|
struct ivpu_hw_info *hw = vdev->hw;
|
|
u32 tile_fuse_config;
|
|
int ret;
|
|
|
|
ret = read_tile_config_fuse(vdev, &tile_fuse_config);
|
|
if (ret)
|
|
return ret;
|
|
|
|
hw->sched_mode = ivpu_sched_mode;
|
|
hw->tile_fuse = tile_fuse_config;
|
|
hw->pll.profiling_freq = PLL_PROFILING_FREQ_DEFAULT;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int ivpu_hw_btrs_info_init(struct ivpu_device *vdev)
|
|
{
|
|
if (ivpu_hw_btrs_gen(vdev) == IVPU_HW_BTRS_MTL)
|
|
return info_init_mtl(vdev);
|
|
else
|
|
return info_init_lnl(vdev);
|
|
}
|
|
|
|
static int wp_request_sync(struct ivpu_device *vdev)
|
|
{
|
|
if (ivpu_hw_btrs_gen(vdev) == IVPU_HW_BTRS_MTL)
|
|
return REGB_POLL_FLD(VPU_HW_BTRS_MTL_WP_REQ_CMD, SEND, 0, PLL_TIMEOUT_US);
|
|
else
|
|
return REGB_POLL_FLD(VPU_HW_BTRS_LNL_WP_REQ_CMD, SEND, 0, PLL_TIMEOUT_US);
|
|
}
|
|
|
|
static int wait_for_status_ready(struct ivpu_device *vdev, bool enable)
|
|
{
|
|
u32 exp_val = enable ? 0x1 : 0x0;
|
|
|
|
if (IVPU_WA(punit_disabled))
|
|
return 0;
|
|
|
|
if (ivpu_hw_btrs_gen(vdev) == IVPU_HW_BTRS_MTL)
|
|
return REGB_POLL_FLD(VPU_HW_BTRS_MTL_VPU_STATUS, READY, exp_val, PLL_TIMEOUT_US);
|
|
else
|
|
return REGB_POLL_FLD(VPU_HW_BTRS_LNL_VPU_STATUS, READY, exp_val, PLL_TIMEOUT_US);
|
|
}
|
|
|
|
struct wp_request {
|
|
u16 min;
|
|
u16 max;
|
|
u16 target;
|
|
u16 cfg;
|
|
u16 epp;
|
|
u16 cdyn;
|
|
};
|
|
|
|
static void wp_request_mtl(struct ivpu_device *vdev, struct wp_request *wp)
|
|
{
|
|
u32 val;
|
|
|
|
val = REGB_RD32(VPU_HW_BTRS_MTL_WP_REQ_PAYLOAD0);
|
|
val = REG_SET_FLD_NUM(VPU_HW_BTRS_MTL_WP_REQ_PAYLOAD0, MIN_RATIO, wp->min, val);
|
|
val = REG_SET_FLD_NUM(VPU_HW_BTRS_MTL_WP_REQ_PAYLOAD0, MAX_RATIO, wp->max, val);
|
|
REGB_WR32(VPU_HW_BTRS_MTL_WP_REQ_PAYLOAD0, val);
|
|
|
|
val = REGB_RD32(VPU_HW_BTRS_MTL_WP_REQ_PAYLOAD1);
|
|
val = REG_SET_FLD_NUM(VPU_HW_BTRS_MTL_WP_REQ_PAYLOAD1, TARGET_RATIO, wp->target, val);
|
|
val = REG_SET_FLD_NUM(VPU_HW_BTRS_MTL_WP_REQ_PAYLOAD1, EPP, PLL_EPP_DEFAULT, val);
|
|
REGB_WR32(VPU_HW_BTRS_MTL_WP_REQ_PAYLOAD1, val);
|
|
|
|
val = REGB_RD32(VPU_HW_BTRS_MTL_WP_REQ_PAYLOAD2);
|
|
val = REG_SET_FLD_NUM(VPU_HW_BTRS_MTL_WP_REQ_PAYLOAD2, CONFIG, wp->cfg, val);
|
|
REGB_WR32(VPU_HW_BTRS_MTL_WP_REQ_PAYLOAD2, val);
|
|
|
|
val = REGB_RD32(VPU_HW_BTRS_MTL_WP_REQ_CMD);
|
|
val = REG_SET_FLD(VPU_HW_BTRS_MTL_WP_REQ_CMD, SEND, val);
|
|
REGB_WR32(VPU_HW_BTRS_MTL_WP_REQ_CMD, val);
|
|
}
|
|
|
|
static void wp_request_lnl(struct ivpu_device *vdev, struct wp_request *wp)
|
|
{
|
|
u32 val;
|
|
|
|
val = REGB_RD32(VPU_HW_BTRS_LNL_WP_REQ_PAYLOAD0);
|
|
val = REG_SET_FLD_NUM(VPU_HW_BTRS_LNL_WP_REQ_PAYLOAD0, MIN_RATIO, wp->min, val);
|
|
val = REG_SET_FLD_NUM(VPU_HW_BTRS_LNL_WP_REQ_PAYLOAD0, MAX_RATIO, wp->max, val);
|
|
REGB_WR32(VPU_HW_BTRS_LNL_WP_REQ_PAYLOAD0, val);
|
|
|
|
val = REGB_RD32(VPU_HW_BTRS_LNL_WP_REQ_PAYLOAD1);
|
|
val = REG_SET_FLD_NUM(VPU_HW_BTRS_LNL_WP_REQ_PAYLOAD1, TARGET_RATIO, wp->target, val);
|
|
val = REG_SET_FLD_NUM(VPU_HW_BTRS_LNL_WP_REQ_PAYLOAD1, EPP, wp->epp, val);
|
|
REGB_WR32(VPU_HW_BTRS_LNL_WP_REQ_PAYLOAD1, val);
|
|
|
|
val = REGB_RD32(VPU_HW_BTRS_LNL_WP_REQ_PAYLOAD2);
|
|
val = REG_SET_FLD_NUM(VPU_HW_BTRS_LNL_WP_REQ_PAYLOAD2, CONFIG, wp->cfg, val);
|
|
val = REG_SET_FLD_NUM(VPU_HW_BTRS_LNL_WP_REQ_PAYLOAD2, CDYN, wp->cdyn, val);
|
|
REGB_WR32(VPU_HW_BTRS_LNL_WP_REQ_PAYLOAD2, val);
|
|
|
|
val = REGB_RD32(VPU_HW_BTRS_LNL_WP_REQ_CMD);
|
|
val = REG_SET_FLD(VPU_HW_BTRS_LNL_WP_REQ_CMD, SEND, val);
|
|
REGB_WR32(VPU_HW_BTRS_LNL_WP_REQ_CMD, val);
|
|
}
|
|
|
|
static void wp_request(struct ivpu_device *vdev, struct wp_request *wp)
|
|
{
|
|
if (ivpu_hw_btrs_gen(vdev) == IVPU_HW_BTRS_MTL)
|
|
wp_request_mtl(vdev, wp);
|
|
else
|
|
wp_request_lnl(vdev, wp);
|
|
}
|
|
|
|
static int wp_request_send(struct ivpu_device *vdev, struct wp_request *wp)
|
|
{
|
|
int ret;
|
|
|
|
ret = wp_request_sync(vdev);
|
|
if (ret) {
|
|
ivpu_err(vdev, "Failed to sync before workpoint request: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
wp_request(vdev, wp);
|
|
|
|
ret = wp_request_sync(vdev);
|
|
if (ret)
|
|
ivpu_err(vdev, "Failed to sync after workpoint request: %d\n", ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void prepare_wp_request(struct ivpu_device *vdev, struct wp_request *wp, bool enable)
|
|
{
|
|
struct ivpu_hw_info *hw = vdev->hw;
|
|
|
|
wp->min = hw->pll.min_ratio;
|
|
wp->max = hw->pll.max_ratio;
|
|
|
|
if (ivpu_hw_btrs_gen(vdev) == IVPU_HW_BTRS_MTL) {
|
|
wp->target = enable ? hw->pll.pn_ratio : 0;
|
|
wp->cfg = enable ? hw->config : 0;
|
|
wp->cdyn = 0;
|
|
wp->epp = 0;
|
|
} else {
|
|
wp->target = hw->pll.pn_ratio;
|
|
wp->cfg = enable ? PLL_CONFIG_DEFAULT : 0;
|
|
wp->cdyn = enable ? PLL_CDYN_DEFAULT : 0;
|
|
wp->epp = enable ? PLL_EPP_DEFAULT : 0;
|
|
}
|
|
|
|
/* Simics cannot start without at least one tile */
|
|
if (enable && ivpu_is_simics(vdev))
|
|
wp->cfg = 1;
|
|
}
|
|
|
|
static int wait_for_pll_lock(struct ivpu_device *vdev, bool enable)
|
|
{
|
|
u32 exp_val = enable ? 0x1 : 0x0;
|
|
|
|
if (ivpu_hw_btrs_gen(vdev) != IVPU_HW_BTRS_MTL)
|
|
return 0;
|
|
|
|
if (IVPU_WA(punit_disabled))
|
|
return 0;
|
|
|
|
return REGB_POLL_FLD(VPU_HW_BTRS_MTL_PLL_STATUS, LOCK, exp_val, PLL_TIMEOUT_US);
|
|
}
|
|
|
|
int ivpu_hw_btrs_wp_drive(struct ivpu_device *vdev, bool enable)
|
|
{
|
|
struct wp_request wp;
|
|
int ret;
|
|
|
|
if (IVPU_WA(punit_disabled)) {
|
|
ivpu_dbg(vdev, PM, "Skipping workpoint request\n");
|
|
return 0;
|
|
}
|
|
|
|
prepare_wp_request(vdev, &wp, enable);
|
|
|
|
ivpu_dbg(vdev, PM, "PLL workpoint request: %u Hz, config: 0x%x, epp: 0x%x, cdyn: 0x%x\n",
|
|
PLL_RATIO_TO_FREQ(wp.target), wp.cfg, wp.epp, wp.cdyn);
|
|
|
|
ret = wp_request_send(vdev, &wp);
|
|
if (ret) {
|
|
ivpu_err(vdev, "Failed to send workpoint request: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
ret = wait_for_pll_lock(vdev, enable);
|
|
if (ret) {
|
|
ivpu_err(vdev, "Timed out waiting for PLL lock\n");
|
|
return ret;
|
|
}
|
|
|
|
ret = wait_for_status_ready(vdev, enable);
|
|
if (ret) {
|
|
ivpu_err(vdev, "Timed out waiting for NPU ready status\n");
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int d0i3_drive_mtl(struct ivpu_device *vdev, bool enable)
|
|
{
|
|
int ret;
|
|
u32 val;
|
|
|
|
ret = REGB_POLL_FLD(VPU_HW_BTRS_MTL_VPU_D0I3_CONTROL, INPROGRESS, 0, TIMEOUT_US);
|
|
if (ret) {
|
|
ivpu_err(vdev, "Failed to sync before D0i3 transition: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
val = REGB_RD32(VPU_HW_BTRS_MTL_VPU_D0I3_CONTROL);
|
|
if (enable)
|
|
val = REG_SET_FLD(VPU_HW_BTRS_MTL_VPU_D0I3_CONTROL, I3, val);
|
|
else
|
|
val = REG_CLR_FLD(VPU_HW_BTRS_MTL_VPU_D0I3_CONTROL, I3, val);
|
|
REGB_WR32(VPU_HW_BTRS_MTL_VPU_D0I3_CONTROL, val);
|
|
|
|
ret = REGB_POLL_FLD(VPU_HW_BTRS_MTL_VPU_D0I3_CONTROL, INPROGRESS, 0, TIMEOUT_US);
|
|
if (ret)
|
|
ivpu_err(vdev, "Failed to sync after D0i3 transition: %d\n", ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int d0i3_drive_lnl(struct ivpu_device *vdev, bool enable)
|
|
{
|
|
int ret;
|
|
u32 val;
|
|
|
|
ret = REGB_POLL_FLD(VPU_HW_BTRS_LNL_D0I3_CONTROL, INPROGRESS, 0, TIMEOUT_US);
|
|
if (ret) {
|
|
ivpu_err(vdev, "Failed to sync before D0i3 transition: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
val = REGB_RD32(VPU_HW_BTRS_LNL_D0I3_CONTROL);
|
|
if (enable)
|
|
val = REG_SET_FLD(VPU_HW_BTRS_LNL_D0I3_CONTROL, I3, val);
|
|
else
|
|
val = REG_CLR_FLD(VPU_HW_BTRS_LNL_D0I3_CONTROL, I3, val);
|
|
REGB_WR32(VPU_HW_BTRS_LNL_D0I3_CONTROL, val);
|
|
|
|
ret = REGB_POLL_FLD(VPU_HW_BTRS_LNL_D0I3_CONTROL, INPROGRESS, 0, TIMEOUT_US);
|
|
if (ret) {
|
|
ivpu_err(vdev, "Failed to sync after D0i3 transition: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int d0i3_drive(struct ivpu_device *vdev, bool enable)
|
|
{
|
|
if (ivpu_hw_btrs_gen(vdev) == IVPU_HW_BTRS_MTL)
|
|
return d0i3_drive_mtl(vdev, enable);
|
|
else
|
|
return d0i3_drive_lnl(vdev, enable);
|
|
}
|
|
|
|
int ivpu_hw_btrs_d0i3_enable(struct ivpu_device *vdev)
|
|
{
|
|
int ret;
|
|
|
|
if (IVPU_WA(punit_disabled))
|
|
return 0;
|
|
|
|
ret = d0i3_drive(vdev, true);
|
|
if (ret)
|
|
ivpu_err(vdev, "Failed to enable D0i3: %d\n", ret);
|
|
|
|
udelay(5); /* VPU requires 5 us to complete the transition */
|
|
|
|
return ret;
|
|
}
|
|
|
|
int ivpu_hw_btrs_d0i3_disable(struct ivpu_device *vdev)
|
|
{
|
|
int ret;
|
|
|
|
if (IVPU_WA(punit_disabled))
|
|
return 0;
|
|
|
|
ret = d0i3_drive(vdev, false);
|
|
if (ret)
|
|
ivpu_err(vdev, "Failed to disable D0i3: %d\n", ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int ivpu_hw_btrs_wait_for_clock_res_own_ack(struct ivpu_device *vdev)
|
|
{
|
|
if (ivpu_hw_btrs_gen(vdev) == IVPU_HW_BTRS_MTL)
|
|
return 0;
|
|
|
|
if (ivpu_is_simics(vdev))
|
|
return 0;
|
|
|
|
return REGB_POLL_FLD(VPU_HW_BTRS_LNL_VPU_STATUS, CLOCK_RESOURCE_OWN_ACK, 1, TIMEOUT_US);
|
|
}
|
|
|
|
void ivpu_hw_btrs_set_port_arbitration_weights_lnl(struct ivpu_device *vdev)
|
|
{
|
|
REGB_WR32(VPU_HW_BTRS_LNL_PORT_ARBITRATION_WEIGHTS, WEIGHTS_DEFAULT);
|
|
REGB_WR32(VPU_HW_BTRS_LNL_PORT_ARBITRATION_WEIGHTS_ATS, WEIGHTS_ATS_DEFAULT);
|
|
}
|
|
|
|
static int ip_reset_mtl(struct ivpu_device *vdev)
|
|
{
|
|
int ret;
|
|
u32 val;
|
|
|
|
ret = REGB_POLL_FLD(VPU_HW_BTRS_MTL_VPU_IP_RESET, TRIGGER, 0, TIMEOUT_US);
|
|
if (ret) {
|
|
ivpu_err(vdev, "Timed out waiting for TRIGGER bit\n");
|
|
return ret;
|
|
}
|
|
|
|
val = REGB_RD32(VPU_HW_BTRS_MTL_VPU_IP_RESET);
|
|
val = REG_SET_FLD(VPU_HW_BTRS_MTL_VPU_IP_RESET, TRIGGER, val);
|
|
REGB_WR32(VPU_HW_BTRS_MTL_VPU_IP_RESET, val);
|
|
|
|
ret = REGB_POLL_FLD(VPU_HW_BTRS_MTL_VPU_IP_RESET, TRIGGER, 0, TIMEOUT_US);
|
|
if (ret)
|
|
ivpu_err(vdev, "Timed out waiting for RESET completion\n");
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int ip_reset_lnl(struct ivpu_device *vdev)
|
|
{
|
|
int ret;
|
|
u32 val;
|
|
|
|
ivpu_hw_btrs_clock_relinquish_disable_lnl(vdev);
|
|
|
|
ret = REGB_POLL_FLD(VPU_HW_BTRS_LNL_IP_RESET, TRIGGER, 0, TIMEOUT_US);
|
|
if (ret) {
|
|
ivpu_err(vdev, "Wait for *_TRIGGER timed out\n");
|
|
return ret;
|
|
}
|
|
|
|
val = REGB_RD32(VPU_HW_BTRS_LNL_IP_RESET);
|
|
val = REG_SET_FLD(VPU_HW_BTRS_LNL_IP_RESET, TRIGGER, val);
|
|
REGB_WR32(VPU_HW_BTRS_LNL_IP_RESET, val);
|
|
|
|
ret = REGB_POLL_FLD(VPU_HW_BTRS_LNL_IP_RESET, TRIGGER, 0, TIMEOUT_US);
|
|
if (ret)
|
|
ivpu_err(vdev, "Timed out waiting for RESET completion\n");
|
|
|
|
return ret;
|
|
}
|
|
|
|
int ivpu_hw_btrs_ip_reset(struct ivpu_device *vdev)
|
|
{
|
|
if (IVPU_WA(punit_disabled))
|
|
return 0;
|
|
|
|
if (ivpu_hw_btrs_gen(vdev) == IVPU_HW_BTRS_MTL)
|
|
return ip_reset_mtl(vdev);
|
|
else
|
|
return ip_reset_lnl(vdev);
|
|
}
|
|
|
|
void ivpu_hw_btrs_profiling_freq_reg_set_lnl(struct ivpu_device *vdev)
|
|
{
|
|
u32 val = REGB_RD32(VPU_HW_BTRS_LNL_VPU_STATUS);
|
|
|
|
if (vdev->hw->pll.profiling_freq == PLL_PROFILING_FREQ_DEFAULT)
|
|
val = REG_CLR_FLD(VPU_HW_BTRS_LNL_VPU_STATUS, PERF_CLK, val);
|
|
else
|
|
val = REG_SET_FLD(VPU_HW_BTRS_LNL_VPU_STATUS, PERF_CLK, val);
|
|
|
|
REGB_WR32(VPU_HW_BTRS_LNL_VPU_STATUS, val);
|
|
}
|
|
|
|
void ivpu_hw_btrs_ats_print_lnl(struct ivpu_device *vdev)
|
|
{
|
|
ivpu_dbg(vdev, MISC, "Buttress ATS: %s\n",
|
|
REGB_RD32(VPU_HW_BTRS_LNL_HM_ATS) ? "Enable" : "Disable");
|
|
}
|
|
|
|
void ivpu_hw_btrs_clock_relinquish_disable_lnl(struct ivpu_device *vdev)
|
|
{
|
|
u32 val = REGB_RD32(VPU_HW_BTRS_LNL_VPU_STATUS);
|
|
|
|
val = REG_SET_FLD(VPU_HW_BTRS_LNL_VPU_STATUS, DISABLE_CLK_RELINQUISH, val);
|
|
REGB_WR32(VPU_HW_BTRS_LNL_VPU_STATUS, val);
|
|
}
|
|
|
|
bool ivpu_hw_btrs_is_idle(struct ivpu_device *vdev)
|
|
{
|
|
u32 val;
|
|
|
|
if (IVPU_WA(punit_disabled))
|
|
return true;
|
|
|
|
if (ivpu_hw_btrs_gen(vdev) == IVPU_HW_BTRS_MTL) {
|
|
val = REGB_RD32(VPU_HW_BTRS_MTL_VPU_STATUS);
|
|
|
|
return REG_TEST_FLD(VPU_HW_BTRS_MTL_VPU_STATUS, READY, val) &&
|
|
REG_TEST_FLD(VPU_HW_BTRS_MTL_VPU_STATUS, IDLE, val);
|
|
} else {
|
|
val = REGB_RD32(VPU_HW_BTRS_LNL_VPU_STATUS);
|
|
|
|
return REG_TEST_FLD(VPU_HW_BTRS_LNL_VPU_STATUS, READY, val) &&
|
|
REG_TEST_FLD(VPU_HW_BTRS_LNL_VPU_STATUS, IDLE, val);
|
|
}
|
|
}
|
|
|
|
int ivpu_hw_btrs_wait_for_idle(struct ivpu_device *vdev)
|
|
{
|
|
if (ivpu_hw_btrs_gen(vdev) == IVPU_HW_BTRS_MTL)
|
|
return REGB_POLL_FLD(VPU_HW_BTRS_MTL_VPU_STATUS, IDLE, 0x1, IDLE_TIMEOUT_US);
|
|
else
|
|
return REGB_POLL_FLD(VPU_HW_BTRS_LNL_VPU_STATUS, IDLE, 0x1, IDLE_TIMEOUT_US);
|
|
}
|
|
|
|
/* Handler for IRQs from Buttress core (irqB) */
|
|
bool ivpu_hw_btrs_irq_handler_mtl(struct ivpu_device *vdev, int irq)
|
|
{
|
|
u32 status = REGB_RD32(VPU_HW_BTRS_MTL_INTERRUPT_STAT) & BTRS_MTL_IRQ_MASK;
|
|
bool schedule_recovery = false;
|
|
|
|
if (!status)
|
|
return false;
|
|
|
|
if (REG_TEST_FLD(VPU_HW_BTRS_MTL_INTERRUPT_STAT, FREQ_CHANGE, status))
|
|
ivpu_dbg(vdev, IRQ, "FREQ_CHANGE irq: %08x",
|
|
REGB_RD32(VPU_HW_BTRS_MTL_CURRENT_PLL));
|
|
|
|
if (REG_TEST_FLD(VPU_HW_BTRS_MTL_INTERRUPT_STAT, ATS_ERR, status)) {
|
|
ivpu_err(vdev, "ATS_ERR irq 0x%016llx", REGB_RD64(VPU_HW_BTRS_MTL_ATS_ERR_LOG_0));
|
|
REGB_WR32(VPU_HW_BTRS_MTL_ATS_ERR_CLEAR, 0x1);
|
|
schedule_recovery = true;
|
|
}
|
|
|
|
if (REG_TEST_FLD(VPU_HW_BTRS_MTL_INTERRUPT_STAT, UFI_ERR, status)) {
|
|
u32 ufi_log = REGB_RD32(VPU_HW_BTRS_MTL_UFI_ERR_LOG);
|
|
|
|
ivpu_err(vdev, "UFI_ERR irq (0x%08x) opcode: 0x%02lx axi_id: 0x%02lx cq_id: 0x%03lx",
|
|
ufi_log, REG_GET_FLD(VPU_HW_BTRS_MTL_UFI_ERR_LOG, OPCODE, ufi_log),
|
|
REG_GET_FLD(VPU_HW_BTRS_MTL_UFI_ERR_LOG, AXI_ID, ufi_log),
|
|
REG_GET_FLD(VPU_HW_BTRS_MTL_UFI_ERR_LOG, CQ_ID, ufi_log));
|
|
REGB_WR32(VPU_HW_BTRS_MTL_UFI_ERR_CLEAR, 0x1);
|
|
schedule_recovery = true;
|
|
}
|
|
|
|
/* This must be done after interrupts are cleared at the source. */
|
|
if (IVPU_WA(interrupt_clear_with_0))
|
|
/*
|
|
* Writing 1 triggers an interrupt, so we can't perform read update write.
|
|
* Clear local interrupt status by writing 0 to all bits.
|
|
*/
|
|
REGB_WR32(VPU_HW_BTRS_MTL_INTERRUPT_STAT, 0x0);
|
|
else
|
|
REGB_WR32(VPU_HW_BTRS_MTL_INTERRUPT_STAT, status);
|
|
|
|
if (schedule_recovery)
|
|
ivpu_pm_trigger_recovery(vdev, "Buttress IRQ");
|
|
|
|
return true;
|
|
}
|
|
|
|
/* Handler for IRQs from Buttress core (irqB) */
|
|
bool ivpu_hw_btrs_irq_handler_lnl(struct ivpu_device *vdev, int irq)
|
|
{
|
|
u32 status = REGB_RD32(VPU_HW_BTRS_LNL_INTERRUPT_STAT) & BTRS_LNL_IRQ_MASK;
|
|
bool schedule_recovery = false;
|
|
|
|
if (!status)
|
|
return false;
|
|
|
|
if (REG_TEST_FLD(VPU_HW_BTRS_LNL_INTERRUPT_STAT, SURV_ERR, status)) {
|
|
ivpu_dbg(vdev, IRQ, "Survivability IRQ\n");
|
|
if (!kfifo_put(&vdev->hw->irq.fifo, IVPU_HW_IRQ_SRC_DCT))
|
|
ivpu_err_ratelimited(vdev, "IRQ FIFO full\n");
|
|
}
|
|
|
|
if (REG_TEST_FLD(VPU_HW_BTRS_LNL_INTERRUPT_STAT, FREQ_CHANGE, status))
|
|
ivpu_dbg(vdev, IRQ, "FREQ_CHANGE irq: %08x", REGB_RD32(VPU_HW_BTRS_LNL_PLL_FREQ));
|
|
|
|
if (REG_TEST_FLD(VPU_HW_BTRS_LNL_INTERRUPT_STAT, ATS_ERR, status)) {
|
|
ivpu_err(vdev, "ATS_ERR LOG1 0x%08x ATS_ERR_LOG2 0x%08x\n",
|
|
REGB_RD32(VPU_HW_BTRS_LNL_ATS_ERR_LOG1),
|
|
REGB_RD32(VPU_HW_BTRS_LNL_ATS_ERR_LOG2));
|
|
REGB_WR32(VPU_HW_BTRS_LNL_ATS_ERR_CLEAR, 0x1);
|
|
schedule_recovery = true;
|
|
}
|
|
|
|
if (REG_TEST_FLD(VPU_HW_BTRS_LNL_INTERRUPT_STAT, CFI0_ERR, status)) {
|
|
ivpu_err(vdev, "CFI0_ERR 0x%08x", REGB_RD32(VPU_HW_BTRS_LNL_CFI0_ERR_LOG));
|
|
REGB_WR32(VPU_HW_BTRS_LNL_CFI0_ERR_CLEAR, 0x1);
|
|
schedule_recovery = true;
|
|
}
|
|
|
|
if (REG_TEST_FLD(VPU_HW_BTRS_LNL_INTERRUPT_STAT, CFI1_ERR, status)) {
|
|
ivpu_err(vdev, "CFI1_ERR 0x%08x", REGB_RD32(VPU_HW_BTRS_LNL_CFI1_ERR_LOG));
|
|
REGB_WR32(VPU_HW_BTRS_LNL_CFI1_ERR_CLEAR, 0x1);
|
|
schedule_recovery = true;
|
|
}
|
|
|
|
if (REG_TEST_FLD(VPU_HW_BTRS_LNL_INTERRUPT_STAT, IMR0_ERR, status)) {
|
|
ivpu_err(vdev, "IMR_ERR_CFI0 LOW: 0x%08x HIGH: 0x%08x",
|
|
REGB_RD32(VPU_HW_BTRS_LNL_IMR_ERR_CFI0_LOW),
|
|
REGB_RD32(VPU_HW_BTRS_LNL_IMR_ERR_CFI0_HIGH));
|
|
REGB_WR32(VPU_HW_BTRS_LNL_IMR_ERR_CFI0_CLEAR, 0x1);
|
|
schedule_recovery = true;
|
|
}
|
|
|
|
if (REG_TEST_FLD(VPU_HW_BTRS_LNL_INTERRUPT_STAT, IMR1_ERR, status)) {
|
|
ivpu_err(vdev, "IMR_ERR_CFI1 LOW: 0x%08x HIGH: 0x%08x",
|
|
REGB_RD32(VPU_HW_BTRS_LNL_IMR_ERR_CFI1_LOW),
|
|
REGB_RD32(VPU_HW_BTRS_LNL_IMR_ERR_CFI1_HIGH));
|
|
REGB_WR32(VPU_HW_BTRS_LNL_IMR_ERR_CFI1_CLEAR, 0x1);
|
|
schedule_recovery = true;
|
|
}
|
|
|
|
/* This must be done after interrupts are cleared at the source. */
|
|
REGB_WR32(VPU_HW_BTRS_LNL_INTERRUPT_STAT, status);
|
|
|
|
if (schedule_recovery)
|
|
ivpu_pm_trigger_recovery(vdev, "Buttress IRQ");
|
|
|
|
return true;
|
|
}
|
|
|
|
int ivpu_hw_btrs_dct_get_request(struct ivpu_device *vdev, bool *enable)
|
|
{
|
|
u32 val = REGB_RD32(VPU_HW_BTRS_LNL_PCODE_MAILBOX_SHADOW);
|
|
u32 cmd = REG_GET_FLD(VPU_HW_BTRS_LNL_PCODE_MAILBOX_SHADOW, CMD, val);
|
|
u32 param1 = REG_GET_FLD(VPU_HW_BTRS_LNL_PCODE_MAILBOX_SHADOW, PARAM1, val);
|
|
|
|
if (cmd != DCT_REQ) {
|
|
ivpu_err_ratelimited(vdev, "Unsupported PCODE command: 0x%x\n", cmd);
|
|
return -EBADR;
|
|
}
|
|
|
|
switch (param1) {
|
|
case DCT_ENABLE:
|
|
*enable = true;
|
|
return 0;
|
|
case DCT_DISABLE:
|
|
*enable = false;
|
|
return 0;
|
|
default:
|
|
ivpu_err_ratelimited(vdev, "Invalid PARAM1 value: %u\n", param1);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
void ivpu_hw_btrs_dct_set_status(struct ivpu_device *vdev, bool enable, u32 active_percent)
|
|
{
|
|
u32 val = 0;
|
|
u32 cmd = enable ? DCT_ENABLE : DCT_DISABLE;
|
|
|
|
val = REG_SET_FLD_NUM(VPU_HW_BTRS_LNL_PCODE_MAILBOX_STATUS, CMD, DCT_REQ, val);
|
|
val = REG_SET_FLD_NUM(VPU_HW_BTRS_LNL_PCODE_MAILBOX_STATUS, PARAM1, cmd, val);
|
|
val = REG_SET_FLD_NUM(VPU_HW_BTRS_LNL_PCODE_MAILBOX_STATUS, PARAM2, active_percent, val);
|
|
|
|
REGB_WR32(VPU_HW_BTRS_LNL_PCODE_MAILBOX_STATUS, val);
|
|
}
|
|
|
|
static u32 pll_ratio_to_freq_mtl(u32 ratio, u32 config)
|
|
{
|
|
u32 pll_clock = PLL_REF_CLK_FREQ * ratio;
|
|
u32 cpu_clock;
|
|
|
|
if ((config & 0xff) == MTL_PLL_RATIO_4_3)
|
|
cpu_clock = pll_clock * 2 / 4;
|
|
else
|
|
cpu_clock = pll_clock * 2 / 5;
|
|
|
|
return cpu_clock;
|
|
}
|
|
|
|
u32 ivpu_hw_btrs_ratio_to_freq(struct ivpu_device *vdev, u32 ratio)
|
|
{
|
|
struct ivpu_hw_info *hw = vdev->hw;
|
|
|
|
if (ivpu_hw_btrs_gen(vdev) == IVPU_HW_BTRS_MTL)
|
|
return pll_ratio_to_freq_mtl(ratio, hw->config);
|
|
else
|
|
return PLL_RATIO_TO_FREQ(ratio);
|
|
}
|
|
|
|
static u32 pll_freq_get_mtl(struct ivpu_device *vdev)
|
|
{
|
|
u32 pll_curr_ratio;
|
|
|
|
pll_curr_ratio = REGB_RD32(VPU_HW_BTRS_MTL_CURRENT_PLL);
|
|
pll_curr_ratio &= VPU_HW_BTRS_MTL_CURRENT_PLL_RATIO_MASK;
|
|
|
|
if (!ivpu_is_silicon(vdev))
|
|
return PLL_SIMULATION_FREQ;
|
|
|
|
return pll_ratio_to_freq_mtl(pll_curr_ratio, vdev->hw->config);
|
|
}
|
|
|
|
static u32 pll_freq_get_lnl(struct ivpu_device *vdev)
|
|
{
|
|
u32 pll_curr_ratio;
|
|
|
|
pll_curr_ratio = REGB_RD32(VPU_HW_BTRS_LNL_PLL_FREQ);
|
|
pll_curr_ratio &= VPU_HW_BTRS_LNL_PLL_FREQ_RATIO_MASK;
|
|
|
|
return PLL_RATIO_TO_FREQ(pll_curr_ratio);
|
|
}
|
|
|
|
u32 ivpu_hw_btrs_pll_freq_get(struct ivpu_device *vdev)
|
|
{
|
|
if (ivpu_hw_btrs_gen(vdev) == IVPU_HW_BTRS_MTL)
|
|
return pll_freq_get_mtl(vdev);
|
|
else
|
|
return pll_freq_get_lnl(vdev);
|
|
}
|
|
|
|
u32 ivpu_hw_btrs_telemetry_offset_get(struct ivpu_device *vdev)
|
|
{
|
|
if (ivpu_hw_btrs_gen(vdev) == IVPU_HW_BTRS_MTL)
|
|
return REGB_RD32(VPU_HW_BTRS_MTL_VPU_TELEMETRY_OFFSET);
|
|
else
|
|
return REGB_RD32(VPU_HW_BTRS_LNL_VPU_TELEMETRY_OFFSET);
|
|
}
|
|
|
|
u32 ivpu_hw_btrs_telemetry_size_get(struct ivpu_device *vdev)
|
|
{
|
|
if (ivpu_hw_btrs_gen(vdev) == IVPU_HW_BTRS_MTL)
|
|
return REGB_RD32(VPU_HW_BTRS_MTL_VPU_TELEMETRY_SIZE);
|
|
else
|
|
return REGB_RD32(VPU_HW_BTRS_LNL_VPU_TELEMETRY_SIZE);
|
|
}
|
|
|
|
u32 ivpu_hw_btrs_telemetry_enable_get(struct ivpu_device *vdev)
|
|
{
|
|
if (ivpu_hw_btrs_gen(vdev) == IVPU_HW_BTRS_MTL)
|
|
return REGB_RD32(VPU_HW_BTRS_MTL_VPU_TELEMETRY_ENABLE);
|
|
else
|
|
return REGB_RD32(VPU_HW_BTRS_LNL_VPU_TELEMETRY_ENABLE);
|
|
}
|
|
|
|
void ivpu_hw_btrs_global_int_disable(struct ivpu_device *vdev)
|
|
{
|
|
if (ivpu_hw_btrs_gen(vdev) == IVPU_HW_BTRS_MTL)
|
|
REGB_WR32(VPU_HW_BTRS_MTL_GLOBAL_INT_MASK, 0x1);
|
|
else
|
|
REGB_WR32(VPU_HW_BTRS_LNL_GLOBAL_INT_MASK, 0x1);
|
|
}
|
|
|
|
void ivpu_hw_btrs_global_int_enable(struct ivpu_device *vdev)
|
|
{
|
|
if (ivpu_hw_btrs_gen(vdev) == IVPU_HW_BTRS_MTL)
|
|
REGB_WR32(VPU_HW_BTRS_MTL_GLOBAL_INT_MASK, 0x0);
|
|
else
|
|
REGB_WR32(VPU_HW_BTRS_LNL_GLOBAL_INT_MASK, 0x0);
|
|
}
|
|
|
|
void ivpu_hw_btrs_irq_enable(struct ivpu_device *vdev)
|
|
{
|
|
if (ivpu_hw_btrs_gen(vdev) == IVPU_HW_BTRS_MTL) {
|
|
REGB_WR32(VPU_HW_BTRS_MTL_LOCAL_INT_MASK, (u32)(~BTRS_MTL_IRQ_MASK));
|
|
REGB_WR32(VPU_HW_BTRS_MTL_GLOBAL_INT_MASK, 0x0);
|
|
} else {
|
|
REGB_WR32(VPU_HW_BTRS_LNL_LOCAL_INT_MASK, (u32)(~BTRS_LNL_IRQ_MASK));
|
|
REGB_WR32(VPU_HW_BTRS_LNL_GLOBAL_INT_MASK, 0x0);
|
|
}
|
|
}
|
|
|
|
void ivpu_hw_btrs_irq_disable(struct ivpu_device *vdev)
|
|
{
|
|
if (ivpu_hw_btrs_gen(vdev) == IVPU_HW_BTRS_MTL) {
|
|
REGB_WR32(VPU_HW_BTRS_MTL_GLOBAL_INT_MASK, 0x1);
|
|
REGB_WR32(VPU_HW_BTRS_MTL_LOCAL_INT_MASK, BTRS_IRQ_DISABLE_MASK);
|
|
} else {
|
|
REGB_WR32(VPU_HW_BTRS_LNL_GLOBAL_INT_MASK, 0x1);
|
|
REGB_WR32(VPU_HW_BTRS_LNL_LOCAL_INT_MASK, BTRS_IRQ_DISABLE_MASK);
|
|
}
|
|
}
|
|
|
|
static void diagnose_failure_mtl(struct ivpu_device *vdev)
|
|
{
|
|
u32 reg = REGB_RD32(VPU_HW_BTRS_MTL_INTERRUPT_STAT) & BTRS_MTL_IRQ_MASK;
|
|
|
|
if (REG_TEST_FLD(VPU_HW_BTRS_MTL_INTERRUPT_STAT, ATS_ERR, reg))
|
|
ivpu_err(vdev, "ATS_ERR irq 0x%016llx", REGB_RD64(VPU_HW_BTRS_MTL_ATS_ERR_LOG_0));
|
|
|
|
if (REG_TEST_FLD(VPU_HW_BTRS_MTL_INTERRUPT_STAT, UFI_ERR, reg)) {
|
|
u32 log = REGB_RD32(VPU_HW_BTRS_MTL_UFI_ERR_LOG);
|
|
|
|
ivpu_err(vdev, "UFI_ERR irq (0x%08x) opcode: 0x%02lx axi_id: 0x%02lx cq_id: 0x%03lx",
|
|
log, REG_GET_FLD(VPU_HW_BTRS_MTL_UFI_ERR_LOG, OPCODE, log),
|
|
REG_GET_FLD(VPU_HW_BTRS_MTL_UFI_ERR_LOG, AXI_ID, log),
|
|
REG_GET_FLD(VPU_HW_BTRS_MTL_UFI_ERR_LOG, CQ_ID, log));
|
|
}
|
|
}
|
|
|
|
static void diagnose_failure_lnl(struct ivpu_device *vdev)
|
|
{
|
|
u32 reg = REGB_RD32(VPU_HW_BTRS_MTL_INTERRUPT_STAT) & BTRS_LNL_IRQ_MASK;
|
|
|
|
if (REG_TEST_FLD(VPU_HW_BTRS_LNL_INTERRUPT_STAT, ATS_ERR, reg)) {
|
|
ivpu_err(vdev, "ATS_ERR_LOG1 0x%08x ATS_ERR_LOG2 0x%08x\n",
|
|
REGB_RD32(VPU_HW_BTRS_LNL_ATS_ERR_LOG1),
|
|
REGB_RD32(VPU_HW_BTRS_LNL_ATS_ERR_LOG2));
|
|
}
|
|
|
|
if (REG_TEST_FLD(VPU_HW_BTRS_LNL_INTERRUPT_STAT, CFI0_ERR, reg))
|
|
ivpu_err(vdev, "CFI0_ERR_LOG 0x%08x\n", REGB_RD32(VPU_HW_BTRS_LNL_CFI0_ERR_LOG));
|
|
|
|
if (REG_TEST_FLD(VPU_HW_BTRS_LNL_INTERRUPT_STAT, CFI1_ERR, reg))
|
|
ivpu_err(vdev, "CFI1_ERR_LOG 0x%08x\n", REGB_RD32(VPU_HW_BTRS_LNL_CFI1_ERR_LOG));
|
|
|
|
if (REG_TEST_FLD(VPU_HW_BTRS_LNL_INTERRUPT_STAT, IMR0_ERR, reg))
|
|
ivpu_err(vdev, "IMR_ERR_CFI0 LOW: 0x%08x HIGH: 0x%08x\n",
|
|
REGB_RD32(VPU_HW_BTRS_LNL_IMR_ERR_CFI0_LOW),
|
|
REGB_RD32(VPU_HW_BTRS_LNL_IMR_ERR_CFI0_HIGH));
|
|
|
|
if (REG_TEST_FLD(VPU_HW_BTRS_LNL_INTERRUPT_STAT, IMR1_ERR, reg))
|
|
ivpu_err(vdev, "IMR_ERR_CFI1 LOW: 0x%08x HIGH: 0x%08x\n",
|
|
REGB_RD32(VPU_HW_BTRS_LNL_IMR_ERR_CFI1_LOW),
|
|
REGB_RD32(VPU_HW_BTRS_LNL_IMR_ERR_CFI1_HIGH));
|
|
|
|
if (REG_TEST_FLD(VPU_HW_BTRS_LNL_INTERRUPT_STAT, SURV_ERR, reg))
|
|
ivpu_err(vdev, "Survivability IRQ\n");
|
|
}
|
|
|
|
void ivpu_hw_btrs_diagnose_failure(struct ivpu_device *vdev)
|
|
{
|
|
if (ivpu_hw_btrs_gen(vdev) == IVPU_HW_BTRS_MTL)
|
|
return diagnose_failure_mtl(vdev);
|
|
else
|
|
return diagnose_failure_lnl(vdev);
|
|
}
|