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: renesas: Add support for R9A09G077 SoC

RZ/T2H has 2 register blocks at different addresses.

The clock tree has configurable dividers and mux selectors.
Add these new clock types, new register layout type, and
registration code for mux and div in registration callback.

Signed-off-by: Thierry Bultel <thierry.bultel.yh@bp.renesas.com>
Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
Link: https://lore.kernel.org/20250515141828.43444-6-thierry.bultel.yh@bp.renesas.com
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>

authored by

Thierry Bultel and committed by
Geert Uytterhoeven
065fe720 e5e8a9cc

+346 -2
+5
drivers/clk/renesas/Kconfig
··· 43 43 select CLK_R9A09G047 if ARCH_R9A09G047 44 44 select CLK_R9A09G056 if ARCH_R9A09G056 45 45 select CLK_R9A09G057 if ARCH_R9A09G057 46 + select CLK_R9A09G077 if ARCH_R9A09G077 46 47 select CLK_SH73A0 if ARCH_SH73A0 47 48 48 49 if CLK_RENESAS ··· 208 207 config CLK_R9A09G057 209 208 bool "RZ/V2H(P) clock support" if COMPILE_TEST 210 209 select CLK_RZV2H 210 + 211 + config CLK_R9A09G077 212 + bool "RZ/T2H clock support" if COMPILE_TEST 213 + select CLK_RENESAS_CPG_MSSR 211 214 212 215 config CLK_SH73A0 213 216 bool "SH-Mobile AG5 clock support" if COMPILE_TEST
+1
drivers/clk/renesas/Makefile
··· 40 40 obj-$(CONFIG_CLK_R9A09G047) += r9a09g047-cpg.o 41 41 obj-$(CONFIG_CLK_R9A09G056) += r9a09g056-cpg.o 42 42 obj-$(CONFIG_CLK_R9A09G057) += r9a09g057-cpg.o 43 + obj-$(CONFIG_CLK_R9A09G077) += r9a09g077-cpg.o 43 44 obj-$(CONFIG_CLK_SH73A0) += clk-sh73a0.o 44 45 45 46 # Family
+241
drivers/clk/renesas/r9a09g077-cpg.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * r9a09g077 Clock Pulse Generator / Module Standby and Software Reset 4 + * 5 + * Copyright (C) 2025 Renesas Electronics Corp. 6 + * 7 + */ 8 + 9 + #include <linux/bitfield.h> 10 + #include <linux/clk-provider.h> 11 + #include <linux/device.h> 12 + #include <linux/init.h> 13 + #include <linux/kernel.h> 14 + 15 + #include <dt-bindings/clock/renesas,r9a09g077-cpg-mssr.h> 16 + #include "renesas-cpg-mssr.h" 17 + 18 + #define RZT2H_REG_BLOCK_SHIFT 11 19 + #define RZT2H_REG_OFFSET_MASK GENMASK(10, 0) 20 + #define RZT2H_REG_CONF(block, offset) (((block) << RZT2H_REG_BLOCK_SHIFT) | \ 21 + ((offset) & RZT2H_REG_OFFSET_MASK)) 22 + 23 + #define RZT2H_REG_BLOCK(x) ((x) >> RZT2H_REG_BLOCK_SHIFT) 24 + #define RZT2H_REG_OFFSET(x) ((x) & RZT2H_REG_OFFSET_MASK) 25 + 26 + #define SCKCR RZT2H_REG_CONF(0, 0x00) 27 + #define SCKCR2 RZT2H_REG_CONF(1, 0x04) 28 + #define SCKCR3 RZT2H_REG_CONF(0, 0x08) 29 + 30 + #define OFFSET_MASK GENMASK(31, 20) 31 + #define SHIFT_MASK GENMASK(19, 12) 32 + #define WIDTH_MASK GENMASK(11, 8) 33 + 34 + #define CONF_PACK(offset, shift, width) \ 35 + (FIELD_PREP_CONST(OFFSET_MASK, (offset)) | \ 36 + FIELD_PREP_CONST(SHIFT_MASK, (shift)) | \ 37 + FIELD_PREP_CONST(WIDTH_MASK, (width))) 38 + 39 + #define GET_SHIFT(val) FIELD_GET(SHIFT_MASK, val) 40 + #define GET_WIDTH(val) FIELD_GET(WIDTH_MASK, val) 41 + #define GET_REG_OFFSET(val) FIELD_GET(OFFSET_MASK, val) 42 + 43 + #define DIVCA55C0 CONF_PACK(SCKCR2, 8, 1) 44 + #define DIVCA55C1 CONF_PACK(SCKCR2, 9, 1) 45 + #define DIVCA55C2 CONF_PACK(SCKCR2, 10, 1) 46 + #define DIVCA55C3 CONF_PACK(SCKCR2, 11, 1) 47 + #define DIVCA55S CONF_PACK(SCKCR2, 12, 1) 48 + 49 + #define DIVSCI0ASYNC CONF_PACK(SCKCR3, 6, 2) 50 + 51 + #define SEL_PLL CONF_PACK(SCKCR, 22, 1) 52 + 53 + 54 + enum rzt2h_clk_types { 55 + CLK_TYPE_RZT2H_DIV = CLK_TYPE_CUSTOM, /* Clock with divider */ 56 + CLK_TYPE_RZT2H_MUX, /* Clock with clock source selector */ 57 + }; 58 + 59 + #define DEF_DIV(_name, _id, _parent, _conf, _dtable) \ 60 + DEF_TYPE(_name, _id, CLK_TYPE_RZT2H_DIV, .conf = _conf, \ 61 + .parent = _parent, .dtable = _dtable, .flag = 0) 62 + #define DEF_MUX(_name, _id, _conf, _parent_names, _num_parents, _mux_flags) \ 63 + DEF_TYPE(_name, _id, CLK_TYPE_RZT2H_MUX, .conf = _conf, \ 64 + .parent_names = _parent_names, .num_parents = _num_parents, \ 65 + .flag = 0, .mux_flags = _mux_flags) 66 + 67 + enum clk_ids { 68 + /* Core Clock Outputs exported to DT */ 69 + LAST_DT_CORE_CLK = R9A09G077_CLK_PCLKM, 70 + 71 + /* External Input Clocks */ 72 + CLK_EXTAL, 73 + 74 + /* Internal Core Clocks */ 75 + CLK_LOCO, 76 + CLK_PLL0, 77 + CLK_PLL1, 78 + CLK_PLL4, 79 + CLK_SEL_CLK_PLL0, 80 + CLK_SEL_CLK_PLL1, 81 + CLK_SEL_CLK_PLL4, 82 + CLK_PLL4D1, 83 + CLK_SCI0ASYNC, 84 + 85 + /* Module Clocks */ 86 + MOD_CLK_BASE, 87 + }; 88 + 89 + static const struct clk_div_table dtable_1_2[] = { 90 + {0, 2}, 91 + {1, 1}, 92 + {0, 0}, 93 + }; 94 + 95 + static const struct clk_div_table dtable_24_25_30_32[] = { 96 + {0, 32}, 97 + {1, 30}, 98 + {2, 25}, 99 + {3, 24}, 100 + {0, 0}, 101 + }; 102 + 103 + /* Mux clock tables */ 104 + 105 + static const char * const sel_clk_pll0[] = { ".loco", ".pll0" }; 106 + static const char * const sel_clk_pll1[] = { ".loco", ".pll1" }; 107 + static const char * const sel_clk_pll4[] = { ".loco", ".pll4" }; 108 + 109 + static const struct cpg_core_clk r9a09g077_core_clks[] __initconst = { 110 + /* External Clock Inputs */ 111 + DEF_INPUT("extal", CLK_EXTAL), 112 + 113 + /* Internal Core Clocks */ 114 + DEF_RATE(".loco", CLK_LOCO, 1000 * 1000), 115 + DEF_FIXED(".pll0", CLK_PLL0, CLK_EXTAL, 1, 48), 116 + DEF_FIXED(".pll1", CLK_PLL1, CLK_EXTAL, 1, 40), 117 + DEF_FIXED(".pll4", CLK_PLL4, CLK_EXTAL, 1, 96), 118 + 119 + DEF_MUX(".sel_clk_pll0", CLK_SEL_CLK_PLL0, SEL_PLL, 120 + sel_clk_pll0, ARRAY_SIZE(sel_clk_pll0), CLK_MUX_READ_ONLY), 121 + DEF_MUX(".sel_clk_pll1", CLK_SEL_CLK_PLL1, SEL_PLL, 122 + sel_clk_pll1, ARRAY_SIZE(sel_clk_pll1), CLK_MUX_READ_ONLY), 123 + DEF_MUX(".sel_clk_pll4", CLK_SEL_CLK_PLL4, SEL_PLL, 124 + sel_clk_pll4, ARRAY_SIZE(sel_clk_pll4), CLK_MUX_READ_ONLY), 125 + 126 + DEF_FIXED(".pll4d1", CLK_PLL4D1, CLK_SEL_CLK_PLL4, 1, 1), 127 + DEF_DIV(".sci0async", CLK_SCI0ASYNC, CLK_PLL4D1, DIVSCI0ASYNC, 128 + dtable_24_25_30_32), 129 + 130 + /* Core output clk */ 131 + DEF_DIV("CA55C0", R9A09G077_CLK_CA55C0, CLK_SEL_CLK_PLL0, DIVCA55C0, 132 + dtable_1_2), 133 + DEF_DIV("CA55C1", R9A09G077_CLK_CA55C1, CLK_SEL_CLK_PLL0, DIVCA55C1, 134 + dtable_1_2), 135 + DEF_DIV("CA55C2", R9A09G077_CLK_CA55C2, CLK_SEL_CLK_PLL0, DIVCA55C2, 136 + dtable_1_2), 137 + DEF_DIV("CA55C3", R9A09G077_CLK_CA55C3, CLK_SEL_CLK_PLL0, DIVCA55C3, 138 + dtable_1_2), 139 + DEF_DIV("CA55S", R9A09G077_CLK_CA55S, CLK_SEL_CLK_PLL0, DIVCA55S, 140 + dtable_1_2), 141 + DEF_FIXED("PCLKGPTL", R9A09G077_CLK_PCLKGPTL, CLK_SEL_CLK_PLL1, 2, 1), 142 + DEF_FIXED("PCLKM", R9A09G077_CLK_PCLKM, CLK_SEL_CLK_PLL1, 8, 1), 143 + }; 144 + 145 + static const struct mssr_mod_clk r9a09g077_mod_clks[] __initconst = { 146 + DEF_MOD("sci0fck", 8, CLK_SCI0ASYNC), 147 + }; 148 + 149 + static struct clk * __init 150 + r9a09g077_cpg_div_clk_register(struct device *dev, 151 + const struct cpg_core_clk *core, 152 + void __iomem *addr, struct cpg_mssr_pub *pub) 153 + { 154 + const struct clk *parent; 155 + const char *parent_name; 156 + struct clk_hw *clk_hw; 157 + 158 + parent = pub->clks[core->parent]; 159 + if (IS_ERR(parent)) 160 + return ERR_CAST(parent); 161 + 162 + parent_name = __clk_get_name(parent); 163 + 164 + if (core->dtable) 165 + clk_hw = clk_hw_register_divider_table(dev, core->name, 166 + parent_name, 0, 167 + addr, 168 + GET_SHIFT(core->conf), 169 + GET_WIDTH(core->conf), 170 + core->flag, 171 + core->dtable, 172 + &pub->rmw_lock); 173 + else 174 + clk_hw = clk_hw_register_divider(dev, core->name, 175 + parent_name, 0, 176 + addr, 177 + GET_SHIFT(core->conf), 178 + GET_WIDTH(core->conf), 179 + core->flag, &pub->rmw_lock); 180 + 181 + if (IS_ERR(clk_hw)) 182 + return ERR_CAST(clk_hw); 183 + 184 + return clk_hw->clk; 185 + 186 + } 187 + 188 + static struct clk * __init 189 + r9a09g077_cpg_mux_clk_register(struct device *dev, 190 + const struct cpg_core_clk *core, 191 + void __iomem *addr, struct cpg_mssr_pub *pub) 192 + { 193 + struct clk_hw *clk_hw; 194 + 195 + clk_hw = devm_clk_hw_register_mux(dev, core->name, 196 + core->parent_names, core->num_parents, 197 + core->flag, 198 + addr, 199 + GET_SHIFT(core->conf), 200 + GET_WIDTH(core->conf), 201 + core->mux_flags, &pub->rmw_lock); 202 + if (IS_ERR(clk_hw)) 203 + return ERR_CAST(clk_hw); 204 + 205 + return clk_hw->clk; 206 + } 207 + 208 + static struct clk * __init 209 + r9a09g077_cpg_clk_register(struct device *dev, const struct cpg_core_clk *core, 210 + const struct cpg_mssr_info *info, 211 + struct cpg_mssr_pub *pub) 212 + { 213 + u32 offset = GET_REG_OFFSET(core->conf); 214 + void __iomem *base = RZT2H_REG_BLOCK(offset) ? pub->base1 : pub->base0; 215 + void __iomem *addr = base + RZT2H_REG_OFFSET(offset); 216 + 217 + switch (core->type) { 218 + case CLK_TYPE_RZT2H_DIV: 219 + return r9a09g077_cpg_div_clk_register(dev, core, addr, pub); 220 + case CLK_TYPE_RZT2H_MUX: 221 + return r9a09g077_cpg_mux_clk_register(dev, core, addr, pub); 222 + default: 223 + return ERR_PTR(-EINVAL); 224 + } 225 + } 226 + 227 + const struct cpg_mssr_info r9a09g077_cpg_mssr_info = { 228 + /* Core Clocks */ 229 + .core_clks = r9a09g077_core_clks, 230 + .num_core_clks = ARRAY_SIZE(r9a09g077_core_clks), 231 + .last_dt_core_clk = LAST_DT_CORE_CLK, 232 + .num_total_core_clks = MOD_CLK_BASE, 233 + 234 + /* Module Clocks */ 235 + .mod_clks = r9a09g077_mod_clks, 236 + .num_mod_clks = ARRAY_SIZE(r9a09g077_mod_clks), 237 + .num_hw_mod_clks = 14 * 32, 238 + 239 + .reg_layout = CLK_REG_LAYOUT_RZ_T2H, 240 + .cpg_clk_register = r9a09g077_cpg_clk_register, 241 + };
+87 -2
drivers/clk/renesas/renesas-cpg-mssr.c
··· 81 81 }; 82 82 83 83 /* 84 + * Module Stop Control Register (RZ/T2H) 85 + * RZ/T2H has 2 registers blocks, 86 + * Bit 12 is used to differentiate them 87 + */ 88 + 89 + #define RZT2H_MSTPCR_BLOCK_SHIFT 12 90 + #define RZT2H_MSTPCR_OFFSET_MASK GENMASK(11, 0) 91 + #define RZT2H_MSTPCR(block, offset) (((block) << RZT2H_MSTPCR_BLOCK_SHIFT) | \ 92 + ((offset) & RZT2H_MSTPCR_OFFSET_MASK)) 93 + 94 + #define RZT2H_MSTPCR_BLOCK(x) ((x) >> RZT2H_MSTPCR_BLOCK_SHIFT) 95 + #define RZT2H_MSTPCR_OFFSET(x) ((x) & RZT2H_MSTPCR_OFFSET_MASK) 96 + 97 + static const u16 mstpcr_for_rzt2h[] = { 98 + RZT2H_MSTPCR(0, 0x300), /* MSTPCRA */ 99 + RZT2H_MSTPCR(0, 0x304), /* MSTPCRB */ 100 + RZT2H_MSTPCR(0, 0x308), /* MSTPCRC */ 101 + RZT2H_MSTPCR(0, 0x30c), /* MSTPCRD */ 102 + RZT2H_MSTPCR(0, 0x310), /* MSTPCRE */ 103 + 0, 104 + RZT2H_MSTPCR(1, 0x318), /* MSTPCRG */ 105 + 0, 106 + RZT2H_MSTPCR(1, 0x320), /* MSTPCRI */ 107 + RZT2H_MSTPCR(0, 0x324), /* MSTPCRJ */ 108 + RZT2H_MSTPCR(0, 0x328), /* MSTPCRK */ 109 + RZT2H_MSTPCR(0, 0x32c), /* MSTPCRL */ 110 + RZT2H_MSTPCR(0, 0x330), /* MSTPCRM */ 111 + RZT2H_MSTPCR(1, 0x334), /* MSTPCRN */ 112 + }; 113 + 114 + /* 84 115 * Standby Control Register offsets (RZ/A) 85 116 * Base address is FRQCR register 86 117 */ ··· 219 188 220 189 #define to_mstp_clock(_hw) container_of(_hw, struct mstp_clock, hw) 221 190 191 + static u32 cpg_rzt2h_mstp_read(struct clk_hw *hw, u16 offset) 192 + { 193 + struct mstp_clock *clock = to_mstp_clock(hw); 194 + struct cpg_mssr_priv *priv = clock->priv; 195 + void __iomem *base = 196 + RZT2H_MSTPCR_BLOCK(offset) ? priv->pub.base1 : priv->pub.base0; 197 + 198 + return readl(base + RZT2H_MSTPCR_OFFSET(offset)); 199 + } 200 + 201 + static void cpg_rzt2h_mstp_write(struct clk_hw *hw, u16 offset, u32 value) 202 + { 203 + struct mstp_clock *clock = to_mstp_clock(hw); 204 + struct cpg_mssr_priv *priv = clock->priv; 205 + void __iomem *base = 206 + RZT2H_MSTPCR_BLOCK(offset) ? priv->pub.base1 : priv->pub.base0; 207 + 208 + writel(value, base + RZT2H_MSTPCR_OFFSET(offset)); 209 + } 210 + 222 211 static int cpg_mstp_clock_endisable(struct clk_hw *hw, bool enable) 223 212 { 224 213 struct mstp_clock *clock = to_mstp_clock(hw); ··· 267 216 readb(priv->pub.base0 + priv->control_regs[reg]); 268 217 barrier_data(priv->pub.base0 + priv->control_regs[reg]); 269 218 219 + } else if (priv->reg_layout == CLK_REG_LAYOUT_RZ_T2H) { 220 + value = cpg_rzt2h_mstp_read(hw, 221 + priv->control_regs[reg]); 222 + 223 + if (enable) 224 + value &= ~bitmask; 225 + else 226 + value |= bitmask; 227 + 228 + cpg_rzt2h_mstp_write(hw, 229 + priv->control_regs[reg], 230 + value); 270 231 } else { 271 232 value = readl(priv->pub.base0 + priv->control_regs[reg]); 272 233 if (enable) ··· 290 227 291 228 spin_unlock_irqrestore(&priv->pub.rmw_lock, flags); 292 229 293 - if (!enable || priv->reg_layout == CLK_REG_LAYOUT_RZ_A) 230 + if (!enable || priv->reg_layout == CLK_REG_LAYOUT_RZ_A || 231 + priv->reg_layout == CLK_REG_LAYOUT_RZ_T2H) 294 232 return 0; 295 233 296 234 error = readl_poll_timeout_atomic(priv->pub.base0 + priv->status_regs[reg], ··· 322 258 323 259 if (priv->reg_layout == CLK_REG_LAYOUT_RZ_A) 324 260 value = readb(priv->pub.base0 + priv->control_regs[reg]); 261 + else if (priv->reg_layout == CLK_REG_LAYOUT_RZ_T2H) 262 + value = cpg_rzt2h_mstp_read(hw, 263 + priv->control_regs[reg]); 325 264 else 326 265 value = readl(priv->pub.base0 + priv->status_regs[reg]); 327 266 ··· 936 869 .data = &r8a779h0_cpg_mssr_info, 937 870 }, 938 871 #endif 872 + #ifdef CONFIG_CLK_R9A09G077 873 + { 874 + .compatible = "renesas,r9a09g077-cpg-mssr", 875 + .data = &r9a09g077_cpg_mssr_info, 876 + }, 877 + #endif 939 878 { /* sentinel */ } 940 879 }; 941 880 ··· 1138 1065 error = -ENOMEM; 1139 1066 goto out_err; 1140 1067 } 1068 + if (info->reg_layout == CLK_REG_LAYOUT_RZ_T2H) { 1069 + priv->pub.base1 = of_iomap(np, 1); 1070 + if (!priv->pub.base1) { 1071 + error = -ENOMEM; 1072 + goto out_err; 1073 + } 1074 + } 1141 1075 1142 1076 priv->num_core_clks = info->num_total_core_clks; 1143 1077 priv->num_mod_clks = info->num_hw_mod_clks; ··· 1158 1078 priv->reset_clear_regs = srstclr; 1159 1079 } else if (priv->reg_layout == CLK_REG_LAYOUT_RZ_A) { 1160 1080 priv->control_regs = stbcr; 1081 + } else if (priv->reg_layout == CLK_REG_LAYOUT_RZ_T2H) { 1082 + priv->control_regs = mstpcr_for_rzt2h; 1161 1083 } else if (priv->reg_layout == CLK_REG_LAYOUT_RCAR_GEN4) { 1162 1084 priv->status_regs = mstpsr_for_gen4; 1163 1085 priv->control_regs = mstpcr_for_gen4; ··· 1190 1108 out_err: 1191 1109 if (priv->pub.base0) 1192 1110 iounmap(priv->pub.base0); 1111 + if (priv->pub.base1) 1112 + iounmap(priv->pub.base1); 1193 1113 kfree(priv); 1194 1114 1195 1115 return error; ··· 1256 1172 goto reserve_exit; 1257 1173 1258 1174 /* Reset Controller not supported for Standby Control SoCs */ 1259 - if (priv->reg_layout == CLK_REG_LAYOUT_RZ_A) 1175 + if (priv->reg_layout == CLK_REG_LAYOUT_RZ_A || 1176 + priv->reg_layout == CLK_REG_LAYOUT_RZ_T2H) 1260 1177 goto reserve_exit; 1261 1178 1262 1179 error = cpg_mssr_reset_controller_register(priv);
+12
drivers/clk/renesas/renesas-cpg-mssr.h
··· 29 29 unsigned int div; 30 30 unsigned int mult; 31 31 unsigned int offset; 32 + union { 33 + const char * const *parent_names; 34 + const struct clk_div_table *dtable; 35 + }; 36 + u32 conf; 37 + u16 flag; 38 + u8 mux_flags; 39 + u8 num_parents; 32 40 }; 33 41 34 42 /** 35 43 * struct cpg_mssr_pub - data shared with device-specific clk registration code 36 44 * 37 45 * @base0: CPG/MSSR register block base0 address 46 + * @base1: CPG/MSSR register block base1 address 38 47 * @notifiers: Notifier chain to save/restore clock state for system resume 39 48 * @rmw_lock: protects RMW register accesses 40 49 * @clks: pointer to clocks 41 50 */ 42 51 struct cpg_mssr_pub { 43 52 void __iomem *base0; 53 + void __iomem *base1; 44 54 struct raw_notifier_head notifiers; 45 55 spinlock_t rmw_lock; 46 56 struct clk **clks; ··· 116 106 CLK_REG_LAYOUT_RCAR_GEN2_AND_GEN3 = 0, 117 107 CLK_REG_LAYOUT_RZ_A, 118 108 CLK_REG_LAYOUT_RCAR_GEN4, 109 + CLK_REG_LAYOUT_RZ_T2H, 119 110 }; 120 111 121 112 /** ··· 208 197 extern const struct cpg_mssr_info r8a779f0_cpg_mssr_info; 209 198 extern const struct cpg_mssr_info r8a779g0_cpg_mssr_info; 210 199 extern const struct cpg_mssr_info r8a779h0_cpg_mssr_info; 200 + extern const struct cpg_mssr_info r9a09g077_cpg_mssr_info; 211 201 212 202 void __init cpg_mssr_early_init(struct device_node *np, 213 203 const struct cpg_mssr_info *info);