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