Skip to content

Commit c62fa61

Browse files
sremmind
authored andcommitted
clk: rockchip: implement linked gate clock support
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 <sebastian.reichel@collabora.com> Link: https://lore.kernel.org/r/20241211165957.94922-5-sebastian.reichel@collabora.com Signed-off-by: Heiko Stuebner <heiko@sntech.de>
1 parent fe0fb66 commit c62fa61

File tree

5 files changed

+165
-21
lines changed

5 files changed

+165
-21
lines changed

drivers/clk/rockchip/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ clk-rockchip-y += clk-inverter.o
1313
clk-rockchip-y += clk-mmc-phase.o
1414
clk-rockchip-y += clk-muxgrf.o
1515
clk-rockchip-y += clk-ddr.o
16+
clk-rockchip-y += gate-link.o
1617
clk-rockchip-$(CONFIG_RESET_CONTROLLER) += softrst.o
1718

1819
obj-$(CONFIG_CLK_PX30) += clk-px30.o

drivers/clk/rockchip/clk-rk3588.c

Lines changed: 2 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -12,25 +12,6 @@
1212
#include <dt-bindings/clock/rockchip,rk3588-cru.h>
1313
#include "clk.h"
1414

15-
/*
16-
* Recent Rockchip SoCs have a new hardware block called Native Interface
17-
* Unit (NIU), which gates clocks to devices behind them. These effectively
18-
* need two parent clocks.
19-
*
20-
* Downstream enables the linked clock via runtime PM whenever the gate is
21-
* enabled. This implementation uses separate clock nodes for each of the
22-
* linked gate clocks, which leaks parts of the clock tree into DT.
23-
*
24-
* The GATE_LINK macro instead takes the second parent via 'linkname', but
25-
* ignores the information. Once the clock framework is ready to handle it, the
26-
* information should be passed on here. But since these clocks are required to
27-
* access multiple relevant IP blocks, such as PCIe or USB, we mark all linked
28-
* clocks critical until a better solution is available. This will waste some
29-
* power, but avoids leaking implementation details into DT or hanging the
30-
* system.
31-
*/
32-
#define GATE_LINK(_id, cname, pname, linkedclk, f, o, b, gf) \
33-
GATE(_id, cname, pname, f, o, b, gf)
3415
#define RK3588_LINKED_CLK CLK_IS_CRITICAL
3516

3617

@@ -2513,8 +2494,8 @@ static int clk_rk3588_probe(struct platform_device *pdev)
25132494
struct device *dev = &pdev->dev;
25142495
struct device_node *np = dev->of_node;
25152496

2516-
rockchip_clk_register_branches(ctx, rk3588_clk_branches,
2517-
ARRAY_SIZE(rk3588_clk_branches));
2497+
rockchip_clk_register_late_branches(dev, ctx, rk3588_clk_branches,
2498+
ARRAY_SIZE(rk3588_clk_branches));
25182499

25192500
rockchip_clk_finalize(ctx);
25202501

drivers/clk/rockchip/clk.c

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include <linux/clk-provider.h>
2020
#include <linux/io.h>
2121
#include <linux/mfd/syscon.h>
22+
#include <linux/platform_device.h>
2223
#include <linux/regmap.h>
2324
#include <linux/reboot.h>
2425

@@ -468,6 +469,29 @@ unsigned long rockchip_clk_find_max_clk_id(struct rockchip_clk_branch *list,
468469
}
469470
EXPORT_SYMBOL_GPL(rockchip_clk_find_max_clk_id);
470471

472+
static struct platform_device *rockchip_clk_register_gate_link(
473+
struct device *parent_dev,
474+
struct rockchip_clk_provider *ctx,
475+
struct rockchip_clk_branch *clkbr)
476+
{
477+
struct rockchip_gate_link_platdata gate_link_pdata = {
478+
.ctx = ctx,
479+
.clkbr = clkbr,
480+
};
481+
482+
struct platform_device_info pdevinfo = {
483+
.parent = parent_dev,
484+
.name = "rockchip-gate-link-clk",
485+
.id = clkbr->id,
486+
.fwnode = dev_fwnode(parent_dev),
487+
.of_node_reused = true,
488+
.data = &gate_link_pdata,
489+
.size_data = sizeof(gate_link_pdata),
490+
};
491+
492+
return platform_device_register_full(&pdevinfo);
493+
}
494+
471495
void rockchip_clk_register_branches(struct rockchip_clk_provider *ctx,
472496
struct rockchip_clk_branch *list,
473497
unsigned int nr_clk)
@@ -593,6 +617,9 @@ void rockchip_clk_register_branches(struct rockchip_clk_provider *ctx,
593617
list->div_width, list->div_flags,
594618
ctx->reg_base, &ctx->lock);
595619
break;
620+
case branch_linked_gate:
621+
/* must be registered late, fall-through for error message */
622+
break;
596623
}
597624

598625
/* none of the cases above matched */
@@ -613,6 +640,31 @@ void rockchip_clk_register_branches(struct rockchip_clk_provider *ctx,
613640
}
614641
EXPORT_SYMBOL_GPL(rockchip_clk_register_branches);
615642

643+
void rockchip_clk_register_late_branches(struct device *dev,
644+
struct rockchip_clk_provider *ctx,
645+
struct rockchip_clk_branch *list,
646+
unsigned int nr_clk)
647+
{
648+
unsigned int idx;
649+
650+
for (idx = 0; idx < nr_clk; idx++, list++) {
651+
struct platform_device *pdev = NULL;
652+
653+
switch (list->branch_type) {
654+
case branch_linked_gate:
655+
pdev = rockchip_clk_register_gate_link(dev, ctx, list);
656+
break;
657+
default:
658+
dev_err(dev, "unknown clock type %d\n", list->branch_type);
659+
break;
660+
}
661+
662+
if (!pdev)
663+
dev_err(dev, "failed to register device for clock %s\n", list->name);
664+
}
665+
}
666+
EXPORT_SYMBOL_GPL(rockchip_clk_register_late_branches);
667+
616668
void rockchip_clk_register_armclk(struct rockchip_clk_provider *ctx,
617669
unsigned int lookup_id,
618670
const char *name, const char *const *parent_names,

drivers/clk/rockchip/clk.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -570,6 +570,7 @@ enum rockchip_clk_branch_type {
570570
branch_divider,
571571
branch_fraction_divider,
572572
branch_gate,
573+
branch_linked_gate,
573574
branch_mmc,
574575
branch_inverter,
575576
branch_factor,
@@ -597,6 +598,7 @@ struct rockchip_clk_branch {
597598
int gate_offset;
598599
u8 gate_shift;
599600
u8 gate_flags;
601+
unsigned int linked_clk_id;
600602
struct rockchip_clk_branch *child;
601603
};
602604

@@ -895,6 +897,20 @@ struct rockchip_clk_branch {
895897
.gate_flags = gf, \
896898
}
897899

900+
#define GATE_LINK(_id, cname, pname, linkedclk, f, o, b, gf) \
901+
{ \
902+
.id = _id, \
903+
.branch_type = branch_linked_gate, \
904+
.name = cname, \
905+
.parent_names = (const char *[]){ pname }, \
906+
.linked_clk_id = linkedclk, \
907+
.num_parents = 1, \
908+
.flags = f, \
909+
.gate_offset = o, \
910+
.gate_shift = b, \
911+
.gate_flags = gf, \
912+
}
913+
898914
#define MMC(_id, cname, pname, offset, shift) \
899915
{ \
900916
.id = _id, \
@@ -1034,6 +1050,11 @@ static inline void rockchip_clk_set_lookup(struct rockchip_clk_provider *ctx,
10341050
ctx->clk_data.clks[id] = clk;
10351051
}
10361052

1053+
struct rockchip_gate_link_platdata {
1054+
struct rockchip_clk_provider *ctx;
1055+
struct rockchip_clk_branch *clkbr;
1056+
};
1057+
10371058
struct rockchip_clk_provider *rockchip_clk_init(struct device_node *np,
10381059
void __iomem *base, unsigned long nr_clks);
10391060
struct rockchip_clk_provider *rockchip_clk_init_early(struct device_node *np,
@@ -1046,6 +1067,10 @@ unsigned long rockchip_clk_find_max_clk_id(struct rockchip_clk_branch *list,
10461067
void rockchip_clk_register_branches(struct rockchip_clk_provider *ctx,
10471068
struct rockchip_clk_branch *list,
10481069
unsigned int nr_clk);
1070+
void rockchip_clk_register_late_branches(struct device *dev,
1071+
struct rockchip_clk_provider *ctx,
1072+
struct rockchip_clk_branch *list,
1073+
unsigned int nr_clk);
10491074
void rockchip_clk_register_plls(struct rockchip_clk_provider *ctx,
10501075
struct rockchip_pll_clock *pll_list,
10511076
unsigned int nr_pll, int grf_lock_offset);

drivers/clk/rockchip/gate-link.c

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
// SPDX-License-Identifier: GPL-2.0-or-later
2+
/*
3+
* Copyright (c) 2024 Collabora Ltd.
4+
* Author: Sebastian Reichel <sebastian.reichel@collabora.com>
5+
*/
6+
7+
#include <linux/clk.h>
8+
#include <linux/platform_device.h>
9+
#include <linux/pm_clock.h>
10+
#include <linux/pm_runtime.h>
11+
#include <linux/property.h>
12+
#include "clk.h"
13+
14+
static int rk_clk_gate_link_register(struct device *dev,
15+
struct rockchip_clk_provider *ctx,
16+
struct rockchip_clk_branch *clkbr)
17+
{
18+
unsigned long flags = clkbr->flags | CLK_SET_RATE_PARENT;
19+
struct clk *clk;
20+
21+
clk = clk_register_gate(dev, clkbr->name, clkbr->parent_names[0],
22+
flags, ctx->reg_base + clkbr->gate_offset,
23+
clkbr->gate_shift, clkbr->gate_flags,
24+
&ctx->lock);
25+
26+
if (IS_ERR(clk))
27+
return PTR_ERR(clk);
28+
29+
rockchip_clk_set_lookup(ctx, clk, clkbr->id);
30+
return 0;
31+
}
32+
33+
static int rk_clk_gate_link_probe(struct platform_device *pdev)
34+
{
35+
struct rockchip_gate_link_platdata *pdata;
36+
struct device *dev = &pdev->dev;
37+
struct clk *linked_clk;
38+
int ret;
39+
40+
pdata = dev_get_platdata(dev);
41+
if (!pdata)
42+
return dev_err_probe(dev, -ENODEV, "missing platform data");
43+
44+
ret = devm_pm_runtime_enable(dev);
45+
if (ret)
46+
return ret;
47+
48+
ret = devm_pm_clk_create(dev);
49+
if (ret)
50+
return ret;
51+
52+
linked_clk = rockchip_clk_get_lookup(pdata->ctx, pdata->clkbr->linked_clk_id);
53+
ret = pm_clk_add_clk(dev, linked_clk);
54+
if (ret)
55+
return ret;
56+
57+
ret = rk_clk_gate_link_register(dev, pdata->ctx, pdata->clkbr);
58+
if (ret)
59+
goto err;
60+
61+
return 0;
62+
63+
err:
64+
pm_clk_remove_clk(dev, linked_clk);
65+
return ret;
66+
}
67+
68+
static const struct dev_pm_ops rk_clk_gate_link_pm_ops = {
69+
SET_RUNTIME_PM_OPS(pm_clk_suspend, pm_clk_resume, NULL)
70+
};
71+
72+
static struct platform_driver rk_clk_gate_link_driver = {
73+
.probe = rk_clk_gate_link_probe,
74+
.driver = {
75+
.name = "rockchip-gate-link-clk",
76+
.pm = &rk_clk_gate_link_pm_ops,
77+
.suppress_bind_attrs = true,
78+
},
79+
};
80+
81+
static int __init rk_clk_gate_link_drv_register(void)
82+
{
83+
return platform_driver_register(&rk_clk_gate_link_driver);
84+
}
85+
core_initcall(rk_clk_gate_link_drv_register);

0 commit comments

Comments
 (0)