| 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | /* |
| 3 | * Sophgo SG2042 RP clock Driver |
| 4 | * |
| 5 | * Copyright (C) 2024 Sophgo Technology Inc. |
| 6 | * Copyright (C) 2024 Chen Wang <unicorn_wang@outlook.com> |
| 7 | */ |
| 8 | |
| 9 | #include <linux/array_size.h> |
| 10 | #include <linux/clk-provider.h> |
| 11 | #include <linux/platform_device.h> |
| 12 | |
| 13 | #include <dt-bindings/clock/sophgo,sg2042-rpgate.h> |
| 14 | |
| 15 | #include "clk-sg2042.h" |
| 16 | |
| 17 | #define R_SYSGATE_BEGIN 0x0368 |
| 18 | #define R_RP_RXU_CLK_ENABLE (0x0368 - R_SYSGATE_BEGIN) |
| 19 | #define R_MP0_STATUS_REG (0x0380 - R_SYSGATE_BEGIN) |
| 20 | #define R_MP0_CONTROL_REG (0x0384 - R_SYSGATE_BEGIN) |
| 21 | #define R_MP1_STATUS_REG (0x0388 - R_SYSGATE_BEGIN) |
| 22 | #define R_MP1_CONTROL_REG (0x038C - R_SYSGATE_BEGIN) |
| 23 | #define R_MP2_STATUS_REG (0x0390 - R_SYSGATE_BEGIN) |
| 24 | #define R_MP2_CONTROL_REG (0x0394 - R_SYSGATE_BEGIN) |
| 25 | #define R_MP3_STATUS_REG (0x0398 - R_SYSGATE_BEGIN) |
| 26 | #define R_MP3_CONTROL_REG (0x039C - R_SYSGATE_BEGIN) |
| 27 | #define R_MP4_STATUS_REG (0x03A0 - R_SYSGATE_BEGIN) |
| 28 | #define R_MP4_CONTROL_REG (0x03A4 - R_SYSGATE_BEGIN) |
| 29 | #define R_MP5_STATUS_REG (0x03A8 - R_SYSGATE_BEGIN) |
| 30 | #define R_MP5_CONTROL_REG (0x03AC - R_SYSGATE_BEGIN) |
| 31 | #define R_MP6_STATUS_REG (0x03B0 - R_SYSGATE_BEGIN) |
| 32 | #define R_MP6_CONTROL_REG (0x03B4 - R_SYSGATE_BEGIN) |
| 33 | #define R_MP7_STATUS_REG (0x03B8 - R_SYSGATE_BEGIN) |
| 34 | #define R_MP7_CONTROL_REG (0x03BC - R_SYSGATE_BEGIN) |
| 35 | #define R_MP8_STATUS_REG (0x03C0 - R_SYSGATE_BEGIN) |
| 36 | #define R_MP8_CONTROL_REG (0x03C4 - R_SYSGATE_BEGIN) |
| 37 | #define R_MP9_STATUS_REG (0x03C8 - R_SYSGATE_BEGIN) |
| 38 | #define R_MP9_CONTROL_REG (0x03CC - R_SYSGATE_BEGIN) |
| 39 | #define R_MP10_STATUS_REG (0x03D0 - R_SYSGATE_BEGIN) |
| 40 | #define R_MP10_CONTROL_REG (0x03D4 - R_SYSGATE_BEGIN) |
| 41 | #define R_MP11_STATUS_REG (0x03D8 - R_SYSGATE_BEGIN) |
| 42 | #define R_MP11_CONTROL_REG (0x03DC - R_SYSGATE_BEGIN) |
| 43 | #define R_MP12_STATUS_REG (0x03E0 - R_SYSGATE_BEGIN) |
| 44 | #define R_MP12_CONTROL_REG (0x03E4 - R_SYSGATE_BEGIN) |
| 45 | #define R_MP13_STATUS_REG (0x03E8 - R_SYSGATE_BEGIN) |
| 46 | #define R_MP13_CONTROL_REG (0x03EC - R_SYSGATE_BEGIN) |
| 47 | #define R_MP14_STATUS_REG (0x03F0 - R_SYSGATE_BEGIN) |
| 48 | #define R_MP14_CONTROL_REG (0x03F4 - R_SYSGATE_BEGIN) |
| 49 | #define R_MP15_STATUS_REG (0x03F8 - R_SYSGATE_BEGIN) |
| 50 | #define R_MP15_CONTROL_REG (0x03FC - R_SYSGATE_BEGIN) |
| 51 | |
| 52 | /** |
| 53 | * struct sg2042_rpgate_clock - Gate clock for RP(riscv processors) subsystem |
| 54 | * @hw: clk_hw for initialization |
| 55 | * @id: used to map clk_onecell_data |
| 56 | * @offset_enable: offset of gate enable registers |
| 57 | * @bit_idx: which bit in the register controls gating of this clock |
| 58 | */ |
| 59 | struct sg2042_rpgate_clock { |
| 60 | struct clk_hw hw; |
| 61 | |
| 62 | unsigned int id; |
| 63 | |
| 64 | u32 offset_enable; |
| 65 | u8 bit_idx; |
| 66 | }; |
| 67 | |
| 68 | /* |
| 69 | * Clock initialization macro naming rules: |
| 70 | * FW: use CLK_HW_INIT_FW_NAME |
| 71 | */ |
| 72 | #define SG2042_GATE_FW(_id, _name, _parent, _flags, \ |
| 73 | _r_enable, _bit_idx) { \ |
| 74 | .hw.init = CLK_HW_INIT_FW_NAME( \ |
| 75 | _name, \ |
| 76 | _parent, \ |
| 77 | NULL, \ |
| 78 | _flags), \ |
| 79 | .id = _id, \ |
| 80 | .offset_enable = _r_enable, \ |
| 81 | .bit_idx = _bit_idx, \ |
| 82 | } |
| 83 | |
| 84 | /* |
| 85 | * Gate clocks for RP subsystem (including the MP subsystem), which control |
| 86 | * registers are defined in SYS_CTRL. |
| 87 | */ |
| 88 | static const struct sg2042_rpgate_clock sg2042_gate_rp[] = { |
| 89 | /* downstream of clk_gate_rp_cpu_normal about rxu */ |
| 90 | SG2042_GATE_FW(GATE_CLK_RXU0, "clk_gate_rxu0" , "rpgate" , |
| 91 | 0, R_RP_RXU_CLK_ENABLE, 0), |
| 92 | SG2042_GATE_FW(GATE_CLK_RXU1, "clk_gate_rxu1" , "rpgate" , |
| 93 | 0, R_RP_RXU_CLK_ENABLE, 1), |
| 94 | SG2042_GATE_FW(GATE_CLK_RXU2, "clk_gate_rxu2" , "rpgate" , |
| 95 | 0, R_RP_RXU_CLK_ENABLE, 2), |
| 96 | SG2042_GATE_FW(GATE_CLK_RXU3, "clk_gate_rxu3" , "rpgate" , |
| 97 | 0, R_RP_RXU_CLK_ENABLE, 3), |
| 98 | SG2042_GATE_FW(GATE_CLK_RXU4, "clk_gate_rxu4" , "rpgate" , |
| 99 | 0, R_RP_RXU_CLK_ENABLE, 4), |
| 100 | SG2042_GATE_FW(GATE_CLK_RXU5, "clk_gate_rxu5" , "rpgate" , |
| 101 | 0, R_RP_RXU_CLK_ENABLE, 5), |
| 102 | SG2042_GATE_FW(GATE_CLK_RXU6, "clk_gate_rxu6" , "rpgate" , |
| 103 | 0, R_RP_RXU_CLK_ENABLE, 6), |
| 104 | SG2042_GATE_FW(GATE_CLK_RXU7, "clk_gate_rxu7" , "rpgate" , |
| 105 | 0, R_RP_RXU_CLK_ENABLE, 7), |
| 106 | SG2042_GATE_FW(GATE_CLK_RXU8, "clk_gate_rxu8" , "rpgate" , |
| 107 | 0, R_RP_RXU_CLK_ENABLE, 8), |
| 108 | SG2042_GATE_FW(GATE_CLK_RXU9, "clk_gate_rxu9" , "rpgate" , |
| 109 | 0, R_RP_RXU_CLK_ENABLE, 9), |
| 110 | SG2042_GATE_FW(GATE_CLK_RXU10, "clk_gate_rxu10" , "rpgate" , |
| 111 | 0, R_RP_RXU_CLK_ENABLE, 10), |
| 112 | SG2042_GATE_FW(GATE_CLK_RXU11, "clk_gate_rxu11" , "rpgate" , |
| 113 | 0, R_RP_RXU_CLK_ENABLE, 11), |
| 114 | SG2042_GATE_FW(GATE_CLK_RXU12, "clk_gate_rxu12" , "rpgate" , |
| 115 | 0, R_RP_RXU_CLK_ENABLE, 12), |
| 116 | SG2042_GATE_FW(GATE_CLK_RXU13, "clk_gate_rxu13" , "rpgate" , |
| 117 | 0, R_RP_RXU_CLK_ENABLE, 13), |
| 118 | SG2042_GATE_FW(GATE_CLK_RXU14, "clk_gate_rxu14" , "rpgate" , |
| 119 | 0, R_RP_RXU_CLK_ENABLE, 14), |
| 120 | SG2042_GATE_FW(GATE_CLK_RXU15, "clk_gate_rxu15" , "rpgate" , |
| 121 | 0, R_RP_RXU_CLK_ENABLE, 15), |
| 122 | SG2042_GATE_FW(GATE_CLK_RXU16, "clk_gate_rxu16" , "rpgate" , |
| 123 | 0, R_RP_RXU_CLK_ENABLE, 16), |
| 124 | SG2042_GATE_FW(GATE_CLK_RXU17, "clk_gate_rxu17" , "rpgate" , |
| 125 | 0, R_RP_RXU_CLK_ENABLE, 17), |
| 126 | SG2042_GATE_FW(GATE_CLK_RXU18, "clk_gate_rxu18" , "rpgate" , |
| 127 | 0, R_RP_RXU_CLK_ENABLE, 18), |
| 128 | SG2042_GATE_FW(GATE_CLK_RXU19, "clk_gate_rxu19" , "rpgate" , |
| 129 | 0, R_RP_RXU_CLK_ENABLE, 19), |
| 130 | SG2042_GATE_FW(GATE_CLK_RXU20, "clk_gate_rxu20" , "rpgate" , |
| 131 | 0, R_RP_RXU_CLK_ENABLE, 20), |
| 132 | SG2042_GATE_FW(GATE_CLK_RXU21, "clk_gate_rxu21" , "rpgate" , |
| 133 | 0, R_RP_RXU_CLK_ENABLE, 21), |
| 134 | SG2042_GATE_FW(GATE_CLK_RXU22, "clk_gate_rxu22" , "rpgate" , |
| 135 | 0, R_RP_RXU_CLK_ENABLE, 22), |
| 136 | SG2042_GATE_FW(GATE_CLK_RXU23, "clk_gate_rxu23" , "rpgate" , |
| 137 | 0, R_RP_RXU_CLK_ENABLE, 23), |
| 138 | SG2042_GATE_FW(GATE_CLK_RXU24, "clk_gate_rxu24" , "rpgate" , |
| 139 | 0, R_RP_RXU_CLK_ENABLE, 24), |
| 140 | SG2042_GATE_FW(GATE_CLK_RXU25, "clk_gate_rxu25" , "rpgate" , |
| 141 | 0, R_RP_RXU_CLK_ENABLE, 25), |
| 142 | SG2042_GATE_FW(GATE_CLK_RXU26, "clk_gate_rxu26" , "rpgate" , |
| 143 | 0, R_RP_RXU_CLK_ENABLE, 26), |
| 144 | SG2042_GATE_FW(GATE_CLK_RXU27, "clk_gate_rxu27" , "rpgate" , |
| 145 | 0, R_RP_RXU_CLK_ENABLE, 27), |
| 146 | SG2042_GATE_FW(GATE_CLK_RXU28, "clk_gate_rxu28" , "rpgate" , |
| 147 | 0, R_RP_RXU_CLK_ENABLE, 28), |
| 148 | SG2042_GATE_FW(GATE_CLK_RXU29, "clk_gate_rxu29" , "rpgate" , |
| 149 | 0, R_RP_RXU_CLK_ENABLE, 29), |
| 150 | SG2042_GATE_FW(GATE_CLK_RXU30, "clk_gate_rxu30" , "rpgate" , |
| 151 | 0, R_RP_RXU_CLK_ENABLE, 30), |
| 152 | SG2042_GATE_FW(GATE_CLK_RXU31, "clk_gate_rxu31" , "rpgate" , |
| 153 | 0, R_RP_RXU_CLK_ENABLE, 31), |
| 154 | |
| 155 | /* downstream of clk_gate_rp_cpu_normal about mp */ |
| 156 | SG2042_GATE_FW(GATE_CLK_MP0, "clk_gate_mp0" , "rpgate" , |
| 157 | CLK_IS_CRITICAL, R_MP0_CONTROL_REG, 0), |
| 158 | SG2042_GATE_FW(GATE_CLK_MP1, "clk_gate_mp1" , "rpgate" , |
| 159 | CLK_IS_CRITICAL, R_MP1_CONTROL_REG, 0), |
| 160 | SG2042_GATE_FW(GATE_CLK_MP2, "clk_gate_mp2" , "rpgate" , |
| 161 | CLK_IS_CRITICAL, R_MP2_CONTROL_REG, 0), |
| 162 | SG2042_GATE_FW(GATE_CLK_MP3, "clk_gate_mp3" , "rpgate" , |
| 163 | CLK_IS_CRITICAL, R_MP3_CONTROL_REG, 0), |
| 164 | SG2042_GATE_FW(GATE_CLK_MP4, "clk_gate_mp4" , "rpgate" , |
| 165 | CLK_IS_CRITICAL, R_MP4_CONTROL_REG, 0), |
| 166 | SG2042_GATE_FW(GATE_CLK_MP5, "clk_gate_mp5" , "rpgate" , |
| 167 | CLK_IS_CRITICAL, R_MP5_CONTROL_REG, 0), |
| 168 | SG2042_GATE_FW(GATE_CLK_MP6, "clk_gate_mp6" , "rpgate" , |
| 169 | CLK_IS_CRITICAL, R_MP6_CONTROL_REG, 0), |
| 170 | SG2042_GATE_FW(GATE_CLK_MP7, "clk_gate_mp7" , "rpgate" , |
| 171 | CLK_IS_CRITICAL, R_MP7_CONTROL_REG, 0), |
| 172 | SG2042_GATE_FW(GATE_CLK_MP8, "clk_gate_mp8" , "rpgate" , |
| 173 | CLK_IS_CRITICAL, R_MP8_CONTROL_REG, 0), |
| 174 | SG2042_GATE_FW(GATE_CLK_MP9, "clk_gate_mp9" , "rpgate" , |
| 175 | CLK_IS_CRITICAL, R_MP9_CONTROL_REG, 0), |
| 176 | SG2042_GATE_FW(GATE_CLK_MP10, "clk_gate_mp10" , "rpgate" , |
| 177 | CLK_IS_CRITICAL, R_MP10_CONTROL_REG, 0), |
| 178 | SG2042_GATE_FW(GATE_CLK_MP11, "clk_gate_mp11" , "rpgate" , |
| 179 | CLK_IS_CRITICAL, R_MP11_CONTROL_REG, 0), |
| 180 | SG2042_GATE_FW(GATE_CLK_MP12, "clk_gate_mp12" , "rpgate" , |
| 181 | CLK_IS_CRITICAL, R_MP12_CONTROL_REG, 0), |
| 182 | SG2042_GATE_FW(GATE_CLK_MP13, "clk_gate_mp13" , "rpgate" , |
| 183 | CLK_IS_CRITICAL, R_MP13_CONTROL_REG, 0), |
| 184 | SG2042_GATE_FW(GATE_CLK_MP14, "clk_gate_mp14" , "rpgate" , |
| 185 | CLK_IS_CRITICAL, R_MP14_CONTROL_REG, 0), |
| 186 | SG2042_GATE_FW(GATE_CLK_MP15, "clk_gate_mp15" , "rpgate" , |
| 187 | CLK_IS_CRITICAL, R_MP15_CONTROL_REG, 0), |
| 188 | }; |
| 189 | |
| 190 | static DEFINE_SPINLOCK(sg2042_clk_lock); |
| 191 | |
| 192 | static int sg2042_clk_register_rpgates(struct device *dev, |
| 193 | struct sg2042_clk_data *clk_data, |
| 194 | const struct sg2042_rpgate_clock gate_clks[], |
| 195 | int num_gate_clks) |
| 196 | { |
| 197 | const struct sg2042_rpgate_clock *gate; |
| 198 | struct clk_hw *hw; |
| 199 | int i, ret = 0; |
| 200 | |
| 201 | for (i = 0; i < num_gate_clks; i++) { |
| 202 | gate = &gate_clks[i]; |
| 203 | hw = devm_clk_hw_register_gate_parent_data |
| 204 | (dev, |
| 205 | gate->hw.init->name, |
| 206 | gate->hw.init->parent_data, |
| 207 | gate->hw.init->flags, |
| 208 | clk_data->iobase + gate->offset_enable, |
| 209 | gate->bit_idx, |
| 210 | 0, |
| 211 | &sg2042_clk_lock); |
| 212 | if (IS_ERR(ptr: hw)) { |
| 213 | pr_err("failed to register clock %s\n" , gate->hw.init->name); |
| 214 | ret = PTR_ERR(ptr: hw); |
| 215 | break; |
| 216 | } |
| 217 | |
| 218 | clk_data->onecell_data.hws[gate->id] = hw; |
| 219 | } |
| 220 | |
| 221 | return ret; |
| 222 | } |
| 223 | |
| 224 | static int sg2042_init_clkdata(struct platform_device *pdev, |
| 225 | int num_clks, |
| 226 | struct sg2042_clk_data **pp_clk_data) |
| 227 | { |
| 228 | struct sg2042_clk_data *clk_data; |
| 229 | |
| 230 | clk_data = devm_kzalloc(dev: &pdev->dev, |
| 231 | struct_size(clk_data, onecell_data.hws, num_clks), |
| 232 | GFP_KERNEL); |
| 233 | if (!clk_data) |
| 234 | return -ENOMEM; |
| 235 | |
| 236 | clk_data->iobase = devm_platform_ioremap_resource(pdev, index: 0); |
| 237 | if (WARN_ON(IS_ERR(clk_data->iobase))) |
| 238 | return PTR_ERR(ptr: clk_data->iobase); |
| 239 | |
| 240 | clk_data->onecell_data.num = num_clks; |
| 241 | |
| 242 | *pp_clk_data = clk_data; |
| 243 | |
| 244 | return 0; |
| 245 | } |
| 246 | |
| 247 | static int sg2042_rpgate_probe(struct platform_device *pdev) |
| 248 | { |
| 249 | struct sg2042_clk_data *clk_data = NULL; |
| 250 | int num_clks; |
| 251 | int ret; |
| 252 | |
| 253 | num_clks = ARRAY_SIZE(sg2042_gate_rp); |
| 254 | |
| 255 | ret = sg2042_init_clkdata(pdev, num_clks, pp_clk_data: &clk_data); |
| 256 | if (ret) |
| 257 | goto error_out; |
| 258 | |
| 259 | ret = sg2042_clk_register_rpgates(dev: &pdev->dev, clk_data, gate_clks: sg2042_gate_rp, |
| 260 | num_gate_clks: num_clks); |
| 261 | if (ret) |
| 262 | goto error_out; |
| 263 | |
| 264 | return devm_of_clk_add_hw_provider(dev: &pdev->dev, |
| 265 | get: of_clk_hw_onecell_get, |
| 266 | data: &clk_data->onecell_data); |
| 267 | |
| 268 | error_out: |
| 269 | pr_err("%s failed error number %d\n" , __func__, ret); |
| 270 | return ret; |
| 271 | } |
| 272 | |
| 273 | static const struct of_device_id sg2042_rpgate_match[] = { |
| 274 | { .compatible = "sophgo,sg2042-rpgate" }, |
| 275 | { /* sentinel */ } |
| 276 | }; |
| 277 | MODULE_DEVICE_TABLE(of, sg2042_rpgate_match); |
| 278 | |
| 279 | static struct platform_driver sg2042_rpgate_driver = { |
| 280 | .probe = sg2042_rpgate_probe, |
| 281 | .driver = { |
| 282 | .name = "clk-sophgo-sg2042-rpgate" , |
| 283 | .of_match_table = sg2042_rpgate_match, |
| 284 | .suppress_bind_attrs = true, |
| 285 | }, |
| 286 | }; |
| 287 | module_platform_driver(sg2042_rpgate_driver); |
| 288 | |
| 289 | MODULE_AUTHOR("Chen Wang" ); |
| 290 | MODULE_DESCRIPTION("Sophgo SG2042 rp subsystem clock driver" ); |
| 291 | MODULE_LICENSE("GPL" ); |
| 292 | |