1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Author: Yinbo Zhu <zhuyinbo@loongson.cn> 4 * Copyright (C) 2022-2023 Loongson Technology Corporation Limited 5 */ 6 7 #include <linux/err.h> 8 #include <linux/init.h> 9 #include <linux/clk-provider.h> 10 #include <linux/slab.h> 11 #include <linux/module.h> 12 #include <linux/platform_device.h> 13 #include <linux/io-64-nonatomic-lo-hi.h> 14 #include <dt-bindings/clock/loongson,ls2k-clk.h> 15 16 static const struct clk_parent_data pdata[] = { 17 { .fw_name = "ref_100m", }, 18 }; 19 20 enum loongson2_clk_type { 21 CLK_TYPE_PLL, 22 CLK_TYPE_SCALE, 23 CLK_TYPE_DIVIDER, 24 CLK_TYPE_GATE, 25 CLK_TYPE_FIXED, 26 CLK_TYPE_NONE, 27 }; 28 29 struct loongson2_clk_provider { 30 void __iomem *base; 31 struct device *dev; 32 spinlock_t clk_lock; /* protect access to DIV registers */ 33 34 /* Must be last --ends in a flexible-array member. */ 35 struct clk_hw_onecell_data clk_data; 36 }; 37 38 struct loongson2_clk_data { 39 struct clk_hw hw; 40 void __iomem *reg; 41 u8 div_shift; 42 u8 div_width; 43 u8 mult_shift; 44 u8 mult_width; 45 }; 46 47 struct loongson2_clk_board_info { 48 u8 id; 49 enum loongson2_clk_type type; 50 const char *name; 51 const char *parent_name; 52 unsigned long fixed_rate; 53 u8 reg_offset; 54 u8 div_shift; 55 u8 div_width; 56 u8 mult_shift; 57 u8 mult_width; 58 u8 bit_idx; 59 }; 60 61 #define CLK_DIV(_id, _name, _pname, _offset, _dshift, _dwidth) \ 62 { \ 63 .id = _id, \ 64 .type = CLK_TYPE_DIVIDER, \ 65 .name = _name, \ 66 .parent_name = _pname, \ 67 .reg_offset = _offset, \ 68 .div_shift = _dshift, \ 69 .div_width = _dwidth, \ 70 } 71 72 #define CLK_PLL(_id, _name, _offset, _mshift, _mwidth, \ 73 _dshift, _dwidth) \ 74 { \ 75 .id = _id, \ 76 .type = CLK_TYPE_PLL, \ 77 .name = _name, \ 78 .parent_name = NULL, \ 79 .reg_offset = _offset, \ 80 .mult_shift = _mshift, \ 81 .mult_width = _mwidth, \ 82 .div_shift = _dshift, \ 83 .div_width = _dwidth, \ 84 } 85 86 #define CLK_SCALE(_id, _name, _pname, _offset, \ 87 _dshift, _dwidth) \ 88 { \ 89 .id = _id, \ 90 .type = CLK_TYPE_SCALE, \ 91 .name = _name, \ 92 .parent_name = _pname, \ 93 .reg_offset = _offset, \ 94 .div_shift = _dshift, \ 95 .div_width = _dwidth, \ 96 } 97 98 #define CLK_GATE(_id, _name, _pname, _offset, _bidx) \ 99 { \ 100 .id = _id, \ 101 .type = CLK_TYPE_GATE, \ 102 .name = _name, \ 103 .parent_name = _pname, \ 104 .reg_offset = _offset, \ 105 .bit_idx = _bidx, \ 106 } 107 108 #define CLK_FIXED(_id, _name, _pname, _rate) \ 109 { \ 110 .id = _id, \ 111 .type = CLK_TYPE_FIXED, \ 112 .name = _name, \ 113 .parent_name = _pname, \ 114 .fixed_rate = _rate, \ 115 } 116 117 static const struct loongson2_clk_board_info ls2k0500_clks[] = { 118 CLK_PLL(LOONGSON2_NODE_PLL, "pll_node", 0, 16, 8, 8, 6), 119 CLK_PLL(LOONGSON2_DDR_PLL, "pll_ddr", 0x8, 16, 8, 8, 6), 120 CLK_PLL(LOONGSON2_DC_PLL, "pll_soc", 0x10, 16, 8, 8, 6), 121 CLK_PLL(LOONGSON2_PIX0_PLL, "pll_pix0", 0x18, 16, 8, 8, 6), 122 CLK_PLL(LOONGSON2_PIX1_PLL, "pll_pix1", 0x20, 16, 8, 8, 6), 123 CLK_DIV(LOONGSON2_NODE_CLK, "clk_node", "pll_node", 0, 24, 6), 124 CLK_DIV(LOONGSON2_DDR_CLK, "clk_ddr", "pll_ddr", 0x8, 24, 6), 125 CLK_DIV(LOONGSON2_HDA_CLK, "clk_hda", "pll_ddr", 0xc, 8, 6), 126 CLK_DIV(LOONGSON2_GPU_CLK, "clk_gpu", "pll_soc", 0x10, 24, 6), 127 CLK_DIV(LOONGSON2_DC_CLK, "clk_sb", "pll_soc", 0x14, 0, 6), 128 CLK_DIV(LOONGSON2_GMAC_CLK, "clk_gmac", "pll_soc", 0x14, 8, 6), 129 CLK_DIV(LOONGSON2_PIX0_CLK, "clk_pix0", "pll_pix0", 0x18, 24, 6), 130 CLK_DIV(LOONGSON2_PIX1_CLK, "clk_pix1", "pll_pix1", 0x20, 24, 6), 131 CLK_SCALE(LOONGSON2_BOOT_CLK, "clk_boot", "clk_sb", 0x28, 8, 3), 132 CLK_SCALE(LOONGSON2_SATA_CLK, "clk_sata", "clk_sb", 0x28, 12, 3), 133 CLK_SCALE(LOONGSON2_USB_CLK, "clk_usb", "clk_sb", 0x28, 16, 3), 134 CLK_SCALE(LOONGSON2_APB_CLK, "clk_apb", "clk_sb", 0x28, 20, 3), 135 { /* Sentinel */ }, 136 }; 137 138 static const struct loongson2_clk_board_info ls2k1000_clks[] = { 139 CLK_PLL(LOONGSON2_NODE_PLL, "pll_node", 0, 32, 10, 26, 6), 140 CLK_PLL(LOONGSON2_DDR_PLL, "pll_ddr", 0x10, 32, 10, 26, 6), 141 CLK_PLL(LOONGSON2_DC_PLL, "pll_dc", 0x20, 32, 10, 26, 6), 142 CLK_PLL(LOONGSON2_PIX0_PLL, "pll_pix0", 0x30, 32, 10, 26, 6), 143 CLK_PLL(LOONGSON2_PIX1_PLL, "pll_pix1", 0x40, 32, 10, 26, 6), 144 CLK_DIV(LOONGSON2_NODE_CLK, "clk_node", "pll_node", 0x8, 0, 6), 145 CLK_DIV(LOONGSON2_DDR_CLK, "clk_ddr", "pll_ddr", 0x18, 0, 6), 146 CLK_DIV(LOONGSON2_GPU_CLK, "clk_gpu", "pll_ddr", 0x18, 22, 6), 147 /* 148 * The hda clk divisor in the upper 32bits and the clk-prodiver 149 * layer code doesn't support 64bit io operation thus a conversion 150 * is required that subtract shift by 32 and add 4byte to the hda 151 * address 152 */ 153 CLK_DIV(LOONGSON2_HDA_CLK, "clk_hda", "pll_ddr", 0x22, 12, 7), 154 CLK_DIV(LOONGSON2_DC_CLK, "clk_dc", "pll_dc", 0x28, 0, 6), 155 CLK_DIV(LOONGSON2_GMAC_CLK, "clk_gmac", "pll_dc", 0x28, 22, 6), 156 CLK_DIV(LOONGSON2_PIX0_CLK, "clk_pix0", "pll_pix0", 0x38, 0, 6), 157 CLK_DIV(LOONGSON2_PIX1_CLK, "clk_pix1", "pll_pix1", 0x38, 0, 6), 158 CLK_SCALE(LOONGSON2_BOOT_CLK, "clk_boot", NULL, 0x50, 8, 3), 159 CLK_SCALE(LOONGSON2_SATA_CLK, "clk_sata", "clk_gmac", 0x50, 12, 3), 160 CLK_SCALE(LOONGSON2_USB_CLK, "clk_usb", "clk_gmac", 0x50, 16, 3), 161 CLK_SCALE(LOONGSON2_APB_CLK, "clk_apb", "clk_gmac", 0x50, 20, 3), 162 { /* Sentinel */ }, 163 }; 164 165 static const struct loongson2_clk_board_info ls2k2000_clks[] = { 166 CLK_PLL(LOONGSON2_DC_PLL, "pll_0", 0, 21, 9, 32, 6), 167 CLK_PLL(LOONGSON2_DDR_PLL, "pll_1", 0x10, 21, 9, 32, 6), 168 CLK_PLL(LOONGSON2_NODE_PLL, "pll_2", 0x20, 21, 9, 32, 6), 169 CLK_PLL(LOONGSON2_PIX0_PLL, "pll_pix0", 0x30, 21, 9, 32, 6), 170 CLK_PLL(LOONGSON2_PIX1_PLL, "pll_pix1", 0x40, 21, 9, 32, 6), 171 CLK_GATE(LOONGSON2_OUT0_GATE, "out0_gate", "pll_0", 0, 40), 172 CLK_GATE(LOONGSON2_GMAC_GATE, "gmac_gate", "pll_0", 0, 41), 173 CLK_GATE(LOONGSON2_RIO_GATE, "rio_gate", "pll_0", 0, 42), 174 CLK_GATE(LOONGSON2_DC_GATE, "dc_gate", "pll_1", 0x10, 40), 175 CLK_GATE(LOONGSON2_DDR_GATE, "ddr_gate", "pll_1", 0x10, 41), 176 CLK_GATE(LOONGSON2_GPU_GATE, "gpu_gate", "pll_1", 0x10, 42), 177 CLK_GATE(LOONGSON2_HDA_GATE, "hda_gate", "pll_2", 0x20, 40), 178 CLK_GATE(LOONGSON2_NODE_GATE, "node_gate", "pll_2", 0x20, 41), 179 CLK_GATE(LOONGSON2_EMMC_GATE, "emmc_gate", "pll_2", 0x20, 42), 180 CLK_GATE(LOONGSON2_PIX0_GATE, "pix0_gate", "pll_pix0", 0x30, 40), 181 CLK_GATE(LOONGSON2_PIX1_GATE, "pix1_gate", "pll_pix1", 0x40, 40), 182 CLK_DIV(LOONGSON2_OUT0_CLK, "clk_out0", "out0_gate", 0, 0, 6), 183 CLK_DIV(LOONGSON2_GMAC_CLK, "clk_gmac", "gmac_gate", 0, 7, 6), 184 CLK_DIV(LOONGSON2_RIO_CLK, "clk_rio", "rio_gate", 0, 14, 6), 185 CLK_DIV(LOONGSON2_DC_CLK, "clk_dc", "dc_gate", 0x10, 0, 6), 186 CLK_DIV(LOONGSON2_GPU_CLK, "clk_gpu", "gpu_gate", 0x10, 7, 6), 187 CLK_DIV(LOONGSON2_DDR_CLK, "clk_ddr", "ddr_gate", 0x10, 14, 6), 188 CLK_DIV(LOONGSON2_HDA_CLK, "clk_hda", "hda_gate", 0x20, 0, 6), 189 CLK_DIV(LOONGSON2_NODE_CLK, "clk_node", "node_gate", 0x20, 7, 6), 190 CLK_DIV(LOONGSON2_EMMC_CLK, "clk_emmc", "emmc_gate", 0x20, 14, 6), 191 CLK_DIV(LOONGSON2_PIX0_CLK, "clk_pix0", "pll_pix0", 0x30, 0, 6), 192 CLK_DIV(LOONGSON2_PIX1_CLK, "clk_pix1", "pll_pix1", 0x40, 0, 6), 193 CLK_SCALE(LOONGSON2_SATA_CLK, "clk_sata", "clk_out0", 0x50, 12, 3), 194 CLK_SCALE(LOONGSON2_USB_CLK, "clk_usb", "clk_out0", 0x50, 16, 3), 195 CLK_SCALE(LOONGSON2_APB_CLK, "clk_apb", "clk_node", 0x50, 20, 3), 196 CLK_SCALE(LOONGSON2_BOOT_CLK, "clk_boot", NULL, 0x50, 23, 3), 197 CLK_SCALE(LOONGSON2_DES_CLK, "clk_des", "clk_node", 0x50, 40, 3), 198 CLK_SCALE(LOONGSON2_I2S_CLK, "clk_i2s", "clk_node", 0x50, 44, 3), 199 CLK_FIXED(LOONGSON2_MISC_CLK, "clk_misc", NULL, 50000000), 200 { /* Sentinel */ }, 201 }; 202 203 static inline struct loongson2_clk_data *to_loongson2_clk(struct clk_hw *hw) 204 { 205 return container_of(hw, struct loongson2_clk_data, hw); 206 } 207 208 static inline unsigned long loongson2_rate_part(u64 val, u8 shift, u8 width) 209 { 210 return (val & GENMASK(shift + width - 1, shift)) >> shift; 211 } 212 213 static unsigned long loongson2_pll_recalc_rate(struct clk_hw *hw, 214 unsigned long parent_rate) 215 { 216 u64 val, mult, div; 217 struct loongson2_clk_data *clk = to_loongson2_clk(hw); 218 219 val = readq(clk->reg); 220 mult = loongson2_rate_part(val, clk->mult_shift, clk->mult_width); 221 div = loongson2_rate_part(val, clk->div_shift, clk->div_width); 222 223 return div_u64((u64)parent_rate * mult, div); 224 } 225 226 static const struct clk_ops loongson2_pll_recalc_ops = { 227 .recalc_rate = loongson2_pll_recalc_rate, 228 }; 229 230 static unsigned long loongson2_freqscale_recalc_rate(struct clk_hw *hw, 231 unsigned long parent_rate) 232 { 233 u64 val, mult; 234 struct loongson2_clk_data *clk = to_loongson2_clk(hw); 235 236 val = readq(clk->reg); 237 mult = loongson2_rate_part(val, clk->div_shift, clk->div_width) + 1; 238 239 return div_u64((u64)parent_rate * mult, 8); 240 } 241 242 static const struct clk_ops loongson2_freqscale_recalc_ops = { 243 .recalc_rate = loongson2_freqscale_recalc_rate, 244 }; 245 246 static struct clk_hw *loongson2_clk_register(struct loongson2_clk_provider *clp, 247 const struct loongson2_clk_board_info *cld, 248 const struct clk_ops *ops) 249 { 250 int ret; 251 struct clk_hw *hw; 252 struct loongson2_clk_data *clk; 253 struct clk_init_data init = { }; 254 255 clk = devm_kzalloc(clp->dev, sizeof(*clk), GFP_KERNEL); 256 if (!clk) 257 return ERR_PTR(-ENOMEM); 258 259 init.name = cld->name; 260 init.ops = ops; 261 init.flags = 0; 262 init.num_parents = 1; 263 264 if (!cld->parent_name) 265 init.parent_data = pdata; 266 else 267 init.parent_names = &cld->parent_name; 268 269 clk->reg = clp->base + cld->reg_offset; 270 clk->div_shift = cld->div_shift; 271 clk->div_width = cld->div_width; 272 clk->mult_shift = cld->mult_shift; 273 clk->mult_width = cld->mult_width; 274 clk->hw.init = &init; 275 276 hw = &clk->hw; 277 ret = devm_clk_hw_register(clp->dev, hw); 278 if (ret) 279 clk = ERR_PTR(ret); 280 281 return hw; 282 } 283 284 static int loongson2_clk_probe(struct platform_device *pdev) 285 { 286 int i, clks_num = 0; 287 struct clk_hw *hw; 288 struct device *dev = &pdev->dev; 289 struct loongson2_clk_provider *clp; 290 const struct loongson2_clk_board_info *p, *data; 291 292 data = device_get_match_data(dev); 293 if (!data) 294 return -EINVAL; 295 296 for (p = data; p->name; p++) 297 clks_num++; 298 299 clp = devm_kzalloc(dev, struct_size(clp, clk_data.hws, clks_num), 300 GFP_KERNEL); 301 if (!clp) 302 return -ENOMEM; 303 304 clp->base = devm_platform_ioremap_resource(pdev, 0); 305 if (IS_ERR(clp->base)) 306 return PTR_ERR(clp->base); 307 308 spin_lock_init(&clp->clk_lock); 309 clp->clk_data.num = clks_num; 310 clp->dev = dev; 311 312 for (i = 0; i < clks_num; i++) { 313 p = &data[i]; 314 switch (p->type) { 315 case CLK_TYPE_PLL: 316 hw = loongson2_clk_register(clp, p, 317 &loongson2_pll_recalc_ops); 318 break; 319 case CLK_TYPE_SCALE: 320 hw = loongson2_clk_register(clp, p, 321 &loongson2_freqscale_recalc_ops); 322 break; 323 case CLK_TYPE_DIVIDER: 324 hw = devm_clk_hw_register_divider(dev, p->name, 325 p->parent_name, 0, 326 clp->base + p->reg_offset, 327 p->div_shift, p->div_width, 328 CLK_DIVIDER_ONE_BASED, 329 &clp->clk_lock); 330 break; 331 case CLK_TYPE_GATE: 332 hw = devm_clk_hw_register_gate(dev, p->name, p->parent_name, 0, 333 clp->base + p->reg_offset, 334 p->bit_idx, 0, 335 &clp->clk_lock); 336 break; 337 case CLK_TYPE_FIXED: 338 hw = clk_hw_register_fixed_rate_parent_data(dev, p->name, pdata, 339 0, p->fixed_rate); 340 break; 341 default: 342 return dev_err_probe(dev, -EINVAL, "Invalid clk type\n"); 343 } 344 345 if (IS_ERR(hw)) 346 return dev_err_probe(dev, PTR_ERR(hw), 347 "Register clk: %s, type: %u failed!\n", 348 p->name, p->type); 349 350 clp->clk_data.hws[p->id] = hw; 351 } 352 353 return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, &clp->clk_data); 354 } 355 356 static const struct of_device_id loongson2_clk_match_table[] = { 357 { .compatible = "loongson,ls2k0500-clk", .data = &ls2k0500_clks }, 358 { .compatible = "loongson,ls2k-clk", .data = &ls2k1000_clks }, 359 { .compatible = "loongson,ls2k2000-clk", .data = &ls2k2000_clks }, 360 { } 361 }; 362 MODULE_DEVICE_TABLE(of, loongson2_clk_match_table); 363 364 static struct platform_driver loongson2_clk_driver = { 365 .probe = loongson2_clk_probe, 366 .driver = { 367 .name = "loongson2-clk", 368 .of_match_table = loongson2_clk_match_table, 369 }, 370 }; 371 module_platform_driver(loongson2_clk_driver); 372 373 MODULE_DESCRIPTION("Loongson2 clock driver"); 374 MODULE_AUTHOR("Loongson Technology Corporation Limited"); 375 MODULE_LICENSE("GPL"); 376