| .. | .. |
|---|
| 4 | 4 | */ |
|---|
| 5 | 5 | |
|---|
| 6 | 6 | #include <linux/clk-provider.h> |
|---|
| 7 | +#include <linux/io.h> |
|---|
| 7 | 8 | #include <linux/of_address.h> |
|---|
| 8 | 9 | #include <linux/platform_device.h> |
|---|
| 9 | 10 | |
|---|
| .. | .. |
|---|
| 120 | 121 | .n = _SUNXI_CCU_MULT_MIN(8, 8, 12), |
|---|
| 121 | 122 | .m = _SUNXI_CCU_DIV(1, 1), /* input divider */ |
|---|
| 122 | 123 | .fixed_post_div = 4, |
|---|
| 124 | + .min_rate = 288000000, |
|---|
| 125 | + .max_rate = 2400000000UL, |
|---|
| 123 | 126 | .common = { |
|---|
| 124 | 127 | .reg = 0x040, |
|---|
| 125 | 128 | .features = CCU_FEATURE_FIXED_POSTDIV, |
|---|
| .. | .. |
|---|
| 136 | 139 | .n = _SUNXI_CCU_MULT_MIN(8, 8, 12), |
|---|
| 137 | 140 | .m = _SUNXI_CCU_DIV(1, 1), /* input divider */ |
|---|
| 138 | 141 | .fixed_post_div = 4, |
|---|
| 142 | + .min_rate = 288000000, |
|---|
| 143 | + .max_rate = 2400000000UL, |
|---|
| 139 | 144 | .common = { |
|---|
| 140 | 145 | .reg = 0x048, |
|---|
| 141 | 146 | .features = CCU_FEATURE_FIXED_POSTDIV, |
|---|
| .. | .. |
|---|
| 198 | 203 | * hardcode it to match with the clock names. |
|---|
| 199 | 204 | */ |
|---|
| 200 | 205 | #define SUN50I_H6_PLL_AUDIO_REG 0x078 |
|---|
| 206 | + |
|---|
| 207 | +static struct ccu_sdm_setting pll_audio_sdm_table[] = { |
|---|
| 208 | + { .rate = 541900800, .pattern = 0xc001288d, .m = 1, .n = 22 }, |
|---|
| 209 | + { .rate = 589824000, .pattern = 0xc00126e9, .m = 1, .n = 24 }, |
|---|
| 210 | +}; |
|---|
| 211 | + |
|---|
| 201 | 212 | static struct ccu_nm pll_audio_base_clk = { |
|---|
| 202 | 213 | .enable = BIT(31), |
|---|
| 203 | 214 | .lock = BIT(28), |
|---|
| 204 | 215 | .n = _SUNXI_CCU_MULT_MIN(8, 8, 12), |
|---|
| 205 | 216 | .m = _SUNXI_CCU_DIV(1, 1), /* input divider */ |
|---|
| 217 | + .sdm = _SUNXI_CCU_SDM(pll_audio_sdm_table, |
|---|
| 218 | + BIT(24), 0x178, BIT(31)), |
|---|
| 206 | 219 | .common = { |
|---|
| 220 | + .features = CCU_FEATURE_SIGMA_DELTA_MOD, |
|---|
| 207 | 221 | .reg = 0x078, |
|---|
| 208 | 222 | .hw.init = CLK_HW_INIT("pll-audio-base", "osc24M", |
|---|
| 209 | 223 | &ccu_nm_ops, |
|---|
| .. | .. |
|---|
| 262 | 276 | 0, 4, /* M */ |
|---|
| 263 | 277 | 24, 1, /* mux */ |
|---|
| 264 | 278 | BIT(31), /* gate */ |
|---|
| 265 | | - 0); |
|---|
| 279 | + CLK_SET_RATE_PARENT); |
|---|
| 266 | 280 | |
|---|
| 267 | 281 | static SUNXI_CCU_GATE(bus_de_clk, "bus-de", "psi-ahb1-ahb2", |
|---|
| 268 | 282 | 0x60c, BIT(0), 0); |
|---|
| .. | .. |
|---|
| 285 | 299 | 0, 3, /* M */ |
|---|
| 286 | 300 | 24, 1, /* mux */ |
|---|
| 287 | 301 | BIT(31), /* gate */ |
|---|
| 288 | | - 0); |
|---|
| 302 | + CLK_SET_RATE_PARENT); |
|---|
| 289 | 303 | |
|---|
| 290 | 304 | static SUNXI_CCU_GATE(bus_gpu_clk, "bus-gpu", "psi-ahb1-ahb2", |
|---|
| 291 | 305 | 0x67c, BIT(0), 0); |
|---|
| .. | .. |
|---|
| 307 | 321 | 0, 3, /* M */ |
|---|
| 308 | 322 | 24, 1, /* mux */ |
|---|
| 309 | 323 | BIT(31), /* gate */ |
|---|
| 310 | | - 0); |
|---|
| 324 | + CLK_SET_RATE_PARENT); |
|---|
| 311 | 325 | |
|---|
| 312 | 326 | static SUNXI_CCU_GATE(bus_ve_clk, "bus-ve", "psi-ahb1-ahb2", |
|---|
| 313 | 327 | 0x69c, BIT(0), 0); |
|---|
| .. | .. |
|---|
| 408 | 422 | |
|---|
| 409 | 423 | static const char * const mmc_parents[] = { "osc24M", "pll-periph0-2x", |
|---|
| 410 | 424 | "pll-periph1-2x" }; |
|---|
| 411 | | -static SUNXI_CCU_MP_WITH_MUX_GATE(mmc0_clk, "mmc0", mmc_parents, 0x830, |
|---|
| 412 | | - 0, 4, /* M */ |
|---|
| 413 | | - 8, 2, /* N */ |
|---|
| 414 | | - 24, 3, /* mux */ |
|---|
| 415 | | - BIT(31),/* gate */ |
|---|
| 416 | | - 0); |
|---|
| 425 | +static SUNXI_CCU_MP_WITH_MUX_GATE_POSTDIV(mmc0_clk, "mmc0", mmc_parents, 0x830, |
|---|
| 426 | + 0, 4, /* M */ |
|---|
| 427 | + 8, 2, /* N */ |
|---|
| 428 | + 24, 2, /* mux */ |
|---|
| 429 | + BIT(31), /* gate */ |
|---|
| 430 | + 2, /* post-div */ |
|---|
| 431 | + 0); |
|---|
| 417 | 432 | |
|---|
| 418 | | -static SUNXI_CCU_MP_WITH_MUX_GATE(mmc1_clk, "mmc1", mmc_parents, 0x834, |
|---|
| 419 | | - 0, 4, /* M */ |
|---|
| 420 | | - 8, 2, /* N */ |
|---|
| 421 | | - 24, 3, /* mux */ |
|---|
| 422 | | - BIT(31),/* gate */ |
|---|
| 423 | | - 0); |
|---|
| 433 | +static SUNXI_CCU_MP_WITH_MUX_GATE_POSTDIV(mmc1_clk, "mmc1", mmc_parents, 0x834, |
|---|
| 434 | + 0, 4, /* M */ |
|---|
| 435 | + 8, 2, /* N */ |
|---|
| 436 | + 24, 2, /* mux */ |
|---|
| 437 | + BIT(31), /* gate */ |
|---|
| 438 | + 2, /* post-div */ |
|---|
| 439 | + 0); |
|---|
| 424 | 440 | |
|---|
| 425 | | -static SUNXI_CCU_MP_WITH_MUX_GATE(mmc2_clk, "mmc2", mmc_parents, 0x838, |
|---|
| 426 | | - 0, 4, /* M */ |
|---|
| 427 | | - 8, 2, /* N */ |
|---|
| 428 | | - 24, 3, /* mux */ |
|---|
| 429 | | - BIT(31),/* gate */ |
|---|
| 430 | | - 0); |
|---|
| 441 | +static SUNXI_CCU_MP_WITH_MUX_GATE_POSTDIV(mmc2_clk, "mmc2", mmc_parents, 0x838, |
|---|
| 442 | + 0, 4, /* M */ |
|---|
| 443 | + 8, 2, /* N */ |
|---|
| 444 | + 24, 2, /* mux */ |
|---|
| 445 | + BIT(31), /* gate */ |
|---|
| 446 | + 2, /* post-div */ |
|---|
| 447 | + 0); |
|---|
| 431 | 448 | |
|---|
| 432 | 449 | static SUNXI_CCU_GATE(bus_mmc0_clk, "bus-mmc0", "ahb3", 0x84c, BIT(0), 0); |
|---|
| 433 | 450 | static SUNXI_CCU_GATE(bus_mmc1_clk, "bus-mmc1", "ahb3", 0x84c, BIT(1), 0); |
|---|
| .. | .. |
|---|
| 497 | 514 | .hw.init = CLK_HW_INIT_PARENTS("i2s3", |
|---|
| 498 | 515 | audio_parents, |
|---|
| 499 | 516 | &ccu_div_ops, |
|---|
| 500 | | - 0), |
|---|
| 517 | + CLK_SET_RATE_PARENT), |
|---|
| 501 | 518 | }, |
|---|
| 502 | 519 | }; |
|---|
| 503 | 520 | |
|---|
| .. | .. |
|---|
| 510 | 527 | .hw.init = CLK_HW_INIT_PARENTS("i2s0", |
|---|
| 511 | 528 | audio_parents, |
|---|
| 512 | 529 | &ccu_div_ops, |
|---|
| 513 | | - 0), |
|---|
| 530 | + CLK_SET_RATE_PARENT), |
|---|
| 514 | 531 | }, |
|---|
| 515 | 532 | }; |
|---|
| 516 | 533 | |
|---|
| .. | .. |
|---|
| 523 | 540 | .hw.init = CLK_HW_INIT_PARENTS("i2s1", |
|---|
| 524 | 541 | audio_parents, |
|---|
| 525 | 542 | &ccu_div_ops, |
|---|
| 526 | | - 0), |
|---|
| 543 | + CLK_SET_RATE_PARENT), |
|---|
| 527 | 544 | }, |
|---|
| 528 | 545 | }; |
|---|
| 529 | 546 | |
|---|
| .. | .. |
|---|
| 536 | 553 | .hw.init = CLK_HW_INIT_PARENTS("i2s2", |
|---|
| 537 | 554 | audio_parents, |
|---|
| 538 | 555 | &ccu_div_ops, |
|---|
| 539 | | - 0), |
|---|
| 556 | + CLK_SET_RATE_PARENT), |
|---|
| 540 | 557 | }, |
|---|
| 541 | 558 | }; |
|---|
| 542 | 559 | |
|---|
| .. | .. |
|---|
| 614 | 631 | static SUNXI_CCU_GATE(bus_ehci3_clk, "bus-ehci3", "ahb3", 0xa8c, BIT(7), 0); |
|---|
| 615 | 632 | static SUNXI_CCU_GATE(bus_otg_clk, "bus-otg", "ahb3", 0xa8c, BIT(8), 0); |
|---|
| 616 | 633 | |
|---|
| 617 | | -static CLK_FIXED_FACTOR(pcie_ref_100m_clk, "pcie-ref-100M", |
|---|
| 618 | | - "pll-periph0-4x", 24, 1, 0); |
|---|
| 634 | +static struct clk_fixed_factor pll_periph0_4x_clk; |
|---|
| 635 | +static CLK_FIXED_FACTOR_HW(pcie_ref_100m_clk, "pcie-ref-100M", |
|---|
| 636 | + &pll_periph0_4x_clk.hw, 24, 1, 0); |
|---|
| 619 | 637 | static SUNXI_CCU_GATE(pcie_ref_clk, "pcie-ref", "pcie-ref-100M", |
|---|
| 620 | 638 | 0xab0, BIT(31), 0); |
|---|
| 621 | 639 | static SUNXI_CCU_GATE(pcie_ref_out_clk, "pcie-ref-out", "pcie-ref", |
|---|
| .. | .. |
|---|
| 649 | 667 | static const struct ccu_mux_fixed_prediv hdmi_cec_predivs[] = { |
|---|
| 650 | 668 | { .index = 1, .div = 36621 }, |
|---|
| 651 | 669 | }; |
|---|
| 670 | + |
|---|
| 671 | +#define SUN50I_H6_HDMI_CEC_CLK_REG 0xb10 |
|---|
| 652 | 672 | static struct ccu_mux hdmi_cec_clk = { |
|---|
| 653 | 673 | .enable = BIT(31), |
|---|
| 654 | 674 | |
|---|
| .. | .. |
|---|
| 682 | 702 | tcon_lcd0_parents, 0xb60, |
|---|
| 683 | 703 | 24, 3, /* mux */ |
|---|
| 684 | 704 | BIT(31), /* gate */ |
|---|
| 685 | | - 0); |
|---|
| 705 | + CLK_SET_RATE_PARENT); |
|---|
| 686 | 706 | |
|---|
| 687 | 707 | static SUNXI_CCU_GATE(bus_tcon_lcd0_clk, "bus-tcon-lcd0", "ahb3", |
|---|
| 688 | 708 | 0xb7c, BIT(0), 0); |
|---|
| .. | .. |
|---|
| 697 | 717 | 8, 2, /* P */ |
|---|
| 698 | 718 | 24, 3, /* mux */ |
|---|
| 699 | 719 | BIT(31), /* gate */ |
|---|
| 700 | | - 0); |
|---|
| 720 | + CLK_SET_RATE_PARENT); |
|---|
| 701 | 721 | |
|---|
| 702 | 722 | static SUNXI_CCU_GATE(bus_tcon_tv0_clk, "bus-tcon-tv0", "ahb3", |
|---|
| 703 | 723 | 0xb9c, BIT(0), 0); |
|---|
| .. | .. |
|---|
| 735 | 755 | static SUNXI_CCU_GATE(bus_hdcp_clk, "bus-hdcp", "ahb3", 0xc4c, BIT(0), 0); |
|---|
| 736 | 756 | |
|---|
| 737 | 757 | /* Fixed factor clocks */ |
|---|
| 738 | | -static CLK_FIXED_FACTOR(osc12M_clk, "osc12M", "osc24M", 2, 1, 0); |
|---|
| 758 | +static CLK_FIXED_FACTOR_FW_NAME(osc12M_clk, "osc12M", "hosc", 2, 1, 0); |
|---|
| 759 | + |
|---|
| 760 | +static const struct clk_hw *clk_parent_pll_audio[] = { |
|---|
| 761 | + &pll_audio_base_clk.common.hw |
|---|
| 762 | +}; |
|---|
| 739 | 763 | |
|---|
| 740 | 764 | /* |
|---|
| 741 | | - * The divider of pll-audio is fixed to 8 now, as pll-audio-4x has a |
|---|
| 742 | | - * fixed post-divider 2. |
|---|
| 765 | + * The divider of pll-audio is fixed to 24 for now, so 24576000 and 22579200 |
|---|
| 766 | + * rates can be set exactly in conjunction with sigma-delta modulation. |
|---|
| 743 | 767 | */ |
|---|
| 744 | | -static CLK_FIXED_FACTOR(pll_audio_clk, "pll-audio", |
|---|
| 745 | | - "pll-audio-base", 8, 1, CLK_SET_RATE_PARENT); |
|---|
| 746 | | -static CLK_FIXED_FACTOR(pll_audio_2x_clk, "pll-audio-2x", |
|---|
| 747 | | - "pll-audio-base", 4, 1, CLK_SET_RATE_PARENT); |
|---|
| 748 | | -static CLK_FIXED_FACTOR(pll_audio_4x_clk, "pll-audio-4x", |
|---|
| 749 | | - "pll-audio-base", 2, 1, CLK_SET_RATE_PARENT); |
|---|
| 768 | +static CLK_FIXED_FACTOR_HWS(pll_audio_clk, "pll-audio", |
|---|
| 769 | + clk_parent_pll_audio, |
|---|
| 770 | + 24, 1, CLK_SET_RATE_PARENT); |
|---|
| 771 | +static CLK_FIXED_FACTOR_HWS(pll_audio_2x_clk, "pll-audio-2x", |
|---|
| 772 | + clk_parent_pll_audio, |
|---|
| 773 | + 4, 1, CLK_SET_RATE_PARENT); |
|---|
| 774 | +static CLK_FIXED_FACTOR_HWS(pll_audio_4x_clk, "pll-audio-4x", |
|---|
| 775 | + clk_parent_pll_audio, |
|---|
| 776 | + 2, 1, CLK_SET_RATE_PARENT); |
|---|
| 750 | 777 | |
|---|
| 751 | | -static CLK_FIXED_FACTOR(pll_periph0_4x_clk, "pll-periph0-4x", |
|---|
| 752 | | - "pll-periph0", 1, 4, 0); |
|---|
| 753 | | -static CLK_FIXED_FACTOR(pll_periph0_2x_clk, "pll-periph0-2x", |
|---|
| 754 | | - "pll-periph0", 1, 2, 0); |
|---|
| 778 | +static const struct clk_hw *pll_periph0_parents[] = { |
|---|
| 779 | + &pll_periph0_clk.common.hw |
|---|
| 780 | +}; |
|---|
| 781 | +static CLK_FIXED_FACTOR_HWS(pll_periph0_4x_clk, "pll-periph0-4x", |
|---|
| 782 | + pll_periph0_parents, |
|---|
| 783 | + 1, 4, 0); |
|---|
| 784 | +static CLK_FIXED_FACTOR_HWS(pll_periph0_2x_clk, "pll-periph0-2x", |
|---|
| 785 | + pll_periph0_parents, |
|---|
| 786 | + 1, 2, 0); |
|---|
| 755 | 787 | |
|---|
| 756 | | -static CLK_FIXED_FACTOR(pll_periph1_4x_clk, "pll-periph1-4x", |
|---|
| 757 | | - "pll-periph1", 1, 4, 0); |
|---|
| 758 | | -static CLK_FIXED_FACTOR(pll_periph1_2x_clk, "pll-periph1-2x", |
|---|
| 759 | | - "pll-periph1", 1, 2, 0); |
|---|
| 788 | +static const struct clk_hw *pll_periph1_parents[] = { |
|---|
| 789 | + &pll_periph1_clk.common.hw |
|---|
| 790 | +}; |
|---|
| 791 | +static CLK_FIXED_FACTOR_HWS(pll_periph1_4x_clk, "pll-periph1-4x", |
|---|
| 792 | + pll_periph1_parents, |
|---|
| 793 | + 1, 4, 0); |
|---|
| 794 | +static CLK_FIXED_FACTOR_HWS(pll_periph1_2x_clk, "pll-periph1-2x", |
|---|
| 795 | + pll_periph1_parents, |
|---|
| 796 | + 1, 2, 0); |
|---|
| 760 | 797 | |
|---|
| 761 | | -static CLK_FIXED_FACTOR(pll_video0_4x_clk, "pll-video0-4x", |
|---|
| 762 | | - "pll-video0", 1, 4, CLK_SET_RATE_PARENT); |
|---|
| 763 | | - |
|---|
| 764 | | -static CLK_FIXED_FACTOR(pll_video1_4x_clk, "pll-video1-4x", |
|---|
| 765 | | - "pll-video1", 1, 4, CLK_SET_RATE_PARENT); |
|---|
| 798 | +static CLK_FIXED_FACTOR_HW(pll_video0_4x_clk, "pll-video0-4x", |
|---|
| 799 | + &pll_video0_clk.common.hw, |
|---|
| 800 | + 1, 4, CLK_SET_RATE_PARENT); |
|---|
| 801 | +static CLK_FIXED_FACTOR_HW(pll_video1_4x_clk, "pll-video1-4x", |
|---|
| 802 | + &pll_video1_clk.common.hw, |
|---|
| 803 | + 1, 4, CLK_SET_RATE_PARENT); |
|---|
| 766 | 804 | |
|---|
| 767 | 805 | static struct ccu_common *sun50i_h6_ccu_clks[] = { |
|---|
| 768 | 806 | &pll_cpux_clk.common, |
|---|
| .. | .. |
|---|
| 1186 | 1224 | } |
|---|
| 1187 | 1225 | |
|---|
| 1188 | 1226 | /* |
|---|
| 1189 | | - * Force the post-divider of pll-audio to 8 and the output divider |
|---|
| 1190 | | - * of it to 1, to make the clock name represents the real frequency. |
|---|
| 1227 | + * Force the post-divider of pll-audio to 12 and the output divider |
|---|
| 1228 | + * of it to 2, so 24576000 and 22579200 rates can be set exactly. |
|---|
| 1191 | 1229 | */ |
|---|
| 1192 | 1230 | val = readl(reg + SUN50I_H6_PLL_AUDIO_REG); |
|---|
| 1193 | 1231 | val &= ~(GENMASK(21, 16) | BIT(0)); |
|---|
| 1194 | | - writel(val | (7 << 16), reg + SUN50I_H6_PLL_AUDIO_REG); |
|---|
| 1232 | + writel(val | (11 << 16) | BIT(0), reg + SUN50I_H6_PLL_AUDIO_REG); |
|---|
| 1233 | + |
|---|
| 1234 | + /* |
|---|
| 1235 | + * First clock parent (osc32K) is unusable for CEC. But since there |
|---|
| 1236 | + * is no good way to force parent switch (both run with same frequency), |
|---|
| 1237 | + * just set second clock parent here. |
|---|
| 1238 | + */ |
|---|
| 1239 | + val = readl(reg + SUN50I_H6_HDMI_CEC_CLK_REG); |
|---|
| 1240 | + val |= BIT(24); |
|---|
| 1241 | + writel(val, reg + SUN50I_H6_HDMI_CEC_CLK_REG); |
|---|
| 1195 | 1242 | |
|---|
| 1196 | 1243 | return sunxi_ccu_probe(pdev->dev.of_node, reg, &sun50i_h6_ccu_desc); |
|---|
| 1197 | 1244 | } |
|---|