Skip to content

Commit a19bffb

Browse files
committed
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: 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
1 parent ab4484c commit a19bffb

File tree

10 files changed

+186
-20
lines changed

10 files changed

+186
-20
lines changed

drivers/accel/ivpu/ivpu_debugfs.c

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// SPDX-License-Identifier: GPL-2.0-only
22
/*
3-
* Copyright (C) 2020-2023 Intel Corporation
3+
* Copyright (C) 2020-2024 Intel Corporation
44
*/
55

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

384+
static int dct_active_get(void *data, u64 *active_percent)
385+
{
386+
struct ivpu_device *vdev = data;
387+
388+
*active_percent = vdev->pm->dct_active_percent;
389+
390+
return 0;
391+
}
392+
393+
static int dct_active_set(void *data, u64 active_percent)
394+
{
395+
struct ivpu_device *vdev = data;
396+
int ret;
397+
398+
if (active_percent > 100)
399+
return -EINVAL;
400+
401+
ret = ivpu_rpm_get(vdev);
402+
if (ret)
403+
return ret;
404+
405+
if (active_percent)
406+
ret = ivpu_pm_dct_enable(vdev, active_percent);
407+
else
408+
ret = ivpu_pm_dct_disable(vdev);
409+
410+
ivpu_rpm_put(vdev);
411+
412+
return ret;
413+
}
414+
415+
DEFINE_DEBUGFS_ATTRIBUTE(ivpu_dct_fops, dct_active_get, dct_active_set, "%llu\n");
416+
384417
void ivpu_debugfs_init(struct ivpu_device *vdev)
385418
{
386419
struct dentry *debugfs_root = vdev->drm.debugfs_root;
@@ -409,7 +442,9 @@ void ivpu_debugfs_init(struct ivpu_device *vdev)
409442
debugfs_create_file("resume_engine", 0200, debugfs_root, vdev,
410443
&ivpu_resume_engine_fops);
411444

412-
if (ivpu_hw_ip_gen(vdev) >= IVPU_HW_IP_40XX)
445+
if (ivpu_hw_ip_gen(vdev) >= IVPU_HW_IP_40XX) {
413446
debugfs_create_file("fw_profiling_freq_drive", 0200,
414447
debugfs_root, vdev, &fw_profiling_freq_fops);
448+
debugfs_create_file("dct", 0644, debugfs_root, vdev, &ivpu_dct_fops);
449+
}
415450
}

drivers/accel/ivpu/ivpu_drv.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -391,8 +391,13 @@ int ivpu_boot(struct ivpu_device *vdev)
391391
ivpu_hw_irq_enable(vdev);
392392
ivpu_ipc_enable(vdev);
393393

394-
if (ivpu_fw_is_cold_boot(vdev))
394+
if (ivpu_fw_is_cold_boot(vdev)) {
395+
ret = ivpu_pm_dct_init(vdev);
396+
if (ret)
397+
return ret;
398+
395399
return ivpu_hw_sched_init(vdev);
400+
}
396401

397402
return 0;
398403
}
@@ -482,6 +487,9 @@ static irqreturn_t ivpu_irq_thread_handler(int irq, void *arg)
482487
case IVPU_HW_IRQ_SRC_MMU_EVTQ:
483488
ivpu_context_abort_invalid(vdev);
484489
break;
490+
case IVPU_HW_IRQ_SRC_DCT:
491+
ivpu_pm_dct_irq_thread_handler(vdev);
492+
break;
485493
default:
486494
ivpu_err_ratelimited(vdev, "Unknown IRQ source: %u\n", irq_src);
487495
break;

drivers/accel/ivpu/ivpu_hw.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
#define IVPU_HW_IRQ_SRC_IPC 1
1818
#define IVPU_HW_IRQ_SRC_MMU_EVTQ 2
19+
#define IVPU_HW_IRQ_SRC_DCT 3
1920

2021
struct ivpu_addr_range {
2122
resource_size_t start;

drivers/accel/ivpu/ivpu_hw_btrs.c

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -643,8 +643,11 @@ bool ivpu_hw_btrs_irq_handler_lnl(struct ivpu_device *vdev, int irq)
643643
if (!status)
644644
return false;
645645

646-
if (REG_TEST_FLD(VPU_HW_BTRS_LNL_INTERRUPT_STAT, SURV_ERR, status))
646+
if (REG_TEST_FLD(VPU_HW_BTRS_LNL_INTERRUPT_STAT, SURV_ERR, status)) {
647647
ivpu_dbg(vdev, IRQ, "Survivability IRQ\n");
648+
if (!kfifo_put(&vdev->hw->irq.fifo, IVPU_HW_IRQ_SRC_DCT))
649+
ivpu_err_ratelimited(vdev, "IRQ FIFO full\n");
650+
}
648651

649652
if (REG_TEST_FLD(VPU_HW_BTRS_LNL_INTERRUPT_STAT, FREQ_CHANGE, status))
650653
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)
694697
return true;
695698
}
696699

697-
static void dct_drive_40xx(struct ivpu_device *vdev, u32 dct_val)
700+
int ivpu_hw_btrs_dct_get_request(struct ivpu_device *vdev, bool *enable)
698701
{
699-
u32 val = REGB_RD32(VPU_HW_BTRS_LNL_PCODE_MAILBOX);
702+
u32 val = REGB_RD32(VPU_HW_BTRS_LNL_PCODE_MAILBOX_SHADOW);
703+
u32 cmd = REG_GET_FLD(VPU_HW_BTRS_LNL_PCODE_MAILBOX_SHADOW, CMD, val);
704+
u32 param1 = REG_GET_FLD(VPU_HW_BTRS_LNL_PCODE_MAILBOX_SHADOW, PARAM1, val);
700705

701-
val = REG_SET_FLD_NUM(VPU_HW_BTRS_LNL_PCODE_MAILBOX, CMD, DCT_REQ, val);
702-
val = REG_SET_FLD_NUM(VPU_HW_BTRS_LNL_PCODE_MAILBOX, PARAM1,
703-
dct_val ? DCT_ENABLE : DCT_DISABLE, val);
704-
val = REG_SET_FLD_NUM(VPU_HW_BTRS_LNL_PCODE_MAILBOX, PARAM2, dct_val, val);
706+
if (cmd != DCT_REQ) {
707+
ivpu_err_ratelimited(vdev, "Unsupported PCODE command: 0x%x\n", cmd);
708+
return -EBADR;
709+
}
705710

706-
REGB_WR32(VPU_HW_BTRS_LNL_PCODE_MAILBOX, val);
711+
switch (param1) {
712+
case DCT_ENABLE:
713+
*enable = true;
714+
return 0;
715+
case DCT_DISABLE:
716+
*enable = false;
717+
return 0;
718+
default:
719+
ivpu_err_ratelimited(vdev, "Invalid PARAM1 value: %u\n", param1);
720+
return -EINVAL;
721+
}
707722
}
708723

709-
void ivpu_hw_btrs_dct_drive(struct ivpu_device *vdev, u32 dct_val)
724+
void ivpu_hw_btrs_dct_set_status(struct ivpu_device *vdev, bool enable, u32 active_percent)
710725
{
711-
return dct_drive_40xx(vdev, dct_val);
726+
u32 val = 0;
727+
u32 cmd = enable ? DCT_ENABLE : DCT_DISABLE;
728+
729+
val = REG_SET_FLD_NUM(VPU_HW_BTRS_LNL_PCODE_MAILBOX_STATUS, CMD, DCT_REQ, val);
730+
val = REG_SET_FLD_NUM(VPU_HW_BTRS_LNL_PCODE_MAILBOX_STATUS, PARAM1, cmd, val);
731+
val = REG_SET_FLD_NUM(VPU_HW_BTRS_LNL_PCODE_MAILBOX_STATUS, PARAM2, active_percent, val);
732+
733+
REGB_WR32(VPU_HW_BTRS_LNL_PCODE_MAILBOX_STATUS, val);
712734
}
713735

714736
static u32 pll_ratio_to_freq_mtl(u32 ratio, u32 config)

drivers/accel/ivpu/ivpu_hw_btrs.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515
#define PLL_PROFILING_FREQ_HIGH 400000000
1616
#define PLL_RATIO_TO_FREQ(x) ((x) * PLL_REF_CLK_FREQ)
1717

18+
#define DCT_DEFAULT_ACTIVE_PERCENT 15u
19+
#define DCT_PERIOD_US 35300u
20+
1821
int ivpu_hw_btrs_info_init(struct ivpu_device *vdev);
1922
void ivpu_hw_btrs_freq_ratios_init(struct ivpu_device *vdev);
2023
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);
3134
void ivpu_hw_btrs_clock_relinquish_disable_lnl(struct ivpu_device *vdev);
3235
bool ivpu_hw_btrs_irq_handler_mtl(struct ivpu_device *vdev, int irq);
3336
bool ivpu_hw_btrs_irq_handler_lnl(struct ivpu_device *vdev, int irq);
34-
void ivpu_hw_btrs_dct_drive(struct ivpu_device *vdev, u32 dct_val);
37+
int ivpu_hw_btrs_dct_get_request(struct ivpu_device *vdev, bool *enable);
38+
void ivpu_hw_btrs_dct_set_status(struct ivpu_device *vdev, bool enable, u32 dct_percent);
3539
u32 ivpu_hw_btrs_pll_freq_get(struct ivpu_device *vdev);
3640
u32 ivpu_hw_btrs_ratio_to_freq(struct ivpu_device *vdev, u32 ratio);
3741
u32 ivpu_hw_btrs_telemetry_offset_get(struct ivpu_device *vdev);

drivers/accel/ivpu/ivpu_hw_btrs_lnl_reg.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,11 @@
4444
#define VPU_HW_BTRS_LNL_IMR_ERR_CFI1_HIGH 0x0000005cu
4545
#define VPU_HW_BTRS_LNL_IMR_ERR_CFI1_CLEAR 0x00000060u
4646

47-
#define VPU_HW_BTRS_LNL_PCODE_MAILBOX 0x00000070u
48-
#define VPU_HW_BTRS_LNL_PCODE_MAILBOX_CMD_MASK GENMASK(7, 0)
49-
#define VPU_HW_BTRS_LNL_PCODE_MAILBOX_PARAM1_MASK GENMASK(15, 8)
50-
#define VPU_HW_BTRS_LNL_PCODE_MAILBOX_PARAM2_MASK GENMASK(23, 16)
51-
#define VPU_HW_BTRS_LNL_PCODE_MAILBOX_PARAM3_MASK GENMASK(31, 24)
47+
#define VPU_HW_BTRS_LNL_PCODE_MAILBOX_STATUS 0x00000070u
48+
#define VPU_HW_BTRS_LNL_PCODE_MAILBOX_STATUS_CMD_MASK GENMASK(7, 0)
49+
#define VPU_HW_BTRS_LNL_PCODE_MAILBOX_STATUS_PARAM1_MASK GENMASK(15, 8)
50+
#define VPU_HW_BTRS_LNL_PCODE_MAILBOX_STATUS_PARAM2_MASK GENMASK(23, 16)
51+
#define VPU_HW_BTRS_LNL_PCODE_MAILBOX_STATUS_PARAM3_MASK GENMASK(31, 24)
5252

5353
#define VPU_HW_BTRS_LNL_PCODE_MAILBOX_SHADOW 0x00000074u
5454
#define VPU_HW_BTRS_LNL_PCODE_MAILBOX_SHADOW_CMD_MASK GENMASK(7, 0)

drivers/accel/ivpu/ivpu_jsm_msg.c

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -543,3 +543,26 @@ int ivpu_jsm_metric_streamer_info(struct ivpu_device *vdev, u64 metric_group_mas
543543

544544
return ret;
545545
}
546+
547+
int ivpu_jsm_dct_enable(struct ivpu_device *vdev, u32 active_us, u32 inactive_us)
548+
{
549+
struct vpu_jsm_msg req = { .type = VPU_JSM_MSG_DCT_ENABLE };
550+
struct vpu_jsm_msg resp;
551+
552+
req.payload.pwr_dct_control.dct_active_us = active_us;
553+
req.payload.pwr_dct_control.dct_inactive_us = inactive_us;
554+
555+
return ivpu_ipc_send_receive_active(vdev, &req, VPU_JSM_MSG_DCT_ENABLE_DONE,
556+
&resp, VPU_IPC_CHAN_ASYNC_CMD,
557+
vdev->timeout.jsm);
558+
}
559+
560+
int ivpu_jsm_dct_disable(struct ivpu_device *vdev)
561+
{
562+
struct vpu_jsm_msg req = { .type = VPU_JSM_MSG_DCT_DISABLE };
563+
struct vpu_jsm_msg resp;
564+
565+
return ivpu_ipc_send_receive_active(vdev, &req, VPU_JSM_MSG_DCT_DISABLE_DONE,
566+
&resp, VPU_IPC_CHAN_ASYNC_CMD,
567+
vdev->timeout.jsm);
568+
}

drivers/accel/ivpu/ivpu_jsm_msg.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,6 @@ int ivpu_jsm_metric_streamer_update(struct ivpu_device *vdev, u64 metric_group_m
4141
u64 buffer_addr, u64 buffer_size, u64 *bytes_written);
4242
int ivpu_jsm_metric_streamer_info(struct ivpu_device *vdev, u64 metric_group_mask, u64 buffer_addr,
4343
u64 buffer_size, u32 *sample_size, u64 *info_size);
44+
int ivpu_jsm_dct_enable(struct ivpu_device *vdev, u32 active_us, u32 inactive_us);
45+
int ivpu_jsm_dct_disable(struct ivpu_device *vdev);
4446
#endif

drivers/accel/ivpu/ivpu_pm.c

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ int ivpu_pm_runtime_suspend_cb(struct device *dev)
245245

246246
ivpu_dbg(vdev, PM, "Runtime suspend..\n");
247247

248-
is_idle = ivpu_hw_is_idle(vdev);
248+
is_idle = ivpu_hw_is_idle(vdev) || vdev->pm->dct_active_percent;
249249
if (!is_idle)
250250
ivpu_err(vdev, "NPU is not idle before autosuspend\n");
251251

@@ -397,3 +397,68 @@ void ivpu_pm_disable(struct ivpu_device *vdev)
397397
pm_runtime_get_noresume(vdev->drm.dev);
398398
pm_runtime_forbid(vdev->drm.dev);
399399
}
400+
401+
int ivpu_pm_dct_init(struct ivpu_device *vdev)
402+
{
403+
if (vdev->pm->dct_active_percent)
404+
return ivpu_pm_dct_enable(vdev, vdev->pm->dct_active_percent);
405+
406+
return 0;
407+
}
408+
409+
int ivpu_pm_dct_enable(struct ivpu_device *vdev, u8 active_percent)
410+
{
411+
u32 active_us, inactive_us;
412+
int ret;
413+
414+
if (active_percent == 0 || active_percent > 100)
415+
return -EINVAL;
416+
417+
active_us = (DCT_PERIOD_US * active_percent) / 100;
418+
inactive_us = DCT_PERIOD_US - active_us;
419+
420+
ret = ivpu_jsm_dct_enable(vdev, active_us, inactive_us);
421+
if (ret) {
422+
ivpu_err_ratelimited(vdev, "Filed to enable DCT: %d\n", ret);
423+
return ret;
424+
}
425+
426+
vdev->pm->dct_active_percent = active_percent;
427+
428+
ivpu_dbg(vdev, PM, "DCT set to %u%% (D0: %uus, D0i2: %uus)\n",
429+
active_percent, active_us, inactive_us);
430+
return 0;
431+
}
432+
433+
int ivpu_pm_dct_disable(struct ivpu_device *vdev)
434+
{
435+
int ret;
436+
437+
ret = ivpu_jsm_dct_disable(vdev);
438+
if (ret) {
439+
ivpu_err_ratelimited(vdev, "Filed to disable DCT: %d\n", ret);
440+
return ret;
441+
}
442+
443+
vdev->pm->dct_active_percent = 0;
444+
445+
ivpu_dbg(vdev, PM, "DCT disabled\n");
446+
return 0;
447+
}
448+
449+
void ivpu_pm_dct_irq_thread_handler(struct ivpu_device *vdev)
450+
{
451+
bool enable;
452+
int ret;
453+
454+
if (ivpu_hw_btrs_dct_get_request(vdev, &enable))
455+
return;
456+
457+
if (vdev->pm->dct_active_percent)
458+
ret = ivpu_pm_dct_enable(vdev, DCT_DEFAULT_ACTIVE_PERCENT);
459+
else
460+
ret = ivpu_pm_dct_disable(vdev);
461+
462+
if (!ret)
463+
ivpu_hw_btrs_dct_set_status(vdev, enable, vdev->pm->dct_active_percent);
464+
}

drivers/accel/ivpu/ivpu_pm.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ struct ivpu_pm_info {
1919
atomic_t reset_counter;
2020
atomic_t reset_pending;
2121
bool is_warmboot;
22+
u8 dct_active_percent;
2223
};
2324

2425
void ivpu_pm_init(struct ivpu_device *vdev);
@@ -42,4 +43,9 @@ void ivpu_pm_trigger_recovery(struct ivpu_device *vdev, const char *reason);
4243
void ivpu_start_job_timeout_detection(struct ivpu_device *vdev);
4344
void ivpu_stop_job_timeout_detection(struct ivpu_device *vdev);
4445

46+
int ivpu_pm_dct_init(struct ivpu_device *vdev);
47+
int ivpu_pm_dct_enable(struct ivpu_device *vdev, u8 active_percent);
48+
int ivpu_pm_dct_disable(struct ivpu_device *vdev);
49+
void ivpu_pm_dct_irq_thread_handler(struct ivpu_device *vdev);
50+
4551
#endif /* __IVPU_PM_H__ */

0 commit comments

Comments
 (0)