1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Marvell Armada 37xx SoC Peripheral clocks 4 * 5 * Marek Behun <marek.behun@nic.cz> 6 * 7 * Based on Linux driver by: 8 * Gregory CLEMENT <gregory.clement@free-electrons.com> 9 */ 10 11 #include <common.h> 12 #include <malloc.h> 13 #include <clk-uclass.h> 14 #include <clk.h> 15 #include <dm.h> 16 #include <asm/io.h> 17 #include <asm/arch/cpu.h> 18 19 #define TBG_SEL 0x0 20 #define DIV_SEL0 0x4 21 #define DIV_SEL1 0x8 22 #define DIV_SEL2 0xC 23 #define CLK_SEL 0x10 24 #define CLK_DIS 0x14 25 26 enum a37xx_periph_parent { 27 TBG_A_P = 0, 28 TBG_B_P = 1, 29 TBG_A_S = 2, 30 TBG_B_S = 3, 31 MAX_TBG_PARENTS = 4, 32 XTAL = 4, 33 MAX_PARENTS = 5, 34 }; 35 36 static const struct { 37 const char *name; 38 enum a37xx_periph_parent parent; 39 } a37xx_periph_parent_names[] = { 40 { "TBG-A-P", TBG_A_P }, 41 { "TBG-B-P", TBG_B_P }, 42 { "TBG-A-S", TBG_A_S }, 43 { "TBG-B-S", TBG_B_S }, 44 { "xtal", XTAL }, 45 }; 46 47 struct clk_periph; 48 49 struct a37xx_periphclk { 50 void __iomem *reg; 51 52 ulong parents[MAX_PARENTS]; 53 54 const struct clk_periph *clks; 55 bool clk_has_periph_parent[16]; 56 int clk_parent[16]; 57 58 int count; 59 }; 60 61 struct clk_div_table { 62 u32 div; 63 u32 val; 64 }; 65 66 struct clk_periph { 67 const char *name; 68 69 const char *parent_name; 70 71 u32 disable_bit; 72 int mux_shift; 73 74 const struct clk_div_table *div_table[2]; 75 s32 div_reg_off[2]; 76 u32 div_mask[2]; 77 int div_shift[2]; 78 79 unsigned can_gate : 1; 80 unsigned can_mux : 1; 81 unsigned dividers : 2; 82 }; 83 84 static const struct clk_div_table div_table1[] = { 85 { 1, 1 }, 86 { 2, 2 }, 87 { 0, 0 }, 88 }; 89 90 static const struct clk_div_table div_table2[] = { 91 { 2, 1 }, 92 { 4, 2 }, 93 { 0, 0 }, 94 }; 95 96 static const struct clk_div_table div_table6[] = { 97 { 1, 1 }, 98 { 2, 2 }, 99 { 3, 3 }, 100 { 4, 4 }, 101 { 5, 5 }, 102 { 6, 6 }, 103 { 0, 0 }, 104 }; 105 106 #define CLK_FULL_DD(_n, _d, _mux, _r0, _r1, _s0, _s1) \ 107 { \ 108 .name = #_n, \ 109 .disable_bit = BIT(_d), \ 110 .mux_shift = _mux, \ 111 .div_table[0] = div_table6, \ 112 .div_table[1] = div_table6, \ 113 .div_reg_off[0] = _r0, \ 114 .div_reg_off[1] = _r1, \ 115 .div_shift[0] = _s0, \ 116 .div_shift[1] = _s1, \ 117 .div_mask[0] = 7, \ 118 .div_mask[1] = 7, \ 119 .can_gate = 1, \ 120 .can_mux = 1, \ 121 .dividers = 2, \ 122 } 123 124 #define CLK_FULL(_n, _d, _mux, _r, _s, _m, _t) \ 125 { \ 126 .name = #_n, \ 127 .disable_bit = BIT(_d), \ 128 .mux_shift = _mux, \ 129 .div_table[0] = _t, \ 130 .div_reg_off[0] = _r, \ 131 .div_shift[0] = _s, \ 132 .div_mask[0] = _m, \ 133 .can_gate = 1, \ 134 .can_mux = 1, \ 135 .dividers = 1, \ 136 } 137 138 #define CLK_GATE_DIV(_n, _d, _r, _s, _m, _t, _p) \ 139 { \ 140 .name = #_n, \ 141 .parent_name = _p, \ 142 .disable_bit = BIT(_d), \ 143 .div_table[0] = _t, \ 144 .div_reg_off[0] = _r, \ 145 .div_shift[0] = _s, \ 146 .div_mask[0] = _m, \ 147 .can_gate = 1, \ 148 .dividers = 1, \ 149 } 150 151 #define CLK_GATE(_n, _d, _p) \ 152 { \ 153 .name = #_n, \ 154 .parent_name = _p, \ 155 .disable_bit = BIT(_d), \ 156 .can_gate = 1, \ 157 } 158 159 #define CLK_MUX_DIV(_n, _mux, _r, _s, _m, _t) \ 160 { \ 161 .name = #_n, \ 162 .mux_shift = _mux, \ 163 .div_table[0] = _t, \ 164 .div_reg_off[0] = _r, \ 165 .div_shift[0] = _s, \ 166 .div_mask[0] = _m, \ 167 .can_mux = 1, \ 168 .dividers = 1, \ 169 } 170 171 #define CLK_MUX_DD(_n, _mux, _r0, _r1, _s0, _s1) \ 172 { \ 173 .name = #_n, \ 174 .mux_shift = _mux, \ 175 .div_table[0] = div_table6, \ 176 .div_table[1] = div_table6, \ 177 .div_reg_off[0] = _r0, \ 178 .div_reg_off[1] = _r1, \ 179 .div_shift[0] = _s0, \ 180 .div_shift[1] = _s1, \ 181 .div_mask[0] = 7, \ 182 .div_mask[1] = 7, \ 183 .can_mux = 1, \ 184 .dividers = 2, \ 185 } 186 187 /* NB periph clocks */ 188 static const struct clk_periph clks_nb[] = { 189 CLK_FULL_DD(mmc, 2, 0, DIV_SEL2, DIV_SEL2, 16, 13), 190 CLK_FULL_DD(sata_host, 3, 2, DIV_SEL2, DIV_SEL2, 10, 7), 191 CLK_FULL_DD(sec_at, 6, 4, DIV_SEL1, DIV_SEL1, 3, 0), 192 CLK_FULL_DD(sec_dap, 7, 6, DIV_SEL1, DIV_SEL1, 9, 6), 193 CLK_FULL_DD(tscem, 8, 8, DIV_SEL1, DIV_SEL1, 15, 12), 194 CLK_FULL(tscem_tmx, 10, 10, DIV_SEL1, 18, 7, div_table6), 195 CLK_GATE(avs, 11, "xtal"), 196 CLK_FULL_DD(sqf, 12, 12, DIV_SEL1, DIV_SEL1, 27, 24), 197 CLK_FULL_DD(pwm, 13, 14, DIV_SEL0, DIV_SEL0, 3, 0), 198 CLK_GATE(i2c_2, 16, "xtal"), 199 CLK_GATE(i2c_1, 17, "xtal"), 200 CLK_GATE_DIV(ddr_phy, 19, DIV_SEL0, 18, 1, div_table2, "TBG-A-S"), 201 CLK_FULL_DD(ddr_fclk, 21, 16, DIV_SEL0, DIV_SEL0, 15, 12), 202 CLK_FULL(trace, 22, 18, DIV_SEL0, 20, 7, div_table6), 203 CLK_FULL(counter, 23, 20, DIV_SEL0, 23, 7, div_table6), 204 CLK_FULL_DD(eip97, 24, 24, DIV_SEL2, DIV_SEL2, 22, 19), 205 CLK_MUX_DIV(cpu, 22, DIV_SEL0, 28, 7, div_table6), 206 { }, 207 }; 208 209 /* SB periph clocks */ 210 static const struct clk_periph clks_sb[] = { 211 CLK_MUX_DD(gbe_50, 6, DIV_SEL2, DIV_SEL2, 6, 9), 212 CLK_MUX_DD(gbe_core, 8, DIV_SEL1, DIV_SEL1, 18, 21), 213 CLK_MUX_DD(gbe_125, 10, DIV_SEL1, DIV_SEL1, 6, 9), 214 CLK_GATE(gbe1_50, 0, "gbe_50"), 215 CLK_GATE(gbe0_50, 1, "gbe_50"), 216 CLK_GATE(gbe1_125, 2, "gbe_125"), 217 CLK_GATE(gbe0_125, 3, "gbe_125"), 218 CLK_GATE_DIV(gbe1_core, 4, DIV_SEL1, 13, 1, div_table1, "gbe_core"), 219 CLK_GATE_DIV(gbe0_core, 5, DIV_SEL1, 14, 1, div_table1, "gbe_core"), 220 CLK_GATE_DIV(gbe_bm, 12, DIV_SEL1, 0, 1, div_table1, "gbe_core"), 221 CLK_FULL_DD(sdio, 11, 14, DIV_SEL0, DIV_SEL0, 3, 6), 222 CLK_FULL_DD(usb32_usb2_sys, 16, 16, DIV_SEL0, DIV_SEL0, 9, 12), 223 CLK_FULL_DD(usb32_ss_sys, 17, 18, DIV_SEL0, DIV_SEL0, 15, 18), 224 { }, 225 }; 226 227 static int get_mux(struct a37xx_periphclk *priv, int shift) 228 { 229 return (readl(priv->reg + TBG_SEL) >> shift) & 3; 230 } 231 232 static void set_mux(struct a37xx_periphclk *priv, int shift, int val) 233 { 234 u32 reg; 235 236 reg = readl(priv->reg + TBG_SEL); 237 reg &= ~(3 << shift); 238 reg |= (val & 3) << shift; 239 writel(reg, priv->reg + TBG_SEL); 240 } 241 242 static ulong periph_clk_get_rate(struct a37xx_periphclk *priv, int id); 243 244 static ulong get_parent_rate(struct a37xx_periphclk *priv, int id) 245 { 246 const struct clk_periph *clk = &priv->clks[id]; 247 ulong res; 248 249 if (clk->can_mux) { 250 /* parent is one of TBG clocks */ 251 int tbg = get_mux(priv, clk->mux_shift); 252 253 res = priv->parents[tbg]; 254 } else if (priv->clk_has_periph_parent[id]) { 255 /* parent is one of other periph clocks */ 256 257 if (priv->clk_parent[id] >= priv->count) 258 return -EINVAL; 259 260 res = periph_clk_get_rate(priv, priv->clk_parent[id]); 261 } else { 262 /* otherwise parent is one of TBGs or XTAL */ 263 264 if (priv->clk_parent[id] >= MAX_PARENTS) 265 return -EINVAL; 266 267 res = priv->parents[priv->clk_parent[id]]; 268 } 269 270 return res; 271 } 272 273 static ulong get_div(struct a37xx_periphclk *priv, 274 const struct clk_periph *clk, int idx) 275 { 276 const struct clk_div_table *i; 277 u32 reg; 278 279 reg = readl(priv->reg + clk->div_reg_off[idx]); 280 reg = (reg >> clk->div_shift[idx]) & clk->div_mask[idx]; 281 282 /* find divisor for register value val */ 283 for (i = clk->div_table[idx]; i && i->div != 0; ++i) 284 if (i->val == reg) 285 return i->div; 286 287 return 0; 288 } 289 290 static void set_div_val(struct a37xx_periphclk *priv, 291 const struct clk_periph *clk, int idx, int val) 292 { 293 u32 reg; 294 295 reg = readl(priv->reg + clk->div_reg_off[idx]); 296 reg &= ~(clk->div_mask[idx] << clk->div_shift[idx]); 297 reg |= (val & clk->div_mask[idx]) << clk->div_shift[idx]; 298 writel(reg, priv->reg + clk->div_reg_off[idx]); 299 } 300 301 static ulong periph_clk_get_rate(struct a37xx_periphclk *priv, int id) 302 { 303 const struct clk_periph *clk = &priv->clks[id]; 304 ulong rate, div; 305 int i; 306 307 rate = get_parent_rate(priv, id); 308 if (rate == -EINVAL) 309 return -EINVAL; 310 311 /* divide the parent rate by dividers */ 312 div = 1; 313 for (i = 0; i < clk->dividers; ++i) 314 div *= get_div(priv, clk, i); 315 316 if (!div) 317 return 0; 318 319 return DIV_ROUND_UP(rate, div); 320 } 321 322 static ulong armada_37xx_periph_clk_get_rate(struct clk *clk) 323 { 324 struct a37xx_periphclk *priv = dev_get_priv(clk->dev); 325 326 if (clk->id >= priv->count) 327 return -EINVAL; 328 329 return periph_clk_get_rate(priv, clk->id); 330 } 331 332 static int periph_clk_enable(struct clk *clk, int enable) 333 { 334 struct a37xx_periphclk *priv = dev_get_priv(clk->dev); 335 const struct clk_periph *periph_clk = &priv->clks[clk->id]; 336 337 if (clk->id >= priv->count) 338 return -EINVAL; 339 340 if (!periph_clk->can_gate) 341 return -ENOTSUPP; 342 343 if (enable) 344 clrbits_le32(priv->reg + CLK_DIS, periph_clk->disable_bit); 345 else 346 setbits_le32(priv->reg + CLK_DIS, periph_clk->disable_bit); 347 348 return 0; 349 } 350 351 static int armada_37xx_periph_clk_enable(struct clk *clk) 352 { 353 return periph_clk_enable(clk, 1); 354 } 355 356 static int armada_37xx_periph_clk_disable(struct clk *clk) 357 { 358 return periph_clk_enable(clk, 0); 359 } 360 361 #define diff(a, b) abs((long)(a) - (long)(b)) 362 363 static ulong find_best_div(const struct clk_div_table *t0, 364 const struct clk_div_table *t1, ulong parent_rate, 365 ulong req_rate, int *v0, int *v1) 366 { 367 const struct clk_div_table *i, *j; 368 ulong rate, best_rate = 0; 369 370 for (i = t0; i && i->div; ++i) { 371 for (j = t1; j && j->div; ++j) { 372 rate = DIV_ROUND_UP(parent_rate, i->div * j->div); 373 374 if (!best_rate || 375 diff(rate, req_rate) < diff(best_rate, req_rate)) { 376 best_rate = rate; 377 *v0 = i->val; 378 *v1 = j->val; 379 } 380 } 381 } 382 383 return best_rate; 384 } 385 386 static ulong armada_37xx_periph_clk_set_rate(struct clk *clk, ulong req_rate) 387 { 388 struct a37xx_periphclk *priv = dev_get_priv(clk->dev); 389 const struct clk_periph *periph_clk = &priv->clks[clk->id]; 390 ulong rate, old_rate, parent_rate; 391 int div_val0 = 0, div_val1 = 0; 392 const struct clk_div_table *t1; 393 static const struct clk_div_table empty_table[2] = { 394 { 1, 0 }, 395 { 0, 0 } 396 }; 397 398 if (clk->id > priv->count) 399 return -EINVAL; 400 401 old_rate = periph_clk_get_rate(priv, clk->id); 402 if (old_rate == -EINVAL) 403 return -EINVAL; 404 405 if (old_rate == req_rate) 406 return old_rate; 407 408 if (!periph_clk->can_gate || !periph_clk->dividers) 409 return -ENOTSUPP; 410 411 parent_rate = get_parent_rate(priv, clk->id); 412 if (parent_rate == -EINVAL) 413 return -EINVAL; 414 415 t1 = empty_table; 416 if (periph_clk->dividers > 1) 417 t1 = periph_clk->div_table[1]; 418 419 rate = find_best_div(periph_clk->div_table[0], t1, parent_rate, 420 req_rate, &div_val0, &div_val1); 421 422 periph_clk_enable(clk, 0); 423 424 set_div_val(priv, periph_clk, 0, div_val0); 425 if (periph_clk->dividers > 1) 426 set_div_val(priv, periph_clk, 1, div_val1); 427 428 periph_clk_enable(clk, 1); 429 430 return rate; 431 } 432 433 static int armada_37xx_periph_clk_set_parent(struct clk *clk, 434 struct clk *parent) 435 { 436 struct a37xx_periphclk *priv = dev_get_priv(clk->dev); 437 const struct clk_periph *periph_clk = &priv->clks[clk->id]; 438 struct clk check_parent; 439 int ret; 440 441 /* We also check if parent is our TBG clock */ 442 if (clk->id > priv->count || parent->id >= MAX_TBG_PARENTS) 443 return -EINVAL; 444 445 if (!periph_clk->can_mux || !periph_clk->can_gate) 446 return -ENOTSUPP; 447 448 ret = clk_get_by_index(clk->dev, 0, &check_parent); 449 if (ret < 0) 450 return ret; 451 452 if (parent->dev != check_parent.dev) 453 ret = -EINVAL; 454 455 clk_free(&check_parent); 456 if (ret < 0) 457 return ret; 458 459 periph_clk_enable(clk, 0); 460 set_mux(priv, periph_clk->mux_shift, parent->id); 461 periph_clk_enable(clk, 1); 462 463 return 0; 464 } 465 466 #if defined(CONFIG_CMD_CLK) && defined(CONFIG_CLK_ARMADA_3720) 467 static int armada_37xx_periph_clk_dump(struct udevice *dev) 468 { 469 struct a37xx_periphclk *priv = dev_get_priv(dev); 470 const struct clk_periph *clks; 471 int i; 472 473 if (!priv) 474 return -ENODEV; 475 476 clks = priv->clks; 477 478 for (i = 0; i < priv->count; ++i) 479 printf(" %s at %lu Hz\n", clks[i].name, 480 periph_clk_get_rate(priv, i)); 481 printf("\n"); 482 483 return 0; 484 } 485 486 static int clk_dump(const char *name, int (*func)(struct udevice *)) 487 { 488 struct udevice *dev; 489 490 if (uclass_get_device_by_name(UCLASS_CLK, name, &dev)) { 491 printf("Cannot find device %s\n", name); 492 return -ENODEV; 493 } 494 495 return func(dev); 496 } 497 498 int armada_37xx_tbg_clk_dump(struct udevice *); 499 500 int soc_clk_dump(void) 501 { 502 printf(" xtal at %u000000 Hz\n\n", get_ref_clk()); 503 504 if (clk_dump("tbg@13200", armada_37xx_tbg_clk_dump)) 505 return 1; 506 507 if (clk_dump("nb-periph-clk@13000", 508 armada_37xx_periph_clk_dump)) 509 return 1; 510 511 if (clk_dump("sb-periph-clk@18000", 512 armada_37xx_periph_clk_dump)) 513 return 1; 514 515 return 0; 516 } 517 #endif 518 519 static int armada_37xx_periph_clk_probe(struct udevice *dev) 520 { 521 struct a37xx_periphclk *priv = dev_get_priv(dev); 522 const struct clk_periph *clks; 523 int ret, i; 524 525 clks = (const struct clk_periph *)dev_get_driver_data(dev); 526 if (!clks) 527 return -ENODEV; 528 529 priv->reg = dev_read_addr_ptr(dev); 530 if (!priv->reg) { 531 dev_err(dev, "no io address\n"); 532 return -ENODEV; 533 } 534 535 /* count clk_periph nodes */ 536 priv->count = 0; 537 while (clks[priv->count].name) 538 priv->count++; 539 540 priv->clks = clks; 541 542 /* assign parent IDs to nodes which have non-NULL parent_name */ 543 for (i = 0; i < priv->count; ++i) { 544 int j; 545 546 if (!clks[i].parent_name) 547 continue; 548 549 /* first try if parent_name is one of TBGs or XTAL */ 550 for (j = 0; j < MAX_PARENTS; ++j) 551 if (!strcmp(clks[i].parent_name, 552 a37xx_periph_parent_names[j].name)) 553 break; 554 555 if (j < MAX_PARENTS) { 556 priv->clk_has_periph_parent[i] = false; 557 priv->clk_parent[i] = 558 a37xx_periph_parent_names[j].parent; 559 continue; 560 } 561 562 /* else parent_name should be one of other periph clocks */ 563 for (j = 0; j < priv->count; ++j) { 564 if (!strcmp(clks[i].parent_name, clks[j].name)) 565 break; 566 } 567 568 if (j < priv->count) { 569 priv->clk_has_periph_parent[i] = true; 570 priv->clk_parent[i] = j; 571 continue; 572 } 573 574 dev_err(dev, "undefined parent %s\n", clks[i].parent_name); 575 return -EINVAL; 576 } 577 578 for (i = 0; i < MAX_PARENTS; ++i) { 579 struct clk clk; 580 581 if (i == XTAL) { 582 priv->parents[i] = get_ref_clk() * 1000000; 583 continue; 584 } 585 586 ret = clk_get_by_index(dev, i, &clk); 587 if (ret) { 588 dev_err(dev, "one of parent clocks (%i) missing: %i\n", 589 i, ret); 590 return -ENODEV; 591 } 592 593 priv->parents[i] = clk_get_rate(&clk); 594 clk_free(&clk); 595 } 596 597 return 0; 598 } 599 600 static const struct clk_ops armada_37xx_periph_clk_ops = { 601 .get_rate = armada_37xx_periph_clk_get_rate, 602 .set_rate = armada_37xx_periph_clk_set_rate, 603 .set_parent = armada_37xx_periph_clk_set_parent, 604 .enable = armada_37xx_periph_clk_enable, 605 .disable = armada_37xx_periph_clk_disable, 606 }; 607 608 static const struct udevice_id armada_37xx_periph_clk_ids[] = { 609 { 610 .compatible = "marvell,armada-3700-periph-clock-nb", 611 .data = (ulong)clks_nb, 612 }, 613 { 614 .compatible = "marvell,armada-3700-periph-clock-sb", 615 .data = (ulong)clks_sb, 616 }, 617 {} 618 }; 619 620 U_BOOT_DRIVER(armada_37xx_periph_clk) = { 621 .name = "armada_37xx_periph_clk", 622 .id = UCLASS_CLK, 623 .of_match = armada_37xx_periph_clk_ids, 624 .ops = &armada_37xx_periph_clk_ops, 625 .priv_auto_alloc_size = sizeof(struct a37xx_periphclk), 626 .probe = armada_37xx_periph_clk_probe, 627 }; 628