* [RFC PATCH v3 0/6] accel/rocket: RK3576 NPU (RKNN) enablement
@ 2026-07-31 4:35 Jiaxing Hu
2026-07-31 4:35 ` [RFC PATCH v3 1/6] dt-bindings: npu: rockchip: add rockchip,rk3576-rknn-core Jiaxing Hu
` (5 more replies)
0 siblings, 6 replies; 11+ messages in thread
From: Jiaxing Hu @ 2026-07-31 4:35 UTC (permalink / raw)
To: tomeu, heiko, robh, krzk+dt, conor+dt, ulfh, p.zabel, ogabbay
Cc: chaoyi.chen, alchark, royalnet026, dri-devel, linux-rockchip,
linux-pm, devicetree, linux-arm-kernel, linux-kernel, Jiaxing Hu
This adds RK3576 NPU support to accel/rocket. Still RFC: the NPU brings up
and runs, but inference is not correct yet. The open bug moved a long way
since v2, so it is worth describing.
Tested on a Radxa ROCK 4D, on next-20260730.
The two iommu patches from v2 are gone from this series. Will picked them
up and they are in linux-next as 841363ebb508 and b10d5920cafa.
What is still wrong
===================
A single convolution is byte exact. Running that same convolution again,
six times in one power session, is byte exact every time. What fails is
running a different one after it: the second configuration loaded into the
block computes nothing and writes out a zero point surface, while the one
already resident keeps working. Going back to it is byte exact again, and
the same holds with the two models swapped, so it is not about which model
is which.
The register command list is identical in both positions, byte for byte, so
it is not what we program. A full NPU reset clears it. With one before every
op, per layer input and weight fetch return at the graph's real shapes and
the DPU writes back, but what lands is still zero point. So dispatch, DMA
and write back are all fine, and the multiply accumulate is what produces
nothing.
That is as far as I have got. If anyone knows the RK3576 NPU internals, the
question is what a second configuration load has to do that the first does
not.
Changes in v3
=============
* dt-bindings: reject sram-supply on non-rk3588 via an allOf else, and
drop the commit message paragraph explaining the schema (Krzysztof)
* pmdomain: rename DOMAIN_M_O_R_G to DOMAIN_M_O_R_G_W rather than
extending it, since the suffixes name the fields it sets and RK3576 is
its only user (Heiko). Drop a stray whitespace hunk.
* accel/rocket: v2 said PC_DONE could not be routed to the GIC and polled
it instead. That reason was wrong, and the bits it polled turn out to be
permanently latched, so the poll condition was always already true.
RK3576 now arms the same DPU completion as RK3588 and samples
INTERRUPT_RAW_STATUS for it, because on this SoC the interrupt does not
reach the GIC. Verified on hardware: without the sampling every job sits
until the scheduler timeout (525 ms per job, 195 timeouts in one run);
with it, jobs finish in 10.8 ms and six repeats are byte exact.
* iommu: both patches dropped, now in linux-next.
Note on overlap: patch 4 sets core->clks[].id for all six clocks. Igor
Paunovic has a standalone fix for the first four in flight
(20260729092939.118779-1-royalnet026@gmail.com). Once that lands this hunk
reduces to the two CBUF entries.
I used Claude Opus 4.8 to help prepare this series and its diffs.
Jiaxing Hu (6):
dt-bindings: npu: rockchip: add rockchip,rk3576-rknn-core
pmdomain/rockchip: add optional per-domain power-on settle delay
pmdomain/rockchip: cycle optional power-domain resets on power-on
accel/rocket: add RK3576 NPU (RKNN) support
arm64: dts: rockchip: rk3576: add NPU (RKNN) nodes
arm64: dts: rockchip: rk3576-rock-4d: enable NPU
.../npu/rockchip,rk3588-rknn-core.yaml | 15 +++-
.../boot/dts/rockchip/rk3576-rock-4d.dts | 16 ++++
arch/arm64/boot/dts/rockchip/rk3576.dtsi | 78 ++++++++++++++++++-
drivers/accel/rocket/rocket_core.c | 26 ++++++-
drivers/accel/rocket/rocket_core.h | 17 +++-
drivers/accel/rocket/rocket_device.c | 4 +
drivers/accel/rocket/rocket_drv.c | 22 +++++-
drivers/accel/rocket/rocket_job.c | 55 +++++++++++++
drivers/pmdomain/rockchip/pm-domains.c | 71 +++++++++++------
9 files changed, 273 insertions(+), 31 deletions(-)
--
2.43.0
^ permalink raw reply [flat|nested] 11+ messages in thread
* [RFC PATCH v3 1/6] dt-bindings: npu: rockchip: add rockchip,rk3576-rknn-core
2026-07-31 4:35 [RFC PATCH v3 0/6] accel/rocket: RK3576 NPU (RKNN) enablement Jiaxing Hu
@ 2026-07-31 4:35 ` Jiaxing Hu
2026-07-31 4:35 ` [RFC PATCH v3 2/6] pmdomain/rockchip: add optional per-domain power-on settle delay Jiaxing Hu
` (4 subsequent siblings)
5 siblings, 0 replies; 11+ messages in thread
From: Jiaxing Hu @ 2026-07-31 4:35 UTC (permalink / raw)
To: tomeu, heiko, robh, krzk+dt, conor+dt, ulfh, p.zabel, ogabbay
Cc: chaoyi.chen, alchark, royalnet026, dri-devel, linux-rockchip,
linux-pm, devicetree, linux-arm-kernel, linux-kernel, Jiaxing Hu
The RK3576 NPU has two cores of the same RKNN block the RK3588 binding
already describes, but no NPU SRAM supply, so sram-supply is required
for rockchip,rk3588-rknn-core only and rejected otherwise.
Signed-off-by: Jiaxing Hu <gahing@gahingwoo.com>
---
.../bindings/npu/rockchip,rk3588-rknn-core.yaml | 15 ++++++++++++++-
1 file changed, 14 insertions(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/npu/rockchip,rk3588-rknn-core.yaml b/Documentation/devicetree/bindings/npu/rockchip,rk3588-rknn-core.yaml
index caca2a490..0a7baac24 100644
--- a/Documentation/devicetree/bindings/npu/rockchip,rk3588-rknn-core.yaml
+++ b/Documentation/devicetree/bindings/npu/rockchip,rk3588-rknn-core.yaml
@@ -21,6 +21,7 @@ properties:
compatible:
enum:
+ - rockchip,rk3576-rknn-core
- rockchip,rk3588-rknn-core
reg:
@@ -75,7 +76,19 @@ required:
- resets
- reset-names
- npu-supply
- - sram-supply
+
+allOf:
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: rockchip,rk3588-rknn-core
+ then:
+ required:
+ - sram-supply
+ else:
+ properties:
+ sram-supply: false
additionalProperties: false
--
2.43.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [RFC PATCH v3 2/6] pmdomain/rockchip: add optional per-domain power-on settle delay
2026-07-31 4:35 [RFC PATCH v3 0/6] accel/rocket: RK3576 NPU (RKNN) enablement Jiaxing Hu
2026-07-31 4:35 ` [RFC PATCH v3 1/6] dt-bindings: npu: rockchip: add rockchip,rk3576-rknn-core Jiaxing Hu
@ 2026-07-31 4:35 ` Jiaxing Hu
2026-07-31 4:35 ` [RFC PATCH v3 3/6] pmdomain/rockchip: cycle optional power-domain resets on power-on Jiaxing Hu
` (3 subsequent siblings)
5 siblings, 0 replies; 11+ messages in thread
From: Jiaxing Hu @ 2026-07-31 4:35 UTC (permalink / raw)
To: tomeu, heiko, robh, krzk+dt, conor+dt, ulfh, p.zabel, ogabbay
Cc: chaoyi.chen, alchark, royalnet026, dri-devel, linux-rockchip,
linux-pm, devicetree, linux-arm-kernel, linux-kernel, Jiaxing Hu
The RK3576 NPU domains need a short settle time after the idle request
is released before the QoS registers behind the domain answer. Without
it rockchip_pmu_restore_qos() reads back zeroes, and the NPU throws an
async SError on the first cold power-on.
Give rockchip_domain_info an optional delay_us and wait for it between
releasing idle and restoring QoS. Rename DOMAIN_M_O_R_G to
DOMAIN_M_O_R_G_W, since the suffixes name the fields the macro sets and
this one now also carries a wakeup delay; RK3576 is its only user, so
the old spelling is not kept around.
Signed-off-by: Jiaxing Hu <gahing@gahingwoo.com>
---
drivers/pmdomain/rockchip/pm-domains.c | 52 +++++++++++++++-----------
1 file changed, 30 insertions(+), 22 deletions(-)
diff --git a/drivers/pmdomain/rockchip/pm-domains.c b/drivers/pmdomain/rockchip/pm-domains.c
index ba66ae719..e1857f878 100644
--- a/drivers/pmdomain/rockchip/pm-domains.c
+++ b/drivers/pmdomain/rockchip/pm-domains.c
@@ -18,6 +18,7 @@
#include <linux/of_address.h>
#include <linux/of_clk.h>
#include <linux/clk.h>
+#include <linux/delay.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/mfd/syscon.h>
@@ -59,6 +60,7 @@ struct rockchip_domain_info {
u32 pwr_offset;
u32 mem_offset;
u32 req_offset;
+ u32 delay_us;
};
struct rockchip_pmu_info {
@@ -185,7 +187,7 @@ struct rockchip_pmu {
.need_regulator = regulator, \
}
-#define DOMAIN_M_O_R_G(_name, p_offset, pwr, status, m_offset, m_status, r_status, r_offset, req, idle, ack, g_mask, wakeup) \
+#define DOMAIN_M_O_R_G_W(_name, p_offset, pwr, status, m_offset, m_status, r_status, r_offset, req, idle, ack, g_mask, delay, wakeup) \
{ \
.name = _name, \
.pwr_offset = p_offset, \
@@ -200,6 +202,7 @@ struct rockchip_pmu {
.req_mask = (req), \
.idle_mask = (idle), \
.clk_ungate_mask = (g_mask), \
+ .delay_us = (delay), \
.ack_mask = (ack), \
.active_wakeup = wakeup, \
}
@@ -258,8 +261,8 @@ struct rockchip_pmu {
#define DOMAIN_RK3568(name, pwr, req, wakeup, regulator) \
DOMAIN_M_R(name, pwr, pwr, req, req, req, wakeup, regulator)
-#define DOMAIN_RK3576(name, p_offset, pwr, status, r_status, r_offset, req, idle, g_mask, wakeup) \
- DOMAIN_M_O_R_G(name, p_offset, pwr, status, 0, r_status, r_status, r_offset, req, idle, idle, g_mask, wakeup)
+#define DOMAIN_RK3576(name, p_offset, pwr, status, r_status, r_offset, req, idle, g_mask, delay, wakeup) \
+ DOMAIN_M_O_R_G_W(name, p_offset, pwr, status, 0, r_status, r_status, r_offset, req, idle, idle, g_mask, delay, wakeup)
/*
* Dynamic Memory Controller may need to coordinate with us -- see
@@ -681,6 +684,10 @@ static int rockchip_pd_power(struct rockchip_pm_domain *pd, bool power_on)
if (ret < 0)
goto out;
+ /* Some domains need to settle before the QoS registers answer. */
+ if (pd->info->delay_us)
+ udelay(pd->info->delay_us);
+
rockchip_pmu_restore_qos(pd);
}
@@ -1300,25 +1307,26 @@ static const struct rockchip_domain_info rk3568_pm_domains[] = {
};
static const struct rockchip_domain_info rk3576_pm_domains[] = {
- [RK3576_PD_NPU] = DOMAIN_RK3576("npu", 0x0, BIT(0), BIT(0), 0, 0x0, 0, 0, 0, false),
- [RK3576_PD_NVM] = DOMAIN_RK3576("nvm", 0x0, BIT(6), 0, BIT(6), 0x4, BIT(2), BIT(18), BIT(2), false),
- [RK3576_PD_SDGMAC] = DOMAIN_RK3576("sdgmac", 0x0, BIT(7), 0, BIT(7), 0x4, BIT(1), BIT(17), 0x6, false),
- [RK3576_PD_AUDIO] = DOMAIN_RK3576("audio", 0x0, BIT(8), 0, BIT(8), 0x4, BIT(0), BIT(16), BIT(0), false),
- [RK3576_PD_PHP] = DOMAIN_RK3576("php", 0x0, BIT(9), 0, BIT(9), 0x0, BIT(15), BIT(15), BIT(15), false),
- [RK3576_PD_SUBPHP] = DOMAIN_RK3576("subphp", 0x0, BIT(10), 0, BIT(10), 0x0, 0, 0, 0, false),
- [RK3576_PD_VOP] = DOMAIN_RK3576("vop", 0x0, BIT(11), 0, BIT(11), 0x0, 0x6000, 0x6000, 0x6000, false),
- [RK3576_PD_VO1] = DOMAIN_RK3576("vo1", 0x0, BIT(14), 0, BIT(14), 0x0, BIT(12), BIT(12), 0x7000, false),
- [RK3576_PD_VO0] = DOMAIN_RK3576("vo0", 0x0, BIT(15), 0, BIT(15), 0x0, BIT(11), BIT(11), 0x6800, false),
- [RK3576_PD_USB] = DOMAIN_RK3576("usb", 0x4, BIT(0), 0, BIT(16), 0x0, BIT(10), BIT(10), 0x6400, true),
- [RK3576_PD_VI] = DOMAIN_RK3576("vi", 0x4, BIT(1), 0, BIT(17), 0x0, BIT(9), BIT(9), BIT(9), false),
- [RK3576_PD_VEPU0] = DOMAIN_RK3576("vepu0", 0x4, BIT(2), 0, BIT(18), 0x0, BIT(7), BIT(7), 0x280, false),
- [RK3576_PD_VEPU1] = DOMAIN_RK3576("vepu1", 0x4, BIT(3), 0, BIT(19), 0x0, BIT(8), BIT(8), BIT(8), false),
- [RK3576_PD_VDEC] = DOMAIN_RK3576("vdec", 0x4, BIT(4), 0, BIT(20), 0x0, BIT(6), BIT(6), BIT(6), false),
- [RK3576_PD_VPU] = DOMAIN_RK3576("vpu", 0x4, BIT(5), 0, BIT(21), 0x0, BIT(5), BIT(5), BIT(5), false),
- [RK3576_PD_NPUTOP] = DOMAIN_RK3576("nputop", 0x4, BIT(6), 0, BIT(22), 0x0, 0x18, 0x18, 0x18, false),
- [RK3576_PD_NPU0] = DOMAIN_RK3576("npu0", 0x4, BIT(7), 0, BIT(23), 0x0, BIT(1), BIT(1), 0x1a, false),
- [RK3576_PD_NPU1] = DOMAIN_RK3576("npu1", 0x4, BIT(8), 0, BIT(24), 0x0, BIT(2), BIT(2), 0x1c, false),
- [RK3576_PD_GPU] = DOMAIN_RK3576("gpu", 0x4, BIT(9), 0, BIT(25), 0x0, BIT(0), BIT(0), BIT(0), false),
+ /* name p_offset pwr status r_status r_offset req idle g_mask delay wakeup */
+ [RK3576_PD_NPU] = DOMAIN_RK3576("npu", 0x0, BIT(0), BIT(0), 0, 0x0, 0, 0, 0, 0, false),
+ [RK3576_PD_NVM] = DOMAIN_RK3576("nvm", 0x0, BIT(6), 0, BIT(6), 0x4, BIT(2), BIT(18), BIT(2), 0, false),
+ [RK3576_PD_SDGMAC] = DOMAIN_RK3576("sdgmac", 0x0, BIT(7), 0, BIT(7), 0x4, BIT(1), BIT(17), 0x6, 0, false),
+ [RK3576_PD_AUDIO] = DOMAIN_RK3576("audio", 0x0, BIT(8), 0, BIT(8), 0x4, BIT(0), BIT(16), BIT(0), 0, false),
+ [RK3576_PD_PHP] = DOMAIN_RK3576("php", 0x0, BIT(9), 0, BIT(9), 0x0, BIT(15), BIT(15), BIT(15), 0, false),
+ [RK3576_PD_SUBPHP] = DOMAIN_RK3576("subphp", 0x0, BIT(10), 0, BIT(10), 0x0, 0, 0, 0, 0, false),
+ [RK3576_PD_VOP] = DOMAIN_RK3576("vop", 0x0, BIT(11), 0, BIT(11), 0x0, 0x6000, 0x6000, 0x6000, 0, false),
+ [RK3576_PD_VO1] = DOMAIN_RK3576("vo1", 0x0, BIT(14), 0, BIT(14), 0x0, BIT(12), BIT(12), 0x7000, 0, false),
+ [RK3576_PD_VO0] = DOMAIN_RK3576("vo0", 0x0, BIT(15), 0, BIT(15), 0x0, BIT(11), BIT(11), 0x6800, 0, false),
+ [RK3576_PD_USB] = DOMAIN_RK3576("usb", 0x4, BIT(0), 0, BIT(16), 0x0, BIT(10), BIT(10), 0x6400, 0, true),
+ [RK3576_PD_VI] = DOMAIN_RK3576("vi", 0x4, BIT(1), 0, BIT(17), 0x0, BIT(9), BIT(9), BIT(9), 0, false),
+ [RK3576_PD_VEPU0] = DOMAIN_RK3576("vepu0", 0x4, BIT(2), 0, BIT(18), 0x0, BIT(7), BIT(7), 0x280, 0, false),
+ [RK3576_PD_VEPU1] = DOMAIN_RK3576("vepu1", 0x4, BIT(3), 0, BIT(19), 0x0, BIT(8), BIT(8), BIT(8), 0, false),
+ [RK3576_PD_VDEC] = DOMAIN_RK3576("vdec", 0x4, BIT(4), 0, BIT(20), 0x0, BIT(6), BIT(6), BIT(6), 0, false),
+ [RK3576_PD_VPU] = DOMAIN_RK3576("vpu", 0x4, BIT(5), 0, BIT(21), 0x0, BIT(5), BIT(5), BIT(5), 0, false),
+ [RK3576_PD_NPUTOP] = DOMAIN_RK3576("nputop", 0x4, BIT(6), 0, BIT(22), 0x0, 0x18, 0x18, 0x18, 15, false),
+ [RK3576_PD_NPU0] = DOMAIN_RK3576("npu0", 0x4, BIT(7), 0, BIT(23), 0x0, BIT(1), BIT(1), 0x1a, 15, false),
+ [RK3576_PD_NPU1] = DOMAIN_RK3576("npu1", 0x4, BIT(8), 0, BIT(24), 0x0, BIT(2), BIT(2), 0x1c, 15, false),
+ [RK3576_PD_GPU] = DOMAIN_RK3576("gpu", 0x4, BIT(9), 0, BIT(25), 0x0, BIT(0), BIT(0), BIT(0), 0, false),
};
static const struct rockchip_domain_info rk3588_pm_domains[] = {
--
2.43.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [RFC PATCH v3 3/6] pmdomain/rockchip: cycle optional power-domain resets on power-on
2026-07-31 4:35 [RFC PATCH v3 0/6] accel/rocket: RK3576 NPU (RKNN) enablement Jiaxing Hu
2026-07-31 4:35 ` [RFC PATCH v3 1/6] dt-bindings: npu: rockchip: add rockchip,rk3576-rknn-core Jiaxing Hu
2026-07-31 4:35 ` [RFC PATCH v3 2/6] pmdomain/rockchip: add optional per-domain power-on settle delay Jiaxing Hu
@ 2026-07-31 4:35 ` Jiaxing Hu
2026-07-31 4:50 ` sashiko-bot
2026-07-31 4:35 ` [RFC PATCH v3 4/6] accel/rocket: add RK3576 NPU (RKNN) support Jiaxing Hu
` (2 subsequent siblings)
5 siblings, 1 reply; 11+ messages in thread
From: Jiaxing Hu @ 2026-07-31 4:35 UTC (permalink / raw)
To: tomeu, heiko, robh, krzk+dt, conor+dt, ulfh, p.zabel, ogabbay
Cc: chaoyi.chen, alchark, royalnet026, dri-devel, linux-rockchip,
linux-pm, devicetree, linux-arm-kernel, linux-kernel, Jiaxing Hu
Some Rockchip domains come out of power-on with their bus interface in
an undefined state. On the RK3576 NPU this shows up as a hang on the
first access after a cold power-on; the vendor kernel cycles the
domain's resets at this point and that clears it.
Take the domain node's resets if it has any, and pulse them between
releasing idle and restoring QoS. The resets are optional, so domains
that do not declare any behave exactly as before.
Signed-off-by: Jiaxing Hu <gahing@gahingwoo.com>
---
drivers/pmdomain/rockchip/pm-domains.c | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/drivers/pmdomain/rockchip/pm-domains.c b/drivers/pmdomain/rockchip/pm-domains.c
index e1857f878..82051f600 100644
--- a/drivers/pmdomain/rockchip/pm-domains.c
+++ b/drivers/pmdomain/rockchip/pm-domains.c
@@ -19,6 +19,7 @@
#include <linux/of_clk.h>
#include <linux/clk.h>
#include <linux/delay.h>
+#include <linux/reset.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/mfd/syscon.h>
@@ -103,6 +104,7 @@ struct rockchip_pm_domain {
struct clk_bulk_data *clks;
struct device_node *node;
struct regulator *supply;
+ struct reset_control *resets;
};
struct rockchip_pmu {
@@ -688,6 +690,13 @@ static int rockchip_pd_power(struct rockchip_pm_domain *pd, bool power_on)
if (pd->info->delay_us)
udelay(pd->info->delay_us);
+ /* Optional: some domains need their resets cycled after power-on. */
+ if (pd->resets) {
+ reset_control_assert(pd->resets);
+ udelay(10);
+ reset_control_deassert(pd->resets);
+ }
+
rockchip_pmu_restore_qos(pd);
}
@@ -857,6 +866,14 @@ static int rockchip_pm_add_one_domain(struct rockchip_pmu *pmu,
if (error)
goto err_put_clocks;
+ pd->resets = of_reset_control_array_get_optional_exclusive(node);
+ if (IS_ERR(pd->resets)) {
+ error = PTR_ERR(pd->resets);
+ dev_err(pmu->dev, "%pOFn: failed to get resets: %d\n", node, error);
+ pd->resets = NULL;
+ goto err_unprepare_clocks;
+ }
+
pd->num_qos = of_count_phandle_with_args(node, "pm_qos",
NULL);
@@ -927,6 +944,7 @@ static int rockchip_pm_add_one_domain(struct rockchip_pmu *pmu,
clk_bulk_unprepare(pd->num_clks, pd->clks);
err_put_clocks:
clk_bulk_put(pd->num_clks, pd->clks);
+ reset_control_put(pd->resets);
return error;
}
@@ -945,6 +963,7 @@ static void rockchip_pm_remove_one_domain(struct rockchip_pm_domain *pd)
clk_bulk_unprepare(pd->num_clks, pd->clks);
clk_bulk_put(pd->num_clks, pd->clks);
+ reset_control_put(pd->resets);
/* protect the zeroing of pm->num_clks */
mutex_lock(&pd->pmu->mutex);
--
2.43.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [RFC PATCH v3 4/6] accel/rocket: add RK3576 NPU (RKNN) support
2026-07-31 4:35 [RFC PATCH v3 0/6] accel/rocket: RK3576 NPU (RKNN) enablement Jiaxing Hu
` (2 preceding siblings ...)
2026-07-31 4:35 ` [RFC PATCH v3 3/6] pmdomain/rockchip: cycle optional power-domain resets on power-on Jiaxing Hu
@ 2026-07-31 4:35 ` Jiaxing Hu
2026-07-31 4:54 ` sashiko-bot
2026-07-31 4:35 ` [RFC PATCH v3 5/6] arm64: dts: rockchip: rk3576: add NPU (RKNN) nodes Jiaxing Hu
2026-07-31 4:35 ` [RFC PATCH v3 6/6] arm64: dts: rockchip: rk3576-rock-4d: enable NPU Jiaxing Hu
5 siblings, 1 reply; 11+ messages in thread
From: Jiaxing Hu @ 2026-07-31 4:35 UTC (permalink / raw)
To: tomeu, heiko, robh, krzk+dt, conor+dt, ulfh, p.zabel, ogabbay
Cc: chaoyi.chen, alchark, royalnet026, dri-devel, linux-rockchip,
linux-pm, devicetree, linux-arm-kernel, linux-kernel, Jiaxing Hu
The RK3576 carries the same RKNN block as the RK3588, with two cores
instead of three and a few platform differences:
- the CBUF (convolution buffer) has its own clock domain, so the core
needs six clocks rather than four;
- the BIU reset moved into the power domain, leaving one reset here;
- the NPU spans two power domains, and a device with more than one is
skipped by the driver-core single-domain auto-attach, so the list has
to be attached explicitly;
- the DPU completion interrupt is armed exactly as on RK3588 but never
reaches the GIC. The completion is visible in INTERRUPT_RAW_STATUS,
so sample that from an hrtimer rather than wait for an interrupt that
does not come. The interrupt stays armed, so if it ever does arrive
the normal handler finalises the job first.
Select all of that from of_device_id match data so the RK3588 path keeps
its existing counts and behaviour unchanged.
Signed-off-by: Jiaxing Hu <gahing@gahingwoo.com>
---
drivers/accel/rocket/rocket_core.c | 26 ++++++++++++-
drivers/accel/rocket/rocket_core.h | 17 ++++++++-
drivers/accel/rocket/rocket_device.c | 4 ++
drivers/accel/rocket/rocket_drv.c | 22 +++++++++--
drivers/accel/rocket/rocket_job.c | 55 ++++++++++++++++++++++++++++
5 files changed, 118 insertions(+), 6 deletions(-)
diff --git a/drivers/accel/rocket/rocket_core.c b/drivers/accel/rocket/rocket_core.c
index b3b2fa9ba..e08288c8c 100644
--- a/drivers/accel/rocket/rocket_core.c
+++ b/drivers/accel/rocket/rocket_core.c
@@ -8,6 +8,7 @@
#include <linux/err.h>
#include <linux/iommu.h>
#include <linux/platform_device.h>
+#include <linux/pm_domain.h>
#include <linux/pm_runtime.h>
#include <linux/reset.h>
@@ -21,14 +22,22 @@ int rocket_core_init(struct rocket_core *core)
u32 version;
int err = 0;
+ /* RK3576 moves the BIU reset into its power domain and takes only srst_a. */
core->resets[0].id = "srst_a";
core->resets[1].id = "srst_h";
- err = devm_reset_control_bulk_get_exclusive(&pdev->dev, ARRAY_SIZE(core->resets),
+ err = devm_reset_control_bulk_get_exclusive(&pdev->dev, core->soc->num_resets,
core->resets);
if (err)
return dev_err_probe(dev, err, "failed to get resets for core %d\n", core->index);
- err = devm_clk_bulk_get(dev, ARRAY_SIZE(core->clks), core->clks);
+ core->clks[0].id = "aclk";
+ core->clks[1].id = "hclk";
+ core->clks[2].id = "npu";
+ core->clks[3].id = "pclk";
+ /* RK3576 clocks the CBUF separately; the compute path stalls without these. */
+ core->clks[4].id = "aclk_cbuf";
+ core->clks[5].id = "hclk_cbuf";
+ err = devm_clk_bulk_get(dev, core->soc->num_clks, core->clks);
if (err)
return dev_err_probe(dev, err, "failed to get clocks for core %d\n", core->index);
@@ -65,6 +74,19 @@ int rocket_core_init(struct rocket_core *core)
return err;
}
+ /*
+ * RK3576 spans two power domains, and a multi-domain device is skipped
+ * by the driver-core single-domain auto-attach, so attach the list here.
+ */
+ if (core->soc->multi_power_domain) {
+ struct dev_pm_domain_list *pd_list;
+
+ err = devm_pm_domain_attach_list(dev, NULL, &pd_list);
+ if (err < 0)
+ return dev_err_probe(dev, err,
+ "failed to attach NPU power domains\n");
+ }
+
pm_runtime_use_autosuspend(dev);
/*
diff --git a/drivers/accel/rocket/rocket_core.h b/drivers/accel/rocket/rocket_core.h
index f6d738285..6afb8180a 100644
--- a/drivers/accel/rocket/rocket_core.h
+++ b/drivers/accel/rocket/rocket_core.h
@@ -6,6 +6,7 @@
#include <drm/gpu_scheduler.h>
#include <linux/clk.h>
+#include <linux/hrtimer.h>
#include <linux/io.h>
#include <linux/mutex_types.h>
#include <linux/reset.h>
@@ -27,16 +28,25 @@
#define rocket_core_writel(core, reg, value) \
writel(value, (core)->core_iomem + (REG_CORE_##reg) - REG_CORE_S_STATUS)
+/* Per-SoC differences, selected by the of_device_id match data. */
+struct rocket_soc_data {
+ unsigned int num_clks; /* clk_bulk count: 4 base, 6 with CBUF */
+ unsigned int num_resets; /* reset_bulk count: 2 base, 1 on RK3576 */
+ bool multi_power_domain; /* device spans more than one PM domain */
+ bool poll_completion; /* completion IRQ never reaches the GIC */
+};
+
struct rocket_core {
struct device *dev;
struct rocket_device *rdev;
+ const struct rocket_soc_data *soc;
unsigned int index;
int irq;
void __iomem *pc_iomem;
void __iomem *cna_iomem;
void __iomem *core_iomem;
- struct clk_bulk_data clks[4];
+ struct clk_bulk_data clks[6];
struct reset_control_bulk_data resets[2];
struct iommu_group *iommu_group;
@@ -52,6 +62,11 @@ struct rocket_core {
atomic_t pending;
} reset;
+ struct hrtimer poll_timer;
+ struct work_struct poll_work;
+ atomic_t poll_active;
+ unsigned int poll_ticks;
+
struct drm_gpu_scheduler sched;
u64 fence_context;
u64 emit_seqno;
diff --git a/drivers/accel/rocket/rocket_device.c b/drivers/accel/rocket/rocket_device.c
index 46e6ee1e7..bfb00f967 100644
--- a/drivers/accel/rocket/rocket_device.c
+++ b/drivers/accel/rocket/rocket_device.c
@@ -31,6 +31,10 @@ struct rocket_device *rocket_device_init(struct platform_device *pdev,
if (of_device_is_available(core_node))
num_cores++;
+ for_each_compatible_node(core_node, NULL, "rockchip,rk3576-rknn-core")
+ if (of_device_is_available(core_node))
+ num_cores++;
+
rdev->cores = devm_kcalloc(dev, num_cores, sizeof(*rdev->cores), GFP_KERNEL);
if (!rdev->cores)
return ERR_PTR(-ENOMEM);
diff --git a/drivers/accel/rocket/rocket_drv.c b/drivers/accel/rocket/rocket_drv.c
index 8bbbce594..7f7dfa374 100644
--- a/drivers/accel/rocket/rocket_drv.c
+++ b/drivers/accel/rocket/rocket_drv.c
@@ -176,6 +176,7 @@ static int rocket_probe(struct platform_device *pdev)
rdev->cores[core].rdev = rdev;
rdev->cores[core].dev = &pdev->dev;
+ rdev->cores[core].soc = of_device_get_match_data(&pdev->dev);
rdev->cores[core].index = core;
rdev->num_cores++;
@@ -213,8 +214,23 @@ static void rocket_remove(struct platform_device *pdev)
}
}
+static const struct rocket_soc_data rk3588_soc_data = {
+ .num_clks = 4,
+ .num_resets = 2,
+ .multi_power_domain = false,
+ .poll_completion = false,
+};
+
+static const struct rocket_soc_data rk3576_soc_data = {
+ .num_clks = 6,
+ .num_resets = 1,
+ .multi_power_domain = true,
+ .poll_completion = true,
+};
+
static const struct of_device_id dt_match[] = {
- { .compatible = "rockchip,rk3588-rknn-core" },
+ { .compatible = "rockchip,rk3588-rknn-core", .data = &rk3588_soc_data },
+ { .compatible = "rockchip,rk3576-rknn-core", .data = &rk3576_soc_data },
{}
};
MODULE_DEVICE_TABLE(of, dt_match);
@@ -240,7 +256,7 @@ static int rocket_device_runtime_resume(struct device *dev)
if (core < 0)
return -ENODEV;
- err = clk_bulk_prepare_enable(ARRAY_SIZE(rdev->cores[core].clks), rdev->cores[core].clks);
+ err = clk_bulk_prepare_enable(rdev->cores[core].soc->num_clks, rdev->cores[core].clks);
if (err) {
dev_err(dev, "failed to enable (%d) clocks for core %d\n", err, core);
return err;
@@ -260,7 +276,7 @@ static int rocket_device_runtime_suspend(struct device *dev)
if (!rocket_job_is_idle(&rdev->cores[core]))
return -EBUSY;
- clk_bulk_disable_unprepare(ARRAY_SIZE(rdev->cores[core].clks), rdev->cores[core].clks);
+ clk_bulk_disable_unprepare(rdev->cores[core].soc->num_clks, rdev->cores[core].clks);
return 0;
}
diff --git a/drivers/accel/rocket/rocket_job.c b/drivers/accel/rocket/rocket_job.c
index bb77b6bf0..e9103a930 100644
--- a/drivers/accel/rocket/rocket_job.c
+++ b/drivers/accel/rocket/rocket_job.c
@@ -7,6 +7,7 @@
#include <drm/drm_file.h>
#include <drm/drm_gem.h>
#include <drm/rocket_accel.h>
+#include <linux/hrtimer.h>
#include <linux/interrupt.h>
#include <linux/overflow.h>
#include <linux/iommu.h>
@@ -21,6 +22,15 @@
#define JOB_TIMEOUT_MS 500
+/*
+ * RK3576 arms the same DPU completion as RK3588, but the interrupt never
+ * reaches the GIC. The completion itself is visible in INTERRUPT_RAW_STATUS,
+ * so sample that instead. The tick cap bounds jobs that never raise it at all,
+ * which is the same open problem as the wrong inference results.
+ */
+#define RK3576_POLL_INTERVAL_NS 1000000LL /* 1 ms */
+#define RK3576_POLL_MAX_TICKS 8
+
static struct rocket_job *
to_rocket_job(struct drm_sched_job *sched_job)
{
@@ -151,6 +161,13 @@ static void rocket_job_hw_submit(struct rocket_core *core, struct rocket_job *jo
rocket_pc_writel(core, OPERATION_ENABLE, PC_OPERATION_ENABLE_OP_EN(1));
+ if (core->soc->poll_completion) {
+ core->poll_ticks = 0;
+ atomic_set(&core->poll_active, 1);
+ hrtimer_start(&core->poll_timer, ns_to_ktime(RK3576_POLL_INTERVAL_NS),
+ HRTIMER_MODE_REL);
+ }
+
dev_dbg(core->dev, "Submitted regcmd at 0x%llx to core %d", task->regcmd, core->index);
}
@@ -341,8 +358,42 @@ static struct dma_fence *rocket_job_run(struct drm_sched_job *sched_job)
return ERR_PTR(ret);
}
+static void rocket_job_handle_irq(struct rocket_core *core);
+
+static enum hrtimer_restart rocket_poll_timer_fn(struct hrtimer *timer)
+{
+ struct rocket_core *core = container_of(timer, struct rocket_core, poll_timer);
+ u32 raw;
+
+ if (!atomic_read(&core->poll_active))
+ return HRTIMER_NORESTART;
+
+ raw = rocket_pc_readl(core, INTERRUPT_RAW_STATUS);
+ if ((raw & (PC_INTERRUPT_RAW_STATUS_DPU_0 | PC_INTERRUPT_RAW_STATUS_DPU_1)) ||
+ ++core->poll_ticks >= RK3576_POLL_MAX_TICKS) {
+ atomic_set(&core->poll_active, 0);
+ schedule_work(&core->poll_work);
+ return HRTIMER_NORESTART;
+ }
+
+ hrtimer_forward_now(timer, ns_to_ktime(RK3576_POLL_INTERVAL_NS));
+ return HRTIMER_RESTART;
+}
+
+static void rocket_poll_work_fn(struct work_struct *work)
+{
+ struct rocket_core *core = container_of(work, struct rocket_core, poll_work);
+
+ rocket_job_handle_irq(core);
+}
+
static void rocket_job_handle_irq(struct rocket_core *core)
{
+ if (core->soc->poll_completion) {
+ atomic_set(&core->poll_active, 0);
+ hrtimer_cancel(&core->poll_timer);
+ }
+
pm_runtime_mark_last_busy(core->dev);
rocket_pc_writel(core, OPERATION_ENABLE, 0x0);
@@ -460,6 +511,10 @@ int rocket_job_init(struct rocket_core *core)
int ret;
INIT_WORK(&core->reset.work, rocket_reset_work);
+ INIT_WORK(&core->poll_work, rocket_poll_work_fn);
+ hrtimer_setup(&core->poll_timer, rocket_poll_timer_fn, CLOCK_MONOTONIC,
+ HRTIMER_MODE_REL);
+ atomic_set(&core->poll_active, 0);
spin_lock_init(&core->fence_lock);
mutex_init(&core->job_lock);
--
2.43.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [RFC PATCH v3 5/6] arm64: dts: rockchip: rk3576: add NPU (RKNN) nodes
2026-07-31 4:35 [RFC PATCH v3 0/6] accel/rocket: RK3576 NPU (RKNN) enablement Jiaxing Hu
` (3 preceding siblings ...)
2026-07-31 4:35 ` [RFC PATCH v3 4/6] accel/rocket: add RK3576 NPU (RKNN) support Jiaxing Hu
@ 2026-07-31 4:35 ` Jiaxing Hu
2026-07-31 4:51 ` sashiko-bot
2026-07-31 4:35 ` [RFC PATCH v3 6/6] arm64: dts: rockchip: rk3576-rock-4d: enable NPU Jiaxing Hu
5 siblings, 1 reply; 11+ messages in thread
From: Jiaxing Hu @ 2026-07-31 4:35 UTC (permalink / raw)
To: tomeu, heiko, robh, krzk+dt, conor+dt, ulfh, p.zabel, ogabbay
Cc: chaoyi.chen, alchark, royalnet026, dri-devel, linux-rockchip,
linux-pm, devicetree, linux-arm-kernel, linux-kernel, Jiaxing Hu
Add the two RKNN cores and their IOMMUs, plus the NPU power-domain
resets the pmdomain driver now cycles on power-on. Both cores are
disabled by default; boards enable what they wire up.
Signed-off-by: Jiaxing Hu <gahing@gahingwoo.com>
---
arch/arm64/boot/dts/rockchip/rk3576.dtsi | 78 +++++++++++++++++++++++-
1 file changed, 76 insertions(+), 2 deletions(-)
diff --git a/arch/arm64/boot/dts/rockchip/rk3576.dtsi b/arch/arm64/boot/dts/rockchip/rk3576.dtsi
index b0c0d3c8b..a9d85e672 100644
--- a/arch/arm64/boot/dts/rockchip/rk3576.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3576.dtsi
@@ -1070,14 +1070,22 @@ power-domain@RK3576_PD_NPUTOP {
power-domain@RK3576_PD_NPU0 {
reg = <RK3576_PD_NPU0>;
clocks = <&cru HCLK_RKNN_ROOT>,
- <&cru ACLK_RKNN0>;
+ <&cru ACLK_RKNN0>,
+ <&cru CLK_RKNN_DSU0>,
+ <&cru ACLK_RKNN_CBUF>,
+ <&cru HCLK_RKNN_CBUF>;
+ resets = <&cru SRST_A_RKNN0_BIU>;
pm_qos = <&qos_npu_m0>;
#power-domain-cells = <0>;
};
power-domain@RK3576_PD_NPU1 {
reg = <RK3576_PD_NPU1>;
clocks = <&cru HCLK_RKNN_ROOT>,
- <&cru ACLK_RKNN1>;
+ <&cru ACLK_RKNN1>,
+ <&cru CLK_RKNN_DSU0>,
+ <&cru ACLK_RKNN_CBUF>,
+ <&cru HCLK_RKNN_CBUF>;
+ resets = <&cru SRST_A_RKNN1_BIU>;
pm_qos = <&qos_npu_m1>;
#power-domain-cells = <0>;
};
@@ -1832,6 +1840,72 @@ qos_npu_m1ro: qos@27f22100 {
reg = <0x0 0x27f22100 0x0 0x20>;
};
+ rknn_core_0: npu@27700000 {
+ compatible = "rockchip,rk3576-rknn-core";
+ reg = <0x0 0x27700000 0x0 0x1000>,
+ <0x0 0x27701000 0x0 0x1000>,
+ <0x0 0x27703000 0x0 0x1000>,
+ <0x0 0x27704000 0x0 0x1000>,
+ <0x0 0x27705000 0x0 0x1000>;
+ reg-names = "pc", "cna", "core", "dpu", "dpu_rdma";
+ interrupts = <GIC_SPI 247 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&cru ACLK_RKNN0>, <&cru HCLK_RKNN_ROOT>,
+ <&cru CLK_RKNN_DSU0>, <&cru PCLK_NPUTOP_ROOT>,
+ <&cru ACLK_RKNN_CBUF>, <&cru HCLK_RKNN_CBUF>;
+ clock-names = "aclk", "hclk", "npu", "pclk",
+ "aclk_cbuf", "hclk_cbuf";
+ resets = <&cru SRST_A_RKNN0>;
+ reset-names = "srst_a";
+ power-domains = <&power RK3576_PD_NPU0>;
+ iommus = <&rknn_mmu_0>;
+ status = "disabled";
+ };
+
+ rknn_mmu_0: iommu@27702000 {
+ compatible = "rockchip,rk3576-iommu", "rockchip,rk3568-iommu";
+ reg = <0x0 0x27702000 0x0 0x100>,
+ <0x0 0x27702100 0x0 0x100>;
+ interrupts = <GIC_SPI 247 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&cru ACLK_RKNN0>, <&cru HCLK_RKNN_ROOT>,
+ <&cru CLK_RKNN_DSU0>, <&cru ACLK_RKNN_CBUF>,
+ <&cru HCLK_RKNN_CBUF>;
+ #iommu-cells = <0>;
+ power-domains = <&power RK3576_PD_NPU0>;
+ status = "disabled";
+ };
+
+ rknn_core_1: npu@27710000 {
+ compatible = "rockchip,rk3576-rknn-core";
+ reg = <0x0 0x27710000 0x0 0x1000>,
+ <0x0 0x27711000 0x0 0x1000>,
+ <0x0 0x27713000 0x0 0x1000>,
+ <0x0 0x27714000 0x0 0x1000>,
+ <0x0 0x27715000 0x0 0x1000>;
+ reg-names = "pc", "cna", "core", "dpu", "dpu_rdma";
+ interrupts = <GIC_SPI 248 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&cru ACLK_RKNN1>, <&cru HCLK_RKNN_ROOT>,
+ <&cru CLK_RKNN_DSU0>, <&cru PCLK_NPUTOP_ROOT>;
+ clock-names = "aclk", "hclk", "npu", "pclk";
+ resets = <&cru SRST_A_RKNN1>;
+ reset-names = "srst_a";
+ power-domains = <&power RK3576_PD_NPU1>;
+ iommus = <&rknn_mmu_1>;
+ status = "disabled";
+ };
+
+ rknn_mmu_1: iommu@2770a000 {
+ compatible = "rockchip,rk3576-iommu", "rockchip,rk3568-iommu";
+ reg = <0x0 0x2770a000 0x0 0x100>,
+ <0x0 0x2770a100 0x0 0x100>;
+ interrupts = <GIC_SPI 248 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&cru ACLK_RKNN1>, <&cru HCLK_RKNN_ROOT>,
+ <&cru CLK_RKNN_DSU0>, <&cru ACLK_RKNN_CBUF>,
+ <&cru HCLK_RKNN_CBUF>;
+ #iommu-cells = <0>;
+ power-domains = <&power RK3576_PD_NPU1>;
+ status = "disabled";
+ };
+
gmac0: ethernet@2a220000 {
compatible = "rockchip,rk3576-gmac", "snps,dwmac-4.20a";
reg = <0x0 0x2a220000 0x0 0x10000>;
--
2.43.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [RFC PATCH v3 6/6] arm64: dts: rockchip: rk3576-rock-4d: enable NPU
2026-07-31 4:35 [RFC PATCH v3 0/6] accel/rocket: RK3576 NPU (RKNN) enablement Jiaxing Hu
` (4 preceding siblings ...)
2026-07-31 4:35 ` [RFC PATCH v3 5/6] arm64: dts: rockchip: rk3576: add NPU (RKNN) nodes Jiaxing Hu
@ 2026-07-31 4:35 ` Jiaxing Hu
2026-07-31 4:51 ` sashiko-bot
5 siblings, 1 reply; 11+ messages in thread
From: Jiaxing Hu @ 2026-07-31 4:35 UTC (permalink / raw)
To: tomeu, heiko, robh, krzk+dt, conor+dt, ulfh, p.zabel, ogabbay
Cc: chaoyi.chen, alchark, royalnet026, dri-devel, linux-rockchip,
linux-pm, devicetree, linux-arm-kernel, linux-kernel, Jiaxing Hu
Enable rknn_core_0 and its IOMMU on the Radxa ROCK 4D and supply the
core from vdd_npu_s0. The core is listed on both NPU power domains, as
the vendor does from its single NPU node.
Signed-off-by: Jiaxing Hu <gahing@gahingwoo.com>
---
arch/arm64/boot/dts/rockchip/rk3576-rock-4d.dts | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/arch/arm64/boot/dts/rockchip/rk3576-rock-4d.dts b/arch/arm64/boot/dts/rockchip/rk3576-rock-4d.dts
index 272af1012..09d2fc98b 100644
--- a/arch/arm64/boot/dts/rockchip/rk3576-rock-4d.dts
+++ b/arch/arm64/boot/dts/rockchip/rk3576-rock-4d.dts
@@ -442,6 +442,7 @@ regulator-state-mem {
};
vdd_npu_s0: dcdc-reg2 {
+ regulator-always-on;
regulator-boot-on;
regulator-enable-ramp-delay = <400>;
regulator-min-microvolt = <550000>;
@@ -869,3 +870,18 @@ vp0_out_hdmi: endpoint@ROCKCHIP_VOP2_EP_HDMI0 {
remote-endpoint = <&hdmi_in_vp0>;
};
};
+
+&rknn_core_0 {
+ npu-supply = <&vdd_npu_s0>;
+ /*
+ * Power BOTH NPU domains (NPU0 + NPU1) like the vendor's single NPU node,
+ * even though rocket computes only on core 0: the CBUF->CMAC read path is
+ * only fully powered with NPU1 up. rocket attaches the multi-PD list.
+ */
+ power-domains = <&power RK3576_PD_NPU0>, <&power RK3576_PD_NPU1>;
+ status = "okay";
+};
+
+&rknn_mmu_0 {
+ status = "okay";
+};
--
2.43.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [RFC PATCH v3 3/6] pmdomain/rockchip: cycle optional power-domain resets on power-on
2026-07-31 4:35 ` [RFC PATCH v3 3/6] pmdomain/rockchip: cycle optional power-domain resets on power-on Jiaxing Hu
@ 2026-07-31 4:50 ` sashiko-bot
0 siblings, 0 replies; 11+ messages in thread
From: sashiko-bot @ 2026-07-31 4:50 UTC (permalink / raw)
To: Jiaxing Hu; +Cc: devicetree, robh, dri-devel, conor+dt
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
New issues:
- [Low] Using `dev_err()` to log a failure to acquire resets causes log spam when the error is `-EPROBE_DEFER`.
Pre-existing issues:
- [Critical] Critical use-after-free and state corruption due to `pm_genpd_remove()` failure during probe error handling.
- [High] Missing cleanup of previously acquired clocks if `of_clk_get()` fails during initialization.
- [High] Use-after-free on the PM domain's device tree node reference (`pd->node`).
--
--- Patch [3]: [RFC PATCH v3 3/6] pmdomain/rockchip: cycle optional power-domain resets on power-on ---
commit 7b67005dcd951b64c273c1af0e37dfcc0b7586dd
Author: Jiaxing Hu <gahing@gahingwoo.com>
pmdomain/rockchip: cycle optional power-domain resets on power-on
Some Rockchip domains come out of power-on with their bus interface in
an undefined state. On the RK3576 NPU this shows up as a hang on the
first access after a cold power-on; the vendor kernel cycles the
domain's resets at this point and that clears it.
Take the domain node's resets if it has any, and pulse them between
releasing idle and restoring QoS. The resets are optional, so domains
that do not declare any behave exactly as before.
> diff --git a/drivers/pmdomain/rockchip/pm-domains.c b/drivers/pmdomain/rockchip/pm-domains.c
> index e1857f8786864..82051f60090f3 100644
> --- a/drivers/pmdomain/rockchip/pm-domains.c
> +++ b/drivers/pmdomain/rockchip/pm-domains.c
[ ... ]
> @@ -857,6 +866,14 @@ static int rockchip_pm_add_one_domain(struct rockchip_pmu *pmu,
> if (error)
> goto err_put_clocks;
[Severity: High]
This is a pre-existing issue, but does this function leak clock handles if
of_clk_get() fails?
Looking at the loop just before this context:
rockchip_pm_add_one_domain() {
for (i = 0; i < pd->num_clks; i++) {
pd->clks[i].clk = of_clk_get(node, i);
if (IS_ERR(pd->clks[i].clk)) {
...
return error;
}
}
}
If of_clk_get() returns -EPROBE_DEFER for an index > 0, it returns
immediately without calling clk_put() for the clocks successfully acquired in
earlier iterations. Should this jump to a cleanup path instead?
[Severity: High]
This is another pre-existing issue in this function, but is it safe to store
pd->node without incrementing its reference count?
In rockchip_pm_domain_probe(), domains are initialized within a loop that
automatically drops the node reference. Later, rockchip_pd_power() uses this
potentially freed node:
rockchip_pm_add_one_domain()
pd->node = node;
rockchip_pd_power()
devm_of_regulator_get(..., pd->node, ...);
Could this lead to a use-after-free if the node is dynamically removed?
>
> + pd->resets = of_reset_control_array_get_optional_exclusive(node);
> + if (IS_ERR(pd->resets)) {
> + error = PTR_ERR(pd->resets);
> + dev_err(pmu->dev, "%pOFn: failed to get resets: %d\n", node, error);
[Severity: Low]
Could this dev_err() cause log spam during normal boot?
If of_reset_control_array_get_optional_exclusive() returns -EPROBE_DEFER
because the reset controller hasn't probed yet, this will print an error.
Would dev_err_probe() be better here to suppress the spam and cleanly track
the deferral?
> + pd->resets = NULL;
> + goto err_unprepare_clocks;
> + }
[ ... ]
> @@ -945,6 +963,7 @@ static void rockchip_pm_remove_one_domain(struct rockchip_pm_domain *pd)
>
> clk_bulk_unprepare(pd->num_clks, pd->clks);
> clk_bulk_put(pd->num_clks, pd->clks);
> + reset_control_put(pd->resets);
>
> /* protect the zeroing of pm->num_clks */
> mutex_lock(&pd->pmu->mutex);
[Severity: Critical]
This isn't a bug introduced by this patch, but could ignoring the return value
of pm_genpd_remove() lead to a use-after-free and state corruption?
During probe error paths, rockchip_pm_domain_cleanup() tries to remove
domains. If child links aren't removed first, pm_genpd_remove() fails with
-EBUSY. Since this failure is ignored:
rockchip_pm_remove_one_domain()
ret = pm_genpd_remove(&pd->genpd);
// ret is -EBUSY, but we continue tearing down
clk_bulk_unprepare(...);
reset_control_put(pd->resets);
The domain remains active in the global gpd_list, but its internal resources
are freed, and devm eventually frees the rockchip_pm_domain struct. When the
subsystem later iterates over domains, won't it dereference the freed memory
or the newly dangling pd->resets pointer?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260731043507.1832277-1-gahing@gahingwoo.com?part=3
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [RFC PATCH v3 6/6] arm64: dts: rockchip: rk3576-rock-4d: enable NPU
2026-07-31 4:35 ` [RFC PATCH v3 6/6] arm64: dts: rockchip: rk3576-rock-4d: enable NPU Jiaxing Hu
@ 2026-07-31 4:51 ` sashiko-bot
0 siblings, 0 replies; 11+ messages in thread
From: sashiko-bot @ 2026-07-31 4:51 UTC (permalink / raw)
To: Jiaxing Hu; +Cc: robh, conor+dt, dri-devel, devicetree
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] Missing cleanup of `poll_timer` and `poll_work` in `rocket_job_fini()` leads to Use-After-Free on unload.
- [High] The driver statically requires 6 clocks for RK3576, causing inevitable probe failure for `rknn_core_1`.
- [Medium] Using `regulator-always-on` for the dedicated NPU regulator prevents runtime power savings and bypasses genpd management.
- [Low] Board-level DeviceTree overrides SoC-internal NPU power domain topology, violating DT schema bindings.
--
--- Patch [6]: [RFC PATCH v3 6/6] arm64: dts: rockchip: rk3576-rock-4d: enable NPU ---
commit 317571d0650068ec684ea45a47d155fe5e0ea20c
Author: Jiaxing Hu <gahing@gahingwoo.com>
arm64: dts: rockchip: rk3576-rock-4d: enable NPU
This commit enables rknn_core_0 and its IOMMU on the Radxa ROCK 4D. It
supplies the core from vdd_npu_s0 and assigns multiple NPU power domains
to the single NPU node.
> diff --git a/arch/arm64/boot/dts/rockchip/rk3576-rock-4d.dts b/arch/arm64/boot/dts/rockchip/rk3576-rock-4d.dts
> --- a/arch/arm64/boot/dts/rockchip/rk3576-rock-4d.dts
> +++ b/arch/arm64/boot/dts/rockchip/rk3576-rock-4d.dts
[ ... ]
> @@ -442,6 +442,7 @@ regulator-state-mem {
> };
>
> vdd_npu_s0: dcdc-reg2 {
> + regulator-always-on;
[Severity: Medium]
Does setting regulator-always-on for the dedicated NPU regulator bypass
the generic power domain management? It appears this will prevent the
regulator from being powered down during runtime suspend, potentially
wasting power when the accelerator is idle.
> regulator-boot-on;
> regulator-enable-ramp-delay = <400>;
[ ... ]
> +&rknn_core_0 {
> + npu-supply = <&vdd_npu_s0>;
> + /*
> + * Power BOTH NPU domains (NPU0 + NPU1) like the vendor's single NPU node,
> + * even though rocket computes only on core 0: the CBUF->CMAC read path is
> + * only fully powered with NPU1 up. rocket attaches the multi-PD list.
> + */
> + power-domains = <&power RK3576_PD_NPU0>, <&power RK3576_PD_NPU1>;
[Severity: Low]
Will this board-level override cause a device tree schema validation
failure? The rockchip,rk3588-rknn-core.yaml binding strictly enforces a
maximum of 1 power domain:
Documentation/devicetree/bindings/npu/rockchip,rk3588-rknn-core.yaml:
power-domains:
maxItems: 1
[ ... ]
[Severity: High]
Does rocket_job_fini() in drivers/accel/rocket/rocket_job.c leave
background polling active when the memory is freed?
drivers/accel/rocket/rocket_job.c:
void rocket_job_fini(struct rocket_core *core)
If the module is unloaded while a job is actively polling, the lack of
hrtimer_cancel() and cancel_work_sync() calls could allow the timer or
workqueue to execute against freed struct rocket_core memory, causing a
use-after-free.
[Severity: High]
Will rknn_core_1 fail to probe on the RK3576 SoC due to a strict clock
requirement?
drivers/accel/rocket/rocket_drv.c:rk3576_soc_data()
.num_clks = 6,
drivers/accel/rocket/rocket_core.c:rocket_core_init()
err = devm_clk_bulk_get(dev, core->soc->num_clks, core->clks);
if (err)
return dev_err_probe(dev, err, "failed to get clocks for core %d\n", core->index);
Because num_clks is statically defined as 6 for the entire SoC, and
rknn_core_1 does not possess the aclk_cbuf and hclk_cbuf clocks, this bulk
request will fail with -ENOENT when rknn_core_1 is enabled.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260731043507.1832277-1-gahing@gahingwoo.com?part=6
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [RFC PATCH v3 5/6] arm64: dts: rockchip: rk3576: add NPU (RKNN) nodes
2026-07-31 4:35 ` [RFC PATCH v3 5/6] arm64: dts: rockchip: rk3576: add NPU (RKNN) nodes Jiaxing Hu
@ 2026-07-31 4:51 ` sashiko-bot
0 siblings, 0 replies; 11+ messages in thread
From: sashiko-bot @ 2026-07-31 4:51 UTC (permalink / raw)
To: Jiaxing Hu; +Cc: robh, devicetree, dri-devel, conor+dt
Thank you for your contribution! Sashiko AI review found 7 potential issue(s) to consider:
- [High] The `rknn_core_1` DT node is missing the CBUF clocks (`aclk_cbuf` and `hclk_cbuf`), causing a fatal probe failure.
- [High] Providing only a single power domain per core triggers the driver-core auto-attach, conflicting with the driver's explicit attach logic and causing a fatal `-EEXIST` error during probe.
- [High] The `rknn_mmu_1` node has an incorrect `reg` base address (`0x2770a000` instead of `0x27712000`), which maps the wrong physical memory for the NPU1 IOMMU.
- [High] Race condition between the IRQ handler and poll_work causes premature task completion and memory corruption.
- [High] poll_work is not cancelled during a job reset, leaving a stale work item that corrupts subsequent jobs.
- [Low] The `rknn_core_0` and `rknn_core_1` nodes violate the `rockchip,rk3588-rknn-core` binding schema constraints.
- [Low] `rknn_mmu_0` and `rknn_mmu_1` nodes violate the `rockchip,iommu` binding schema.
--
--- Patch [5]: [RFC PATCH v3 5/6] arm64: dts: rockchip: rk3576: add NPU (RKNN) nodes ---
commit 844bc539be73e9227d1af3ee91d59e16d7988bfd
Author: Jiaxing Hu <gahing@gahingwoo.com>
arm64: dts: rockchip: rk3576: add NPU (RKNN) nodes
This commit adds the two RKNN cores and their IOMMUs, alongside the NPU
power-domain resets. Both cores are disabled by default and can be enabled
by boards as needed.
> --- a/arch/arm64/boot/dts/rockchip/rk3576.dtsi
> +++ b/arch/arm64/boot/dts/rockchip/rk3576.dtsi
[ ... ]
> @@ -1832,6 +1840,72 @@ qos_npu_m1ro: qos@27f22100 {
> reg = <0x0 0x27f22100 0x0 0x20>;
> };
>
> + rknn_core_0: npu@27700000 {
> + compatible = "rockchip,rk3576-rknn-core";
> + reg = <0x0 0x27700000 0x0 0x1000>,
> + <0x0 0x27701000 0x0 0x1000>,
> + <0x0 0x27703000 0x0 0x1000>,
> + <0x0 0x27704000 0x0 0x1000>,
> + <0x0 0x27705000 0x0 0x1000>;
> + reg-names = "pc", "cna", "core", "dpu", "dpu_rdma";
[Severity: Low]
Does this exceed the maxItems allowed by the schema?
The schema for rockchip,rk3588-rknn-core enforces maxItems: 3 for the reg
property and maxItems: 4 for clocks. It also requires npu-supply
unconditionally and demands two resets (srst_a and srst_h), which appear to
be omitted here.
Could this trigger unconditional dtbs_check validation failures?
> + interrupts = <GIC_SPI 247 IRQ_TYPE_LEVEL_HIGH>;
> + clocks = <&cru ACLK_RKNN0>, <&cru HCLK_RKNN_ROOT>,
> + <&cru CLK_RKNN_DSU0>, <&cru PCLK_NPUTOP_ROOT>,
> + <&cru ACLK_RKNN_CBUF>, <&cru HCLK_RKNN_CBUF>;
> + clock-names = "aclk", "hclk", "npu", "pclk",
> + "aclk_cbuf", "hclk_cbuf";
> + resets = <&cru SRST_A_RKNN0>;
> + reset-names = "srst_a";
> + power-domains = <&power RK3576_PD_NPU0>;
[Severity: High]
Will specifying only a single power domain here conflict with the driver's
explicit attach logic?
Looking at rocket_core_init() in drivers/accel/rocket/rocket_core.c, the
driver sets multi_power_domain = true and explicitly calls
devm_pm_domain_attach_list().
Because only a single power-domains entry is specified, the driver-core will
automatically attach the domain during platform_probe(), populating
dev->pm_domain before the driver's probe runs.
This would cause devm_pm_domain_attach_list() to return -EEXIST, aborting
the probe for both NPU cores.
> + iommus = <&rknn_mmu_0>;
> + status = "disabled";
> + };
> +
> + rknn_mmu_0: iommu@27702000 {
> + compatible = "rockchip,rk3576-iommu", "rockchip,rk3568-iommu";
> + reg = <0x0 0x27702000 0x0 0x100>,
> + <0x0 0x27702100 0x0 0x100>;
> + interrupts = <GIC_SPI 247 IRQ_TYPE_LEVEL_HIGH>;
> + clocks = <&cru ACLK_RKNN0>, <&cru HCLK_RKNN_ROOT>,
> + <&cru CLK_RKNN_DSU0>, <&cru ACLK_RKNN_CBUF>,
> + <&cru HCLK_RKNN_CBUF>;
[Severity: Low]
Does this violate the rockchip,iommu binding schema?
The rockchip,iommu.yaml schema requires exactly two clocks (aclk and iface)
and strictly mandates the clock-names property.
Here, 5 unnamed clocks are defined and the clock-names property is omitted,
which might cause dtbs_check validation failures.
> + #iommu-cells = <0>;
> + power-domains = <&power RK3576_PD_NPU0>;
> + status = "disabled";
> + };
> +
> + rknn_core_1: npu@27710000 {
[ ... ]
> + interrupts = <GIC_SPI 248 IRQ_TYPE_LEVEL_HIGH>;
> + clocks = <&cru ACLK_RKNN1>, <&cru HCLK_RKNN_ROOT>,
> + <&cru CLK_RKNN_DSU0>, <&cru PCLK_NPUTOP_ROOT>;
> + clock-names = "aclk", "hclk", "npu", "pclk";
[Severity: High]
Are the CBUF clocks (aclk_cbuf and hclk_cbuf) missing from this node?
The rocket driver explicitly requests 6 clocks via devm_clk_bulk_get() (as
defined by .num_clks = 6 in rk3576_soc_data).
Since devm_clk_bulk_get() is not optional, it will fail with -ENOENT when it
cannot find the aclk_cbuf and hclk_cbuf clocks for NPU1, preventing the
device from probing.
> + resets = <&cru SRST_A_RKNN1>;
> + reset-names = "srst_a";
> + power-domains = <&power RK3576_PD_NPU1>;
> + iommus = <&rknn_mmu_1>;
> + status = "disabled";
> + };
> +
> + rknn_mmu_1: iommu@2770a000 {
> + compatible = "rockchip,rk3576-iommu", "rockchip,rk3568-iommu";
> + reg = <0x0 0x2770a000 0x0 0x100>,
> + <0x0 0x2770a100 0x0 0x100>;
[Severity: High]
Is this reg base address correct for the NPU1 IOMMU?
NPU1 starts at 0x27710000, so the corresponding IOMMU hole is typically at
offset +0x2000 (0x27712000).
Mapping this to 0x2770a000 places it inside the 0x2770xxxx memory space
belonging to NPU0. When the IOMMU driver interacts with this wrong base
address, it might configure incorrect hardware registers or trigger faults.
Additionally, looking at the related driver code in
drivers/accel/rocket/rocket_job.c:
drivers/accel/rocket/rocket_job.c:rocket_job_handle_irq() {
rocket_pc_writel(core, OPERATION_ENABLE, 0x0);
rocket_pc_writel(core, INTERRUPT_CLEAR, 0x1ffff);
scoped_guard(mutex, &core->job_lock)
if (core->in_flight_job) {
if (core->in_flight_job->next_task_idx < core->in_flight_job->task_count) {
rocket_job_hw_submit(core, core->in_flight_job);
return;
}
...
[Severity: High]
Can a race condition occur here between the IRQ handler and poll_work?
rocket_job_handle_irq() can execute concurrently from both
rocket_job_irq_handler_thread and rocket_poll_work_fn. Because there is no
mutual exclusion protecting the initial register writes (like
OPERATION_ENABLE), one thread could complete a task, submit the next task to
the hardware via rocket_job_hw_submit(), and release the lock.
Another thread might then immediately acquire the lock, see
core->in_flight_job with next_task_idx advanced, and erroneously signal the
newly running task as complete.
Does this risk detaching the IOMMU while the NPU is actively executing and
writing to memory?
Also in drivers/accel/rocket/rocket_job.c:
drivers/accel/rocket/rocket_job.c:rocket_reset() {
static void
rocket_reset(struct rocket_core *core, struct drm_sched_job *bad)
{
if (!atomic_read(&core->reset.pending))
return;
drm_sched_stop(&core->sched, bad);
scoped_guard(mutex, &core->job_lock) {
...
[Severity: High]
Is poll_work properly cancelled during a job reset here?
When an NPU job times out, rocket_reset() resets the hardware and restarts
the DRM scheduler, but it appears to miss cancelling core->poll_timer and
flushing core->poll_work.
If a timeout occurs just as the polling timer fires, the unhandled poll_work
could persist. When the next valid job is submitted by the restarted
scheduler, the stale work item might execute, improperly cancel the new
job's timer, stop the hardware, and erroneously signal completion for the
brand-new job.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260731043507.1832277-1-gahing@gahingwoo.com?part=5
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [RFC PATCH v3 4/6] accel/rocket: add RK3576 NPU (RKNN) support
2026-07-31 4:35 ` [RFC PATCH v3 4/6] accel/rocket: add RK3576 NPU (RKNN) support Jiaxing Hu
@ 2026-07-31 4:54 ` sashiko-bot
0 siblings, 0 replies; 11+ messages in thread
From: sashiko-bot @ 2026-07-31 4:54 UTC (permalink / raw)
To: Jiaxing Hu; +Cc: devicetree, robh, conor+dt, dri-devel
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
New issues:
- [High] Missing teardown of `poll_timer` and `poll_work` causes use-after-free on module unload or device teardown.
- [High] Stale `poll_work` execution can prematurely complete and corrupt newly submitted jobs because pending work is not cancelled.
Pre-existing issues:
- [High] The shared IRQ handler accesses hardware registers without ensuring the device is active, causing potential crashes if the device is suspended.
- [High] Unbalanced `iommu_group_get()` leaks an IOMMU group reference on every job completion.
--
--- Patch [4]: [RFC PATCH v3 4/6] accel/rocket: add RK3576 NPU (RKNN) support ---
commit dd0a45989eab5a4ff6b90c355a58e175aad35c8f
Author: Jiaxing Hu <gahing@gahingwoo.com>
accel/rocket: add RK3576 NPU (RKNN) support
The RK3576 carries the same RKNN block as the RK3588, with two cores
instead of three and a few platform differences:
- the CBUF (convolution buffer) has its own clock domain, so the core
needs six clocks rather than four;
- the BIU reset moved into the power domain, leaving one reset here;
- the NPU spans two power domains, and a device with more than one is
skipped by the driver-core single-domain auto-attach, so the list has
to be attached explicitly;
- the DPU completion interrupt is armed exactly as on RK3588 but never
reaches the GIC. The completion is visible in INTERRUPT_RAW_STATUS,
so sample that from an hrtimer rather than wait for an interrupt that
does not come. The interrupt stays armed, so if it ever does arrive
the normal handler finalises the job first.
Select all of that from of_device_id match data so the RK3588 path keeps
its existing counts and behaviour unchanged.
> diff --git a/drivers/accel/rocket/rocket_drv.c b/drivers/accel/rocket/rocket_drv.c
> index 8bbbce594883e..7f7dfa3742fc8 100644
> --- a/drivers/accel/rocket/rocket_drv.c
> +++ b/drivers/accel/rocket/rocket_drv.c
[ ... ]
> @@ -260,7 +276,7 @@ static int rocket_device_runtime_suspend(struct device *dev)
> if (!rocket_job_is_idle(&rdev->cores[core]))
> return -EBUSY;
>
> - clk_bulk_disable_unprepare(ARRAY_SIZE(rdev->cores[core].clks), rdev->cores[core].clks);
> + clk_bulk_disable_unprepare(rdev->cores[core].soc->num_clks, rdev->cores[core].clks);
>
> return 0;
> }
[Severity: High]
This is a pre-existing issue, but can the shared IRQ handler crash if it runs
while the device is suspended?
The NPU driver registers its interrupt handler rocket_job_irq_handler() with
IRQF_SHARED. The hardirq handler unconditionally reads the
INTERRUPT_RAW_STATUS MMIO register without calling pm_runtime_get_if_active().
Additionally, rocket_device_runtime_suspend() disables hardware clocks here
without calling synchronize_irq().
If another device sharing the IRQ line triggers an interrupt while the NPU
is runtime-suspended, the handler will attempt to read powered-down hardware.
Should the suspend callback use synchronize_irq() and the IRQ handler verify
the device is active?
> diff --git a/drivers/accel/rocket/rocket_job.c b/drivers/accel/rocket/rocket_job.c
> index bb77b6bf0f231..e9103a93020cc 100644
> --- a/drivers/accel/rocket/rocket_job.c
> +++ b/drivers/accel/rocket/rocket_job.c
[ ... ]
> @@ -341,8 +358,42 @@ static struct dma_fence *rocket_job_run(struct drm_sched_job *sched_job)
> return ERR_PTR(ret);
> }
>
> +static void rocket_job_handle_irq(struct rocket_core *core);
> +
> +static enum hrtimer_restart rocket_poll_timer_fn(struct hrtimer *timer)
> +{
> + struct rocket_core *core = container_of(timer, struct rocket_core, poll_timer);
> + u32 raw;
> +
> + if (!atomic_read(&core->poll_active))
> + return HRTIMER_NORESTART;
> +
> + raw = rocket_pc_readl(core, INTERRUPT_RAW_STATUS);
> + if ((raw & (PC_INTERRUPT_RAW_STATUS_DPU_0 | PC_INTERRUPT_RAW_STATUS_DPU_1)) ||
> + ++core->poll_ticks >= RK3576_POLL_MAX_TICKS) {
> + atomic_set(&core->poll_active, 0);
> + schedule_work(&core->poll_work);
> + return HRTIMER_NORESTART;
> + }
> +
> + hrtimer_forward_now(timer, ns_to_ktime(RK3576_POLL_INTERVAL_NS));
> + return HRTIMER_RESTART;
> +}
> +
> +static void rocket_poll_work_fn(struct work_struct *work)
> +{
> + struct rocket_core *core = container_of(work, struct rocket_core, poll_work);
> +
> + rocket_job_handle_irq(core);
> +}
> +
> static void rocket_job_handle_irq(struct rocket_core *core)
> {
> + if (core->soc->poll_completion) {
> + atomic_set(&core->poll_active, 0);
> + hrtimer_cancel(&core->poll_timer);
> + }
> +
[Severity: High]
Can a stale poll_work execution corrupt the next job?
If a real hardware IRQ arrives after poll_timer queues poll_work, the real
IRQ handles the completion and cancels the timer here. However, poll_work
could remain pending in the workqueue.
The DRM scheduler could then immediately submit the next job. When the stale
poll_work executes, it calls rocket_job_handle_irq() which would incorrectly
mark the new job as finished, signal its completion fence early, detach its
IOMMU group, and halt the hardware.
Does the driver need a way to cancel the pending work or ignore it if it is
no longer relevant, both here and in rocket_reset()?
[Severity: High]
This is a pre-existing issue, but does this function leak an IOMMU group
reference on every job completion?
Further down in rocket_job_handle_irq(), the code calls:
iommu_detach_group(NULL, iommu_group_get(core->dev));
The iommu_group_get() function increments the kobject reference count, but
iommu_detach_group() does not consume it. Because there is no matching
iommu_group_put(), a reference is leaked every time a job finishes. Should
this use the existing core->iommu_group pointer instead?
> pm_runtime_mark_last_busy(core->dev);
>
> rocket_pc_writel(core, OPERATION_ENABLE, 0x0);
[ ... ]
> @@ -460,6 +511,10 @@ int rocket_job_init(struct rocket_core *core)
> int ret;
>
> INIT_WORK(&core->reset.work, rocket_reset_work);
> + INIT_WORK(&core->poll_work, rocket_poll_work_fn);
> + hrtimer_setup(&core->poll_timer, rocket_poll_timer_fn, CLOCK_MONOTONIC,
> + HRTIMER_MODE_REL);
> + atomic_set(&core->poll_active, 0);
[Severity: High]
Does this code leak the timer and work on module unload?
Since poll_timer and poll_work are initialized here, shouldn't they be
cancelled in rocket_job_fini() with hrtimer_cancel() and cancel_work_sync()?
Because the rocket_core struct is allocated with devm_kcalloc(), it is freed
shortly after driver removal. If the timer fires or the pending work executes
after the memory is freed, it could cause a use-after-free crash.
> spin_lock_init(&core->fence_lock);
> mutex_init(&core->job_lock);
>
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260731043507.1832277-1-gahing@gahingwoo.com?part=4
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2026-07-31 4:54 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-31 4:35 [RFC PATCH v3 0/6] accel/rocket: RK3576 NPU (RKNN) enablement Jiaxing Hu
2026-07-31 4:35 ` [RFC PATCH v3 1/6] dt-bindings: npu: rockchip: add rockchip,rk3576-rknn-core Jiaxing Hu
2026-07-31 4:35 ` [RFC PATCH v3 2/6] pmdomain/rockchip: add optional per-domain power-on settle delay Jiaxing Hu
2026-07-31 4:35 ` [RFC PATCH v3 3/6] pmdomain/rockchip: cycle optional power-domain resets on power-on Jiaxing Hu
2026-07-31 4:50 ` sashiko-bot
2026-07-31 4:35 ` [RFC PATCH v3 4/6] accel/rocket: add RK3576 NPU (RKNN) support Jiaxing Hu
2026-07-31 4:54 ` sashiko-bot
2026-07-31 4:35 ` [RFC PATCH v3 5/6] arm64: dts: rockchip: rk3576: add NPU (RKNN) nodes Jiaxing Hu
2026-07-31 4:51 ` sashiko-bot
2026-07-31 4:35 ` [RFC PATCH v3 6/6] arm64: dts: rockchip: rk3576-rock-4d: enable NPU Jiaxing Hu
2026-07-31 4:51 ` sashiko-bot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox