Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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 <nicolas.frattaroli@collabora.com>
Link: https://lore.kernel.org/r/20250502-rk3576-sai-v3-3-376cef19dd7c@collabora.com
Signed-off-by: Heiko Stuebner <heiko@sntech.de>

authored by

Nicolas Frattaroli and committed by
Heiko Stuebner
e277168c 70a114da

+134 -1
+1
drivers/clk/rockchip/Makefile
··· 14 14 clk-rockchip-y += clk-muxgrf.o 15 15 clk-rockchip-y += clk-ddr.o 16 16 clk-rockchip-y += gate-link.o 17 + clk-rockchip-y += gate-grf.o 17 18 clk-rockchip-$(CONFIG_RESET_CONTROLLER) += softrst.o 18 19 19 20 obj-$(CONFIG_CLK_PX30) += clk-px30.o
+8 -1
drivers/clk/rockchip/clk.c
··· 509 509 clk = NULL; 510 510 511 511 /* 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) && 513 513 list->grf_type != grf_type_sys) { 514 514 hash_for_each_possible(ctx->aux_grf_table, agrf, node, list->grf_type) { 515 515 if (agrf->type == list->grf_type) { ··· 587 587 list->parent_names[0], flags, 588 588 ctx->reg_base + list->gate_offset, 589 589 list->gate_shift, list->gate_flags, &ctx->lock); 590 + 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); 590 597 break; 591 598 case branch_composite: 592 599 clk = rockchip_clk_register_branch(list->name,
+20
drivers/clk/rockchip/clk.h
··· 647 647 int flags, struct regmap *grf, int reg, 648 648 int shift, int width, int mux_flags); 649 649 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 + 650 655 #define PNAME(x) static const char *const x[] __initconst 651 656 652 657 enum rockchip_clk_branch_type { ··· 661 656 branch_divider, 662 657 branch_fraction_divider, 663 658 branch_gate, 659 + branch_grf_gate, 664 660 branch_linked_gate, 665 661 branch_mmc, 666 662 branch_inverter, ··· 989 983 .gate_offset = o, \ 990 984 .gate_shift = b, \ 991 985 .gate_flags = gf, \ 986 + } 987 + 988 + #define GATE_GRF(_id, cname, pname, f, o, b, gf, gt) \ 989 + { \ 990 + .id = _id, \ 991 + .branch_type = branch_grf_gate, \ 992 + .name = cname, \ 993 + .parent_names = (const char *[]){ pname }, \ 994 + .num_parents = 1, \ 995 + .flags = f, \ 996 + .gate_offset = o, \ 997 + .gate_shift = b, \ 998 + .gate_flags = gf, \ 999 + .grf_type = gt, \ 992 1000 } 993 1001 994 1002 #define GATE_LINK(_id, cname, pname, linkedclk, f, o, b, gf) \
+105
drivers/clk/rockchip/gate-grf.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + /* 3 + * Copyright (c) 2025 Collabora Ltd. 4 + * Author: Nicolas Frattaroli <nicolas.frattaroli@collabora.com> 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 + }