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 } 106