/* * Marvell EBU clock core handling defined at reset * * Copyright (C) 2012 Marvell * * Gregory CLEMENT * Sebastian Hesselbarth * * This file is licensed under the terms of the GNU General Public * License version 2. This program is licensed "as is" without any * warranty of any kind, whether express or implied. */ #include #include #include #include #include #include #include #include "clk-core.h" struct core_ratio { int id; const char *name; }; struct core_clocks { u32 (*get_tclk_freq)(void __iomem *sar); u32 (*get_cpu_freq)(void __iomem *sar); void (*get_clk_ratio)(void __iomem *sar, int id, int *mult, int *div); const struct core_ratio *ratios; int num_ratios; }; static struct clk_onecell_data clk_data; static void __init mvebu_clk_core_setup(struct device_node *np, struct core_clocks *coreclk) { const char *tclk_name = "tclk"; const char *cpuclk_name = "cpuclk"; void __iomem *base; unsigned long rate; int n; base = of_iomap(np, 0); if (WARN_ON(!base)) return; /* * Allocate struct for TCLK, cpu clk, and core ratio clocks */ clk_data.clk_num = 2 + coreclk->num_ratios; clk_data.clks = kzalloc(clk_data.clk_num * sizeof(struct clk *), GFP_KERNEL); if (WARN_ON(!clk_data.clks)) return; /* * Register TCLK */ of_property_read_string_index(np, "clock-output-names", 0, &tclk_name); rate = coreclk->get_tclk_freq(base); clk_data.clks[0] = clk_register_fixed_rate(NULL, tclk_name, NULL, CLK_IS_ROOT, rate); WARN_ON(IS_ERR(clk_data.clks[0])); /* * Register CPU clock */ of_property_read_string_index(np, "clock-output-names", 1, &cpuclk_name); rate = coreclk->get_cpu_freq(base); clk_data.clks[1] = clk_register_fixed_rate(NULL, cpuclk_name, NULL, CLK_IS_ROOT, rate); WARN_ON(IS_ERR(clk_data.clks[1])); /* * Register fixed-factor clocks derived from CPU clock */ for (n = 0; n < coreclk->num_ratios; n++) { const char *rclk_name = coreclk->ratios[n].name; int mult, div; of_property_read_string_index(np, "clock-output-names", 2+n, &rclk_name); coreclk->get_clk_ratio(base, coreclk->ratios[n].id, &mult, &div); clk_data.clks[2+n] = clk_register_fixed_factor(NULL, rclk_name, cpuclk_name, 0, mult, div); WARN_ON(IS_ERR(clk_data.clks[2+n])); }; /* * SAR register isn't needed anymore */ iounmap(base); of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); } #ifdef CONFIG_MACH_ARMADA_370_XP /* * Armada 370/XP Sample At Reset is a 64 bit bitfiled split in two * register of 32 bits */ #define SARL 0 /* Low part [0:31] */ #define SARL_AXP_PCLK_FREQ_OPT 21 #define SARL_AXP_PCLK_FREQ_OPT_MASK 0x7 #define SARL_A370_PCLK_FREQ_OPT 11 #define SARL_A370_PCLK_FREQ_OPT_MASK 0xF #define SARL_AXP_FAB_FREQ_OPT 24 #define SARL_AXP_FAB_FREQ_OPT_MASK 0xF #define SARL_A370_FAB_FREQ_OPT 15 #define SARL_A370_FAB_FREQ_OPT_MASK 0x1F #define SARL_A370_TCLK_FREQ_OPT 20 #define SARL_A370_TCLK_FREQ_OPT_MASK 0x1 #define SARH 4 /* High part [32:63] */ #define SARH_AXP_PCLK_FREQ_OPT (52-32) #define SARH_AXP_PCLK_FREQ_OPT_MASK 0x1 #define SARH_AXP_PCLK_FREQ_OPT_SHIFT 3 #define SARH_AXP_FAB_FREQ_OPT (51-32) #define SARH_AXP_FAB_FREQ_OPT_MASK 0x1 #define SARH_AXP_FAB_FREQ_OPT_SHIFT 4 static const u32 __initconst armada_370_tclk_frequencies[] = { 16600000, 20000000, }; static u32 __init armada_370_get_tclk_freq(void __iomem *sar) { u8 tclk_freq_select = 0; tclk_freq_select = ((readl(sar) >> SARL_A370_TCLK_FREQ_OPT) & SARL_A370_TCLK_FREQ_OPT_MASK); return armada_370_tclk_frequencies[tclk_freq_select]; } static const u32 __initconst armada_370_cpu_frequencies[] = { 400000000, 533000000, 667000000, 800000000, 1000000000, 1067000000, 1200000000, }; static u32 __init armada_370_get_cpu_freq(void __iomem *sar) { u32 cpu_freq; u8 cpu_freq_select = 0; cpu_freq_select = ((readl(sar) >> SARL_A370_PCLK_FREQ_OPT) & SARL_A370_PCLK_FREQ_OPT_MASK); if (cpu_freq_select >= ARRAY_SIZE(armada_370_cpu_frequencies)) { pr_err("CPU freq select unsupported %d\n", cpu_freq_select); cpu_freq = 0; } else cpu_freq = armada_370_cpu_frequencies[cpu_freq_select]; return cpu_freq; } enum { A370_XP_NBCLK, A370_XP_HCLK, A370_XP_DRAMCLK }; static const struct core_ratio __initconst armada_370_xp_core_ratios[] = { { .id = A370_XP_NBCLK, .name = "nbclk" }, { .id = A370_XP_HCLK, .name = "hclk" }, { .id = A370_XP_DRAMCLK, .name = "dramclk" }, }; static const int __initconst armada_370_xp_nbclk_ratios[32][2] = { {0, 1}, {1, 2}, {2, 2}, {2, 2}, {1, 2}, {1, 2}, {1, 1}, {2, 3}, {0, 1}, {1, 2}, {2, 4}, {0, 1}, {1, 2}, {0, 1}, {0, 1}, {2, 2}, {0, 1}, {0, 1}, {0, 1}, {1, 1}, {2, 3}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {1, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, }; static const int __initconst armada_370_xp_hclk_ratios[32][2] = { {0, 1}, {1, 2}, {2, 6}, {2, 3}, {1, 3}, {1, 4}, {1, 2}, {2, 6}, {0, 1}, {1, 6}, {2, 10}, {0, 1}, {1, 4}, {0, 1}, {0, 1}, {2, 5}, {0, 1}, {0, 1}, {0, 1}, {1, 2}, {2, 6}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {1, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, }; static const int __initconst armada_370_xp_dramclk_ratios[32][2] = { {0, 1}, {1, 2}, {2, 3}, {2, 3}, {1, 3}, {1, 2}, {1, 2}, {2, 6}, {0, 1}, {1, 3}, {2, 5}, {0, 1}, {1, 4}, {0, 1}, {0, 1}, {2, 5}, {0, 1}, {0, 1}, {0, 1}, {1, 1}, {2, 3}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {1, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, }; static void __init armada_370_xp_get_clk_ratio(u32 opt, void __iomem *sar, int id, int *mult, int *div) { switch (id) { case A370_XP_NBCLK: *mult = armada_370_xp_nbclk_ratios[opt][0]; *div = armada_370_xp_nbclk_ratios[opt][1]; break; case A370_XP_HCLK: *mult = armada_370_xp_hclk_ratios[opt][0]; *div = armada_370_xp_hclk_ratios[opt][1]; break; case A370_XP_DRAMCLK: *mult = armada_370_xp_dramclk_ratios[opt][0]; *div = armada_370_xp_dramclk_ratios[opt][1]; break; } } static void __init armada_370_get_clk_ratio( void __iomem *sar, int id, int *mult, int *div) { u32 opt = ((readl(sar) >> SARL_A370_FAB_FREQ_OPT) & SARL_A370_FAB_FREQ_OPT_MASK); armada_370_xp_get_clk_ratio(opt, sar, id, mult, div); } static const struct core_clocks armada_370_core_clocks = { .get_tclk_freq = armada_370_get_tclk_freq, .get_cpu_freq = armada_370_get_cpu_freq, .get_clk_ratio = armada_370_get_clk_ratio, .ratios = armada_370_xp_core_ratios, .num_ratios = ARRAY_SIZE(armada_370_xp_core_ratios), }; static const u32 __initconst armada_xp_cpu_frequencies[] = { 1000000000, 1066000000, 1200000000, 1333000000, 1500000000, 1666000000, 1800000000, 2000000000, 667000000, 0, 800000000, 1600000000, }; /* For Armada XP TCLK frequency is fix: 250MHz */ static u32 __init armada_xp_get_tclk_freq(void __iomem *sar) { return 250 * 1000 * 1000; } static u32 __init armada_xp_get_cpu_freq(void __iomem *sar) { u32 cpu_freq; u8 cpu_freq_select = 0; cpu_freq_select = ((readl(sar) >> SARL_AXP_PCLK_FREQ_OPT) & SARL_AXP_PCLK_FREQ_OPT_MASK); /* * The upper bit is not contiguous to the other ones and * located in the high part of the SAR registers */ cpu_freq_select |= (((readl(sar+4) >> SARH_AXP_PCLK_FREQ_OPT) & SARH_AXP_PCLK_FREQ_OPT_MASK) << SARH_AXP_PCLK_FREQ_OPT_SHIFT); if (cpu_freq_select >= ARRAY_SIZE(armada_xp_cpu_frequencies)) { pr_err("CPU freq select unsupported: %d\n", cpu_freq_select); cpu_freq = 0; } else cpu_freq = armada_xp_cpu_frequencies[cpu_freq_select]; return cpu_freq; } static void __init armada_xp_get_clk_ratio( void __iomem *sar, int id, int *mult, int *div) { u32 opt = ((readl(sar) >> SARL_AXP_FAB_FREQ_OPT) & SARL_AXP_FAB_FREQ_OPT_MASK); /* * The upper bit is not contiguous to the other ones and * located in the high part of the SAR registers */ opt |= (((readl(sar+4) >> SARH_AXP_FAB_FREQ_OPT) & SARH_AXP_FAB_FREQ_OPT_MASK) << SARH_AXP_FAB_FREQ_OPT_SHIFT); armada_370_xp_get_clk_ratio(opt, sar, id, mult, div); } static const struct core_clocks armada_xp_core_clocks = { .get_tclk_freq = armada_xp_get_tclk_freq, .get_cpu_freq = armada_xp_get_cpu_freq, .get_clk_ratio = armada_xp_get_clk_ratio, .ratios = armada_370_xp_core_ratios, .num_ratios = ARRAY_SIZE(armada_370_xp_core_ratios), }; #endif /* CONFIG_MACH_ARMADA_370_XP */ /* * Dove PLL sample-at-reset configuration * * SAR0[8:5] : CPU frequency * 5 = 1000 MHz * 6 = 933 MHz * 7 = 933 MHz * 8 = 800 MHz * 9 = 800 MHz * 10 = 800 MHz * 11 = 1067 MHz * 12 = 667 MHz * 13 = 533 MHz * 14 = 400 MHz * 15 = 333 MHz * others reserved. * * SAR0[11:9] : CPU to L2 Clock divider ratio * 0 = (1/1) * CPU * 2 = (1/2) * CPU * 4 = (1/3) * CPU * 6 = (1/4) * CPU * others reserved. * * SAR0[15:12] : CPU to DDR DRAM Clock divider ratio * 0 = (1/1) * CPU * 2 = (1/2) * CPU * 3 = (2/5) * CPU * 4 = (1/3) * CPU * 6 = (1/4) * CPU * 8 = (1/5) * CPU * 10 = (1/6) * CPU * 12 = (1/7) * CPU * 14 = (1/8) * CPU * 15 = (1/10) * CPU * others reserved. * * SAR0[24:23] : TCLK frequency * 0 = 166 MHz * 1 = 125 MHz * others reserved. */ #ifdef CONFIG_ARCH_DOVE #define SAR_DOVE_CPU_FREQ 5 #define SAR_DOVE_CPU_FREQ_MASK 0xf #define SAR_DOVE_L2_RATIO 9 #define SAR_DOVE_L2_RATIO_MASK 0x7 #define SAR_DOVE_DDR_RATIO 12 #define SAR_DOVE_DDR_RATIO_MASK 0xf #define SAR_DOVE_TCLK_FREQ 23 #define SAR_DOVE_TCLK_FREQ_MASK 0x3 static const u32 __initconst dove_tclk_frequencies[] = { 166666667, 125000000, 0, 0 }; static u32 __init dove_get_tclk_freq(void __iomem *sar) { u32 opt = (readl(sar) >> SAR_DOVE_TCLK_FREQ) & SAR_DOVE_TCLK_FREQ_MASK; return dove_tclk_frequencies[opt]; } static const u32 __initconst dove_cpu_frequencies[] = { 0, 0, 0, 0, 0, 1000000000, 933333333, 933333333, 800000000, 800000000, 800000000, 1066666667, 666666667, 533333333, 400000000, 333333333 }; static u32 __init dove_get_cpu_freq(void __iomem *sar) { u32 opt = (readl(sar) >> SAR_DOVE_CPU_FREQ) & SAR_DOVE_CPU_FREQ_MASK; return dove_cpu_frequencies[opt]; } enum { DOVE_CPU_TO_L2, DOVE_CPU_TO_DDR }; static const struct core_ratio __initconst dove_core_ratios[] = { { .id = DOVE_CPU_TO_L2, .name = "l2clk", }, { .id = DOVE_CPU_TO_DDR, .name = "ddrclk", } }; static const int __initconst dove_cpu_l2_ratios[8][2] = { { 1, 1 }, { 0, 1 }, { 1, 2 }, { 0, 1 }, { 1, 3 }, { 0, 1 }, { 1, 4 }, { 0, 1 } }; static const int __initconst dove_cpu_ddr_ratios[16][2] = { { 1, 1 }, { 0, 1 }, { 1, 2 }, { 2, 5 }, { 1, 3 }, { 0, 1 }, { 1, 4 }, { 0, 1 }, { 1, 5 }, { 0, 1 }, { 1, 6 }, { 0, 1 }, { 1, 7 }, { 0, 1 }, { 1, 8 }, { 1, 10 } }; static void __init dove_get_clk_ratio( void __iomem *sar, int id, int *mult, int *div) { switch (id) { case DOVE_CPU_TO_L2: { u32 opt = (readl(sar) >> SAR_DOVE_L2_RATIO) & SAR_DOVE_L2_RATIO_MASK; *mult = dove_cpu_l2_ratios[opt][0]; *div = dove_cpu_l2_ratios[opt][1]; break; } case DOVE_CPU_TO_DDR: { u32 opt = (readl(sar) >> SAR_DOVE_DDR_RATIO) & SAR_DOVE_DDR_RATIO_MASK; *mult = dove_cpu_ddr_ratios[opt][0]; *div = dove_cpu_ddr_ratios[opt][1]; break; } } } static const struct core_clocks dove_core_clocks = { .get_tclk_freq = dove_get_tclk_freq, .get_cpu_freq = dove_get_cpu_freq, .get_clk_ratio = dove_get_clk_ratio, .ratios = dove_core_ratios, .num_ratios = ARRAY_SIZE(dove_core_ratios), }; #endif /* CONFIG_ARCH_DOVE */ /* * Kirkwood PLL sample-at-reset configuration * (6180 has different SAR layout than other Kirkwood SoCs) * * SAR0[4:3,22,1] : CPU frequency (6281,6292,6282) * 4 = 600 MHz * 6 = 800 MHz * 7 = 1000 MHz * 9 = 1200 MHz * 12 = 1500 MHz * 13 = 1600 MHz * 14 = 1800 MHz * 15 = 2000 MHz * others reserved. * * SAR0[19,10:9] : CPU to L2 Clock divider ratio (6281,6292,6282) * 1 = (1/2) * CPU * 3 = (1/3) * CPU * 5 = (1/4) * CPU * others reserved. * * SAR0[8:5] : CPU to DDR DRAM Clock divider ratio (6281,6292,6282) * 2 = (1/2) * CPU * 4 = (1/3) * CPU * 6 = (1/4) * CPU * 7 = (2/9) * CPU * 8 = (1/5) * CPU * 9 = (1/6) * CPU * others reserved. * * SAR0[4:2] : Kirkwood 6180 cpu/l2/ddr clock configuration (6180 only) * 5 = [CPU = 600 MHz, L2 = (1/2) * CPU, DDR = 200 MHz = (1/3) * CPU] * 6 = [CPU = 800 MHz, L2 = (1/2) * CPU, DDR = 200 MHz = (1/4) * CPU] * 7 = [CPU = 1000 MHz, L2 = (1/2) * CPU, DDR = 200 MHz = (1/5) * CPU] * others reserved. * * SAR0[21] : TCLK frequency * 0 = 200 MHz * 1 = 166 MHz * others reserved. */ #ifdef CONFIG_ARCH_KIRKWOOD #define SAR_KIRKWOOD_CPU_FREQ(x) \ (((x & (1 << 1)) >> 1) | \ ((x & (1 << 22)) >> 21) | \ ((x & (3 << 3)) >> 1)) #define SAR_KIRKWOOD_L2_RATIO(x) \ (((x & (3 << 9)) >> 9) | \ (((x & (1 << 19)) >> 17))) #define SAR_KIRKWOOD_DDR_RATIO 5 #define SAR_KIRKWOOD_DDR_RATIO_MASK 0xf #define SAR_MV88F6180_CLK 2 #define SAR_MV88F6180_CLK_MASK 0x7 #define SAR_KIRKWOOD_TCLK_FREQ 21 #define SAR_KIRKWOOD_TCLK_FREQ_MASK 0x1 enum { KIRKWOOD_CPU_TO_L2, KIRKWOOD_CPU_TO_DDR }; static const struct core_ratio __initconst kirkwood_core_ratios[] = { { .id = KIRKWOOD_CPU_TO_L2, .name = "l2clk", }, { .id = KIRKWOOD_CPU_TO_DDR, .name = "ddrclk", } }; static u32 __init kirkwood_get_tclk_freq(void __iomem *sar) { u32 opt = (readl(sar) >> SAR_KIRKWOOD_TCLK_FREQ) & SAR_KIRKWOOD_TCLK_FREQ_MASK; return (opt) ? 166666667 : 200000000; } static const u32 __initconst kirkwood_cpu_frequencies[] = { 0, 0, 0, 0, 600000000, 0, 800000000, 1000000000, 0, 1200000000, 0, 0, 1500000000, 1600000000, 1800000000, 2000000000 }; static u32 __init kirkwood_get_cpu_freq(void __iomem *sar) { u32 opt = SAR_KIRKWOOD_CPU_FREQ(readl(sar)); return kirkwood_cpu_frequencies[opt]; } static const int __initconst kirkwood_cpu_l2_ratios[8][2] = { { 0, 1 }, { 1, 2 }, { 0, 1 }, { 1, 3 }, { 0, 1 }, { 1, 4 }, { 0, 1 }, { 0, 1 } }; static const int __initconst kirkwood_cpu_ddr_ratios[16][2] = { { 0, 1 }, { 0, 1 }, { 1, 2 }, { 0, 1 }, { 1, 3 }, { 0, 1 }, { 1, 4 }, { 2, 9 }, { 1, 5 }, { 1, 6 }, { 0, 1 }, { 0, 1 }, { 0, 1 }, { 0, 1 }, { 0, 1 }, { 0, 1 } }; static void __init kirkwood_get_clk_ratio( void __iomem *sar, int id, int *mult, int *div) { switch (id) { case KIRKWOOD_CPU_TO_L2: { u32 opt = SAR_KIRKWOOD_L2_RATIO(readl(sar)); *mult = kirkwood_cpu_l2_ratios[opt][0]; *div = kirkwood_cpu_l2_ratios[opt][1]; break; } case KIRKWOOD_CPU_TO_DDR: { u32 opt = (readl(sar) >> SAR_KIRKWOOD_DDR_RATIO) & SAR_KIRKWOOD_DDR_RATIO_MASK; *mult = kirkwood_cpu_ddr_ratios[opt][0]; *div = kirkwood_cpu_ddr_ratios[opt][1]; break; } } } static const struct core_clocks kirkwood_core_clocks = { .get_tclk_freq = kirkwood_get_tclk_freq, .get_cpu_freq = kirkwood_get_cpu_freq, .get_clk_ratio = kirkwood_get_clk_ratio, .ratios = kirkwood_core_ratios, .num_ratios = ARRAY_SIZE(kirkwood_core_ratios), }; static const u32 __initconst mv88f6180_cpu_frequencies[] = { 0, 0, 0, 0, 0, 600000000, 800000000, 1000000000 }; static u32 __init mv88f6180_get_cpu_freq(void __iomem *sar) { u32 opt = (readl(sar) >> SAR_MV88F6180_CLK) & SAR_MV88F6180_CLK_MASK; return mv88f6180_cpu_frequencies[opt]; } static const int __initconst mv88f6180_cpu_ddr_ratios[8][2] = { { 0, 1 }, { 0, 1 }, { 0, 1 }, { 0, 1 }, { 0, 1 }, { 1, 3 }, { 1, 4 }, { 1, 5 } }; static void __init mv88f6180_get_clk_ratio( void __iomem *sar, int id, int *mult, int *div) { switch (id) { case KIRKWOOD_CPU_TO_L2: { /* mv88f6180 has a fixed 1:2 CPU-to-L2 ratio */ *mult = 1; *div = 2; break; } case KIRKWOOD_CPU_TO_DDR: { u32 opt = (readl(sar) >> SAR_MV88F6180_CLK) & SAR_MV88F6180_CLK_MASK; *mult = mv88f6180_cpu_ddr_ratios[opt][0]; *div = mv88f6180_cpu_ddr_ratios[opt][1]; break; } } } static const struct core_clocks mv88f6180_core_clocks = { .get_tclk_freq = kirkwood_get_tclk_freq, .get_cpu_freq = mv88f6180_get_cpu_freq, .get_clk_ratio = mv88f6180_get_clk_ratio, .ratios = kirkwood_core_ratios, .num_ratios = ARRAY_SIZE(kirkwood_core_ratios), }; #endif /* CONFIG_ARCH_KIRKWOOD */ static const __initdata struct of_device_id clk_core_match[] = { #ifdef CONFIG_MACH_ARMADA_370_XP { .compatible = "marvell,armada-370-core-clock", .data = &armada_370_core_clocks, }, { .compatible = "marvell,armada-xp-core-clock", .data = &armada_xp_core_clocks, }, #endif #ifdef CONFIG_ARCH_DOVE { .compatible = "marvell,dove-core-clock", .data = &dove_core_clocks, }, #endif #ifdef CONFIG_ARCH_KIRKWOOD { .compatible = "marvell,kirkwood-core-clock", .data = &kirkwood_core_clocks, }, { .compatible = "marvell,mv88f6180-core-clock", .data = &mv88f6180_core_clocks, }, #endif { } }; void __init mvebu_core_clk_init(void) { struct device_node *np; for_each_matching_node(np, clk_core_match) { const struct of_device_id *match = of_match_node(clk_core_match, np); mvebu_clk_core_setup(np, (struct core_clocks *)match->data); } }