From patchwork Mon Mar 25 19:33:37 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sebastian Reichel X-Patchwork-Id: 13602819 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 3B286C54E58 for ; Mon, 25 Mar 2024 19:36:34 +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=guMVGg2NDDaDbe8SOc5lUGG9AiXU9vx0RThxaits/74=; b=Ruil3yN9Z1tWX4 gWaj7EiPvFOM1fVXDV/LRfni4VBkfSXdHocc54Rtq5VEZc522atvhgSGtcFUg1TYMs41hacT5q8zL TR1cqqCm7GqCIvOCNJSFRBAGG9zHTxxnSBojE2bqQ2PHXlNpYh3egALKlarKIvU80QGTqKFFQDO4j x+bPnhELmkT93Bo8sC1vlP9bdcSIAD8oF/ZHASj1cnA1yNQjM5MoOUHQL8BFYWTlCcjcUQ+PtykEW sLlFgM1kncfn6xy/zI2Un/afg7spxfFMYECgjUHewDMPYrrMRg4PlEYJ0oBzM1/EyLxP50LG2OaeC h+h+gXEZnHvsGQ6cC/Gg==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.97.1 #2 (Red Hat Linux)) id 1roq7S-00000001cwR-0nWq; Mon, 25 Mar 2024 19:36:30 +0000 Received: from madrid.collaboradmins.com ([2a00:1098:ed:100::25]) by bombadil.infradead.org with esmtps (Exim 4.97.1 #2 (Red Hat Linux)) id 1roq7G-00000001cnV-0Vz0 for linux-rockchip@lists.infradead.org; Mon, 25 Mar 2024 19:36:20 +0000 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=collabora.com; s=mail; t=1711395372; bh=FjhaOTGVzjEbcGLVOL/kBkKfOigUTPmdSq8QinPi4lE=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=4iByGiq5a0JGj7fJVoc8BT3WqZHeswgv+k0AJasUUtaetAH12DGgCwTaOZmNDxiX+ n/zGe4iRJAKDEnh3UuVItlQ2XKNhvzaURSUVU+y4QmsenvvZpAMmgBDFi1HLWiAI9l CT2irf42TZAlEMAtvdpZG5kHOta7c+Ot2RLOCnjbJzsuteQiJFKQ4xQSy7Z45kI6ep ioT64KD4yKLGRS3QAXmNjHhz/P2K+ER5QqbAFygcGQ/3it4aEasOKUnNJNomRDM/DV 1zh2AIM6Fpkw2ykxsONKwAZ0SGJ9fvFqEfc4/OuHIhc8IFveXTQBla/ATWyIXGlCG9 MlFp61lciIAaA== Received: from jupiter.universe (cola.collaboradmins.com [195.201.22.229]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits)) (No client certificate requested) (Authenticated sender: sre) by madrid.collaboradmins.com (Postfix) with ESMTPSA id EECA837820FD; Mon, 25 Mar 2024 19:36:11 +0000 (UTC) Received: by jupiter.universe (Postfix, from userid 1000) id 1A4E24800D3; Mon, 25 Mar 2024 20:36:11 +0100 (CET) From: Sebastian Reichel To: Michael Turquette , Stephen Boyd , linux-clk@vger.kernel.org Cc: Elaine Zhang , Kever Yang , Heiko Stuebner , Rob Herring , Krzysztof Kozlowski , Conor Dooley , huangtao@rock-chips.com, andy.yan@rock-chips.com, Michal Tomek , Ilya K , Chad LeClair , devicetree@vger.kernel.org, linux-rockchip@lists.infradead.org, Sebastian Reichel , kernel@collabora.com Subject: [PATCH v9 6/7] clk: rockchip: implement linked gate clock support Date: Mon, 25 Mar 2024 20:33:37 +0100 Message-ID: <20240325193609.237182-7-sebastian.reichel@collabora.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20240325193609.237182-1-sebastian.reichel@collabora.com> References: <20240325193609.237182-1-sebastian.reichel@collabora.com> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20240325_123618_424230_29234C4E X-CRM114-Status: GOOD ( 33.12 ) 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: , Sender: "Linux-rockchip" Errors-To: linux-rockchip-bounces+linux-rockchip=archiver.kernel.org@lists.infradead.org Recent Rockchip SoCs have a new hardware block called Native Interface Unit (NIU), which gates clocks to devices behind them. These clock gates will only have a running output clock when all of the following conditions are met: 1. the parent clock is enabled 2. the enable bit is set correctly 3. the linked clock is enabled To handle them this code registers them as a normal gate type clock, which takes care of condition 1 + 2. The linked clock is handled by using runtime PM clocks. Handling it via runtime PM requires setting up a struct device for each of these clocks with a driver attached to use the correct runtime PM operations. Thus the complete handling of these clocks has been moved into its own driver. Signed-off-by: Sebastian Reichel --- drivers/clk/rockchip/Makefile | 1 + drivers/clk/rockchip/clk-rk3588.c | 23 +------ drivers/clk/rockchip/clk.c | 52 ++++++++++++++++ drivers/clk/rockchip/clk.h | 25 ++++++++ drivers/clk/rockchip/gate-link.c | 99 +++++++++++++++++++++++++++++++ 5 files changed, 179 insertions(+), 21 deletions(-) create mode 100644 drivers/clk/rockchip/gate-link.c diff --git a/drivers/clk/rockchip/Makefile b/drivers/clk/rockchip/Makefile index 36894f6a7022..179be95c6ffb 100644 --- a/drivers/clk/rockchip/Makefile +++ b/drivers/clk/rockchip/Makefile @@ -13,6 +13,7 @@ clk-rockchip-y += clk-inverter.o clk-rockchip-y += clk-mmc-phase.o clk-rockchip-y += clk-muxgrf.o clk-rockchip-y += clk-ddr.o +clk-rockchip-y += gate-link.o clk-rockchip-$(CONFIG_RESET_CONTROLLER) += softrst.o obj-$(CONFIG_CLK_PX30) += clk-px30.o diff --git a/drivers/clk/rockchip/clk-rk3588.c b/drivers/clk/rockchip/clk-rk3588.c index 29f31a5526fa..1bf84ac44e85 100644 --- a/drivers/clk/rockchip/clk-rk3588.c +++ b/drivers/clk/rockchip/clk-rk3588.c @@ -12,25 +12,6 @@ #include #include "clk.h" -/* - * Recent Rockchip SoCs have a new hardware block called Native Interface - * Unit (NIU), which gates clocks to devices behind them. These effectively - * need two parent clocks. - * - * Downstream enables the linked clock via runtime PM whenever the gate is - * enabled. This implementation uses separate clock nodes for each of the - * linked gate clocks, which leaks parts of the clock tree into DT. - * - * The GATE_LINK macro instead takes the second parent via 'linkname', but - * ignores the information. Once the clock framework is ready to handle it, the - * information should be passed on here. But since these clocks are required to - * access multiple relevant IP blocks, such as PCIe or USB, we mark all linked - * clocks critical until a better solution is available. This will waste some - * power, but avoids leaking implementation details into DT or hanging the - * system. - */ -#define GATE_LINK(_id, cname, pname, linkedclk, f, o, b, gf) \ - GATE(_id, cname, pname, f, o, b, gf) #define RK3588_LINKED_CLK CLK_IS_CRITICAL @@ -2513,8 +2494,8 @@ static int clk_rk3588_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; - rockchip_clk_register_branches(ctx, rk3588_clk_branches, - ARRAY_SIZE(rk3588_clk_branches)); + rockchip_clk_register_late_branches(dev, ctx, rk3588_clk_branches, + ARRAY_SIZE(rk3588_clk_branches)); rk3588_rst_init(np, ctx->reg_base); rockchip_register_restart_notifier(ctx, RK3588_GLB_SRST_FST, NULL); diff --git a/drivers/clk/rockchip/clk.c b/drivers/clk/rockchip/clk.c index e150bc1fc319..f5f11cc60046 100644 --- a/drivers/clk/rockchip/clk.c +++ b/drivers/clk/rockchip/clk.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -440,6 +441,29 @@ unsigned long rockchip_clk_find_max_clk_id(struct rockchip_clk_branch *list, } EXPORT_SYMBOL_GPL(rockchip_clk_find_max_clk_id); +static struct platform_device *rockchip_clk_register_gate_link( + struct device *parent_dev, + struct rockchip_clk_provider *ctx, + struct rockchip_clk_branch *clkbr) +{ + struct rockchip_gate_link_platdata gate_link_pdata = { + .ctx = ctx, + .clkbr = clkbr, + }; + + struct platform_device_info pdevinfo = { + .parent = parent_dev, + .name = "rockchip-gate-link-clk", + .id = clkbr->id, + .fwnode = dev_fwnode(parent_dev), + .of_node_reused = true, + .data = &gate_link_pdata, + .size_data = sizeof(gate_link_pdata), + }; + + return platform_device_register_full(&pdevinfo); +} + void rockchip_clk_register_branches(struct rockchip_clk_provider *ctx, struct rockchip_clk_branch *list, unsigned int nr_clk) @@ -565,6 +589,9 @@ void rockchip_clk_register_branches(struct rockchip_clk_provider *ctx, list->div_width, list->div_flags, ctx->reg_base, &ctx->lock); break; + case branch_linked_gate: + /* must be registered late, fall-through for error message */ + break; } /* none of the cases above matched */ @@ -585,6 +612,31 @@ void rockchip_clk_register_branches(struct rockchip_clk_provider *ctx, } EXPORT_SYMBOL_GPL(rockchip_clk_register_branches); +void rockchip_clk_register_late_branches(struct device *dev, + struct rockchip_clk_provider *ctx, + struct rockchip_clk_branch *list, + unsigned int nr_clk) +{ + unsigned int idx; + + for (idx = 0; idx < nr_clk; idx++, list++) { + struct platform_device *pdev = NULL; + + switch (list->branch_type) { + case branch_linked_gate: + pdev = rockchip_clk_register_gate_link(dev, ctx, list); + break; + default: + dev_err(dev, "unknown clock type %d\n", list->branch_type); + break; + } + + if (!pdev) + dev_err(dev, "failed to register device for clock %s\n", list->name); + } +} +EXPORT_SYMBOL_GPL(rockchip_clk_register_late_branches); + void rockchip_clk_register_armclk(struct rockchip_clk_provider *ctx, unsigned int lookup_id, const char *name, const char *const *parent_names, diff --git a/drivers/clk/rockchip/clk.h b/drivers/clk/rockchip/clk.h index e39392e1c2a2..15aa2fd5265b 100644 --- a/drivers/clk/rockchip/clk.h +++ b/drivers/clk/rockchip/clk.h @@ -517,6 +517,7 @@ enum rockchip_clk_branch_type { branch_divider, branch_fraction_divider, branch_gate, + branch_linked_gate, branch_mmc, branch_inverter, branch_factor, @@ -544,6 +545,7 @@ struct rockchip_clk_branch { int gate_offset; u8 gate_shift; u8 gate_flags; + unsigned int linked_clk_id; struct rockchip_clk_branch *child; }; @@ -842,6 +844,20 @@ struct rockchip_clk_branch { .gate_flags = gf, \ } +#define GATE_LINK(_id, cname, pname, linkedclk, f, o, b, gf) \ + { \ + .id = _id, \ + .branch_type = branch_linked_gate, \ + .name = cname, \ + .parent_names = (const char *[]){ pname }, \ + .linked_clk_id = linkedclk, \ + .num_parents = 1, \ + .flags = f, \ + .gate_offset = o, \ + .gate_shift = b, \ + .gate_flags = gf, \ + } + #define MMC(_id, cname, pname, offset, shift) \ { \ .id = _id, \ @@ -981,6 +997,11 @@ static inline void rockchip_clk_set_lookup(struct rockchip_clk_provider *ctx, ctx->clk_data.clks[id] = clk; } +struct rockchip_gate_link_platdata { + struct rockchip_clk_provider *ctx; + struct rockchip_clk_branch *clkbr; +}; + struct rockchip_clk_provider *rockchip_clk_init(struct device_node *np, void __iomem *base, unsigned long nr_clks); void rockchip_clk_of_add_provider(struct device_node *np, @@ -990,6 +1011,10 @@ unsigned long rockchip_clk_find_max_clk_id(struct rockchip_clk_branch *list, void rockchip_clk_register_branches(struct rockchip_clk_provider *ctx, struct rockchip_clk_branch *list, unsigned int nr_clk); +void rockchip_clk_register_late_branches(struct device *dev, + struct rockchip_clk_provider *ctx, + struct rockchip_clk_branch *list, + unsigned int nr_clk); void rockchip_clk_register_plls(struct rockchip_clk_provider *ctx, struct rockchip_pll_clock *pll_list, unsigned int nr_pll, int grf_lock_offset); diff --git a/drivers/clk/rockchip/gate-link.c b/drivers/clk/rockchip/gate-link.c new file mode 100644 index 000000000000..47b6f3e7a6a2 --- /dev/null +++ b/drivers/clk/rockchip/gate-link.c @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2024 Collabora Ltd. + * Author: Sebastian Reichel + */ + +#include +#include +#include +#include +#include +#include "clk.h" + +static int rk_clk_gate_link_register(struct device *dev, + struct rockchip_clk_provider *ctx, + struct rockchip_clk_branch *clkbr) +{ + unsigned long flags = clkbr->flags | CLK_SET_RATE_PARENT; + struct clk *clk; + + clk = clk_register_gate(dev, clkbr->name, clkbr->parent_names[0], + flags, ctx->reg_base + clkbr->gate_offset, + clkbr->gate_shift, clkbr->gate_flags, + &ctx->lock); + + if (IS_ERR(clk)) + return PTR_ERR(clk); + + rockchip_clk_set_lookup(ctx, clk, clkbr->id); + return 0; +} + +static int rk_clk_gate_link_probe(struct platform_device *pdev) +{ + struct rockchip_gate_link_platdata *pdata; + struct device *dev = &pdev->dev; + struct clk *linked_clk; + int ret; + + pdata = dev_get_platdata(dev); + if (!pdata) + return dev_err_probe(dev, -ENODEV, "missing platform data"); + + ret = devm_pm_runtime_enable(dev); + if (ret) + return ret; + + ret = devm_pm_clk_create(dev); + if (ret) + return ret; + + linked_clk = rockchip_clk_get_lookup(pdata->ctx, pdata->clkbr->linked_clk_id); + ret = pm_clk_add_clk(dev, linked_clk); + if (ret) + return ret; + + ret = rk_clk_gate_link_register(dev, pdata->ctx, pdata->clkbr); + if (ret) + goto err; + + return 0; + +err: + pm_clk_remove_clk(dev, linked_clk); + return ret; +} + +static void rk_clk_gate_link_remove(struct platform_device *pdev) +{ + struct rockchip_gate_link_platdata *pdata; + struct device *dev = &pdev->dev; + struct clk *clk, *linked_clk; + + pdata = dev_get_platdata(dev); + clk = rockchip_clk_get_lookup(pdata->ctx, pdata->clkbr->id); + linked_clk = rockchip_clk_get_lookup(pdata->ctx, pdata->clkbr->linked_clk_id); + rockchip_clk_set_lookup(pdata->ctx, ERR_PTR(-ENODEV), pdata->clkbr->id); + clk_unregister_gate(clk); + pm_clk_remove_clk(dev, linked_clk); +} + +static const struct dev_pm_ops rk_clk_gate_link_pm_ops = { + SET_RUNTIME_PM_OPS(pm_clk_suspend, pm_clk_resume, NULL) +}; + +struct platform_driver rk_clk_gate_link_driver = { + .probe = rk_clk_gate_link_probe, + .remove_new = rk_clk_gate_link_remove, + .driver = { + .name = "rockchip-gate-link-clk", + .pm = &rk_clk_gate_link_pm_ops, + }, +}; + +static int __init rk_clk_gate_link_drv_register(void) +{ + return platform_driver_register(&rk_clk_gate_link_driver); +} +core_initcall(rk_clk_gate_link_drv_register);