Commit 3198a62e authored by Andrzej Kacprowski's avatar Andrzej Kacprowski Committed by Stanislaw Gruszka
Browse files

accel/ivpu: Add support for delayed D0i3 entry message



Currently the VPU firmware prepares for D0i3 every time the VPU
is entering D0i2 Idle state. This is not optimal as we might not
enter D0i3 every time we enter D0i2 Idle and this preparation
is quite costly.

This optimization moves D0i3 preparation to a dedicated
message sent from the host driver only when the driver is about
to enter D0i3 - this reduces power consumption and latency for
certain workloads, for example audio workloads that submit
inference every 10 ms.

The VPU needs non zero time to enter IDLE state after responding to
D0i3 entry message. If the driver does not wait for the VPU to enter
IDLE state it could cause warm boot failures.

Signed-off-by: default avatarAndrzej Kacprowski <andrzej.kacprowski@linux.intel.com>
Reviewed-by: default avatarStanislaw Gruszka <stanislaw.gruszka@linux.intel.com>
Signed-off-by: default avatarStanislaw Gruszka <stanislaw.gruszka@linux.intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20231028133415.1169975-12-stanislaw.gruszka@linux.intel.com
parent cc19feda
Loading
Loading
Loading
Loading
+7 −3
Original line number Diff line number Diff line
@@ -87,6 +87,7 @@ struct ivpu_wa_table {
	bool d3hot_after_power_off;
	bool interrupt_clear_with_0;
	bool disable_clock_relinquish;
	bool disable_d0i3_msg;
};

struct ivpu_hw_info;
@@ -125,6 +126,7 @@ struct ivpu_device {
		int tdr;
		int reschedule_suspend;
		int autosuspend;
		int d0i3_entry_msg;
	} timeout;
};

@@ -150,6 +152,8 @@ extern bool ivpu_disable_mmu_cont_pages;
#define IVPU_TEST_MODE_FW_TEST            BIT(0)
#define IVPU_TEST_MODE_NULL_HW            BIT(1)
#define IVPU_TEST_MODE_NULL_SUBMISSION    BIT(2)
#define IVPU_TEST_MODE_D0I3_MSG_DISABLE   BIT(4)
#define IVPU_TEST_MODE_D0I3_MSG_ENABLE    BIT(5)
extern int ivpu_test_mode;

struct ivpu_file_priv *ivpu_file_priv_get(struct ivpu_file_priv *file_priv);
+45 −3
Original line number Diff line number Diff line
@@ -33,12 +33,17 @@

#define ADDR_TO_L2_CACHE_CFG(addr) ((addr) >> 31)

#define IVPU_FW_CHECK_API(vdev, fw_hdr, name, min_major) \
/* Check if FW API is compatible with the driver */
#define IVPU_FW_CHECK_API_COMPAT(vdev, fw_hdr, name, min_major) \
	ivpu_fw_check_api(vdev, fw_hdr, #name, \
			  VPU_##name##_API_VER_INDEX, \
			  VPU_##name##_API_VER_MAJOR, \
			  VPU_##name##_API_VER_MINOR, min_major)

/* Check if API version is lower that the given version */
#define IVPU_FW_CHECK_API_VER_LT(vdev, fw_hdr, name, major, minor) \
	ivpu_fw_check_api_ver_lt(vdev, fw_hdr, #name, VPU_##name##_API_VER_INDEX, major, minor)

static char *ivpu_firmware;
module_param_named_unsafe(firmware, ivpu_firmware, charp, 0644);
MODULE_PARM_DESC(firmware, "VPU firmware binary in /lib/firmware/..");
@@ -105,6 +110,19 @@ ivpu_fw_check_api(struct ivpu_device *vdev, const struct vpu_firmware_header *fw
	return 0;
}

static bool
ivpu_fw_check_api_ver_lt(struct ivpu_device *vdev, const struct vpu_firmware_header *fw_hdr,
			 const char *str, int index, u16 major, u16 minor)
{
	u16 fw_major = (u16)(fw_hdr->api_version[index] >> 16);
	u16 fw_minor = (u16)(fw_hdr->api_version[index]);

	if (fw_major < major || (fw_major == major && fw_minor < minor))
		return true;

	return false;
}

static int ivpu_fw_parse(struct ivpu_device *vdev)
{
	struct ivpu_fw_info *fw = vdev->fw;
@@ -164,9 +182,9 @@ static int ivpu_fw_parse(struct ivpu_device *vdev)
	ivpu_info(vdev, "Firmware: %s, version: %s", fw->name,
		  (const char *)fw_hdr + VPU_FW_HEADER_SIZE);

	if (IVPU_FW_CHECK_API(vdev, fw_hdr, BOOT, 3))
	if (IVPU_FW_CHECK_API_COMPAT(vdev, fw_hdr, BOOT, 3))
		return -EINVAL;
	if (IVPU_FW_CHECK_API(vdev, fw_hdr, JSM, 3))
	if (IVPU_FW_CHECK_API_COMPAT(vdev, fw_hdr, JSM, 3))
		return -EINVAL;

	fw->runtime_addr = runtime_addr;
@@ -197,6 +215,24 @@ static void ivpu_fw_release(struct ivpu_device *vdev)
	release_firmware(vdev->fw->file);
}

/* Initialize workarounds that depend on FW version */
static void
ivpu_fw_init_wa(struct ivpu_device *vdev)
{
	const struct vpu_firmware_header *fw_hdr = (const void *)vdev->fw->file->data;

	if (IVPU_FW_CHECK_API_VER_LT(vdev, fw_hdr, BOOT, 3, 17) ||
	    (ivpu_hw_gen(vdev) > IVPU_HW_37XX) ||
	    (ivpu_test_mode & IVPU_TEST_MODE_D0I3_MSG_DISABLE))
		vdev->wa.disable_d0i3_msg = true;

	/* Force enable the feature for testing purposes */
	if (ivpu_test_mode & IVPU_TEST_MODE_D0I3_MSG_ENABLE)
		vdev->wa.disable_d0i3_msg = false;

	IVPU_PRINT_WA(disable_d0i3_msg);
}

static int ivpu_fw_update_global_range(struct ivpu_device *vdev)
{
	struct ivpu_fw_info *fw = vdev->fw;
@@ -299,6 +335,8 @@ int ivpu_fw_init(struct ivpu_device *vdev)
	if (ret)
		goto err_fw_release;

	ivpu_fw_init_wa(vdev);

	ret = ivpu_fw_mem_init(vdev);
	if (ret)
		goto err_fw_release;
@@ -426,6 +464,8 @@ static void ivpu_fw_boot_params_print(struct ivpu_device *vdev, struct vpu_boot_
		 boot_params->vpu_telemetry_enable);
	ivpu_dbg(vdev, FW_BOOT, "boot_params.dvfs_mode = %u\n",
		 boot_params->dvfs_mode);
	ivpu_dbg(vdev, FW_BOOT, "boot_params.d0i3_delayed_entry = %d\n",
		 boot_params->d0i3_delayed_entry);
	ivpu_dbg(vdev, FW_BOOT, "boot_params.d0i3_residency_time_us = %lld\n",
		 boot_params->d0i3_residency_time_us);
	ivpu_dbg(vdev, FW_BOOT, "boot_params.d0i3_entry_vpu_ts = %llu\n",
@@ -510,6 +550,8 @@ void ivpu_fw_boot_params_setup(struct ivpu_device *vdev, struct vpu_boot_params
	boot_params->punit_telemetry_sram_size = ivpu_hw_reg_telemetry_size_get(vdev);
	boot_params->vpu_telemetry_enable = ivpu_hw_reg_telemetry_enable_get(vdev);
	boot_params->dvfs_mode = vdev->fw->dvfs_mode;
	if (!IVPU_WA(disable_d0i3_msg))
		boot_params->d0i3_delayed_entry = 1;
	boot_params->d0i3_residency_time_us = 0;
	boot_params->d0i3_entry_vpu_ts = 0;

+6 −0
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@ struct ivpu_hw_ops {
	int (*boot_fw)(struct ivpu_device *vdev);
	int (*power_down)(struct ivpu_device *vdev);
	bool (*is_idle)(struct ivpu_device *vdev);
	int (*wait_for_idle)(struct ivpu_device *vdev);
	void (*wdt_disable)(struct ivpu_device *vdev);
	void (*diagnose_failure)(struct ivpu_device *vdev);
	u32 (*reg_pll_freq_get)(struct ivpu_device *vdev);
@@ -86,6 +87,11 @@ static inline bool ivpu_hw_is_idle(struct ivpu_device *vdev)
	return vdev->hw->ops->is_idle(vdev);
};

static inline int ivpu_hw_wait_for_idle(struct ivpu_device *vdev)
{
	return vdev->hw->ops->wait_for_idle(vdev);
};

static inline int ivpu_hw_power_down(struct ivpu_device *vdev)
{
	ivpu_dbg(vdev, PM, "HW power down\n");
+8 −1
Original line number Diff line number Diff line
@@ -37,7 +37,7 @@
#define TIMEOUT_US		     (150 * USEC_PER_MSEC)
#define PWR_ISLAND_STATUS_TIMEOUT_US (5 * USEC_PER_MSEC)
#define PLL_TIMEOUT_US		     (1500 * USEC_PER_MSEC)
#define IDLE_TIMEOUT_US		     (500 * USEC_PER_MSEC)
#define IDLE_TIMEOUT_US		     (5 * USEC_PER_MSEC)

#define ICB_0_IRQ_MASK ((REG_FLD(VPU_37XX_HOST_SS_ICB_STATUS_0, HOST_IPC_FIFO_INT)) | \
			(REG_FLD(VPU_37XX_HOST_SS_ICB_STATUS_0, MMU_IRQ_0_INT)) | \
@@ -90,6 +90,7 @@ static void ivpu_hw_timeouts_init(struct ivpu_device *vdev)
	vdev->timeout.tdr = 2000;
	vdev->timeout.reschedule_suspend = 10;
	vdev->timeout.autosuspend = 10;
	vdev->timeout.d0i3_entry_msg = 5;
}

static int ivpu_pll_wait_for_cmd_send(struct ivpu_device *vdev)
@@ -714,6 +715,11 @@ static bool ivpu_hw_37xx_is_idle(struct ivpu_device *vdev)
	       REG_TEST_FLD(VPU_37XX_BUTTRESS_VPU_STATUS, IDLE, val);
}

static int ivpu_hw_37xx_wait_for_idle(struct ivpu_device *vdev)
{
	return REGB_POLL_FLD(VPU_37XX_BUTTRESS_VPU_STATUS, IDLE, 0x1, IDLE_TIMEOUT_US);
}

static void ivpu_hw_37xx_save_d0i3_entry_timestamp(struct ivpu_device *vdev)
{
	vdev->hw->d0i3_entry_host_ts = ktime_get_boottime();
@@ -1001,6 +1007,7 @@ const struct ivpu_hw_ops ivpu_hw_37xx_ops = {
	.info_init = ivpu_hw_37xx_info_init,
	.power_up = ivpu_hw_37xx_power_up,
	.is_idle = ivpu_hw_37xx_is_idle,
	.wait_for_idle = ivpu_hw_37xx_wait_for_idle,
	.power_down = ivpu_hw_37xx_power_down,
	.boot_fw = ivpu_hw_37xx_boot_fw,
	.wdt_disable = ivpu_hw_37xx_wdt_disable,
+10 −0
Original line number Diff line number Diff line
@@ -39,6 +39,7 @@
#define TIMEOUT_US		     (150 * USEC_PER_MSEC)
#define PWR_ISLAND_STATUS_TIMEOUT_US (5 * USEC_PER_MSEC)
#define PLL_TIMEOUT_US		     (1500 * USEC_PER_MSEC)
#define IDLE_TIMEOUT_US		     (5 * USEC_PER_MSEC)

#define WEIGHTS_DEFAULT              0xf711f711u
#define WEIGHTS_ATS_DEFAULT          0x0000f711u
@@ -140,18 +141,21 @@ static void ivpu_hw_timeouts_init(struct ivpu_device *vdev)
		vdev->timeout.tdr = 2000000;
		vdev->timeout.reschedule_suspend = 1000;
		vdev->timeout.autosuspend = -1;
		vdev->timeout.d0i3_entry_msg = 500;
	} else if (ivpu_is_simics(vdev)) {
		vdev->timeout.boot = 50;
		vdev->timeout.jsm = 500;
		vdev->timeout.tdr = 10000;
		vdev->timeout.reschedule_suspend = 10;
		vdev->timeout.autosuspend = -1;
		vdev->timeout.d0i3_entry_msg = 100;
	} else {
		vdev->timeout.boot = 1000;
		vdev->timeout.jsm = 500;
		vdev->timeout.tdr = 2000;
		vdev->timeout.reschedule_suspend = 10;
		vdev->timeout.autosuspend = 10;
		vdev->timeout.d0i3_entry_msg = 5;
	}
}

@@ -879,6 +883,11 @@ static bool ivpu_hw_40xx_is_idle(struct ivpu_device *vdev)
	       REG_TEST_FLD(VPU_40XX_BUTTRESS_VPU_STATUS, IDLE, val);
}

static int ivpu_hw_40xx_wait_for_idle(struct ivpu_device *vdev)
{
	return REGB_POLL_FLD(VPU_40XX_BUTTRESS_VPU_STATUS, IDLE, 0x1, IDLE_TIMEOUT_US);
}

static void ivpu_hw_40xx_save_d0i3_entry_timestamp(struct ivpu_device *vdev)
{
	vdev->hw->d0i3_entry_host_ts = ktime_get_boottime();
@@ -1168,6 +1177,7 @@ const struct ivpu_hw_ops ivpu_hw_40xx_ops = {
	.info_init = ivpu_hw_40xx_info_init,
	.power_up = ivpu_hw_40xx_power_up,
	.is_idle = ivpu_hw_40xx_is_idle,
	.wait_for_idle = ivpu_hw_40xx_wait_for_idle,
	.power_down = ivpu_hw_40xx_power_down,
	.boot_fw = ivpu_hw_40xx_boot_fw,
	.wdt_disable = ivpu_hw_40xx_wdt_disable,
Loading