1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Marvell Armada 380/385 SoC clocks 4 * 5 * Copyright (C) 2014 Marvell 6 * 7 * Gregory CLEMENT <gregory.clement@free-electrons.com> 8 * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com> 9 * Andrew Lunn <andrew@lunn.ch> 10 * 11 */ 12 13 #include <linux/kernel.h> 14 #include <linux/clk-provider.h> 15 #include <linux/io.h> 16 #include <linux/of.h> 17 #include "common.h" 18 19 /* 20 * SAR[14:10] : Ratios between PCLK0, NBCLK, HCLK and DRAM clocks 21 * 22 * SAR[15] : TCLK frequency 23 * 0 = 250 MHz 24 * 1 = 200 MHz 25 */ 26 27 #define SAR_A380_TCLK_FREQ_OPT 15 28 #define SAR_A380_TCLK_FREQ_OPT_MASK 0x1 29 #define SAR_A380_CPU_DDR_L2_FREQ_OPT 10 30 #define SAR_A380_CPU_DDR_L2_FREQ_OPT_MASK 0x1F 31 32 static const u32 armada_38x_tclk_frequencies[] __initconst = { 33 250000000, 34 200000000, 35 }; 36 37 static u32 __init armada_38x_get_tclk_freq(void __iomem *sar) 38 { 39 u8 tclk_freq_select; 40 41 tclk_freq_select = ((readl(sar) >> SAR_A380_TCLK_FREQ_OPT) & 42 SAR_A380_TCLK_FREQ_OPT_MASK); 43 return armada_38x_tclk_frequencies[tclk_freq_select]; 44 } 45 46 static const u32 armada_38x_cpu_frequencies[] __initconst = { 47 666 * 1000 * 1000, 0, 800 * 1000 * 1000, 0, 48 1066 * 1000 * 1000, 0, 1200 * 1000 * 1000, 0, 49 1332 * 1000 * 1000, 0, 0, 0, 50 1600 * 1000 * 1000, 0, 0, 0, 51 1866 * 1000 * 1000, 0, 0, 2000 * 1000 * 1000, 52 }; 53 54 static u32 __init armada_38x_get_cpu_freq(void __iomem *sar) 55 { 56 u8 cpu_freq_select; 57 58 cpu_freq_select = ((readl(sar) >> SAR_A380_CPU_DDR_L2_FREQ_OPT) & 59 SAR_A380_CPU_DDR_L2_FREQ_OPT_MASK); 60 if (cpu_freq_select >= ARRAY_SIZE(armada_38x_cpu_frequencies)) { 61 pr_err("Selected CPU frequency (%d) unsupported\n", 62 cpu_freq_select); 63 return 0; 64 } 65 66 return armada_38x_cpu_frequencies[cpu_freq_select]; 67 } 68 69 enum { A380_CPU_TO_DDR, A380_CPU_TO_L2 }; 70 71 static const struct coreclk_ratio armada_38x_coreclk_ratios[] __initconst = { 72 { .id = A380_CPU_TO_L2, .name = "l2clk" }, 73 { .id = A380_CPU_TO_DDR, .name = "ddrclk" }, 74 }; 75 76 static const int armada_38x_cpu_l2_ratios[32][2] __initconst = { 77 {1, 2}, {0, 1}, {1, 2}, {0, 1}, 78 {1, 2}, {0, 1}, {1, 2}, {0, 1}, 79 {1, 2}, {0, 1}, {0, 1}, {0, 1}, 80 {1, 2}, {0, 1}, {0, 1}, {0, 1}, 81 {1, 2}, {0, 1}, {0, 1}, {1, 2}, 82 {0, 1}, {0, 1}, {0, 1}, {0, 1}, 83 {0, 1}, {0, 1}, {0, 1}, {0, 1}, 84 {0, 1}, {0, 1}, {0, 1}, {0, 1}, 85 }; 86 87 static const int armada_38x_cpu_ddr_ratios[32][2] __initconst = { 88 {0, 1}, {0, 1}, {0, 1}, {0, 1}, 89 {1, 2}, {0, 1}, {0, 1}, {0, 1}, 90 {1, 2}, {0, 1}, {0, 1}, {0, 1}, 91 {1, 2}, {0, 1}, {0, 1}, {0, 1}, 92 {1, 2}, {0, 1}, {0, 1}, {7, 15}, 93 {0, 1}, {0, 1}, {0, 1}, {0, 1}, 94 {0, 1}, {0, 1}, {0, 1}, {0, 1}, 95 {0, 1}, {0, 1}, {0, 1}, {0, 1}, 96 }; 97 98 static void __init armada_38x_get_clk_ratio( 99 void __iomem *sar, int id, int *mult, int *div) 100 { 101 u32 opt = ((readl(sar) >> SAR_A380_CPU_DDR_L2_FREQ_OPT) & 102 SAR_A380_CPU_DDR_L2_FREQ_OPT_MASK); 103 104 switch (id) { 105 case A380_CPU_TO_L2: 106 *mult = armada_38x_cpu_l2_ratios[opt][0]; 107 *div = armada_38x_cpu_l2_ratios[opt][1]; 108 break; 109 case A380_CPU_TO_DDR: 110 *mult = armada_38x_cpu_ddr_ratios[opt][0]; 111 *div = armada_38x_cpu_ddr_ratios[opt][1]; 112 break; 113 } 114 } 115 116 static const struct coreclk_soc_desc armada_38x_coreclks = { 117 .get_tclk_freq = armada_38x_get_tclk_freq, 118 .get_cpu_freq = armada_38x_get_cpu_freq, 119 .get_clk_ratio = armada_38x_get_clk_ratio, 120 .ratios = armada_38x_coreclk_ratios, 121 .num_ratios = ARRAY_SIZE(armada_38x_coreclk_ratios), 122 }; 123 124 static void __init armada_38x_coreclk_init(struct device_node *np) 125 { 126 mvebu_coreclk_setup(np, &armada_38x_coreclks); 127 } 128 CLK_OF_DECLARE(armada_38x_core_clk, "marvell,armada-380-core-clock", 129 armada_38x_coreclk_init); 130 131 /* 132 * Clock Gating Control 133 */ 134 static const struct clk_gating_soc_desc armada_38x_gating_desc[] __initconst = { 135 { "audio", NULL, 0 }, 136 { "ge2", NULL, 2 }, 137 { "ge1", NULL, 3 }, 138 { "ge0", NULL, 4 }, 139 { "pex1", NULL, 5 }, 140 { "pex2", NULL, 6 }, 141 { "pex3", NULL, 7 }, 142 { "pex0", NULL, 8 }, 143 { "usb3h0", NULL, 9 }, 144 { "usb3h1", NULL, 10 }, 145 { "usb3d", NULL, 11 }, 146 { "bm", NULL, 13 }, 147 { "crypto0z", NULL, 14 }, 148 { "sata0", NULL, 15 }, 149 { "crypto1z", NULL, 16 }, 150 { "sdio", NULL, 17 }, 151 { "usb2", NULL, 18 }, 152 { "crypto1", NULL, 21 }, 153 { "xor0", NULL, 22 }, 154 { "crypto0", NULL, 23 }, 155 { "tdm", NULL, 25 }, 156 { "xor1", NULL, 28 }, 157 { "sata1", NULL, 30 }, 158 { } 159 }; 160 161 static void __init armada_38x_clk_gating_init(struct device_node *np) 162 { 163 mvebu_clk_gating_setup(np, armada_38x_gating_desc); 164 } 165 CLK_OF_DECLARE(armada_38x_clk_gating, "marvell,armada-380-gating-clock", 166 armada_38x_clk_gating_init); 167