nvdla: kmd: support power management with HFRP

Jira DLA-7800

Change-Id: If02567cb5a30a6fbeae7d5256ba612317605fb48
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/3303252
Signed-off-by: Amit Sharma <amisharma@nvidia.com>
Signed-off-by: Arvind M <am@nvidia.com>
(cherry picked from commit c71198a1ef7cc7715401da08c24ef2bbc4b44de0)
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/3337347
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
Reviewed-by: Mitch Harwell <mharwell@nvidia.com>
This commit is contained in:
Arvind M
2025-02-14 11:14:10 +00:00
committed by Jon Hunter
parent 3ed7b12ef5
commit 3f7a9abe07
11 changed files with 2295 additions and 62 deletions

View File

@@ -23,6 +23,7 @@ nvhost-nvdla-objs += \
$(NVDLA_COMMON_OBJS) \ $(NVDLA_COMMON_OBJS) \
port/device/nvdla_device_host1x.o \ port/device/nvdla_device_host1x.o \
port/fw/nvdla_fw_flcn.o \ port/fw/nvdla_fw_flcn.o \
port/pm/nvdla_pm_stub.o \
port/sync/nvdla_sync_syncpt.o \ port/sync/nvdla_sync_syncpt.o \
port/fw/nvdla_falcon.o port/fw/nvdla_falcon.o

View File

@@ -36,6 +36,8 @@ NVDLA_OBJS := \
$(NVDLA_COMMON_OBJS) \ $(NVDLA_COMMON_OBJS) \
port/device/nvdla_device_axi.o \ port/device/nvdla_device_axi.o \
port/fw/nvdla_fw_riscv.o \ port/fw/nvdla_fw_riscv.o \
port/pm/nvdla_pm_hfrp.o \
port/pm/nvdla_pm_hfrp_cmd_v1_2.o \
port/sync/nvdla_sync_syncpt_emu.o port/sync/nvdla_sync_syncpt_emu.o
# Duplicate source file for forcing compilation. # Duplicate source file for forcing compilation.

View File

@@ -4,6 +4,7 @@
* NVDLA debug utils * NVDLA debug utils
*/ */
#include <linux/arm64-barrier.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/debugfs.h> #include <linux/debugfs.h>
#include "port/nvdla_host_wrapper.h" #include "port/nvdla_host_wrapper.h"
@@ -17,6 +18,7 @@
#include "nvdla_debug.h" #include "nvdla_debug.h"
#include "port/nvdla_fw.h" #include "port/nvdla_fw.h"
#include "port/nvdla_device.h" #include "port/nvdla_device.h"
#include "port/nvdla_pm.h"
/* /*
* Header in ring buffer consist (start, end) two uint32_t values. * Header in ring buffer consist (start, end) two uint32_t values.
@@ -32,6 +34,35 @@
debug_set_trace_event_config(pdev, event_mask, \ debug_set_trace_event_config(pdev, event_mask, \
DLA_SET_TRACE_EVENT_MASK); \ DLA_SET_TRACE_EVENT_MASK); \
static LIST_HEAD(s_debug_ctrl_list);
static DEFINE_MUTEX(s_debug_ctrl_list_lock);
struct nvdla_debug_ctrl {
struct platform_device *pdev;
uint32_t clock_idledelay_us;
uint32_t power_idledelay_us;
uint32_t rail_idledelay_us;
struct list_head list;
};
static struct nvdla_debug_ctrl *s_nvdla_debug_ctrl_get_by_pdev(
struct platform_device *pdev)
{
struct nvdla_debug_ctrl *ctrl = NULL;
mutex_lock(&s_debug_ctrl_list_lock);
list_for_each_entry(ctrl, &s_debug_ctrl_list, list) {
if (ctrl->pdev == pdev)
break;
}
spec_bar(); /* break_spec_p#5_1 */
mutex_unlock(&s_debug_ctrl_list_lock);
return ctrl;
}
static int s_nvdla_get_pdev_from_file(struct file *file, struct platform_device **pdev) static int s_nvdla_get_pdev_from_file(struct file *file, struct platform_device **pdev)
{ {
struct seq_file *priv_data; struct seq_file *priv_data;
@@ -544,7 +575,7 @@ static int nvdla_get_dvfs_statdump(struct nvdla_device *nvdla_dev)
/* prepare command data */ /* prepare command data */
cmd_data.method_id = DLA_CMD_GET_STATISTICS2; cmd_data.method_id = DLA_CMD_GET_STATISTICS2;
/* method data is not used for this command, passing 0U */ /* method data is not used for this command, passing 0U */
cmd_data.method_data = ALIGNED_DMA(0U); cmd_data.method_data = ALIGNED_DMA(0U);
cmd_data.wait = true; cmd_data.wait = true;
@@ -628,39 +659,86 @@ static int debug_dla_ctrl_clk_enable_show(struct seq_file *s, void *data)
{ {
int err; int err;
struct platform_device *pdev; struct platform_device *pdev;
bool gated;
(void) data; (void) data;
err = s_nvdla_get_pdev_from_seq(s, &pdev); err = s_nvdla_get_pdev_from_seq(s, &pdev);
if (err < 0) if (err < 0) {
return -1; nvdla_dbg_err(pdev, "failed to get pdev. err: %d", err);
nvdla_dbg_info(pdev, "[CTRL/CLK] enable show"); goto fail;
}
nvdla_dbg_info(pdev, "[CTRL/CLK] enable show");
err = nvdla_pm_clock_is_gated(pdev, &gated);
if (err < 0) {
nvdla_dbg_err(pdev, "failed to get status. err: %d", err);
goto fail;
}
seq_printf(s, "%x\n", (int) !gated);
return 0; return 0;
fail:
return err;
} }
static int debug_dla_ctrl_clk_core_khz_show(struct seq_file *s, void *data) static int debug_dla_ctrl_clk_core_khz_show(struct seq_file *s, void *data)
{ {
int err; int err;
struct platform_device *pdev; struct platform_device *pdev;
uint32_t freq_khz;
(void) data; (void) data;
err = s_nvdla_get_pdev_from_seq(s, &pdev); err = s_nvdla_get_pdev_from_seq(s, &pdev);
if (err < 0) if (err < 0) {
return -1; nvdla_dbg_err(pdev, "failed to fetch pdev\n");
goto fail;
}
nvdla_dbg_info(pdev, "[CTRL/CLK] core_khz show"); nvdla_dbg_info(pdev, "[CTRL/CLK] core_khz show");
err = nvdla_pm_clock_get_core_freq(pdev, &freq_khz);
if (err < 0) {
nvdla_dbg_err(pdev, "failed to fetch frequency. err: %d\n",
err);
goto fail;
}
seq_printf(s, "%u\n", freq_khz);
return 0; return 0;
fail:
return err;
} }
static int debug_dla_ctrl_clk_mcu_khz_show(struct seq_file *s, void *data) static int debug_dla_ctrl_clk_mcu_khz_show(struct seq_file *s, void *data)
{ {
int err; int err;
struct platform_device *pdev; struct platform_device *pdev;
uint32_t freq_khz;
(void) data; (void) data;
err = s_nvdla_get_pdev_from_seq(s, &pdev); err = s_nvdla_get_pdev_from_seq(s, &pdev);
if (err < 0) if (err < 0) {
return -1; nvdla_dbg_err(pdev, "failed to fetch pdev\n");
goto fail;
}
nvdla_dbg_info(pdev, "[CTRL/CLK] mcu_khz show"); nvdla_dbg_info(pdev, "[CTRL/CLK] mcu_khz show");
err = nvdla_pm_clock_get_mcu_freq(pdev, &freq_khz);
if (err < 0) {
nvdla_dbg_err(pdev, "failed to fetch frequency. err: %d\n",
err);
goto fail;
}
seq_printf(s, "%u\n", freq_khz);
return 0; return 0;
fail:
return err;
} }
static int debug_dla_ctrl_clk_idlecount_show(struct seq_file *s, void *data) static int debug_dla_ctrl_clk_idlecount_show(struct seq_file *s, void *data)
@@ -680,13 +758,30 @@ static int debug_dla_ctrl_clk_idledelay_us_show(struct seq_file *s, void *data)
{ {
int err; int err;
struct platform_device *pdev; struct platform_device *pdev;
struct nvdla_debug_ctrl *ctrl;
(void) data; (void) data;
err = s_nvdla_get_pdev_from_seq(s, &pdev); err = s_nvdla_get_pdev_from_seq(s, &pdev);
if (err < 0) if (err < 0) {
return -1; nvdla_dbg_err(pdev, "failed to fetch pdev\n");
goto fail;
}
nvdla_dbg_info(pdev, "[CTRL/CLK] idledelay_us show"); nvdla_dbg_info(pdev, "[CTRL/CLK] idledelay_us show");
ctrl = s_nvdla_debug_ctrl_get_by_pdev(pdev);
if (ctrl == NULL) {
nvdla_dbg_err(pdev, "No ctrl node available\n");
err = -ENOMEM;
goto fail;
}
seq_printf(s, "%x\n", (uint32_t) ctrl->clock_idledelay_us);
return 0; return 0;
fail:
return err;
} }
static int debug_dla_ctrl_clk_idletime_us_show(struct seq_file *s, void *data) static int debug_dla_ctrl_clk_idletime_us_show(struct seq_file *s, void *data)
@@ -733,13 +828,28 @@ static int debug_dla_ctrl_power_enable_show(struct seq_file *s, void *data)
{ {
int err; int err;
struct platform_device *pdev; struct platform_device *pdev;
bool gated;
(void) data; (void) data;
err = s_nvdla_get_pdev_from_seq(s, &pdev); err = s_nvdla_get_pdev_from_seq(s, &pdev);
if (err < 0) if (err < 0) {
return -1; nvdla_dbg_err(pdev, "failed to get pdev. err: %d", err);
nvdla_dbg_info(pdev, "[CTRL/POWER] enable show"); goto fail;
}
nvdla_dbg_info(pdev, "[CTRL/POWER] enable show");
err = nvdla_pm_power_is_gated(pdev, &gated);
if (err < 0) {
nvdla_dbg_err(pdev, "failed to get status. err: %d", err);
goto fail;
}
seq_printf(s, "%x\n", (int) !gated);
return 0; return 0;
fail:
return err;
} }
static int debug_dla_ctrl_power_idlecount_show(struct seq_file *s, void *data) static int debug_dla_ctrl_power_idlecount_show(struct seq_file *s, void *data)
@@ -759,13 +869,30 @@ static int debug_dla_ctrl_power_idledelay_us_show(struct seq_file *s, void *data
{ {
int err; int err;
struct platform_device *pdev; struct platform_device *pdev;
struct nvdla_debug_ctrl *ctrl;
(void) data; (void) data;
err = s_nvdla_get_pdev_from_seq(s, &pdev); err = s_nvdla_get_pdev_from_seq(s, &pdev);
if (err < 0) if (err < 0) {
return -1; nvdla_dbg_err(pdev, "failed to fetch pdev\n");
goto fail;
}
nvdla_dbg_info(pdev, "[CTRL/POWER] idledelay_us show"); nvdla_dbg_info(pdev, "[CTRL/POWER] idledelay_us show");
ctrl = s_nvdla_debug_ctrl_get_by_pdev(pdev);
if (ctrl == NULL) {
nvdla_dbg_err(pdev, "No ctrl node available\n");
err = -ENOMEM;
goto fail;
}
seq_printf(s, "%x\n", (uint32_t) ctrl->power_idledelay_us);
return 0; return 0;
fail:
return err;
} }
static int debug_dla_ctrl_power_idletime_us_show(struct seq_file *s, void *data) static int debug_dla_ctrl_power_idletime_us_show(struct seq_file *s, void *data)
@@ -851,13 +978,28 @@ static int debug_dla_ctrl_rail_enable_show(struct seq_file *s, void *data)
{ {
int err; int err;
struct platform_device *pdev; struct platform_device *pdev;
bool gated;
(void) data; (void) data;
err = s_nvdla_get_pdev_from_seq(s, &pdev); err = s_nvdla_get_pdev_from_seq(s, &pdev);
if (err < 0) if (err < 0) {
return -1; nvdla_dbg_err(pdev, "failed to get pdev. err: %d", err);
nvdla_dbg_info(pdev, "[CTRL/RAIL] enable show"); goto fail;
}
nvdla_dbg_info(pdev, "[CTRL/RAIL] enable show");
err = nvdla_pm_rail_is_gated(pdev, &gated);
if (err < 0) {
nvdla_dbg_err(pdev, "failed to get status. err: %d", err);
goto fail;
}
seq_printf(s, "%x\n", (int) !gated);
return 0; return 0;
fail:
return err;
} }
static int debug_dla_ctrl_rail_idlecount_show(struct seq_file *s, void *data) static int debug_dla_ctrl_rail_idlecount_show(struct seq_file *s, void *data)
@@ -877,13 +1019,30 @@ static int debug_dla_ctrl_rail_idledelay_us_show(struct seq_file *s, void *data)
{ {
int err; int err;
struct platform_device *pdev; struct platform_device *pdev;
struct nvdla_debug_ctrl *ctrl;
(void) data; (void) data;
err = s_nvdla_get_pdev_from_seq(s, &pdev); err = s_nvdla_get_pdev_from_seq(s, &pdev);
if (err < 0) if (err < 0) {
return -1; nvdla_dbg_err(pdev, "failed to fetch pdev\n");
goto fail;
}
nvdla_dbg_info(pdev, "[CTRL/RAIL] idledelay_us show"); nvdla_dbg_info(pdev, "[CTRL/RAIL] idledelay_us show");
ctrl = s_nvdla_debug_ctrl_get_by_pdev(pdev);
if (ctrl == NULL) {
nvdla_dbg_err(pdev, "No ctrl node available\n");
err = -ENOMEM;
goto fail;
}
seq_printf(s, "%x\n", (uint32_t) ctrl->rail_idledelay_us);
return 0; return 0;
fail:
return err;
} }
static int debug_dla_ctrl_rail_idletime_us_show(struct seq_file *s, void *data) static int debug_dla_ctrl_rail_idletime_us_show(struct seq_file *s, void *data)
@@ -1145,6 +1304,7 @@ static ssize_t debug_dla_ctrl_clk_enable_write(struct file *file,
int err; int err;
struct platform_device *pdev; struct platform_device *pdev;
long write_value; long write_value;
struct nvdla_debug_ctrl *ctrl;
/* Fetch user requested write-value. */ /* Fetch user requested write-value. */
err = kstrtol_from_user(buffer, count, 10, &write_value); err = kstrtol_from_user(buffer, count, 10, &write_value);
@@ -1157,6 +1317,17 @@ static ssize_t debug_dla_ctrl_clk_enable_write(struct file *file,
nvdla_dbg_info(pdev, "[CTRL/CLK] enable = %u\n", (unsigned int) write_value); nvdla_dbg_info(pdev, "[CTRL/CLK] enable = %u\n", (unsigned int) write_value);
ctrl = s_nvdla_debug_ctrl_get_by_pdev(pdev);
if (ctrl == NULL) {
nvdla_dbg_err(pdev, "No ctrl node available\n");
goto fail;
}
if (write_value > 0U)
err = nvdla_pm_clock_ungate(pdev);
else
err = nvdla_pm_clock_gate(pdev, ctrl->clock_idledelay_us, false);
return count; return count;
fail: fail:
@@ -1179,7 +1350,8 @@ static ssize_t debug_dla_ctrl_clk_core_khz_write(struct file *file,
if (err < 0) if (err < 0)
goto fail; goto fail;
nvdla_dbg_info(pdev, "[CTRL/CLK] core_khz = %u\n", (unsigned int) write_value); nvdla_dbg_info(pdev, "[CTRL/CLK] core_khz = %u\n",
(unsigned int) write_value);
return count; return count;
@@ -1187,44 +1359,6 @@ fail:
return -1; return -1;
} }
static int nvdla_set_mcu_freq_khz(struct nvdla_device *nvdla_dev, uint32_t mcu_freq_khz)
{
int err = 0;
struct nvdla_cmd_data cmd_data;
struct platform_device *pdev;
/* prepare command data */
cmd_data.method_id = DLA_CMD_SET_CLOCK_FREQ;
cmd_data.method_data = mcu_freq_khz;
cmd_data.wait = true;
pdev = nvdla_dev->pdev;
if (pdev == NULL) {
err = -EFAULT;
goto fail_no_dev;
}
/* make sure that device is powered on */
err = nvdla_module_busy(pdev);
if (err != 0) {
nvdla_dbg_err(pdev, "failed to power on\n");
err = -ENODEV;
goto fail_no_dev;
}
/* pass set debug command to falcon */
err = nvdla_fw_send_cmd(pdev, &cmd_data);
if (err != 0) {
nvdla_dbg_err(pdev, "failed to send set mcu freq command");
goto fail_to_send_cmd;
}
fail_to_send_cmd:
nvdla_module_idle(pdev);
fail_no_dev:
return err;
}
static ssize_t debug_dla_ctrl_clk_mcu_khz_write(struct file *file, static ssize_t debug_dla_ctrl_clk_mcu_khz_write(struct file *file,
const char __user *buffer, size_t count, loff_t *off) const char __user *buffer, size_t count, loff_t *off)
{ {
@@ -1254,7 +1388,7 @@ static ssize_t debug_dla_ctrl_clk_mcu_khz_write(struct file *file,
if (write_value > UINT_MAX) if (write_value > UINT_MAX)
goto fail; goto fail;
err = nvdla_set_mcu_freq_khz(nvdla_dev, write_value); err = nvdla_pm_clock_set_mcu_freq(pdev, write_value);
if (err != 0) { if (err != 0) {
nvdla_dbg_err(pdev, "Failed to send set mcu freq command"); nvdla_dbg_err(pdev, "Failed to send set mcu freq command");
goto fail; goto fail;
@@ -1272,6 +1406,7 @@ static ssize_t debug_dla_ctrl_clk_idledelay_us_write(struct file *file,
int err; int err;
struct platform_device *pdev; struct platform_device *pdev;
long write_value; long write_value;
struct nvdla_debug_ctrl *ctrl;
/* Fetch user requested write-value. */ /* Fetch user requested write-value. */
err = kstrtol_from_user(buffer, count, 10, &write_value); err = kstrtol_from_user(buffer, count, 10, &write_value);
@@ -1284,6 +1419,14 @@ static ssize_t debug_dla_ctrl_clk_idledelay_us_write(struct file *file,
nvdla_dbg_info(pdev, "[CTRL/CLK] idledelay_us = %u\n", (unsigned int) write_value); nvdla_dbg_info(pdev, "[CTRL/CLK] idledelay_us = %u\n", (unsigned int) write_value);
ctrl = s_nvdla_debug_ctrl_get_by_pdev(pdev);
if (ctrl == NULL) {
nvdla_dbg_err(pdev, "No ctrl node available\n");
goto fail;
}
ctrl->clock_idledelay_us = (uint32_t) write_value;
return count; return count;
fail: fail:
@@ -1297,6 +1440,7 @@ static ssize_t debug_dla_ctrl_power_enable_write(struct file *file,
int err; int err;
struct platform_device *pdev; struct platform_device *pdev;
long write_value; long write_value;
struct nvdla_debug_ctrl *ctrl;
/* Fetch user requested write-value. */ /* Fetch user requested write-value. */
err = kstrtol_from_user(buffer, count, 10, &write_value); err = kstrtol_from_user(buffer, count, 10, &write_value);
@@ -1307,7 +1451,19 @@ static ssize_t debug_dla_ctrl_power_enable_write(struct file *file,
if (err < 0) if (err < 0)
goto fail; goto fail;
nvdla_dbg_info(pdev, "[CTRL/POWER] enable = %u\n", (unsigned int) write_value); nvdla_dbg_info(pdev, "[CTRL/POWER] enable = %u\n",
(unsigned int) write_value);
ctrl = s_nvdla_debug_ctrl_get_by_pdev(pdev);
if (ctrl == NULL) {
nvdla_dbg_err(pdev, "No ctrl node available\n");
goto fail;
}
if (write_value > 0U)
err = nvdla_pm_power_ungate(pdev);
else
err = nvdla_pm_power_gate(pdev, ctrl->power_idledelay_us, false);
return count; return count;
@@ -1321,6 +1477,7 @@ static ssize_t debug_dla_ctrl_power_idledelay_us_write(struct file *file,
int err; int err;
struct platform_device *pdev; struct platform_device *pdev;
long write_value; long write_value;
struct nvdla_debug_ctrl *ctrl;
/* Fetch user requested write-value. */ /* Fetch user requested write-value. */
err = kstrtol_from_user(buffer, count, 10, &write_value); err = kstrtol_from_user(buffer, count, 10, &write_value);
@@ -1333,6 +1490,14 @@ static ssize_t debug_dla_ctrl_power_idledelay_us_write(struct file *file,
nvdla_dbg_info(pdev, "[CTRL/POWER] idledelay_us = %u\n", (unsigned int) write_value); nvdla_dbg_info(pdev, "[CTRL/POWER] idledelay_us = %u\n", (unsigned int) write_value);
ctrl = s_nvdla_debug_ctrl_get_by_pdev(pdev);
if (ctrl == NULL) {
nvdla_dbg_err(pdev, "No ctrl node available\n");
goto fail;
}
ctrl->power_idledelay_us = (uint32_t) write_value;
return count; return count;
fail: fail:
@@ -1370,6 +1535,7 @@ static ssize_t debug_dla_ctrl_rail_enable_write(struct file *file,
int err; int err;
struct platform_device *pdev; struct platform_device *pdev;
long write_value; long write_value;
struct nvdla_debug_ctrl *ctrl;
/* Fetch user requested write-value. */ /* Fetch user requested write-value. */
err = kstrtol_from_user(buffer, count, 10, &write_value); err = kstrtol_from_user(buffer, count, 10, &write_value);
@@ -1380,7 +1546,19 @@ static ssize_t debug_dla_ctrl_rail_enable_write(struct file *file,
if (err < 0) if (err < 0)
goto fail; goto fail;
nvdla_dbg_info(pdev, "[CTRL/RAIL] enable = %u\n", (unsigned int) write_value); nvdla_dbg_info(pdev, "[CTRL/RAIL] enable = %u...\n",
(unsigned int) write_value);
ctrl = s_nvdla_debug_ctrl_get_by_pdev(pdev);
if (ctrl == NULL) {
nvdla_dbg_err(pdev, "No ctrl node available\n");
goto fail;
}
if (write_value > 0U)
err = nvdla_pm_rail_ungate(pdev);
else
err = nvdla_pm_rail_gate(pdev, ctrl->rail_idledelay_us, false);
return count; return count;
@@ -1394,6 +1572,7 @@ static ssize_t debug_dla_ctrl_rail_idledelay_us_write(struct file *file,
int err; int err;
struct platform_device *pdev; struct platform_device *pdev;
long write_value; long write_value;
struct nvdla_debug_ctrl *ctrl;
/* Fetch user requested write-value. */ /* Fetch user requested write-value. */
err = kstrtol_from_user(buffer, count, 10, &write_value); err = kstrtol_from_user(buffer, count, 10, &write_value);
@@ -1406,6 +1585,14 @@ static ssize_t debug_dla_ctrl_rail_idledelay_us_write(struct file *file,
nvdla_dbg_info(pdev, "[CTRL/RAIL] idledelay_us = %u\n", (unsigned int) write_value); nvdla_dbg_info(pdev, "[CTRL/RAIL] idledelay_us = %u\n", (unsigned int) write_value);
ctrl = s_nvdla_debug_ctrl_get_by_pdev(pdev);
if (ctrl == NULL) {
nvdla_dbg_err(pdev, "No ctrl node available\n");
goto fail;
}
ctrl->rail_idledelay_us = (uint32_t) write_value;
return count; return count;
fail: fail:
@@ -2509,11 +2696,21 @@ void nvdla_debug_init(struct platform_device *pdev)
{ {
struct nvhost_device_data *pdata = platform_get_drvdata(pdev); struct nvhost_device_data *pdata = platform_get_drvdata(pdev);
struct nvdla_device *nvdla_dev = pdata->private_data; struct nvdla_device *nvdla_dev = pdata->private_data;
struct nvdla_debug_ctrl *ctrl;
struct dentry *de = pdata->debugfs; struct dentry *de = pdata->debugfs;
if (!de) if (!de)
return; return;
ctrl = devm_kzalloc(&pdev->dev, sizeof(*ctrl), GFP_KERNEL);
if (!ctrl)
return;
ctrl->pdev = pdev;
mutex_lock(&s_debug_ctrl_list_lock);
list_add_tail(&ctrl->list, &s_debug_ctrl_list);
mutex_unlock(&s_debug_ctrl_list_lock);
debugfs_create_u32("debug_mask", 0644, de, debugfs_create_u32("debug_mask", 0644, de,
&nvdla_dev->dbg_mask); &nvdla_dev->dbg_mask);
debugfs_create_u32("bitbang", 0644, de, &nvdla_dev->bitbang); debugfs_create_u32("bitbang", 0644, de, &nvdla_dev->bitbang);

View File

@@ -7,6 +7,7 @@
#include <nvidia/conftest.h> #include <nvidia/conftest.h>
#include "../nvdla_device.h" #include "../nvdla_device.h"
#include "../nvdla_pm.h"
#include "../../dla_queue.h" #include "../../dla_queue.h"
#include "../../nvdla_debug.h" #include "../../nvdla_debug.h"
@@ -277,10 +278,20 @@ int32_t nvdla_module_init(struct platform_device *pdev)
goto disable_pm; goto disable_pm;
} }
if (pdev->num_resources > 1U) {
err = nvdla_pm_init(pdev);
if (err < 0) {
nvdla_dbg_err(pdev, "failed to init pm. err %d\n", err);
goto destroy_device;
}
}
pdata->debugfs = debugfs_create_dir(pdata->devfs_name, NULL); pdata->debugfs = debugfs_create_dir(pdata->devfs_name, NULL);
return 0; return 0;
destroy_device:
s_nvdla_module_device_destroy(pdev);
disable_pm: disable_pm:
s_nvdla_module_pm_disable(pdev); s_nvdla_module_pm_disable(pdev);
fail: fail:
@@ -294,6 +305,7 @@ void nvdla_module_deinit(struct platform_device *pdev)
if ((pdata != NULL) && (pdata->debugfs != NULL)) if ((pdata != NULL) && (pdata->debugfs != NULL))
debugfs_remove_recursive(pdata->debugfs); debugfs_remove_recursive(pdata->debugfs);
nvdla_pm_deinit(pdev);
s_nvdla_module_device_destroy(pdev); s_nvdla_module_device_destroy(pdev);
s_nvdla_module_pm_disable(pdev); s_nvdla_module_pm_disable(pdev);
} }

View File

@@ -0,0 +1,199 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
*
* NVDLA Power Management
*/
#ifndef __NVDLA_PM_H_
#define __NVDLA_PM_H_
#include <linux/platform_device.h>
/**
* @brief Initializes power management unit
*
* @param[in] pdev Platform device with which power management is done.
*
* @return
* - zero, up on successful operation.
* - non-zero, otherwise.
**/
int32_t nvdla_pm_init(struct platform_device *pdev);
/**
* @brief Deinitializes power management unit
*
* @param[in] pdev Platform device with which power management is done.
**/
void nvdla_pm_deinit(struct platform_device *pdev);
/**
* @brief Triggers request to rail gate after specified 'timeout_us'.
*
* @param[in] pdev Platform device that is to be rail gated.
* @param[in] timeout_us Timeout after which DLA must be rail gated.
* @param[in] blocking if set, waits for the gating to complete.
*
* @return
* - zero, up on successful operation.
* - non-zero, otherwise
**/
int32_t nvdla_pm_rail_gate(struct platform_device *pdev,
uint32_t timeout_us,
bool blocking);
/**
* @brief Power un-gating.
*
* @param[in] pdev Platform device that is to be rail ungated.
*
* @return
* - zero, up on successful rail ungating.
* - non-zero, otherwise
**/
int32_t nvdla_pm_rail_ungate(struct platform_device *pdev);
/**
* @brief Checks if the rail is gated or not.
*
* @param[in] pdev Platform device.
* @param[out] gated Pointer where the status is saved.
*
* @return
* - zero, up on successful operation.
* - non-zero, otherwise
**/
int32_t nvdla_pm_rail_is_gated(struct platform_device *pdev,
bool *gated);
/**
* @brief Triggers request to power gate after specified 'timeout_us'.
*
* @param[in] pdev Platform device that is to be power gated.
* @param[in] timeout_us Timeout after which DLA must be power gated.
* @param[in] blocking if set, waits for the gating to complete.
*
* @return
* - zero, up on successful operation.
* - non-zero, otherwise
**/
int32_t nvdla_pm_power_gate(struct platform_device *pdev,
uint32_t timeout_us,
bool blocking);
/**
* @brief Power un-gating.
*
* @param[in] pdev Platform device that is to be power ungated.
*
* @return
* - zero, up on successful power ungating.
* - non-zero, otherwise
**/
int32_t nvdla_pm_power_ungate(struct platform_device *pdev);
/**
* @brief Checks if the power is gated or not.
*
* @param[in] pdev Platform device.
* @param[out] gated Pointer where the status is saved.
*
* @return
* - zero, up on successful operation.
* - non-zero, otherwise
**/
int32_t nvdla_pm_power_is_gated(struct platform_device *pdev,
bool *gated);
/**
* @brief Triggers request to clock gate after specified 'timeout_us'.
*
* @param[in] pdev Platform device that is to be clock gated.
* @param[in] timeout_us Timeout after which DLA must be clock gated.
* @param[in] blocking if set, waits for the gating to complete.
*
* @return
* - zero, up on successful operation.
* - non-zero, otherwise
**/
int32_t nvdla_pm_clock_gate(struct platform_device *pdev,
uint32_t timeout_us,
bool blocking);
/**
* @brief Power un-gating.
*
* @param[in] pdev Platform device that is to be clock ungated.
*
* @return
* - zero, up on successful clock ungating.
* - non-zero, otherwise
**/
int32_t nvdla_pm_clock_ungate(struct platform_device *pdev);
/**
* @brief Checks if the clock is gated or not.
*
* @param[in] pdev Platform device.
* @param[out] gated Pointer where the status is saved.
*
* @return
* - zero, up on successful operation.
* - non-zero, otherwise
**/
int32_t nvdla_pm_clock_is_gated(struct platform_device *pdev,
bool *gated);
/**
* @brief Sets DLA MCU frequency.
*
* @param[in] pdev platform device.
* @param[in] freq_khz frequency (in khz) that is to be set.
*
* @return
* - zero, with successful operation.
* - non-zero, otherwise
**/
int32_t nvdla_pm_clock_set_mcu_freq(struct platform_device *pdev,
uint32_t freq_khz);
/**
* @brief Gets current DLA MCU frequency.
*
* @param[in] pdev platform device.
* @param[out] freq_khz frequency (in khz).
*
* @return
* - zero, with successful operation.
* - non-zero, otherwise
**/
int32_t nvdla_pm_clock_get_mcu_freq(struct platform_device *pdev,
uint32_t *freq_khz);
/**
* @brief Sets DLA core frequency.
*
* @param[in] pdev Platform device.
* @param[in] freq_khz Frequency (in KHz) that is to be set.
*
* @return
* - zero, with successful operation.
* - non-zero, otherwise
**/
int32_t nvdla_pm_clock_set_core_freq(struct platform_device *pdev,
uint32_t freq_khz);
/**
* @brief Gets current DLA core frequency.
*
* @param[in] pdev platform device.
* @param[out] freq_khz frequency (in khz).
*
* @return
* - zero, with successful operation.
* - non-zero, otherwise
**/
int32_t nvdla_pm_clock_get_core_freq(struct platform_device *pdev,
uint32_t *freq_khz);
#endif /* __NVDLA_PM_H_ */

View File

@@ -0,0 +1,770 @@
// SPDX-License-Identifier: GPL-2.0-only
/* SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
*
* NVDLA Power Management - stub implementation
*/
#include "../nvdla_pm.h"
#include "../nvdla_device.h"
#include "../nvdla_fw.h"
#include "../nvdla_host_wrapper.h"
#include "../../dla_os_interface.h"
#include "../../nvdla.h"
#include "../../nvdla_debug.h"
#include "nvdla_pm_hfrp.h"
#include "nvdla_pm_hfrp_reg.h"
#include <linux/arm64-barrier.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/list.h>
#include <linux/mutex.h>
#define NVDLA_HFRP_APERTURE_IDX 1U
static LIST_HEAD(s_hfrp_list);
static DEFINE_MUTEX(s_hfrp_list_lock);
static struct hfrp_cmd_sequence *s_hfrp_cmd_sequence_alloc(struct hfrp *hfrp)
{
struct hfrp_cmd_sequence *sequence = NULL;
if (list_empty(&hfrp->seq_freelist))
goto done;
sequence = list_first_entry(&hfrp->seq_freelist, typeof(*sequence),
list);
/* Allocated successfully, delete it from the free list */
list_del(&sequence->list);
done:
return sequence;
}
static void s_hfrp_cmd_sequence_destroy(struct hfrp_cmd_sequence *sequence)
{
struct hfrp *hfrp = sequence->hfrp;
/* Return the sequence back to the free list */
list_add_tail(&sequence->list, &hfrp->seq_freelist);
}
static struct hfrp *s_hfrp_get_by_pdev(struct platform_device *pdev)
{
struct hfrp *hfrp = NULL;
mutex_lock(&s_hfrp_list_lock);
list_for_each_entry(hfrp, &s_hfrp_list, list) {
if (hfrp->pdev == pdev)
break;
}
spec_bar(); /* break_spec_p#5_1 */
mutex_unlock(&s_hfrp_list_lock);
return hfrp;
}
static irqreturn_t s_hfrp_isr(int irq, void *dev_id)
{
struct platform_device *pdev;
struct hfrp *hfrp;
uint32_t intstat;
uint32_t clientoffs;
uint32_t serveroffs;
uint32_t head_offset;
uint32_t tail_offset;
uint32_t buffer_size;
pdev = (struct platform_device *)(dev_id);
hfrp = s_hfrp_get_by_pdev(pdev);
intstat = hfrp_reg_read(hfrp, hfrp_irq_out_set_r());
nvdla_dbg_info(pdev, "Received interrupt: %x\n", intstat);
if (intstat == 0U) {
nvdla_dbg_warn(pdev, "Spurious interrupt\n");
goto done;
}
if (hfrp_irq_out_set_reset_v(intstat) > 0) {
/* Clear the reset bit */
hfrp_reg_write(hfrp, hfrp_irq_out_clr_reset_f(1U),
hfrp_irq_out_clr_r());
}
if (hfrp_irq_out_set_doorbell_v(intstat) == 0) {
/* No more action pending. */
goto done;
}
clientoffs = hfrp_reg_read(hfrp, hfrp_buffer_clientoffs_r());
serveroffs = hfrp_reg_read(hfrp, hfrp_buffer_serveroffs_r());
head_offset = hfrp_buffer_serveroffs_resp_head_v(serveroffs);
tail_offset = hfrp_buffer_clientoffs_resp_tail_v(clientoffs);
buffer_size = DLA_HFRP_RESP_BUFFER_SIZE;
/* Read all the pending response. */
while (tail_offset != head_offset) {
uint32_t ii;
uint32_t header32;
uint32_t seqid;
uint32_t respid;
uint32_t size;
uint32_t payload_size;
uint32_t payload[DLA_HFRP_RESP_PAYLOAD_MAX_LEN];
uint8_t *payload8b;
struct hfrp_cmd_sequence *sequence;
/* read header */
header32 = 0U;
for (ii = 0U; ii < sizeof(header32); ii++) {
uint8_t header_byte;
header_byte = hfrp_reg_read1B(hfrp,
hfrp_buffer_resp_r(tail_offset));
header32 |= (((uint32_t) header_byte) << (ii * 8U));
tail_offset = ((tail_offset + 1U) % buffer_size);
}
seqid = hfrp_buffer_resp_header_seqid_v(header32);
respid = hfrp_buffer_resp_header_seqid_v(header32);
size = hfrp_buffer_resp_header_size_v(header32);
payload_size = size - sizeof(header32);
nvdla_dbg_info(pdev, "seqid=%x respid=%x size=%x\n",
seqid, respid, size);
/* read payload. */
payload8b = (uint8_t *) payload;
for (ii = 0U; ii < payload_size; ii++) {
payload8b[ii] = hfrp_reg_read1B(hfrp,
hfrp_buffer_resp_r(tail_offset));
tail_offset = ((tail_offset + 1U) % buffer_size);
}
/* Signal sequence ID only on success. */
if (seqid != DLA_HFRP_SEQUENCE_ID_ASYNC) {
sequence = &hfrp->sequence_pool[seqid];
hfrp_handle_response(hfrp, sequence->cmdid,
(uint8_t *) payload, sizeof(payload));
complete(&sequence->completion);
s_hfrp_cmd_sequence_destroy(sequence);
}
}
/* Update the tail offset */
clientoffs &= ~(hfrp_buffer_clientoffs_resp_tail_m());
clientoffs |= hfrp_buffer_clientoffs_resp_tail_f(head_offset);
hfrp_reg_write(hfrp, clientoffs, hfrp_buffer_clientoffs_r());
/* Clear the doorbell */
hfrp_reg_write(hfrp, hfrp_irq_out_clr_doorbell_f(1U),
hfrp_irq_out_clr_r());
(void) irq;
done:
/* For now force clear all the register. */
hfrp_reg_write(hfrp, 0xffffffffU,
hfrp_irq_out_clr_r());
return IRQ_HANDLED;
}
int32_t hfrp_send_cmd(struct hfrp *hfrp,
uint32_t cmd,
uint8_t *payload,
uint32_t payload_size,
bool blocking)
{
int32_t err;
int32_t ii;
uint32_t header32;
uint32_t header_size;
uint8_t *header;
uint32_t clientoffs;
uint32_t serveroffs;
uint32_t head_offset;
uint32_t tail_offset;
uint32_t buffer_size;
uint32_t buffer_used;
uint64_t timeout;
struct hfrp_cmd_sequence *sequence;
header32 = 0U;
header_size = sizeof(header32);
header = (uint8_t *) &header32;
/* Check if we have sufficient space for sending command. */
clientoffs = hfrp_reg_read(hfrp, hfrp_buffer_clientoffs_r());
serveroffs = hfrp_reg_read(hfrp, hfrp_buffer_serveroffs_r());
head_offset = hfrp_buffer_clientoffs_cmd_head_v(clientoffs);
tail_offset = hfrp_buffer_serveroffs_cmd_tail_v(serveroffs);
buffer_size = DLA_HFRP_CMD_BUFFER_SIZE;
buffer_used = ((buffer_size + head_offset - tail_offset) % buffer_size);
if ((buffer_used + header_size + payload_size) >= buffer_size) {
err = -ENOMEM;
goto fail;
}
sequence = s_hfrp_cmd_sequence_alloc(hfrp);
if (sequence == NULL) {
err = -ENOMEM;
goto fail;
}
sequence->cmdid = cmd;
nvdla_dbg_info(hfrp->pdev, "seqid=%x cmdid=%x\n",
sequence->seqid, sequence->cmdid);
/* Construct header */
header32 |= hfrp_buffer_cmd_header_size_f((payload_size + header_size));
header32 |= hfrp_buffer_cmd_header_seqid_f(sequence->seqid);
header32 |= hfrp_buffer_cmd_header_cmdid_f(cmd);
/* Copy the header and payload byte-by-byte (to be safe). */
for (ii = 0U; ii < header_size; ii++) {
hfrp_reg_write1B(hfrp, header[ii],
hfrp_buffer_cmd_r(head_offset));
head_offset = ((head_offset + 1U) % buffer_size);
}
for (ii = 0U; ii < payload_size; ii++) {
hfrp_reg_write1B(hfrp, payload[ii],
hfrp_buffer_cmd_r(head_offset));
head_offset = ((head_offset + 1U) % buffer_size);
}
/* Update the command head pointer */
clientoffs &= ~(hfrp_buffer_clientoffs_cmd_head_m());
clientoffs |= hfrp_buffer_clientoffs_cmd_head_f(head_offset);
hfrp_reg_write(hfrp, clientoffs, hfrp_buffer_clientoffs_r());
/* Ring the door bell */
hfrp_reg_write(hfrp, hfrp_irq_in_set_doorbell_f(1U),
hfrp_irq_in_set_r());
/* Block if requested for response and error with 1s timeout */
if (blocking) {
timeout = msecs_to_jiffies(1000U);
if (!wait_for_completion_timeout(&sequence->completion,
timeout)) {
nvdla_dbg_err(hfrp->pdev,
"DLA-HFRP response timedout.\n");
err = -ETIMEDOUT;
goto fail;
}
}
return 0;
fail:
return err;
}
/* PM Implementation */
int32_t nvdla_pm_init(struct platform_device *pdev)
{
int32_t err;
int32_t irq;
struct hfrp *hfrp;
struct nvhost_device_data *pdata;
struct nvdla_device *nvdladev;
void __iomem *regs;
struct hfrp_cmd_sequence *seqpool;
uint32_t ii;
pdata = platform_get_drvdata(pdev);
nvdladev = pdata->private_data;
regs = pdata->aperture[NVDLA_HFRP_APERTURE_IDX];
irq = platform_get_irq(pdev, NVDLA_HFRP_APERTURE_IDX);
if (irq < 0) {
nvdla_dbg_err(pdev, "failed to get HFRP IRQ\n");
err = -ENXIO;
goto fail;
}
err = devm_request_irq(&pdev->dev, irq, s_hfrp_isr, 0,
dev_name(&pdev->dev), pdev);
if (err) {
nvdla_dbg_err(pdev, "failed to request hfrp irq. err %d\n",
err);
goto fail;
}
/* Keep the interrupt disabled */
disable_irq(irq);
/* Device managed allocation - for HFRP */
hfrp = devm_kzalloc(&pdev->dev, sizeof(*hfrp), GFP_KERNEL);
if (!hfrp) {
err = -ENOMEM;
goto free_irq;
}
/* Device managed allocation - for sequence pool */
seqpool = devm_kzalloc(&pdev->dev,
sizeof(struct hfrp_cmd_sequence) *
DLA_HFRP_MAX_NUM_SEQ,
GFP_KERNEL);
if (!seqpool) {
err = -ENOMEM;
goto free_hfrp;
}
hfrp->pdev = pdev;
hfrp->irq = irq;
hfrp->regs = regs;
hfrp->sequence_pool = seqpool;
hfrp->nsequences = DLA_HFRP_MAX_NUM_SEQ;
INIT_LIST_HEAD(&hfrp->seq_freelist);
mutex_init(&hfrp->cmd_lock);
for (ii = 0U; ii < DLA_HFRP_MAX_NUM_SEQ; ii++) {
struct hfrp_cmd_sequence *sequence;
sequence = &hfrp->sequence_pool[ii];
sequence->hfrp = hfrp;
sequence->seqid = ii;
sequence->cmdid = 0U;
init_completion(&sequence->completion);
list_add_tail(&sequence->list, &hfrp->seq_freelist);
}
mutex_lock(&s_hfrp_list_lock);
list_add_tail(&hfrp->list, &s_hfrp_list);
mutex_unlock(&s_hfrp_list_lock);
enable_irq(irq);
/* Select circular mode */
hfrp_reg_write(hfrp, hfrp_mailbox0_mode_circular_v(),
hfrp_mailbox0_mode_r());
return 0;
free_hfrp:
devm_kfree(&pdev->dev, hfrp);
free_irq:
devm_free_irq(&pdev->dev, irq, pdev);
fail:
return err;
}
void nvdla_pm_deinit(struct platform_device *pdev)
{
struct hfrp *hfrp;
hfrp = s_hfrp_get_by_pdev(pdev);
if (hfrp != NULL) {
mutex_lock(&s_hfrp_list_lock);
list_del(&hfrp->list);
mutex_unlock(&s_hfrp_list_lock);
mutex_destroy(&hfrp->cmd_lock);
disable_irq(hfrp->irq);
devm_free_irq(&pdev->dev, hfrp->irq, pdev);
devm_kfree(&pdev->dev, hfrp->sequence_pool);
devm_kfree(&pdev->dev, hfrp);
}
}
int32_t nvdla_pm_rail_gate(struct platform_device *pdev,
uint32_t timeout_us,
bool blocking)
{
int32_t err;
struct nvdla_hfrp_cmd_config config_cmd;
struct nvdla_hfrp_cmd_power_ctrl cmd;
struct hfrp *hfrp;
hfrp = s_hfrp_get_by_pdev(pdev);
if (hfrp == NULL) {
err = -EINVAL;
goto fail;
}
/* Configure delay values through config command */
if (timeout_us > 0U) {
memset(&config_cmd, 0, sizeof(config_cmd));
config_cmd.rg_delay_ms = timeout_us >> 10;
err = nvdla_hfrp_send_cmd_config(hfrp, &config_cmd, false);
if (err < 0) {
nvdla_dbg_err(pdev, "Config cmd send fail. err %d",
err);
goto fail;
}
}
/* Send power control command */
memset(&cmd, 0, sizeof(cmd));
if (timeout_us > 0U)
cmd.rail_delayed_off = true;
else
cmd.rail_off = true;
cmd.pps = 3U;
err = nvdla_hfrp_send_cmd_power_ctrl(hfrp, &cmd, false);
if (err < 0) {
nvdla_dbg_err(pdev, "Power ctrl cmd send fail. err %d", err);
goto fail;
}
(void) blocking;
return 0;
fail:
return err;
}
int32_t nvdla_pm_rail_ungate(struct platform_device *pdev)
{
int32_t err;
struct nvdla_hfrp_cmd_power_ctrl cmd;
struct hfrp *hfrp;
hfrp = s_hfrp_get_by_pdev(pdev);
if (hfrp == NULL) {
err = -EINVAL;
goto fail;
}
memset(&cmd, 0, sizeof(cmd));
cmd.rail_on = true;
err = nvdla_hfrp_send_cmd_power_ctrl(hfrp, &cmd, false);
if (err < 0) {
nvdla_dbg_err(pdev, "Power ctrl cmd send fail. err %d", err);
goto fail;
}
return 0;
fail:
return err;
}
int32_t nvdla_pm_rail_is_gated(struct platform_device *pdev,
bool *gated)
{
int32_t err;
struct hfrp *hfrp;
hfrp = s_hfrp_get_by_pdev(pdev);
if (hfrp == NULL) {
err = -EINVAL;
goto fail;
}
mutex_lock(&hfrp->cmd_lock);
*gated = hfrp->rail_gated;
mutex_unlock(&hfrp->cmd_lock);
return 0;
fail:
return err;
}
int32_t nvdla_pm_power_gate(struct platform_device *pdev,
uint32_t timeout_us,
bool blocking)
{
int32_t err;
struct nvdla_hfrp_cmd_config config_cmd;
struct nvdla_hfrp_cmd_power_ctrl cmd;
struct hfrp *hfrp;
hfrp = s_hfrp_get_by_pdev(pdev);
if (hfrp == NULL) {
err = -EINVAL;
goto fail;
}
/* Configure delay values through config command */
if (timeout_us > 0U) {
memset(&config_cmd, 0, sizeof(config_cmd));
config_cmd.pg_delay_ms = timeout_us >> 10;
err = nvdla_hfrp_send_cmd_config(hfrp, &config_cmd, false);
if (err < 0) {
nvdla_dbg_err(pdev, "Config cmd send fail. err %d",
err);
goto fail;
}
}
memset(&cmd, 0, sizeof(cmd));
if (timeout_us > 0U)
cmd.power_delayed_off = true;
else
cmd.power_off = true;
err = nvdla_hfrp_send_cmd_power_ctrl(hfrp, &cmd, false);
if (err < 0) {
nvdla_dbg_err(pdev, "Power ctrl cmd send fail. err %d", err);
goto fail;
}
(void) blocking;
return 0;
fail:
return err;
}
int32_t nvdla_pm_power_ungate(struct platform_device *pdev)
{
int32_t err;
struct nvdla_hfrp_cmd_power_ctrl cmd;
struct hfrp *hfrp;
hfrp = s_hfrp_get_by_pdev(pdev);
if (hfrp == NULL) {
err = -EINVAL;
goto fail;
}
memset(&cmd, 0, sizeof(cmd));
cmd.power_on = true;
err = nvdla_hfrp_send_cmd_power_ctrl(hfrp, &cmd, false);
if (err < 0) {
nvdla_dbg_err(pdev, "Power ctrl cmd send fail. err %d", err);
goto fail;
}
return 0;
fail:
return err;
}
int32_t nvdla_pm_power_is_gated(struct platform_device *pdev,
bool *gated)
{
int32_t err;
struct hfrp *hfrp;
hfrp = s_hfrp_get_by_pdev(pdev);
if (hfrp == NULL) {
err = -EINVAL;
goto fail;
}
mutex_lock(&hfrp->cmd_lock);
*gated = hfrp->power_gated;
mutex_unlock(&hfrp->cmd_lock);
return 0;
fail:
return err;
}
int32_t nvdla_pm_clock_gate(struct platform_device *pdev,
uint32_t timeout_us,
bool blocking)
{
int32_t err;
struct nvdla_hfrp_cmd_config config_cmd;
struct nvdla_hfrp_cmd_power_ctrl cmd;
struct hfrp *hfrp;
hfrp = s_hfrp_get_by_pdev(pdev);
if (hfrp == NULL) {
err = -EINVAL;
goto fail;
}
/* Configure delay values through config command */
if (timeout_us > 0U) {
memset(&config_cmd, 0, sizeof(config_cmd));
config_cmd.cg_delay_ms = timeout_us >> 10;
err = nvdla_hfrp_send_cmd_config(hfrp, &config_cmd, false);
if (err < 0) {
nvdla_dbg_err(pdev, "Config cmd send fail. err %d",
err);
goto fail;
}
}
memset(&cmd, 0, sizeof(cmd));
if (timeout_us > 0)
cmd.clock_delayed_off = true;
else
cmd.clock_off = true;
err = nvdla_hfrp_send_cmd_power_ctrl(hfrp, &cmd, false);
if (err < 0) {
nvdla_dbg_err(pdev, "Power ctrl cmd send fail. err %d", err);
goto fail;
}
return 0;
fail:
return err;
}
int32_t nvdla_pm_clock_ungate(struct platform_device *pdev)
{
int32_t err;
struct nvdla_hfrp_cmd_power_ctrl cmd;
struct hfrp *hfrp;
hfrp = s_hfrp_get_by_pdev(pdev);
if (hfrp == NULL) {
err = -EINVAL;
goto fail;
}
memset(&cmd, 0, sizeof(cmd));
cmd.clock_on = true;
err = nvdla_hfrp_send_cmd_power_ctrl(hfrp, &cmd, false);
if (err < 0) {
nvdla_dbg_err(pdev, "Power ctrl cmd send fail. err %d", err);
goto fail;
}
return 0;
fail:
return err;
}
int32_t nvdla_pm_clock_is_gated(struct platform_device *pdev,
bool *gated)
{
int32_t err;
struct hfrp *hfrp;
hfrp = s_hfrp_get_by_pdev(pdev);
if (hfrp == NULL) {
err = -EINVAL;
goto fail;
}
mutex_lock(&hfrp->cmd_lock);
*gated = hfrp->clock_gated;
mutex_unlock(&hfrp->cmd_lock);
return 0;
fail:
return err;
}
int32_t nvdla_pm_clock_set_mcu_freq(struct platform_device *pdev,
uint32_t freq_khz)
{
int32_t err = 0;
struct nvdla_cmd_data cmd_data;
/* prepare command data */
cmd_data.method_id = DLA_CMD_SET_CLOCK_FREQ;
cmd_data.method_data = freq_khz;
cmd_data.wait = true;
if (pdev == NULL) {
err = -EFAULT;
goto fail_no_dev;
}
/* make sure that device is powered on */
err = nvdla_module_busy(pdev);
if (err != 0) {
nvdla_dbg_err(pdev, "failed to power on\n");
err = -ENODEV;
goto fail_no_dev;
}
/* pass set debug command to falcon */
err = nvdla_fw_send_cmd(pdev, &cmd_data);
if (err != 0) {
nvdla_dbg_err(pdev, "failed to send set mcu freq command");
goto fail_to_send_cmd;
}
fail_to_send_cmd:
nvdla_module_idle(pdev);
fail_no_dev:
return err;
}
int32_t nvdla_pm_clock_get_mcu_freq(struct platform_device *pdev,
uint32_t *freq_khz)
{
int32_t err;
err = nvdla_pm_clock_get_core_freq(pdev, freq_khz);
if (err < 0) {
nvdla_dbg_err(pdev, "failed to get mcu freq. err: %d\n", err);
goto fail;
}
/* Core frequency is twice the MCU frequency. */
*freq_khz = ((*freq_khz) >> 1);
return 0;
fail:
return err;
}
int32_t nvdla_pm_clock_set_core_freq(struct platform_device *pdev,
uint32_t freq_khz)
{
int32_t err;
/* Core frequency is twice the MCU frequency. */
err = nvdla_pm_clock_set_mcu_freq(pdev, (freq_khz >> 1));
if (err < 0) {
nvdla_dbg_err(pdev, "failed to get mcu freq. err: %d\n", err);
goto fail;
}
return 0;
fail:
return err;
}
int32_t nvdla_pm_clock_get_core_freq(struct platform_device *pdev,
uint32_t *freq_khz)
{
int32_t err;
struct hfrp *hfrp;
hfrp = s_hfrp_get_by_pdev(pdev);
if (hfrp == NULL) {
err = -EINVAL;
goto fail;
}
err = nvdla_hfrp_send_cmd_get_current_freq(hfrp, true);
if (err < 0) {
nvdla_dbg_err(pdev, "failed to get freq. err: %d\n", err);
goto fail;
}
*freq_khz = hfrp->core_freq_khz;
return 0;
fail:
return err;
}

View File

@@ -0,0 +1,223 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
*
*/
#ifndef __NVDLA_PM_HFRP_H_
#define __NVDLA_PM_HFRP_H_
#include <linux/completion.h>
#include <linux/io.h>
#include <linux/mutex.h>
#include <linux/platform_device.h>
/* Header and Payload Defines */
#define DLA_HFRP_CMD_PAYLOAD_MAX_LEN 32U
#define DLA_HFRP_RESP_PAYLOAD_MAX_LEN 32U
/* Circular buffer size */
#define DLA_HFRP_CMD_BUFFER_SIZE 116U
#define DLA_HFRP_RESP_BUFFER_SIZE 116U
/* This is reasonable number given command buffer size 116 bytes */
#define DLA_HFRP_MAX_NUM_SEQ 64U
struct hfrp_cmd_sequence {
struct hfrp *hfrp;
#define DLA_HFRP_SEQUENCE_ID_ASYNC 0x3ffU
uint32_t seqid;
uint32_t cmdid;
struct completion completion;
/* Node pointer to be part of HFRP free list */
struct list_head list;
};
struct hfrp {
/* General */
struct platform_device *pdev;
void __iomem *regs;
int irq;
/* HFRP command management */
struct mutex cmd_lock;
struct hfrp_cmd_sequence *sequence_pool;
uint32_t nsequences;
struct list_head seq_freelist;
/* Cache latest response for quick accesses */
bool rail_gated;
bool power_gated;
bool clock_gated;
uint32_t core_freq_khz;
/* Node pointer to be a part of a list. */
struct list_head list;
};
static inline void hfrp_reg_write1B(struct hfrp *hfrp,
uint8_t value,
uint32_t offset)
{
uint32_t write_value;
write_value = readl(hfrp->regs + ((offset >> 2U) << 2U));
write_value &= ~(((uint32_t) 0xffU) << ((offset % 4U) * 8U));
write_value |= (((uint32_t) value) << ((offset % 4U) * 8U));
writel(write_value, hfrp->regs + ((offset >> 2U) << 2U));
}
static inline uint8_t hfrp_reg_read1B(struct hfrp *hfrp,
uint32_t offset)
{
uint32_t read_value;
read_value = readl(hfrp->regs + ((offset >> 2U) << 2U));
read_value = read_value >> ((offset % 4U) * 8U);
read_value = read_value & 0xffU;
return (uint8_t) read_value;
}
static inline void hfrp_reg_write(struct hfrp *hfrp,
uint32_t value,
uint32_t offset)
{
writel(value, hfrp->regs + offset);
}
static inline uint32_t hfrp_reg_read(struct hfrp *hfrp, uint32_t offset)
{
return readl(hfrp->regs + offset);
}
/* Command and Response Headers */
static inline uint32_t hfrp_buffer_cmd_header_size_f(uint32_t v)
{
/* BUFFER_CMD_HEADER_SIZE 0:7 */
return (v & 0xffU);
}
static inline uint32_t hfrp_buffer_cmd_header_size_v(uint32_t r)
{
/* BUFFER_CMD_HEADER_SIZE 0:7 */
return (r & 0xffU);
}
static inline uint32_t hfrp_buffer_cmd_header_seqid_f(uint32_t v)
{
/* BUFFER_CMD_HEADER_SEQUENCE_ID 17:8 */
return ((v & 0x3ffU) << 8);
}
static inline uint32_t hfrp_buffer_cmd_header_seqid_v(uint32_t r)
{
/* BUFFER_CMD_HEADER_SEQUENCE_ID 17:8 */
return ((r >> 8) & 0x3ffU);
}
static inline uint32_t hfrp_buffer_cmd_header_cmdid_f(uint32_t v)
{
/* BUFFER_CMD_HEADER_COMMAND_ID 27:18 */
return ((v & 0x3ffU) << 18);
}
static inline uint32_t hfrp_buffer_cmd_header_cmdid_v(uint32_t r)
{
/* BUFFER_CMD_HEADER_COMMAND_ID 27:18 */
return ((r >> 18) & 0x3ffU);
}
static inline uint32_t hfrp_buffer_resp_header_size_f(uint32_t v)
{
/* BUFFER_RESPONSE_HEADER_SIZE 0:7 */
return (v & 0xffU);
}
static inline uint32_t hfrp_buffer_resp_header_size_v(uint32_t r)
{
/* BUFFER_RESPONSE_HEADER_SIZE 0:7 */
return (r & 0xffU);
}
static inline uint32_t hfrp_buffer_resp_header_seqid_f(uint32_t v)
{
/* BUFFER_RESPONSE_HEADER_SEQUENCE_ID 17:8 */
return ((v & 0x3ffU) << 8);
}
static inline uint32_t hfrp_buffer_resp_header_seqid_v(uint32_t r)
{
/* BUFFER_RESPONSE_HEADER_SEQUENCE_ID 17:8 */
return ((r >> 8) & 0x3ffU);
}
static inline uint32_t hfrp_buffer_resp_header_respid_f(uint32_t v)
{
/* BUFFER_RESPONSE_HEADER_RESPONSE_ID 27:18 */
return ((v & 0x3ffU) << 18);
}
static inline uint32_t hfrp_buffer_resp_header_respid_v(uint32_t r)
{
/* BUFFER_RESPONSE_HEADER_RESPONSE_ID 27:18 */
return ((r >> 18) & 0x3ffU);
}
/**
* HFRP Communication protocol
**/
int32_t hfrp_send_cmd(struct hfrp *hfrp,
uint32_t cmd,
uint8_t *payload,
uint32_t payload_size,
bool blocking);
/**
* DLA-HFRP Command Request and Response Policy
**/
void hfrp_handle_response(struct hfrp *hfrp,
uint32_t cmd,
uint8_t *payload,
uint32_t payload_size);
/* For gating and ungating of Clock, Power, and Rail */
struct nvdla_hfrp_cmd_power_ctrl {
bool power_on;
bool power_off;
bool power_delayed_off;
bool rail_on;
bool rail_off;
bool rail_delayed_off;
bool clock_on;
bool clock_off;
bool clock_delayed_off;
int8_t pps;
};
int32_t nvdla_hfrp_send_cmd_power_ctrl(struct hfrp *hfrp,
struct nvdla_hfrp_cmd_power_ctrl *cmd,
bool blocking);
/* For configuration of delay value in the event of delayed gating */
struct nvdla_hfrp_cmd_config {
uint16_t cg_delay_ms;
uint16_t pg_delay_ms;
uint16_t rg_delay_ms;
uint32_t pg_entry_freq_khz;
uint32_t pg_exit_freq_khz;
};
int32_t nvdla_hfrp_send_cmd_config(struct hfrp *hfrp,
struct nvdla_hfrp_cmd_config *cmd,
bool blocking);
/* For getting the current frequency */
int32_t nvdla_hfrp_send_cmd_get_current_freq(struct hfrp *hfrp,
bool blocking);
#endif /* __NVDLA_PM_HFRP_H_ */

View File

@@ -0,0 +1,291 @@
// SPDX-License-Identifier: GPL-2.0-only
/* SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
*
* NVDLA HFRP Command Implementation
*/
#include "nvdla_pm_hfrp.h"
#include "../../nvdla_debug.h"
/* Commands */
#define DLA_HFRP_CMD_POWER_CONTROL 400
#define DLA_HFRP_CMD_CONFIG 401
#define DLA_HFRP_CMD_GET_CURRENT_VOLTAGE 402
#define DLA_HFRP_CMD_GET_CURRENT_POWER_DRAW 405
#define DLA_HFRP_CMD_SET_POWER_DRAW_CAP 406
#define DLA_HFRP_CMD_GET_POWER_DRAW_CAP 407
#define DLA_HFRP_CMD_SET_POWER_CONTROL_TUNING 408
#define DLA_HFRP_CMD_RESOURCE_REQUEST 409
#define DLA_HFRP_CMD_RESET 410
#define DLA_HFRP_CMD_RESET_IDLE 411
#define DLA_HFRP_CMD_SRAM_HOLD 412
#define DLA_HFRP_CMD_CLOCK_GATE 413
#define DLA_HFRP_CMD_GET_CURRENT_CLOCK_FREQ 414
#define DLA_HFRP_CMD_GET_AVG_CLOCK_FREQ 416
#define DLA_HFRP_CMD_SLCG_OVERRIDE 417
#define DLA_HFRP_CMD_GET_CURRENT_MEM_CLOCK_FREQ 418
#define DLA_HFRP_CMD_GET_MAXIMUM_MEM_CLOCK_FREQ 419
#define DLA_HFRP_CMD_GET_MAXIMUM_MEMORY_BW 420
#define DLA_HFRP_CMD_CONFIG_EMI_BW_MONITOR_CTR 421
#define DLA_HFRP_CMD_READ_EMI_BW_MONITOR_CTR 422
#define DLA_HFRP_CMD_CONFIG_SLC_BW_MONITOR_CTR 423
#define DLA_HFRP_CMD_READ_SLC_BW_MONITOR_CTR 424
#define DLA_HFRP_CMD_SYS_GET_TEMPERATURE 425
#define DLA_HFRP_CMD_SYS_GET_TEMPERATURE_LIMIT 426
#define DLA_HFRP_CMD_GET_PERF_LIMIT_REASON 427
/* Command and header buffer size */
#define SZ(cmd) cmd##_SZ
#define DLA_HFRP_CMD_POWER_CONTROL_SZ 2U
#define DLA_HFRP_CMD_CONFIG_SZ 12U
#define DLA_HFRP_CMD_GET_CURRENT_VOLTAGE_SZ 4U
#define DLA_HFRP_CMD_GET_CURRENT_POWER_DRAW_SZ 4U
#define DLA_HFRP_CMD_SET_POWER_DRAW_CAP_SZ 4U
#define DLA_HFRP_CMD_GET_POWER_DRAW_CAP_SZ 4U
#define DLA_HFRP_CMD_SET_POWER_CONTROL_TUNING_SZ 36U
#define DLA_HFRP_CMD_RESOURCE_REQUEST_SZ 8U
#define DLA_HFRP_CMD_RESET_SZ 1U
#define DLA_HFRP_CMD_RESET_IDLE_SZ 1U
#define DLA_HFRP_CMD_SRAM_HOLD_SZ 1U
#define DLA_HFRP_CMD_CLOCK_GATE_SZ 1U
#define DLA_HFRP_CMD_GET_CURRENT_CLOCK_FREQ_SZ 0U
#define DLA_HFRP_CMD_GET_AVG_CLOCK_FREQ_SZ 4U
#define DLA_HFRP_CMD_SLCG_OVERRIDE_SZ 1U
#define DLA_HFRP_CMD_GET_CURRENT_MEM_CLOCK_FREQ_SZ 4U
#define DLA_HFRP_CMD_GET_MAXIMUM_MEM_CLOCK_FREQ_SZ 4U
#define DLA_HFRP_CMD_GET_MAXIMUM_MEMORY_BW_SZ 4U
#define DLA_HFRP_CMD_CONFIG_EMI_BW_MONITOR_CTR_SZ 8U
#define DLA_HFRP_CMD_READ_EMI_BW_MONITOR_CTR_SZ 8U
#define DLA_HFRP_CMD_CONFIG_SLC_BW_MONITOR_CTR_SZ 12U
#define DLA_HFRP_CMD_READ_SLC_BW_MONITOR_CTR_SZ 8U
#define DLA_HFRP_CMD_SYS_GET_TEMPERATURE_SZ 2U
#define DLA_HFRP_CMD_SYS_GET_TEMPERATURE_LIMIT_SZ 4U
#define DLA_HFRP_CMD_GET_PERF_LIMIT_REASON_SZ 1U
static int32_t s_nvdla_hfrp_send_cmd_clock_gate(struct hfrp *hfrp,
struct nvdla_hfrp_cmd_power_ctrl *cmd,
bool blocking)
{
int32_t err;
uint32_t payload[(SZ(DLA_HFRP_CMD_CLOCK_GATE) >> 2) + 1U];
uint32_t payload_size;
memset(payload, 0, sizeof(payload));
payload_size = SZ(DLA_HFRP_CMD_CLOCK_GATE);
/**
* Core 0:0
* MCU 1:1
* CFG 2:2
**/
payload[0] |= (((uint32_t)(cmd->clock_off)) << 0);
payload[0] |= (((uint32_t)(cmd->clock_off)) << 1);
payload[0] |= (((uint32_t)(cmd->clock_off)) << 2);
err = (hfrp_send_cmd(hfrp, DLA_HFRP_CMD_CLOCK_GATE,
(uint8_t *) payload, payload_size, blocking));
return err;
}
static int32_t s_nvdla_hfrp_send_cmd_power_ctrl(struct hfrp *hfrp,
struct nvdla_hfrp_cmd_power_ctrl *cmd,
bool blocking)
{
int32_t err;
uint32_t payload[(SZ(DLA_HFRP_CMD_POWER_CONTROL) >> 2) + 1U];
uint32_t payload_size;
memset(payload, 0, sizeof(payload));
payload_size = SZ(DLA_HFRP_CMD_POWER_CONTROL);
/**
* MTCMOS OFF 0:0
* RAIL OFF 1:1
* MTCMOS ON 2:2
* RAIL ON 3:3
* MTCMOS DELAYED_OFF 4:4
* RAIL DELAYED_OFF 5:5
* PPS 8:15
**/
payload[0] |= (((uint32_t)(cmd->power_off)) << 0);
payload[0] |= (((uint32_t)(cmd->rail_off)) << 1);
payload[0] |= (((uint32_t)(cmd->power_on)) << 2);
payload[0] |= (((uint32_t)(cmd->rail_on)) << 3);
payload[0] |= (((uint32_t)(cmd->power_delayed_off)) << 4);
payload[0] |= (((uint32_t)(cmd->rail_delayed_off)) << 5);
payload[0] |= (((uint32_t)(cmd->pps) & 0xffU) << 8);
err = (hfrp_send_cmd(hfrp, DLA_HFRP_CMD_POWER_CONTROL,
(uint8_t *) payload, payload_size, blocking));
return err;
}
int32_t nvdla_hfrp_send_cmd_power_ctrl(struct hfrp *hfrp,
struct nvdla_hfrp_cmd_power_ctrl *cmd,
bool blocking)
{
int32_t err;
struct platform_device *pdev;
pdev = hfrp->pdev;
if (cmd->clock_off || cmd->clock_on || cmd->clock_delayed_off) {
err = s_nvdla_hfrp_send_cmd_clock_gate(hfrp, cmd, blocking);
if (err < 0) {
nvdla_dbg_err(pdev, "Failed to send clk gate. err %d",
err);
goto fail;
}
}
if (cmd->power_off || cmd->power_on || cmd->power_delayed_off ||
cmd->rail_off || cmd->rail_on ||
cmd->rail_delayed_off) {
err = s_nvdla_hfrp_send_cmd_power_ctrl(hfrp, cmd, blocking);
if (err < 0) {
nvdla_dbg_err(pdev, "Failed to send power ctrl. err %d",
err);
goto fail;
}
}
return 0;
fail:
return err;
}
int32_t nvdla_hfrp_send_cmd_config(struct hfrp *hfrp,
struct nvdla_hfrp_cmd_config *cmd,
bool blocking)
{
int32_t err;
uint32_t payload[(SZ(DLA_HFRP_CMD_CONFIG) >> 2) + 1U];
uint32_t payload_size;
memset(payload, 0, sizeof(payload));
payload_size = SZ(DLA_HFRP_CMD_CONFIG);
/**
* PG_DELAY 15:0
* RG_DELAY 31:16 (15:0 byte 2)
* PG_ENTRY_FREQ 31:0 (byte 4)
* PG_EXIT_FREQ 31:0 (byte 8)
**/
payload[0] |= (((uint32_t)(cmd->pg_delay_ms) & 0xffffU));
payload[0] |= (((uint32_t)(cmd->rg_delay_ms) & 0xffffU) << 16);
payload[1] |= ((uint32_t)(cmd->pg_entry_freq_khz));
payload[2] |= ((uint32_t)(cmd->pg_exit_freq_khz));
err = (hfrp_send_cmd(hfrp, DLA_HFRP_CMD_CONFIG,
(uint8_t *) payload, payload_size, blocking));
return err;
}
int32_t nvdla_hfrp_send_cmd_get_current_freq(struct hfrp *hfrp,
bool blocking)
{
int32_t err;
/* No payload */
err = hfrp_send_cmd(hfrp, DLA_HFRP_CMD_GET_CURRENT_CLOCK_FREQ, NULL,
SZ(DLA_HFRP_CMD_GET_CURRENT_CLOCK_FREQ), blocking);
return err;
}
static void s_nvdla_hfrp_handle_response_clock_gate(struct hfrp *hfrp,
uint8_t *payload,
uint32_t payload_size)
{
uint32_t *response = (uint32_t *) payload;
/**
* Core 0:0
* MCU 1:1
* CFG 2:2
**/
hfrp->clock_gated = ((response[0]) & 0x1U);
hfrp->clock_gated = hfrp->clock_gated || ((response[0] >> 1) & 0x1U);
hfrp->clock_gated = hfrp->clock_gated || ((response[0] >> 2) & 0x1U);
}
static void s_nvdla_hfrp_handle_response_power_ctrl(struct hfrp *hfrp,
uint8_t *payload,
uint32_t payload_size)
{
uint32_t *response = (uint32_t *) payload;
/**
* MTCMOS OFF 0:0
* RAIL OFF 1:1
* MTCMOS ON 2:2
* RAIL ON 3:3
* MTCMOS DELAYED_OFF 4:4
* RAIL DELAYED_OFF 5:5
* PPS 8:15
**/
/* Gated if off or delayed off, Ungated if on. */
if (((response[0]) & 0x1U) || ((response[0] >> 4) & 0x1U))
hfrp->power_gated = true;
if (((response[0] >> 2) & 0x1U))
hfrp->power_gated = false;
if (((response[0] >> 1) & 0x1U) || ((response[0] >> 5) & 0x1U))
hfrp->rail_gated = true;
if (((response[0] >> 3) & 0x1U))
hfrp->rail_gated = false;
/* PPS is unused currently but hfrp shall be extended in the future */
}
static void s_nvdla_hfrp_handle_response_get_current_freq(struct hfrp *hfrp,
uint8_t *payload,
uint32_t payload_size)
{
uint32_t *response = (uint32_t *) payload;
/**
* FREQ 31:0
**/
hfrp->core_freq_khz = response[0];
}
void hfrp_handle_response(struct hfrp *hfrp,
uint32_t cmd,
uint8_t *payload,
uint32_t payload_size)
{
// Expects payload to be 4byte aligned. Eases out the parsing.
BUG_ON(payload_size % 4U != 0U);
switch (cmd) {
case DLA_HFRP_CMD_POWER_CONTROL: {
s_nvdla_hfrp_handle_response_power_ctrl(hfrp,
payload, payload_size);
break;
}
case DLA_HFRP_CMD_CLOCK_GATE: {
s_nvdla_hfrp_handle_response_clock_gate(hfrp,
payload, payload_size);
break;
}
case DLA_HFRP_CMD_GET_CURRENT_CLOCK_FREQ: {
s_nvdla_hfrp_handle_response_get_current_freq(hfrp,
payload, payload_size);
break;
}
default:
/* Nothing to handle */
break;
}
}

View File

@@ -0,0 +1,222 @@
// SPDX-License-Identifier: GPL-2.0-only
/* SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
*
* NVDLA HFRP Command Implementation
*/
#include "nvdla_pm_hfrp.h"
/* Commands */
#define DLA_HFRP_CMD_POWER_CONTROL 400
#define DLA_HFRP_CMD_CONFIG 401
#define DLA_HFRP_CMD_GET_CURRENT_VOLTAGE 402
#define DLA_HFRP_CMD_GET_CURRENT_POWER_DRAW 405
#define DLA_HFRP_CMD_SET_POWER_DRAW_CAP 406
#define DLA_HFRP_CMD_GET_POWER_DRAW_CAP 407
#define DLA_HFRP_CMD_SET_POWER_CONTROL_TUNING 408
#define DLA_HFRP_CMD_RESOURCE_REQUEST 409
#define DLA_HFRP_CMD_RESET 410
#define DLA_HFRP_CMD_RESET_IDLE 411
#define DLA_HFRP_CMD_SRAM_HOLD 412
#define DLA_HFRP_CMD_CLOCK_GATE 413
#define DLA_HFRP_CMD_GET_CURRENT_CLOCK_FREQ 414
#define DLA_HFRP_CMD_GET_AVG_CLOCK_FREQ 416
#define DLA_HFRP_CMD_SLCG_OVERRIDE 417
#define DLA_HFRP_CMD_GET_CURRENT_MEM_CLOCK_FREQ 418
#define DLA_HFRP_CMD_GET_MAXIMUM_MEM_CLOCK_FREQ 419
#define DLA_HFRP_CMD_GET_MAXIMUM_MEMORY_BW 420
#define DLA_HFRP_CMD_CONFIG_EMI_BW_MONITOR_CTR 421
#define DLA_HFRP_CMD_READ_EMI_BW_MONITOR_CTR 422
#define DLA_HFRP_CMD_CONFIG_SLC_BW_MONITOR_CTR 423
#define DLA_HFRP_CMD_READ_SLC_BW_MONITOR_CTR 424
#define DLA_HFRP_CMD_SYS_GET_TEMPERATURE 425
#define DLA_HFRP_CMD_SYS_GET_TEMPERATURE_LIMIT 426
#define DLA_HFRP_CMD_GET_PERF_LIMIT_REASON 427
/* Command and header buffer size */
#define SZ(cmd) cmd##_SZ
#define DLA_HFRP_CMD_POWER_CONTROL_SZ 3U
#define DLA_HFRP_CMD_CONFIG_SZ 14U
#define DLA_HFRP_CMD_GET_CURRENT_VOLTAGE_SZ 4U
#define DLA_HFRP_CMD_GET_CURRENT_POWER_DRAW_SZ 4U
#define DLA_HFRP_CMD_SET_POWER_DRAW_CAP_SZ 4U
#define DLA_HFRP_CMD_GET_POWER_DRAW_CAP_SZ 4U
#define DLA_HFRP_CMD_SET_POWER_CONTROL_TUNING_SZ 36U
#define DLA_HFRP_CMD_RESOURCE_REQUEST_SZ 8U
#define DLA_HFRP_CMD_RESET_SZ 1U
#define DLA_HFRP_CMD_RESET_IDLE_SZ 1U
#define DLA_HFRP_CMD_SRAM_HOLD_SZ 1U
#define DLA_HFRP_CMD_CLOCK_GATE_SZ 1U
#define DLA_HFRP_CMD_GET_CURRENT_CLOCK_FREQ_SZ 0U
#define DLA_HFRP_CMD_GET_AVG_CLOCK_FREQ_SZ 4U
#define DLA_HFRP_CMD_SLCG_OVERRIDE_SZ 1U
#define DLA_HFRP_CMD_GET_CURRENT_MEM_CLOCK_FREQ_SZ 4U
#define DLA_HFRP_CMD_GET_MAXIMUM_MEM_CLOCK_FREQ_SZ 4U
#define DLA_HFRP_CMD_GET_MAXIMUM_MEMORY_BW_SZ 4U
#define DLA_HFRP_CMD_CONFIG_EMI_BW_MONITOR_CTR_SZ 8U
#define DLA_HFRP_CMD_READ_EMI_BW_MONITOR_CTR_SZ 8U
#define DLA_HFRP_CMD_CONFIG_SLC_BW_MONITOR_CTR_SZ 12U
#define DLA_HFRP_CMD_READ_SLC_BW_MONITOR_CTR_SZ 8U
#define DLA_HFRP_CMD_SYS_GET_TEMPERATURE_SZ 2U
#define DLA_HFRP_CMD_SYS_GET_TEMPERATURE_LIMIT_SZ 4U
#define DLA_HFRP_CMD_GET_PERF_LIMIT_REASON_SZ 1U
int32_t nvdla_hfrp_send_cmd_power_ctrl(struct hfrp *hfrp,
struct nvdla_hfrp_cmd_power_ctrl *cmd,
bool blocking)
{
int32_t err;
uint32_t payload[(SZ(DLA_HFRP_CMD_POWER_CONTROL) >> 2) + 1U];
uint32_t payload_size;
memset(payload, 0, sizeof(payload));
payload_size = SZ(DLA_HFRP_CMD_POWER_CONTROL);
/**
* MTCMOS OFF 0:0
* RAIL OFF 1:1
* MTCMOS ON 2:2
* RAIL ON 3:3
* MTCMOS DELAYED_OFF 4:4
* RAIL DELAYED_OFF 5:5
* CLOCK OFF 6:6
* CLOCK ON 7:7
* CLOCK_DELAYED_OFF 8:8
* PPS 16:23
**/
payload[0] |= (((uint32_t)(cmd->power_off)) << 0);
payload[0] |= (((uint32_t)(cmd->rail_off)) << 1);
payload[0] |= (((uint32_t)(cmd->power_on)) << 2);
payload[0] |= (((uint32_t)(cmd->rail_on)) << 3);
payload[0] |= (((uint32_t)(cmd->power_delayed_off)) << 4);
payload[0] |= (((uint32_t)(cmd->rail_delayed_off)) << 5);
payload[0] |= (((uint32_t)(cmd->clock_off)) << 6);
payload[0] |= (((uint32_t)(cmd->clock_on)) << 7);
payload[0] |= (((uint32_t)(cmd->clock_delayed_off)) << 8);
payload[0] |= (((uint32_t)(cmd->pps) & 0xffU) << 16);
err = (hfrp_send_cmd(hfrp, DLA_HFRP_CMD_POWER_CONTROL,
(uint8_t *) payload, payload_size, blocking));
return err;
}
int32_t nvdla_hfrp_send_cmd_config(struct hfrp *hfrp,
struct nvdla_hfrp_cmd_config *cmd,
bool blocking)
{
int32_t err;
uint32_t payload[(SZ(DLA_HFRP_CMD_CONFIG) >> 2) + 1U];
uint32_t payload_size;
memset(payload, 0, sizeof(payload));
payload_size = SZ(DLA_HFRP_CMD_CONFIG);
/**
* PG_DELAY 15:0
* RG_DELAY 31:16 (15:0 byte 2)
* PG_ENTRY_FREQ 31:0 (byte 4)
* PG_EXIT_FREQ 31:0 (byte 8)
* CG_DELAY 15:0 (byte 12)
**/
payload[0] |= (((uint32_t)(cmd->pg_delay_ms) & 0xffffU));
payload[0] |= (((uint32_t)(cmd->rg_delay_ms) & 0xffffU) << 16);
payload[1] |= ((uint32_t)(cmd->pg_entry_freq_khz));
payload[2] |= ((uint32_t)(cmd->pg_exit_freq_khz));
payload[3] |= (((uint32_t)(cmd->cg_delay_ms) & 0xffffU));
err = (hfrp_send_cmd(hfrp, DLA_HFRP_CMD_CONFIG,
(uint8_t *) payload, payload_size, blocking));
return err;
}
int32_t nvdla_hfrp_send_cmd_get_current_freq(struct hfrp *hfrp,
bool blocking)
{
int32_t err;
/* No payload */
err = hfrp_send_cmd(hfrp, DLA_HFRP_CMD_GET_CURRENT_CLOCK_FREQ, NULL,
SZ(DLA_HFRP_CMD_GET_CURRENT_CLOCK_FREQ), blocking);
return err;
}
static void s_nvdla_hfrp_handle_response_power_ctrl(struct hfrp *hfrp,
uint8_t *payload,
uint32_t payload_size)
{
uint32_t *response = (uint32_t *) payload;
/**
* MTCMOS OFF 0:0
* RAIL OFF 1:1
* MTCMOS ON 2:2
* RAIL ON 3:3
* MTCMOS DELAYED_OFF 4:4
* RAIL DELAYED_OFF 5:5
* CLOCK OFF 6:6
* CLOCK ON 7:7
* CLOCK_DELAYED_OFF 8:8
* PPS 16:23
**/
/* Gated if off or delayed off, Ungated if on. */
if (((response[0]) & 0x1U) || ((response[0] >> 4) & 0x1U))
hfrp->power_gated = true;
if (((response[0] >> 2) & 0x1U))
hfrp->power_gated = false;
if (((response[0] >> 1) & 0x1U) || ((response[0] >> 5) & 0x1U))
hfrp->rail_gated = true;
if (((response[0] >> 3) & 0x1U))
hfrp->rail_gated = false;
if (((response[0] >> 6) & 0x1U) || ((response[0] >> 8) & 0x1U))
hfrp->clock_gated = true;
if (((response[0] >> 7) & 0x1U))
hfrp->clock_gated = false;
/* PPS is unused currently but hfrp shall be extended in the future */
}
static void s_nvdla_hfrp_handle_response_get_current_freq(struct hfrp *hfrp,
uint8_t *payload,
uint32_t payload_size)
{
uint32_t *response = (uint32_t *) payload;
/**
* FREQ 31:0
**/
hfrp->core_freq_khz = response[0];
}
void hfrp_handle_response(struct hfrp *hfrp,
uint32_t cmd,
uint8_t *payload,
uint32_t payload_size)
{
// Expects payload to be 4byte aligned. Eases out the parsing.
BUG_ON(payload_size % 4U != 0U);
switch (cmd) {
case DLA_HFRP_CMD_POWER_CONTROL: {
s_nvdla_hfrp_handle_response_power_ctrl(hfrp,
payload, payload_size);
break;
}
case DLA_HFRP_CMD_GET_CURRENT_CLOCK_FREQ: {
s_nvdla_hfrp_handle_response_get_current_freq(hfrp,
payload, payload_size);
break;
}
default:
/* Nothing to handle */
break;
}
}

View File

@@ -0,0 +1,180 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
*
* Function naming determines intended use:
*
* <x>_r(void) : Returns the offset for register <x>.
*
* <x>_o(void) : Returns the offset for element <x>.
*
* <x>_w(void) : Returns the word offset for word (4 byte) element <x>.
*
* <x>_<y>_s(void) : Returns size of field <y> of register <x> in bits.
*
* <x>_<y>_f(u32 v) : Returns a value based on 'v' which has been shifted
* and masked to place it at field <y> of register <x>. This value
* can be |'d with others to produce a full register value for
* register <x>.
*
* <x>_<y>_m(void) : Returns a mask for field <y> of register <x>. This
* value can be ~'d and then &'d to clear the value of field <y> for
* register <x>.
*
* <x>_<y>_<z>_f(void) : Returns the constant value <z> after being shifted
* to place it at field <y> of register <x>. This value can be |'d
* with others to produce a full register value for <x>.
*
* <x>_<y>_v(u32 r) : Returns the value of field <y> from a full register
* <x> value 'r' after being shifted to place its LSB at bit 0.
* This value is suitable for direct comparison with other unshifted
* values appropriate for use in field <y> of register <x>.
*
* <x>_<y>_<z>_v(void) : Returns the constant value for <z> defined for
* field <y> of register <x>. This value is suitable for direct
* comparison with unshifted values appropriate for use in field <y>
* of register <x>.
*/
#ifndef __NVDLA_PM_HFRP_REG_H_
#define __NVDLA_PM_HFRP_REG_H_
static inline uint32_t hfrp_mailbox0_mode_r(void)
{
/* MAILBOX0_MODE */
return 0x00000004U;
}
static inline uint32_t hfrp_mailbox0_mode_circular_v(void)
{
/* MAILBOX0_MODE_CIRCULAR (0:31) */
return 0x00000001U;
}
static inline uint32_t hfrp_irq_in_set_r(void)
{
/* IRQ_IN_SET */
return 0x00000100U;
}
static inline uint32_t hfrp_irq_in_set_doorbell_f(uint32_t v)
{
/* IRQ_IN_SET_DOOR_BELL (1:1) */
return ((v & 0x1) << 1);
}
static inline uint32_t hfrp_irq_out_set_r(void)
{
/* IRQ_OUT_SET */
return 0x00000104U;
}
static inline uint32_t hfrp_irq_out_set_doorbell_v(uint32_t r)
{
/* IRQ_OUT_SET_DOOR_BELL (1:1) */
return (r >> 1) & 0x1;
}
static inline uint32_t hfrp_irq_out_set_reset_v(uint32_t r)
{
/* IRQ_OUT_SET_RESET (0:0) */
return r & 0x1;
}
static inline uint32_t hfrp_irq_in_clr_r(void)
{
/* IRQ_IN_CLR */
return 0x00000108U;
}
static inline uint32_t hfrp_irq_out_clr_r(void)
{
/* IRQ_OUT_CLR */
return 0x0000010cU;
}
static inline uint32_t hfrp_irq_out_clr_doorbell_f(uint32_t v)
{
/* IRQ_OUT_CLR_DOOR_BELL (1:1) */
return ((v & 0x1) << 1);
}
static inline uint32_t hfrp_irq_out_clr_reset_f(uint32_t v)
{
/* IRQ_OUT_CLR_RESET (0:0) */
return ((v & 0x1) << 1);
}
static inline uint32_t hfrp_buffer_clientoffs_r(void)
{
/* BUFFER_CLIENTOFFS */
return 0x00000110U;
}
static inline uint32_t hfrp_buffer_clientoffs_cmd_head_m(void)
{
/* BUFFER_CLIENTOFFS_CMD_HEAD 0:7 */
return 0xffU;
}
static inline uint32_t hfrp_buffer_clientoffs_resp_tail_m(void)
{
/* BUFFER_CLIENTOFFS_RESPONSE_TAIL 8:15 */
return (0xffU << 8);
}
static inline uint32_t hfrp_buffer_clientoffs_cmd_head_f(uint32_t v)
{
/* BUFFER_CLIENTOFFS_CMD_HEAD 0:7 */
return (v & 0xffU);
}
static inline uint32_t hfrp_buffer_clientoffs_cmd_head_v(uint32_t r)
{
/* BUFFER_CLIENTOFFS_CMD_HEAD 0:7 */
return (r & 0xffU);
}
static inline uint32_t hfrp_buffer_clientoffs_resp_tail_f(uint32_t v)
{
/* BUFFER_CLIENTOFFS_RESPONSE_TAIL 8:15 */
return ((v & 0xffU) << 8);
}
static inline uint32_t hfrp_buffer_clientoffs_resp_tail_v(uint32_t r)
{
/* BUFFER_CLIENTOFFS_RESPONSE_TAIL 8:15 */
return ((r >> 8) & 0xffU);
}
static inline uint32_t hfrp_buffer_serveroffs_r(void)
{
/* BUFFER_SERVEROFFS */
return 0x00000188U;
}
static inline uint32_t hfrp_buffer_serveroffs_resp_head_v(uint32_t r)
{
/* BUFFER_SERVEROFFS_RESPONSE_HEAD 0:7 */
return (r & 0xffU);
}
static inline uint32_t hfrp_buffer_serveroffs_cmd_tail_v(uint32_t r)
{
/* BUFFER_SERVEROFFS_CMD_TAIL 8:15 */
return ((r >> 8) & 0xffU);
}
static inline uint32_t hfrp_buffer_cmd_r(uint32_t index)
{
/* BUFFER_CMD 116B */
return (0x00000114U + index);
}
static inline uint32_t hfrp_buffer_resp_r(uint32_t index)
{
/* BUFFER_RESPONSE 116B */
return (0x0000018cU + index);
}
#endif /* __NVDLA_PM_HFRP_REG_H_ */

View File

@@ -0,0 +1,136 @@
// SPDX-License-Identifier: GPL-2.0-only
/* SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
*
* NVDLA Power Management - stub implementation
*/
#include "../nvdla_pm.h"
int32_t nvdla_pm_init(struct platform_device *pdev)
{
(void) pdev;
return -1;
}
void nvdla_pm_deinit(struct platform_device *pdev)
{
(void) pdev;
}
int32_t nvdla_pm_rail_gate(struct platform_device *pdev,
uint32_t timeout_us,
bool blocking)
{
(void) pdev;
(void) timeout_us;
(void) blocking;
return -1;
}
int32_t nvdla_pm_rail_ungate(struct platform_device *pdev)
{
(void) pdev;
return -1;
}
int32_t nvdla_pm_rail_is_gated(struct platform_device *pdev,
bool *gated)
{
(void) pdev;
(void) gated;
return -1;
}
int32_t nvdla_pm_power_gate(struct platform_device *pdev,
uint32_t timeout_us,
bool blocking)
{
(void) pdev;
(void) timeout_us;
(void) blocking;
return -1;
}
int32_t nvdla_pm_power_ungate(struct platform_device *pdev)
{
(void) pdev;
return -1;
}
int32_t nvdla_pm_power_is_gated(struct platform_device *pdev,
bool *gated)
{
(void) pdev;
(void) gated;
return -1;
}
int32_t nvdla_pm_clock_gate(struct platform_device *pdev,
uint32_t timeout_us,
bool blocking)
{
(void) pdev;
(void) timeout_us;
(void) blocking;
return -1;
}
int32_t nvdla_pm_clock_ungate(struct platform_device *pdev)
{
(void) pdev;
return -1;
}
int32_t nvdla_pm_clock_is_gated(struct platform_device *pdev,
bool *gated)
{
(void) pdev;
(void) gated;
return -1;
}
int32_t nvdla_pm_clock_set_mcu_freq(struct platform_device *pdev,
uint32_t freq_khz)
{
(void) pdev;
(void) freq_khz;
return -1;
}
int32_t nvdla_pm_clock_get_mcu_freq(struct platform_device *pdev,
uint32_t *freq_khz)
{
(void) pdev;
(void) freq_khz;
return -1;
}
int32_t nvdla_pm_clock_set_core_freq(struct platform_device *pdev,
uint32_t freq_khz)
{
(void) pdev;
(void) freq_khz;
return -1;
}
int32_t nvdla_pm_clock_get_core_freq(struct platform_device *pdev,
uint32_t *freq_khz)
{
(void) pdev;
(void) freq_khz;
return -1;
}