accel/ivpu: Add support for limiting NPU frequency

Add configurable frequency limits to allow users to constrain the NPU
operating frequency range for power and thermal management. This support
requires firmware API version 3.34.0 or newer.

New sysfs interface:

The freq/ subdirectory contains the following attributes:

- hw_min_freq: Minimum frequency supported by hardware (read-only)
- hw_max_freq: Maximum frequency supported by hardware (read-only)
- hw_efficient_freq: Hardware's optimal operating frequency (read-only)
- current_freq: Current NPU frequency in MHz (read-only)
- set_min_freq: Configure minimum operating frequency (50XX+ devices)
- set_max_freq: Configure maximum operating frequency (50XX+ devices)

Legacy attributes npu_max_frequency_mhz and npu_current_frequency_mhz
are maintained for backward compatibility.

Implementation details:

- Frequency configuration is communicated to firmware via JSM messages
- User-specified frequency values are clamped to hardware limits
- Power-efficient frequency (pn_ratio) is adjusted dynamically to stay
  within the configured range
- Frequency configuration is initialized during device boot
- The JSM API header is updated to version 3.34.0 to support the new
  VPU_JSM_MSG_FREQ_CONFIG firmware message

Added description for the sysfs attributes in the Documentation/ABI.

Signed-off-by: Andrzej Kacprowski <andrzej.kacprowski@linux.intel.com>
Reviewed-by: Karol Wachowski <karol.wachowski@linux.intel.com>
Signed-off-by: Karol Wachowski <karol.wachowski@linux.intel.com>
Link: https://patch.msgid.link/20260408150152.2093638-1-andrzej.kacprowski@linux.intel.com
This commit is contained in:
Andrzej Kacprowski 2026-04-08 17:01:52 +02:00 committed by Karol Wachowski
parent c82a16b0ca
commit 7455a0583a
11 changed files with 421 additions and 67 deletions

View File

@ -0,0 +1,30 @@
What: /sys/bus/pci/drivers/intel_vpu/.../sched_mode
Date: October 2024
KernelVersion: 6.12
Contact: dri-devel@lists.freedesktop.org
Description: Current NPU scheduling mode. Returns one of the following strings:
- "HW" - Hardware Scheduler mode
- "OS" - Operating System Scheduler mode
Read-only.
Deprecated since the "OS" scheduling mode is not usable
and will be removed from future versions of the driver.
Will be removed in 2027
What: /sys/bus/pci/drivers/intel_vpu/.../npu_max_frequency_mhz
Date: April 2025
KernelVersion: 6.15
Contact: dri-devel@lists.freedesktop.org
Description: Legacy alias for /sys/bus/pci/drivers/intel_vpu/.../freq/hw_max_freq.
Shows maximum frequency in MHz of the NPU's data processing unit.
Read-only.
Will be removed in 2027
What: /sys/bus/pci/drivers/intel_vpu/.../npu_current_frequency_mhz
Date: April 2025
KernelVersion: 6.15
Contact: dri-devel@lists.freedesktop.org
Description: Legacy alias for /sys/bus/pci/drivers/intel_vpu/.../freq/current_freq.
Shows current frequency in MHz of the NPU's data processing unit.
The value is read only when the device is active; otherwise it returns 0.
Read-only.
Will be removed in 2027

View File

@ -0,0 +1,65 @@
What: /sys/bus/pci/drivers/intel_vpu/.../npu_busy_time_us
Date: May 2024
KernelVersion: 6.11
Contact: dri-devel@lists.freedesktop.org
Description: Time in microseconds that the device spent executing jobs. The time is
counted when and only when there are jobs submitted to firmware. This time
can be used to measure the utilization of NPU, either by calculating the
difference between two timepoints or monitoring utilization percentage by
reading periodically. Recommended read period is 1 second to avoid impact
on job submission performance. Read-only.
What: /sys/bus/pci/drivers/intel_vpu/.../npu_memory_utilization
Date: Jan 2025
KernelVersion: 6.15
Contact: dri-devel@lists.freedesktop.org
Description: Current NPU memory utilization in bytes. Reports the total size of all
resident buffer objects allocated for NPU use. Read-only.
What: /sys/bus/pci/drivers/intel_vpu/.../freq/hw_min_freq
Date: April 2026
KernelVersion: 7.2
Contact: dri-devel@lists.freedesktop.org
Description: Minimum frequency in MHz supported by the NPU hardware. This is a
hardware capability and cannot be changed. Read-only.
What: /sys/bus/pci/drivers/intel_vpu/.../freq/hw_efficient_freq
Date: April 2026
KernelVersion: 7.2
Contact: dri-devel@lists.freedesktop.org
Description: Most efficient operating frequency in MHz for the NPU. This represents
the frequency at which the NPU operates most efficiently in terms of power
and performance. Read-only.
What: /sys/bus/pci/drivers/intel_vpu/.../freq/hw_max_freq
Date: April 2026
KernelVersion: 7.2
Contact: dri-devel@lists.freedesktop.org
Description: Maximum frequency in MHz supported by the NPU hardware. This is a
hardware capability and cannot be changed. Read-only.
What: /sys/bus/pci/drivers/intel_vpu/.../freq/current_freq
Date: April 2026
KernelVersion: 7.2
Contact: dri-devel@lists.freedesktop.org
Description: Current operating frequency in MHz of the NPU. The value is valid only
when the device is active; returns 0 when idle. The actual frequency may
be lower than the requested range due to power or thermal constraints.
Read-only.
What: /sys/bus/pci/drivers/intel_vpu/.../freq/set_min_freq
Date: April 2026
KernelVersion: 7.2
Contact: dri-devel@lists.freedesktop.org
Description: Configured minimum operating frequency in MHz (50XX devices and newer).
Values written are clamped to hardware limits (hw_min_freq to hw_max_freq).
If set_min_freq exceeds set_max_freq, the driver clamps set_min_freq to
set_max_freq when selecting the operating frequency. Read-write.
What: /sys/bus/pci/drivers/intel_vpu/.../freq/set_max_freq
Date: April 2026
KernelVersion: 7.2
Contact: dri-devel@lists.freedesktop.org
Description: Configured maximum operating frequency in MHz (50XX devices and newer).
Values written are clamped to hardware limits (hw_min_freq to hw_max_freq).
Read-write.

View File

@ -7840,6 +7840,8 @@ M: Karol Wachowski <karol.wachowski@linux.intel.com>
L: dri-devel@lists.freedesktop.org
S: Supported
T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
F: Documentation/ABI/obsolete/sysfs-driver-ivpu
F: Documentation/ABI/testing/sysfs-driver-ivpu
F: drivers/accel/ivpu/
F: include/uapi/drm/ivpu_accel.h

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2020-2025 Intel Corporation
* Copyright (C) 2020-2026 Intel Corporation
*/
#include <linux/firmware.h>
@ -234,7 +234,7 @@ static int ivpu_get_param_ioctl(struct drm_device *dev, void *data, struct drm_f
args->value = vdev->platform;
break;
case DRM_IVPU_PARAM_CORE_CLOCK_RATE:
args->value = ivpu_hw_dpu_max_freq_get(vdev);
args->value = ivpu_hw_btrs_pll_ratio_to_hz(vdev, vdev->hw->pll.max_ratio);
break;
case DRM_IVPU_PARAM_NUM_CONTEXTS:
args->value = file_priv->user_limits->max_ctx_count;
@ -487,6 +487,10 @@ int ivpu_boot(struct ivpu_device *vdev)
ret = ivpu_hw_sched_init(vdev);
if (ret)
goto err_disable_ipc;
ret = ivpu_hw_btrs_cfg_freq_init(vdev);
if (ret)
goto err_disable_ipc;
}
return 0;

View File

@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2020-2025 Intel Corporation
* Copyright (C) 2020-2026 Intel Corporation
*/
#ifndef __IVPU_HW_H__
@ -28,6 +28,7 @@ struct ivpu_hw_info {
struct ivpu_addr_range dma;
} ranges;
struct {
/* Hardware min and max pll ratio */
u8 min_ratio;
u8 max_ratio;
/*
@ -35,6 +36,9 @@ struct ivpu_hw_info {
* performance to power ratio at this frequency.
*/
u8 pn_ratio;
/* Pll ratios configured via sysfs interface */
u8 cfg_min_ratio;
u8 cfg_max_ratio;
u32 profiling_freq;
} pll;
struct {
@ -80,16 +84,6 @@ static inline u64 ivpu_hw_range_size(const struct ivpu_addr_range *range)
return range->end - range->start;
}
static inline u32 ivpu_hw_dpu_max_freq_get(struct ivpu_device *vdev)
{
return ivpu_hw_btrs_dpu_max_freq_get(vdev);
}
static inline u32 ivpu_hw_dpu_freq_get(struct ivpu_device *vdev)
{
return ivpu_hw_btrs_dpu_freq_get(vdev);
}
static inline void ivpu_hw_irq_clear(struct ivpu_device *vdev)
{
ivpu_hw_ip_irq_clear(vdev);

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2020-2025 Intel Corporation
* Copyright (C) 2020-2026 Intel Corporation
*/
#include <linux/units.h>
@ -11,6 +11,7 @@
#include "ivpu_hw_btrs_lnl_reg.h"
#include "ivpu_hw_btrs_mtl_reg.h"
#include "ivpu_hw_reg_io.h"
#include "ivpu_jsm_msg.h"
#include "ivpu_pm.h"
#define BTRS_MTL_IRQ_MASK ((REG_FLD(VPU_HW_BTRS_MTL_INTERRUPT_STAT, ATS_ERR)) | \
@ -33,8 +34,7 @@
#define PLL_CDYN_DEFAULT 0x80
#define PLL_EPP_DEFAULT 0x80
#define PLL_REF_CLK_FREQ 50000000ull
#define PLL_RATIO_TO_FREQ(x) ((x) * PLL_REF_CLK_FREQ)
#define PLL_REF_CLK_FREQ_MHZ 50
#define PLL_TIMEOUT_US (1500 * USEC_PER_MSEC)
#define IDLE_TIMEOUT_US (5 * USEC_PER_MSEC)
@ -59,8 +59,6 @@
#define DCT_ENABLE 0x1
#define DCT_DISABLE 0x0
static u32 pll_ratio_to_dpu_freq(struct ivpu_device *vdev, u32 ratio);
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);
@ -111,6 +109,8 @@ void ivpu_hw_btrs_freq_ratios_init(struct ivpu_device *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);
hw->pll.cfg_max_ratio = hw->pll.max_ratio;
hw->pll.cfg_min_ratio = hw->pll.min_ratio;
}
static bool tile_disable_check(u32 config)
@ -341,8 +341,8 @@ int ivpu_hw_btrs_wp_drive(struct ivpu_device *vdev, bool enable)
prepare_wp_request(vdev, &wp, enable);
ivpu_dbg(vdev, PM, "PLL workpoint request: %lu MHz, config: 0x%x, epp: 0x%x, cdyn: 0x%x\n",
pll_ratio_to_dpu_freq(vdev, wp.target) / HZ_PER_MHZ, wp.cfg, wp.epp, wp.cdyn);
ivpu_dbg(vdev, PM, "PLL workpoint request: %u MHz, config: 0x%x, epp: 0x%x, cdyn: 0x%x\n",
ivpu_hw_btrs_pll_ratio_to_mhz(vdev, wp.target), wp.cfg, wp.epp, wp.cdyn);
ret = wp_request_send(vdev, &wp);
if (ret) {
@ -598,35 +598,101 @@ static u32 pll_config_get_lnl(struct ivpu_device *vdev)
return REGB_RD32(VPU_HW_BTRS_LNL_PLL_FREQ);
}
static u32 pll_ratio_to_dpu_freq_mtl(u16 ratio)
static u32 pll_ratio_to_mhz_mtl(u8 pll_ratio)
{
return (PLL_RATIO_TO_FREQ(ratio) * 2) / 3;
return (pll_ratio * PLL_REF_CLK_FREQ_MHZ * 2) / 3;
}
static u32 pll_ratio_to_dpu_freq_lnl(u16 ratio)
static u32 pll_ratio_to_mhz_lnl(u8 pll_ratio)
{
return PLL_RATIO_TO_FREQ(ratio) / 2;
return (pll_ratio * PLL_REF_CLK_FREQ_MHZ) / 2;
}
static u32 pll_ratio_to_dpu_freq(struct ivpu_device *vdev, u32 ratio)
u32 ivpu_hw_btrs_pll_ratio_to_mhz(struct ivpu_device *vdev, u8 pll_ratio)
{
if (ivpu_hw_btrs_gen(vdev) == IVPU_HW_BTRS_MTL)
return pll_ratio_to_dpu_freq_mtl(ratio);
return pll_ratio_to_mhz_mtl(pll_ratio);
else
return pll_ratio_to_dpu_freq_lnl(ratio);
return pll_ratio_to_mhz_lnl(pll_ratio);
}
u32 ivpu_hw_btrs_dpu_max_freq_get(struct ivpu_device *vdev)
u32 ivpu_hw_btrs_pll_ratio_to_hz(struct ivpu_device *vdev, u8 pll_ratio)
{
return pll_ratio_to_dpu_freq(vdev, vdev->hw->pll.max_ratio);
return ivpu_hw_btrs_pll_ratio_to_mhz(vdev, pll_ratio) * HZ_PER_MHZ;
}
u32 ivpu_hw_btrs_dpu_freq_get(struct ivpu_device *vdev)
u32 ivpu_hw_btrs_current_freq_get(struct ivpu_device *vdev)
{
if (ivpu_hw_btrs_gen(vdev) == IVPU_HW_BTRS_MTL)
return pll_ratio_to_dpu_freq_mtl(pll_config_get_mtl(vdev));
return pll_ratio_to_mhz_mtl(pll_config_get_mtl(vdev));
else
return pll_ratio_to_dpu_freq_lnl(pll_config_get_lnl(vdev));
return pll_ratio_to_mhz_lnl(pll_config_get_lnl(vdev));
}
static int ivpu_hw_btrs_cfg_freq_set(struct ivpu_device *vdev, u8 cfg_min_ratio, u8 cfg_max_ratio)
{
u8 min_ratio = clamp_t(u8, cfg_min_ratio, vdev->hw->pll.min_ratio, cfg_max_ratio);
u8 pn_ratio = clamp_t(u8, vdev->hw->pll.pn_ratio, min_ratio, cfg_max_ratio);
int ret;
ivpu_dbg(vdev, PM, "Set frequency range to min: %u, pn: %u, max: %u MHz\n",
ivpu_hw_btrs_pll_ratio_to_mhz(vdev, min_ratio),
ivpu_hw_btrs_pll_ratio_to_mhz(vdev, pn_ratio),
ivpu_hw_btrs_pll_ratio_to_mhz(vdev, cfg_max_ratio));
ret = ivpu_rpm_get(vdev);
if (ret < 0)
return ret;
ret = ivpu_jsm_msg_freq_config(vdev, min_ratio, pn_ratio, cfg_max_ratio);
ivpu_rpm_put(vdev);
if (ret) {
ivpu_warn(vdev,
"Failed to set frequency to min: %u, pn: %u, max: %u MHz, ret %d\n",
ivpu_hw_btrs_pll_ratio_to_mhz(vdev, min_ratio),
ivpu_hw_btrs_pll_ratio_to_mhz(vdev, pn_ratio),
ivpu_hw_btrs_pll_ratio_to_mhz(vdev, cfg_max_ratio),
ret);
return ret;
}
vdev->hw->pll.cfg_min_ratio = cfg_min_ratio;
vdev->hw->pll.cfg_max_ratio = cfg_max_ratio;
return 0;
}
static u8 dpu_mhz_to_pll_ratio_lnl(u32 freq_mhz)
{
return clamp_t(u32, freq_mhz / (PLL_REF_CLK_FREQ_MHZ / 2), 0, U8_MAX);
}
int ivpu_hw_btrs_cfg_max_freq_set(struct ivpu_device *vdev, u32 max_freq_mhz)
{
u8 ratio = dpu_mhz_to_pll_ratio_lnl(max_freq_mhz);
u8 cfg_max_ratio = clamp_t(u8, ratio, vdev->hw->pll.min_ratio, vdev->hw->pll.max_ratio);
return ivpu_hw_btrs_cfg_freq_set(vdev, vdev->hw->pll.cfg_min_ratio, cfg_max_ratio);
}
int ivpu_hw_btrs_cfg_min_freq_set(struct ivpu_device *vdev, u32 min_freq_mhz)
{
u8 ratio = dpu_mhz_to_pll_ratio_lnl(min_freq_mhz);
u8 cfg_min_ratio = clamp_t(u8, ratio, vdev->hw->pll.min_ratio, vdev->hw->pll.max_ratio);
return ivpu_hw_btrs_cfg_freq_set(vdev, cfg_min_ratio, vdev->hw->pll.cfg_max_ratio);
}
int ivpu_hw_btrs_cfg_freq_init(struct ivpu_device *vdev)
{
if (vdev->hw->pll.min_ratio == vdev->hw->pll.cfg_min_ratio &&
vdev->hw->pll.max_ratio == vdev->hw->pll.cfg_max_ratio)
return 0;
return ivpu_hw_btrs_cfg_freq_set(vdev,
vdev->hw->pll.cfg_min_ratio,
vdev->hw->pll.cfg_max_ratio);
}
/* Handler for IRQs from Buttress core (irqB) */
@ -641,8 +707,8 @@ bool ivpu_hw_btrs_irq_handler_mtl(struct ivpu_device *vdev, int irq)
if (REG_TEST_FLD(VPU_HW_BTRS_MTL_INTERRUPT_STAT, FREQ_CHANGE, status)) {
u32 pll = pll_config_get_mtl(vdev);
ivpu_dbg(vdev, IRQ, "FREQ_CHANGE irq, wp %08x, %lu MHz",
pll, pll_ratio_to_dpu_freq_mtl(pll) / HZ_PER_MHZ);
ivpu_dbg(vdev, IRQ, "FREQ_CHANGE irq, wp %08x, %u MHz",
pll, pll_ratio_to_mhz_mtl(pll));
}
if (REG_TEST_FLD(VPU_HW_BTRS_MTL_INTERRUPT_STAT, ATS_ERR, status)) {
@ -695,8 +761,8 @@ bool ivpu_hw_btrs_irq_handler_lnl(struct ivpu_device *vdev, int irq)
if (REG_TEST_FLD(VPU_HW_BTRS_LNL_INTERRUPT_STAT, FREQ_CHANGE, status)) {
u32 pll = pll_config_get_lnl(vdev);
ivpu_dbg(vdev, IRQ, "FREQ_CHANGE irq, wp %08x, %lu MHz",
pll, pll_ratio_to_dpu_freq_lnl(pll) / HZ_PER_MHZ);
ivpu_dbg(vdev, IRQ, "FREQ_CHANGE irq, wp %08x, %u MHz",
pll, pll_ratio_to_mhz_lnl(pll));
}
if (REG_TEST_FLD(VPU_HW_BTRS_LNL_INTERRUPT_STAT, ATS_ERR, status)) {

View File

@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2020-2025 Intel Corporation
* Copyright (C) 2020-2026 Intel Corporation
*/
#ifndef __IVPU_HW_BTRS_H__
@ -31,8 +31,12 @@ int ivpu_hw_btrs_ip_reset(struct ivpu_device *vdev);
void ivpu_hw_btrs_profiling_freq_reg_set_lnl(struct ivpu_device *vdev);
void ivpu_hw_btrs_ats_print_lnl(struct ivpu_device *vdev);
void ivpu_hw_btrs_clock_relinquish_disable_lnl(struct ivpu_device *vdev);
u32 ivpu_hw_btrs_dpu_max_freq_get(struct ivpu_device *vdev);
u32 ivpu_hw_btrs_dpu_freq_get(struct ivpu_device *vdev);
u32 ivpu_hw_btrs_pll_ratio_to_mhz(struct ivpu_device *vdev, u8 pll_ratio);
u32 ivpu_hw_btrs_pll_ratio_to_hz(struct ivpu_device *vdev, u8 pll_ratio);
u32 ivpu_hw_btrs_current_freq_get(struct ivpu_device *vdev);
int ivpu_hw_btrs_cfg_min_freq_set(struct ivpu_device *vdev, u32 freq_mhz);
int ivpu_hw_btrs_cfg_max_freq_set(struct ivpu_device *vdev, u32 freq_mhz);
int ivpu_hw_btrs_cfg_freq_init(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);
int ivpu_hw_btrs_dct_get_request(struct ivpu_device *vdev, bool *enable);

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2020-2024 Intel Corporation
* Copyright (C) 2020-2026 Intel Corporation
*/
#include "ivpu_drv.h"
@ -86,6 +86,9 @@ const char *ivpu_jsm_msg_type_to_str(enum vpu_ipc_msg_type type)
IVPU_CASE_TO_STR(VPU_JSM_MSG_DCT_ENABLE_DONE);
IVPU_CASE_TO_STR(VPU_JSM_MSG_DCT_DISABLE);
IVPU_CASE_TO_STR(VPU_JSM_MSG_DCT_DISABLE_DONE);
IVPU_CASE_TO_STR(VPU_JSM_MSG_FREQ_CONFIG);
IVPU_CASE_TO_STR(VPU_JSM_MSG_FREQ_CONFIG_RSP);
IVPU_CASE_TO_STR(VPU_JSM_MSG_RESERVED_111E);
}
#undef IVPU_CASE_TO_STR
@ -571,3 +574,16 @@ int ivpu_jsm_state_dump_no_reply(struct ivpu_device *vdev)
return ivpu_ipc_send_and_wait(vdev, &req, VPU_IPC_CHAN_ASYNC_CMD,
vdev->timeout.state_dump_msg);
}
int ivpu_jsm_msg_freq_config(struct ivpu_device *vdev, u16 min_ratio, u16 pn_ratio, u16 max_ratio)
{
struct vpu_jsm_msg req = { .type = VPU_JSM_MSG_FREQ_CONFIG};
struct vpu_jsm_msg resp;
req.payload.freq_config.min_freq_pll_ratio = min_ratio;
req.payload.freq_config.pn_freq_pll_ratio = pn_ratio;
req.payload.freq_config.max_freq_pll_ratio = max_ratio;
return ivpu_ipc_send_receive_internal(vdev, &req, VPU_JSM_MSG_FREQ_CONFIG_RSP, &resp,
VPU_IPC_CHAN_ASYNC_CMD, vdev->timeout.jsm);
}

View File

@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2020-2024 Intel Corporation
* Copyright (C) 2020-2026 Intel Corporation
*/
#ifndef __IVPU_JSM_MSG_H__
@ -45,5 +45,6 @@ int ivpu_jsm_dct_enable(struct ivpu_device *vdev, u32 active_us, u32 inactive_us
int ivpu_jsm_dct_disable(struct ivpu_device *vdev);
int ivpu_jsm_state_dump(struct ivpu_device *vdev);
int ivpu_jsm_state_dump_no_reply(struct ivpu_device *vdev);
int ivpu_jsm_msg_freq_config(struct ivpu_device *vdev, u16 min_ratio, u16 pn_ratio, u16 max_ratio);
#endif

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2024-2025 Intel Corporation
* Copyright (C) 2024-2026 Intel Corporation
*/
#include <linux/device.h>
@ -82,8 +82,7 @@ static DEVICE_ATTR_RO(npu_memory_utilization);
* - "OS" - Operating System Scheduler mode
*
*/
static ssize_t
sched_mode_show(struct device *dev, struct device_attribute *attr, char *buf)
static ssize_t sched_mode_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct drm_device *drm = dev_get_drvdata(dev);
struct ivpu_device *vdev = to_ivpu_device(drm);
@ -94,47 +93,162 @@ sched_mode_show(struct device *dev, struct device_attribute *attr, char *buf)
static DEVICE_ATTR_RO(sched_mode);
/**
* DOC: npu_max_frequency
* DOC: NPU frequency control and information
*
* The npu_max_frequency shows maximum frequency in MHz of the NPU's data
* processing unit
* Hardware frequency capabilities:
* freq/hw_min_freq - Minimum frequency supported by the NPU hardware.
* freq/hw_max_freq - Maximum frequency supported by the NPU hardware.
* freq/hw_efficient_freq - Most efficient operating frequency for the NPU.
*
* Configurable frequency limits (50XX devices and newer):
* freq/set_min_freq - Configured minimum operating frequency.
* freq/set_max_freq - Configured maximum operating frequency.
*
* Clamping behavior: Values written to set_min_freq and set_max_freq are
* clamped to hardware limits. If set_min_freq exceeds set_max_freq, the driver
* clamps set_min_freq to set_max_freq when selecting the operating frequency.
*
* Current operating frequency:
* freq/current_freq - Current frequency in MHz. Valid only when the device is
* active; returns 0 when idle. May be lower than the requested range due to
* power or thermal constraints.
*
* Legacy attributes (backward compatibility):
* npu_max_frequency_mhz - Alias for freq/hw_max_freq.
* npu_current_frequency_mhz - Alias for freq/current_freq.
*/
static ssize_t
npu_max_frequency_mhz_show(struct device *dev, struct device_attribute *attr, char *buf)
static ssize_t hw_min_freq_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct drm_device *drm = dev_get_drvdata(dev);
struct ivpu_device *vdev = to_ivpu_device(drm);
u32 freq = ivpu_hw_dpu_max_freq_get(vdev);
u32 freq_mhz = ivpu_hw_btrs_pll_ratio_to_mhz(vdev, vdev->hw->pll.min_ratio);
return sysfs_emit(buf, "%lu\n", freq / HZ_PER_MHZ);
return sysfs_emit(buf, "%u\n", freq_mhz);
}
static DEVICE_ATTR_RO(npu_max_frequency_mhz);
static DEVICE_ATTR_RO(hw_min_freq);
/**
* DOC: npu_current_frequency_mhz
*
* The npu_current_frequency_mhz shows current frequency in MHz of the NPU's
* data processing unit
*/
static ssize_t
npu_current_frequency_mhz_show(struct device *dev, struct device_attribute *attr, char *buf)
static ssize_t hw_efficient_freq_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct drm_device *drm = dev_get_drvdata(dev);
struct ivpu_device *vdev = to_ivpu_device(drm);
u32 freq = 0;
u32 freq_mhz = ivpu_hw_btrs_pll_ratio_to_mhz(vdev, vdev->hw->pll.pn_ratio);
return sysfs_emit(buf, "%u\n", freq_mhz);
}
static DEVICE_ATTR_RO(hw_efficient_freq);
static ssize_t hw_max_freq_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct drm_device *drm = dev_get_drvdata(dev);
struct ivpu_device *vdev = to_ivpu_device(drm);
u32 freq_mhz = ivpu_hw_btrs_pll_ratio_to_mhz(vdev, vdev->hw->pll.max_ratio);
return sysfs_emit(buf, "%u\n", freq_mhz);
}
static DEVICE_ATTR_RO(hw_max_freq);
static ssize_t set_min_freq_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct drm_device *drm = dev_get_drvdata(dev);
struct ivpu_device *vdev = to_ivpu_device(drm);
u32 freq_mhz = ivpu_hw_btrs_pll_ratio_to_mhz(vdev, vdev->hw->pll.cfg_min_ratio);
return sysfs_emit(buf, "%u\n", freq_mhz);
}
static ssize_t
set_min_freq_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct drm_device *drm = dev_get_drvdata(dev);
struct ivpu_device *vdev = to_ivpu_device(drm);
u32 freq_mhz;
int ret;
ret = kstrtou32(buf, 10, &freq_mhz);
if (ret)
return ret;
ret = ivpu_hw_btrs_cfg_min_freq_set(vdev, freq_mhz);
if (ret)
return ret;
return count;
}
static DEVICE_ATTR_RW(set_min_freq);
static ssize_t set_max_freq_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct drm_device *drm = dev_get_drvdata(dev);
struct ivpu_device *vdev = to_ivpu_device(drm);
u32 freq_mhz = ivpu_hw_btrs_pll_ratio_to_mhz(vdev, vdev->hw->pll.cfg_max_ratio);
return sysfs_emit(buf, "%u\n", freq_mhz);
}
static ssize_t
set_max_freq_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct drm_device *drm = dev_get_drvdata(dev);
struct ivpu_device *vdev = to_ivpu_device(drm);
u32 freq_mhz;
int ret;
ret = kstrtou32(buf, 10, &freq_mhz);
if (ret)
return ret;
/* Convert MHz to Hz and set max frequency */
ret = ivpu_hw_btrs_cfg_max_freq_set(vdev, freq_mhz);
if (ret)
return ret;
return count;
}
static DEVICE_ATTR_RW(set_max_freq);
static ssize_t current_freq_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct drm_device *drm = dev_get_drvdata(dev);
struct ivpu_device *vdev = to_ivpu_device(drm);
u32 freq_mhz = 0;
/* Read frequency only if device is active, otherwise frequency is 0 */
if (pm_runtime_get_if_active(vdev->drm.dev) > 0) {
freq = ivpu_hw_dpu_freq_get(vdev);
freq_mhz = ivpu_hw_btrs_current_freq_get(vdev);
pm_runtime_put_autosuspend(vdev->drm.dev);
}
return sysfs_emit(buf, "%lu\n", freq / HZ_PER_MHZ);
return sysfs_emit(buf, "%u\n", freq_mhz);
}
static DEVICE_ATTR_RO(npu_current_frequency_mhz);
static DEVICE_ATTR_RO(current_freq);
/* Alias to current_freq for legacy compat */
static struct device_attribute dev_attr_npu_max_frequency_mhz =
__ATTR(npu_max_frequency_mhz, 0444, hw_max_freq_show, NULL);
static struct device_attribute dev_attr_npu_current_frequency_mhz =
__ATTR(npu_current_frequency_mhz, 0444, current_freq_show, NULL);
static struct attribute *ivpu_freq_attrs[] = {
&dev_attr_hw_min_freq.attr,
&dev_attr_hw_efficient_freq.attr,
&dev_attr_hw_max_freq.attr,
&dev_attr_current_freq.attr,
NULL,
};
static struct attribute_group ivpu_freq_attr_group = {
.name = "freq",
.attrs = ivpu_freq_attrs,
};
static struct attribute *ivpu_dev_attrs[] = {
&dev_attr_npu_busy_time_us.attr,
@ -154,6 +268,32 @@ void ivpu_sysfs_init(struct ivpu_device *vdev)
int ret;
ret = devm_device_add_group(vdev->drm.dev, &ivpu_dev_attr_group);
if (ret)
ivpu_warn(vdev, "Failed to add group to device, ret %d", ret);
if (ret) {
ivpu_warn(vdev, "Failed to add sysfs group to device, ret %d", ret);
return;
}
ret = devm_device_add_group(vdev->drm.dev, &ivpu_freq_attr_group);
if (ret) {
ivpu_warn(vdev, "Failed to add sysfs freq group, ret %d", ret);
return;
}
if (ivpu_hw_ip_gen(vdev) >= IVPU_HW_IP_50XX) {
ret = sysfs_add_file_to_group(&vdev->drm.dev->kobj,
&dev_attr_set_min_freq.attr,
"freq");
if (ret) {
ivpu_warn(vdev, "Failed to add sysfs set_min_freq to device, ret %d", ret);
return;
}
ret = sysfs_add_file_to_group(&vdev->drm.dev->kobj,
&dev_attr_set_max_freq.attr,
"freq");
if (ret) {
ivpu_warn(vdev, "Failed to add sysfs set_max_freq to device, ret %d", ret);
return;
}
}
}

View File

@ -23,7 +23,7 @@
/*
* Minor version changes when API backward compatibility is preserved.
*/
#define VPU_JSM_API_VER_MINOR 33
#define VPU_JSM_API_VER_MINOR 34
/*
* API header changed (field names, documentation, formatting) but API itself has not been changed
@ -604,6 +604,16 @@ enum vpu_ipc_msg_type {
* This command has no payload
*/
VPU_JSM_MSG_DCT_DISABLE = 0x111d,
/**
* Reserved command ID to ensure that the following command requests /
* responses have the same lower byte value.
*/
VPU_JSM_MSG_RESERVED_111E = 0x111e,
/**
* Control command: Configure VPU frequency scaling parameters.
* @see vpu_ipc_msg_payload_freq_config
*/
VPU_JSM_MSG_FREQ_CONFIG = 0x111f,
/**
* Dump VPU state. To be used for debug purposes only.
* This command has no payload.
@ -766,6 +776,11 @@ enum vpu_ipc_msg_type {
* This command has no payload
*/
VPU_JSM_MSG_DCT_DISABLE_DONE = 0x221e,
/**
* Response to control command: Configure VPU frequency scaling parameters.
* @see vpu_ipc_msg_payload_freq_config
*/
VPU_JSM_MSG_FREQ_CONFIG_RSP = 0x221f,
/**
* Response to state dump control command.
* This command has no payload.
@ -1650,6 +1665,22 @@ struct vpu_ipc_msg_payload_pwr_dct_control {
u32 dct_inactive_us;
};
/**
* Payload for @ref VPU_JSM_MSG_FREQ_CONFIG message.
*
* This payload allows the host to configure the VPU frequency scaling parameters.
*/
struct vpu_ipc_msg_payload_freq_config {
/** Minimum frequency PLL ratio */
u32 min_freq_pll_ratio;
/** Efficiency frequency PLL ratio */
u32 pn_freq_pll_ratio;
/** Maximum frequency PLL ratio */
u32 max_freq_pll_ratio;
/** Reserved for 64-bit alignment */
u32 reserved_0;
};
/*
* Payloads union, used to define complete message format.
*/
@ -1692,6 +1723,7 @@ union vpu_ipc_msg_payload {
struct vpu_ipc_msg_payload_hws_resume_engine hws_resume_engine;
struct vpu_ipc_msg_payload_pwr_d0i3_enter pwr_d0i3_enter;
struct vpu_ipc_msg_payload_pwr_dct_control pwr_dct_control;
struct vpu_ipc_msg_payload_freq_config freq_config;
};
/**