From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 195FCC79F8B for ; Fri, 4 Sep 2026 13:09:40 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender: Content-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-ID:Date:Subject:Cc:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=ch93kAI0iFli6p3RXdxz6qiNDw1P5CAuXJMV5WPbFp8=; b=XNjE/IDgx1kJoG x1zq9al/DitgcH5kzAZF2uXcsg40Wx6tG3Ot0Rz/LsnmH/d9AExTDm2z7eeyi6uDVOjGOAO998E7r tdBSi3HpRqdXpPAEe0yXmDK9S+Vlwqec31woz6DdDRptfxtdIabDjz4XC8qnnzFlxQVbf6p7zeD9m PbFAPHylCR2IiDBaRxaJ5mJRrAEJn4NINo3qWbG+t46NhGkyNI2z8omGoAUCmkxQMOzDLTrbySZ6B xYVw2oQK4NUPU87EiucM+6APc+e6K06Gx2C3AdfAFR0DYBm0OLGjjoRlOB4lvQiBLfCKznpTcO0wZ yXdu5DIqJ3MkAidwyjyQ==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.99.1 #2 (Red Hat Linux)) id 1x2Tfl-000000026ji-2hkJ; Fri, 04 Sep 2026 13:09:37 +0000 Received: from mail-wr1-x42b.google.com ([2a00:1450:4864:20::42b]) by bombadil.infradead.org with esmtps (Exim 4.99.1 #2 (Red Hat Linux)) id 1x2Tfg-000000026cb-1grA for linux-rockchip@lists.infradead.org; Fri, 04 Sep 2026 13:09:33 +0000 Received: by mail-wr1-x42b.google.com with SMTP id ffacd0b85a97d-4843886fc9cso157787f8f.3 for ; Fri, 04 Sep 2026 06:09:31 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1788527370; x=1789132170; darn=lists.infradead.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=0Nc493Zxn3lB2uIUk0uEHaMMSyj53T93ezhvvuGgReI=; b=efG3dqaw0GStk9zdOJPnE6/bN29ZyhDwsVKFxR5krzUi3dMSgO+w/dWO7Psp9hYfCX 6NGUo8N3OE5p93sWI53c3NZXDf6heNoP2/lH9g2spfDxhWcZEf/otGj4eAqCdmc/RJ3O 4dmvRVxEvp/kNwkPOGxuOMI7nfF+CR8g7VnXnAO235DnIY6wbmk+f8lL4GX/N4kRxSQ3 k1HpTuJBULf89yfIu8MVogaEYmIOFn8frln6u6X6ZIxaokx15TGDjqtvJGn53hvZNy24 sGNd6pzx2lWBQPn/jfdmmpZchTcS3bjyjiL7y1aium5pkxW+IdKVack0I48IGVM+z1pB srJg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1788527370; x=1789132170; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=0Nc493Zxn3lB2uIUk0uEHaMMSyj53T93ezhvvuGgReI=; b=dxKqicFgi/LgVSAVUxKQOzspVKAQK9CrJ3uxWeIEZKMLhq0vOIUrmFApcsZi9F0CON 7DKOT6QBHC+vvnGuVWvPk1/tUuf6GQ694h9TLkNuAqmw9xuunRK5iJS0waQMwZcTpRQc KP3f4og80kPeXzEnsXku4Ezso4hYA/Nb8vC1QRLX2tyo79/fLLsc2sypOgxF2gAaFdWL oehAXkB3gAPz87KEHiKL9TLaelWyVp/2S78k5ltjEp70+WYgqagowp7mFnC3zMHjMURe qBxrtgEH82UETkUK10MkMMFJa0iN9g/kRwhpiODVAVlGr1LgnFmdTzJ+tz2jviojM9Q9 QOCg== X-Forwarded-Encrypted: i=1; AKwUvBzM+cOAzEdILak32sdh+KzltFjstJILJOLvYIEWxnICVwGJBnlbPe7D5QVx/af6xRjwXrb8SqiCFhFhQ2scnA==@lists.infradead.org X-Gm-Message-State: AFuF++l2iRh03xRqnBMKi6mpG6uytxfVOA65ZmBKTSMzpywMN3kBfzwE +zRl06FWAuNKN2t4yvUiQ7CXNXaqvWqQj4lt5X3sU654S314iaMHGZYN X-Gm-Gg: AYBFou1Vqz4SAXpFeQ7f+YTTqg/4xdF6xX9ICq2tDnTmZx0QPREH+3Q+SE71t7v+l8U roWYQ4sw2hLJZpmBVQD+THP2UguNnUfc/LVaTOt6ydFpvtUiuQQPTP8R309M9q95jshrGbFr4eR wnHTjXS48W5B2pDoYk2rAC1eqU3yif98yLNs7wp2kxp9/Zv4ZTiojRtfki91GNjUtzf022TlcYE 7AVzmWwof29GwyAGjT7OkvD0KW4jbr2iH73KDewqNDQSwFf4xT+qwyW2fWs2bMaGIBc5s+JUdMJ WgUO//PjTiSr5h2eJE3rJcamne3bVZfb8CdpHcvmQb1HZNaGPDtrN6pgpWnGA6VHHlcnMsCLeuP Vfifvf4/lZP49iMO3zGt4OMv0HDIhez2ycg3/jp3h/Qa/p35BzbACU7MDaoHGwHouwbbpQ35NPE l2Nm6b9hehKaWWpCFLUcuYIbzOVyMtgjL+2l4Q96TtCNCJWEfXwtQeRP5JAEvJRz9upl0oLMVXE MPTjn4erwhlQ4lfS0M7HAjzc31c1V94G/XkKBHLXVXzwJmN8xDiGLiZ1V+CXOlLsZKA X-Received: by 2002:a05:600c:1d26:b0:49c:cbf4:572b with SMTP id 5b1f17b1804b1-49cf8245fe1mr47842485e9.2.1788527370519; Fri, 04 Sep 2026 06:09:30 -0700 (PDT) Received: from OrangePi5-Plus.BB-HOME (20014C4E1B871500CB6EF488A18F9C42.dsl.pool.telekom.hu. [2001:4c4e:1b87:1500:cb6e:f488:a18f:9c42]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-49ce554d52esm135575435e9.3.2026.09.04.06.09.27 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 04 Sep 2026 06:09:29 -0700 (PDT) From: Igor Paunovic To: Tomeu Vizoso , Oded Gabbay , Heiko Stuebner Cc: Rob Herring , Krzysztof Kozlowski , Conor Dooley , Sidong Yang , Diederik de Haas , Sebastian Reichel , Jiaxing Hu , Nicolas Dufresne , Jonas Karlman , dri-devel@lists.freedesktop.org, linux-rockchip@lists.infradead.org, linux-arm-kernel@lists.infradead.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, Igor Paunovic Subject: [PATCH 4/7] accel/rocket: restore the NPU clock boot rate before powering the cores down Date: Fri, 4 Sep 2026 15:08:55 +0200 Message-ID: <20260904130858.27803-5-royalnet026@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260904130858.27803-1-royalnet026@gmail.com> References: <20260904130858.27803-1-royalnet026@gmail.com> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.9.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20260904_060932_471278_51C3DF11 X-CRM114-Status: GOOD ( 31.10 ) X-BeenThere: linux-rockchip@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: Upstream kernel work for Rockchip platforms List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Sender: "Linux-rockchip" Errors-To: linux-rockchip-bounces+linux-rockchip=archiver.kernel.org@lists.infradead.org The compute clock is generated by a PVTPLL that lives inside the NPU power island. Powering an island up while that clock is above the rate the bootloader left it at does not work: the domain never acks the power-on, and the first register access into it afterwards takes an asynchronous SError. So the rate has to be back down before the last core goes away. Nothing in the driver raises the clock today, which makes this a no-op on its own, but it is the guard that has to be in the tree before anything does, and the next patches do. The .shutdown hook is the same guard for the handover: once devfreq is driving the clock, a reboot or a kexec would otherwise pass the raised rate to the next kernel, which powers the islands up before it looks at it. What this cannot do is rescue a rate it did not set - the rate read at probe is taken as the boot rate whatever it is. The rate is read at probe rather than hardcoded. Mainline pins the RK3588 cores at 200 MHz with assigned-clock-rates, but that is a devicetree property, not a property of the hardware, and a SoC whose devicetree does not set it would be left running at a rate this driver had invented. All three cores share the clock, so only the last core to suspend may lower it; the others just drop the count. Lowering it is safe with the islands already down, because the firmware serves the boot rate from GPLL and writes only CRU clock selectors to get there, never a register inside the NPU. Signed-off-by: Igor Paunovic Assisted-by: LLM sparse checkpatch --- drivers/accel/rocket/rocket_core.c | 12 +++++++++ drivers/accel/rocket/rocket_device.h | 10 +++++++ drivers/accel/rocket/rocket_drv.c | 40 ++++++++++++++++++++++++++++ 3 files changed, 62 insertions(+) diff --git a/drivers/accel/rocket/rocket_core.c b/drivers/accel/rocket/rocket_core.c index 5dd260bacbff6..61200e5d5ac0d 100644 --- a/drivers/accel/rocket/rocket_core.c +++ b/drivers/accel/rocket/rocket_core.c @@ -12,6 +12,7 @@ #include #include "rocket_core.h" +#include "rocket_device.h" #include "rocket_job.h" int rocket_core_init(struct rocket_core *core) @@ -36,6 +37,17 @@ int rocket_core_init(struct rocket_core *core) if (err) return dev_err_probe(dev, err, "failed to get clocks for core %d\n", core->index); + /* + * Record what the compute clock was running at before anything here + * touched it, on the first core to probe. Reading it rather than + * hardcoding a rate keeps this working on a SoC whose devicetree does + * not pin the clock with assigned-clock-rates. + */ + if (!core->rdev->npu_clk) { + core->rdev->npu_clk = core->clks[2].clk; + core->rdev->npu_boot_rate = clk_get_rate(core->rdev->npu_clk); + } + core->pc_iomem = devm_platform_ioremap_resource_byname(pdev, "pc"); if (IS_ERR(core->pc_iomem)) { dev_err(dev, "couldn't find PC registers %ld\n", PTR_ERR(core->pc_iomem)); diff --git a/drivers/accel/rocket/rocket_device.h b/drivers/accel/rocket/rocket_device.h index c62d567010696..466ebc4c26a8a 100644 --- a/drivers/accel/rocket/rocket_device.h +++ b/drivers/accel/rocket/rocket_device.h @@ -20,6 +20,16 @@ struct rocket_device { struct rocket_core *cores; unsigned int num_cores; unsigned int max_cores; + + /* + * The cores have no clock of their own: one clock feeds all of them, + * so any core's handle refers to the same thing. npu_boot_rate is the + * rate it was left at before the driver touched it, and active_cores + * counts the cores that are runtime resumed right now. + */ + struct clk *npu_clk; + unsigned long npu_boot_rate; + atomic_t active_cores; }; struct rocket_device *rocket_device_init(struct platform_device *pdev, diff --git a/drivers/accel/rocket/rocket_drv.c b/drivers/accel/rocket/rocket_drv.c index 2bcfe4ab3c68f..b7199de57ccc7 100644 --- a/drivers/accel/rocket/rocket_drv.c +++ b/drivers/accel/rocket/rocket_drv.c @@ -231,6 +231,28 @@ static int find_core_for_dev(struct device *dev) return -1; } +/* + * Put the compute clock back where the bootloader had it. The cores share + * this clock, so this is only correct once none of them is running any more. + * + * Lowering the rate is safe with the power islands down: the firmware serves + * the boot rate from GPLL and touches only the CRU clock selectors on the way + * there, none of the NPU's own registers. + */ +static void rocket_npu_restore_boot_rate(struct rocket_device *rdev) +{ + int err; + + if (!rdev->npu_clk) + return; + + err = clk_set_rate(rdev->npu_clk, rdev->npu_boot_rate); + if (err) + dev_warn(rdev->cores[0].dev, + "failed to restore the NPU boot rate of %lu Hz: %d\n", + rdev->npu_boot_rate, err); +} + static int rocket_device_runtime_resume(struct device *dev) { struct rocket_device *rdev = dev_get_drvdata(dev); @@ -246,6 +268,8 @@ static int rocket_device_runtime_resume(struct device *dev) return err; } + atomic_inc(&rdev->active_cores); + return 0; } @@ -262,6 +286,9 @@ static int rocket_device_runtime_suspend(struct device *dev) clk_bulk_disable_unprepare(ARRAY_SIZE(rdev->cores[core].clks), rdev->cores[core].clks); + if (atomic_dec_and_test(&rdev->active_cores)) + rocket_npu_restore_boot_rate(rdev); + return 0; } @@ -270,9 +297,22 @@ EXPORT_GPL_DEV_PM_OPS(rocket_pm_ops) = { SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) }; +/* + * A kexec or a reboot hands the next kernel whatever rate is set here, and + * that kernel will power the islands up before it looks at the clock. + */ +static void rocket_shutdown(struct platform_device *pdev) +{ + struct rocket_device *rdev = dev_get_drvdata(&pdev->dev); + + if (rdev) + rocket_npu_restore_boot_rate(rdev); +} + static struct platform_driver rocket_driver = { .probe = rocket_probe, .remove = rocket_remove, + .shutdown = rocket_shutdown, .driver = { .name = "rocket", .pm = pm_ptr(&rocket_pm_ops), -- 2.43.0 _______________________________________________ Linux-rockchip mailing list Linux-rockchip@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-rockchip