Skip to content

Commit e277168

Browse files
Nicolas Frattarolimmind
authored andcommitted
clk: rockchip: introduce GRF gates
Some rockchip SoCs, namely the RK3576, have bits in a General Register File (GRF) that act just like clock gates. The downstream vendor kernel simply maps over the already mapped GRF range with a generic clock gate driver. This solution isn't suitable for upstream, as a memory range will be in use by multiple drivers at the same time, and it leaks implementation details into the device tree. Instead, implement this with a new clock branch type in the Rockchip clock driver: GRF gates. Somewhat akin to MUXGRF, this clock branch depends on the type of GRF, but functions like a gate instead. Signed-off-by: Nicolas Frattaroli <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Heiko Stuebner <[email protected]>
1 parent 70a114d commit e277168

File tree

4 files changed

+134
-1
lines changed

4 files changed

+134
-1
lines changed

drivers/clk/rockchip/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ clk-rockchip-y += clk-mmc-phase.o
1414
clk-rockchip-y += clk-muxgrf.o
1515
clk-rockchip-y += clk-ddr.o
1616
clk-rockchip-y += gate-link.o
17+
clk-rockchip-y += gate-grf.o
1718
clk-rockchip-$(CONFIG_RESET_CONTROLLER) += softrst.o
1819

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

drivers/clk/rockchip/clk.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -509,7 +509,7 @@ void rockchip_clk_register_branches(struct rockchip_clk_provider *ctx,
509509
clk = NULL;
510510

511511
/* for GRF-dependent branches, choose the right grf first */
512-
if (list->branch_type == branch_muxgrf &&
512+
if ((list->branch_type == branch_muxgrf || list->branch_type == branch_grf_gate) &&
513513
list->grf_type != grf_type_sys) {
514514
hash_for_each_possible(ctx->aux_grf_table, agrf, node, list->grf_type) {
515515
if (agrf->type == list->grf_type) {
@@ -588,6 +588,13 @@ void rockchip_clk_register_branches(struct rockchip_clk_provider *ctx,
588588
ctx->reg_base + list->gate_offset,
589589
list->gate_shift, list->gate_flags, &ctx->lock);
590590
break;
591+
case branch_grf_gate:
592+
flags |= CLK_SET_RATE_PARENT;
593+
clk = rockchip_clk_register_gate_grf(list->name,
594+
list->parent_names[0], flags, grf,
595+
list->gate_offset, list->gate_shift,
596+
list->gate_flags);
597+
break;
591598
case branch_composite:
592599
clk = rockchip_clk_register_branch(list->name,
593600
list->parent_names, list->num_parents,

drivers/clk/rockchip/clk.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -647,6 +647,11 @@ struct clk *rockchip_clk_register_muxgrf(const char *name,
647647
int flags, struct regmap *grf, int reg,
648648
int shift, int width, int mux_flags);
649649

650+
struct clk *rockchip_clk_register_gate_grf(const char *name,
651+
const char *parent_name, unsigned long flags,
652+
struct regmap *regmap, unsigned int reg,
653+
unsigned int shift, u8 gate_flags);
654+
650655
#define PNAME(x) static const char *const x[] __initconst
651656

652657
enum rockchip_clk_branch_type {
@@ -656,6 +661,7 @@ enum rockchip_clk_branch_type {
656661
branch_divider,
657662
branch_fraction_divider,
658663
branch_gate,
664+
branch_grf_gate,
659665
branch_linked_gate,
660666
branch_mmc,
661667
branch_inverter,
@@ -985,6 +991,20 @@ struct rockchip_clk_branch {
985991
.gate_flags = gf, \
986992
}
987993

994+
#define GATE_GRF(_id, cname, pname, f, o, b, gf, gt) \
995+
{ \
996+
.id = _id, \
997+
.branch_type = branch_grf_gate, \
998+
.name = cname, \
999+
.parent_names = (const char *[]){ pname }, \
1000+
.num_parents = 1, \
1001+
.flags = f, \
1002+
.gate_offset = o, \
1003+
.gate_shift = b, \
1004+
.gate_flags = gf, \
1005+
.grf_type = gt, \
1006+
}
1007+
9881008
#define GATE_LINK(_id, cname, pname, linkedclk, f, o, b, gf) \
9891009
{ \
9901010
.id = _id, \

drivers/clk/rockchip/gate-grf.c

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
// SPDX-License-Identifier: GPL-2.0-or-later
2+
/*
3+
* Copyright (c) 2025 Collabora Ltd.
4+
* Author: Nicolas Frattaroli <[email protected]>
5+
*
6+
* Certain clocks on Rockchip are "gated" behind an additional register bit
7+
* write in a GRF register, such as the SAI MCLKs on RK3576. This code
8+
* implements a clock driver for these types of gates, based on regmaps.
9+
*/
10+
11+
#include <linux/clk.h>
12+
#include <linux/clk-provider.h>
13+
#include <linux/regmap.h>
14+
#include <linux/slab.h>
15+
#include "clk.h"
16+
17+
struct rockchip_gate_grf {
18+
struct clk_hw hw;
19+
struct regmap *regmap;
20+
unsigned int reg;
21+
unsigned int shift;
22+
u8 flags;
23+
};
24+
25+
#define to_gate_grf(_hw) container_of(_hw, struct rockchip_gate_grf, hw)
26+
27+
static int rockchip_gate_grf_enable(struct clk_hw *hw)
28+
{
29+
struct rockchip_gate_grf *gate = to_gate_grf(hw);
30+
u32 val = !(gate->flags & CLK_GATE_SET_TO_DISABLE) ? BIT(gate->shift) : 0;
31+
u32 hiword = ((gate->flags & CLK_GATE_HIWORD_MASK) ? 1 : 0) << (gate->shift + 16);
32+
int ret;
33+
34+
ret = regmap_update_bits(gate->regmap, gate->reg,
35+
hiword | BIT(gate->shift), hiword | val);
36+
37+
return ret;
38+
}
39+
40+
static void rockchip_gate_grf_disable(struct clk_hw *hw)
41+
{
42+
struct rockchip_gate_grf *gate = to_gate_grf(hw);
43+
u32 val = !(gate->flags & CLK_GATE_SET_TO_DISABLE) ? 0 : BIT(gate->shift);
44+
u32 hiword = ((gate->flags & CLK_GATE_HIWORD_MASK) ? 1 : 0) << (gate->shift + 16);
45+
46+
regmap_update_bits(gate->regmap, gate->reg,
47+
hiword | BIT(gate->shift), hiword | val);
48+
}
49+
50+
static int rockchip_gate_grf_is_enabled(struct clk_hw *hw)
51+
{
52+
struct rockchip_gate_grf *gate = to_gate_grf(hw);
53+
bool invert = !!(gate->flags & CLK_GATE_SET_TO_DISABLE);
54+
int ret;
55+
56+
ret = regmap_test_bits(gate->regmap, gate->reg, BIT(gate->shift));
57+
if (ret < 0)
58+
ret = 0;
59+
60+
return invert ? 1 - ret : ret;
61+
62+
}
63+
64+
static const struct clk_ops rockchip_gate_grf_ops = {
65+
.enable = rockchip_gate_grf_enable,
66+
.disable = rockchip_gate_grf_disable,
67+
.is_enabled = rockchip_gate_grf_is_enabled,
68+
};
69+
70+
struct clk *rockchip_clk_register_gate_grf(const char *name,
71+
const char *parent_name, unsigned long flags,
72+
struct regmap *regmap, unsigned int reg, unsigned int shift,
73+
u8 gate_flags)
74+
{
75+
struct rockchip_gate_grf *gate;
76+
struct clk_init_data init;
77+
struct clk *clk;
78+
79+
if (IS_ERR(regmap)) {
80+
pr_err("%s: regmap not available\n", __func__);
81+
return ERR_PTR(-EOPNOTSUPP);
82+
}
83+
84+
gate = kzalloc(sizeof(*gate), GFP_KERNEL);
85+
if (!gate)
86+
return ERR_PTR(-ENOMEM);
87+
88+
init.name = name;
89+
init.flags = flags;
90+
init.num_parents = parent_name ? 1 : 0;
91+
init.parent_names = parent_name ? &parent_name : NULL;
92+
init.ops = &rockchip_gate_grf_ops;
93+
94+
gate->hw.init = &init;
95+
gate->regmap = regmap;
96+
gate->reg = reg;
97+
gate->shift = shift;
98+
gate->flags = gate_flags;
99+
100+
clk = clk_register(NULL, &gate->hw);
101+
if (IS_ERR(clk))
102+
kfree(gate);
103+
104+
return clk;
105+
}

0 commit comments

Comments
 (0)