Commit a19bffb1 authored by Jacek Lawrynowicz's avatar Jacek Lawrynowicz
Browse files

accel/ivpu: Implement DCT handling



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: default avatarJacek Lawrynowicz <jacek.lawrynowicz@linux.intel.com>
Reviewed-by: default avatarWachowski, Karol <karol.wachowski@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20240611120433.1012423-7-jacek.lawrynowicz@linux.intel.com
parent ab4484cd
Loading
Loading
Loading
Loading
+37 −2
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (C) 2020-2023 Intel Corporation
 * Copyright (C) 2020-2024 Intel Corporation
 */

#include <linux/debugfs.h>
@@ -381,6 +381,39 @@ static const struct file_operations ivpu_resume_engine_fops = {
	.write = ivpu_resume_engine_fn,
};

static int dct_active_get(void *data, u64 *active_percent)
{
	struct ivpu_device *vdev = data;

	*active_percent = vdev->pm->dct_active_percent;

	return 0;
}

static int dct_active_set(void *data, u64 active_percent)
{
	struct ivpu_device *vdev = data;
	int ret;

	if (active_percent > 100)
		return -EINVAL;

	ret = ivpu_rpm_get(vdev);
	if (ret)
		return ret;

	if (active_percent)
		ret = ivpu_pm_dct_enable(vdev, active_percent);
	else
		ret = ivpu_pm_dct_disable(vdev);

	ivpu_rpm_put(vdev);

	return ret;
}

DEFINE_DEBUGFS_ATTRIBUTE(ivpu_dct_fops, dct_active_get, dct_active_set, "%llu\n");

void ivpu_debugfs_init(struct ivpu_device *vdev)
{
	struct dentry *debugfs_root = vdev->drm.debugfs_root;
@@ -409,7 +442,9 @@ void ivpu_debugfs_init(struct ivpu_device *vdev)
	debugfs_create_file("resume_engine", 0200, debugfs_root, vdev,
			    &ivpu_resume_engine_fops);

	if (ivpu_hw_ip_gen(vdev) >= IVPU_HW_IP_40XX)
	if (ivpu_hw_ip_gen(vdev) >= IVPU_HW_IP_40XX) {
		debugfs_create_file("fw_profiling_freq_drive", 0200,
				    debugfs_root, vdev, &fw_profiling_freq_fops);
		debugfs_create_file("dct", 0644, debugfs_root, vdev, &ivpu_dct_fops);
	}
}
+9 −1
Original line number Diff line number Diff line
@@ -391,8 +391,13 @@ int ivpu_boot(struct ivpu_device *vdev)
	ivpu_hw_irq_enable(vdev);
	ivpu_ipc_enable(vdev);

	if (ivpu_fw_is_cold_boot(vdev))
	if (ivpu_fw_is_cold_boot(vdev)) {
		ret = ivpu_pm_dct_init(vdev);
		if (ret)
			return ret;

		return ivpu_hw_sched_init(vdev);
	}

	return 0;
}
@@ -482,6 +487,9 @@ static irqreturn_t ivpu_irq_thread_handler(int irq, void *arg)
		case IVPU_HW_IRQ_SRC_MMU_EVTQ:
			ivpu_context_abort_invalid(vdev);
			break;
		case IVPU_HW_IRQ_SRC_DCT:
			ivpu_pm_dct_irq_thread_handler(vdev);
			break;
		default:
			ivpu_err_ratelimited(vdev, "Unknown IRQ source: %u\n", irq_src);
			break;
+1 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

#define IVPU_HW_IRQ_SRC_IPC 1
#define IVPU_HW_IRQ_SRC_MMU_EVTQ 2
#define IVPU_HW_IRQ_SRC_DCT 3

struct ivpu_addr_range {
	resource_size_t start;
+32 −10
Original line number Diff line number Diff line
@@ -643,8 +643,11 @@ bool ivpu_hw_btrs_irq_handler_lnl(struct ivpu_device *vdev, int irq)
	if (!status)
		return false;

	if (REG_TEST_FLD(VPU_HW_BTRS_LNL_INTERRUPT_STAT, SURV_ERR, status))
	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));
@@ -694,21 +697,40 @@ bool ivpu_hw_btrs_irq_handler_lnl(struct ivpu_device *vdev, int irq)
	return true;
}

static void dct_drive_40xx(struct ivpu_device *vdev, u32 dct_val)
int ivpu_hw_btrs_dct_get_request(struct ivpu_device *vdev, bool *enable)
{
	u32 val = REGB_RD32(VPU_HW_BTRS_LNL_PCODE_MAILBOX);
	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);

	val = REG_SET_FLD_NUM(VPU_HW_BTRS_LNL_PCODE_MAILBOX, CMD, DCT_REQ, val);
	val = REG_SET_FLD_NUM(VPU_HW_BTRS_LNL_PCODE_MAILBOX, PARAM1,
			      dct_val ? DCT_ENABLE : DCT_DISABLE, val);
	val = REG_SET_FLD_NUM(VPU_HW_BTRS_LNL_PCODE_MAILBOX, PARAM2, dct_val, val);
	if (cmd != DCT_REQ) {
		ivpu_err_ratelimited(vdev, "Unsupported PCODE command: 0x%x\n", cmd);
		return -EBADR;
	}

	REGB_WR32(VPU_HW_BTRS_LNL_PCODE_MAILBOX, val);
	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_drive(struct ivpu_device *vdev, u32 dct_val)
void ivpu_hw_btrs_dct_set_status(struct ivpu_device *vdev, bool enable, u32 active_percent)
{
	return dct_drive_40xx(vdev, dct_val);
	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)
+5 −1
Original line number Diff line number Diff line
@@ -15,6 +15,9 @@
#define PLL_PROFILING_FREQ_HIGH      400000000
#define PLL_RATIO_TO_FREQ(x)         ((x) * PLL_REF_CLK_FREQ)

#define DCT_DEFAULT_ACTIVE_PERCENT 15u
#define DCT_PERIOD_US		   35300u

int ivpu_hw_btrs_info_init(struct ivpu_device *vdev);
void ivpu_hw_btrs_freq_ratios_init(struct ivpu_device *vdev);
int ivpu_hw_btrs_irqs_clear_with_0_mtl(struct ivpu_device *vdev);
@@ -31,7 +34,8 @@ void ivpu_hw_btrs_ats_print_lnl(struct ivpu_device *vdev);
void ivpu_hw_btrs_clock_relinquish_disable_lnl(struct ivpu_device *vdev);
bool ivpu_hw_btrs_irq_handler_mtl(struct ivpu_device *vdev, int irq);
bool ivpu_hw_btrs_irq_handler_lnl(struct ivpu_device *vdev, int irq);
void ivpu_hw_btrs_dct_drive(struct ivpu_device *vdev, u32 dct_val);
int ivpu_hw_btrs_dct_get_request(struct ivpu_device *vdev, bool *enable);
void ivpu_hw_btrs_dct_set_status(struct ivpu_device *vdev, bool enable, u32 dct_percent);
u32 ivpu_hw_btrs_pll_freq_get(struct ivpu_device *vdev);
u32 ivpu_hw_btrs_ratio_to_freq(struct ivpu_device *vdev, u32 ratio);
u32 ivpu_hw_btrs_telemetry_offset_get(struct ivpu_device *vdev);
Loading