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 0, 0, 0, 0, 50 1066 * 1000 * 1000, 0, 0, 0, 51 1332 * 1000 * 1000, 0, 0, 0, 52 1600 * 1000 * 1000, 53 }; 54 55 static u32 __init armada_38x_get_cpu_freq(void __iomem *sar) 56 { 57 u8 cpu_freq_select; 58 59 cpu_freq_select = ((readl(sar) >> SAR_A380_CPU_DDR_L2_FREQ_OPT) & 60 SAR_A380_CPU_DDR_L2_FREQ_OPT_MASK); 61 if (cpu_freq_select >= ARRAY_SIZE(armada_38x_cpu_frequencies)) { 62 pr_err("Selected CPU frequency (%d) unsupported\n", 63 cpu_freq_select); 64 return 0; 65 } 66 67 return armada_38x_cpu_frequencies[cpu_freq_select]; 68 } 69 70 enum { A380_CPU_TO_DDR, A380_CPU_TO_L2 }; 71 72 static const struct coreclk_ratio armada_38x_coreclk_ratios[] __initconst = { 73 { .id = A380_CPU_TO_L2, .name = "l2clk" }, 74 { .id = A380_CPU_TO_DDR, .name = "ddrclk" }, 75 }; 76 77 static const int armada_38x_cpu_l2_ratios[32][2] __initconst = { 78 {0, 1}, {0, 1}, {0, 1}, {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}, {0, 1}, 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 {0, 1}, {0, 1}, {0, 1}, {0, 1}, 86 }; 87 88 static const int armada_38x_cpu_ddr_ratios[32][2] __initconst = { 89 {0, 1}, {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}, {0, 1}, 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 {0, 1}, {0, 1}, {0, 1}, {0, 1}, 97 }; 98 99 static void __init armada_38x_get_clk_ratio( 100 void __iomem *sar, int id, int *mult, int *div) 101 { 102 u32 opt = ((readl(sar) >> SAR_A380_CPU_DDR_L2_FREQ_OPT) & 103 SAR_A380_CPU_DDR_L2_FREQ_OPT_MASK); 104 105 switch (id) { 106 case A380_CPU_TO_L2: 107 *mult = armada_38x_cpu_l2_ratios[opt][0]; 108 *div = armada_38x_cpu_l2_ratios[opt][1]; 109 break; 110 case A380_CPU_TO_DDR: 111 *mult = armada_38x_cpu_ddr_ratios[opt][0]; 112 *div = armada_38x_cpu_ddr_ratios[opt][1]; 113 break; 114 } 115 } 116 117 static const struct coreclk_soc_desc armada_38x_coreclks = { 118 .get_tclk_freq = armada_38x_get_tclk_freq, 119 .get_cpu_freq = armada_38x_get_cpu_freq, 120 .get_clk_ratio = armada_38x_get_clk_ratio, 121 .ratios = armada_38x_coreclk_ratios, 122 .num_ratios = ARRAY_SIZE(armada_38x_coreclk_ratios), 123 }; 124 125 static void __init armada_38x_coreclk_init(struct device_node *np) 126 { 127 mvebu_coreclk_setup(np, &armada_38x_coreclks); 128 } 129 CLK_OF_DECLARE(armada_38x_core_clk, "marvell,armada-380-core-clock", 130 armada_38x_coreclk_init); 131 132 /* 133 * Clock Gating Control 134 */ 135 static const struct clk_gating_soc_desc armada_38x_gating_desc[] __initconst = { 136 { "audio", NULL, 0 }, 137 { "ge2", NULL, 2 }, 138 { "ge1", NULL, 3 }, 139 { "ge0", NULL, 4 }, 140 { "pex1", NULL, 5 }, 141 { "pex2", NULL, 6 }, 142 { "pex3", NULL, 7 }, 143 { "pex0", NULL, 8 }, 144 { "usb3h0", NULL, 9 }, 145 { "usb3h1", NULL, 10 }, 146 { "usb3d", NULL, 11 }, 147 { "bm", NULL, 13 }, 148 { "crypto0z", NULL, 14 }, 149 { "sata0", NULL, 15 }, 150 { "crypto1z", NULL, 16 }, 151 { "sdio", NULL, 17 }, 152 { "usb2", NULL, 18 }, 153 { "crypto1", NULL, 21 }, 154 { "xor0", NULL, 22 }, 155 { "crypto0", NULL, 23 }, 156 { "tdm", NULL, 25 }, 157 { "xor1", NULL, 28 }, 158 { "sata1", NULL, 30 }, 159 { } 160 }; 161 162 static void __init armada_38x_clk_gating_init(struct device_node *np) 163 { 164 mvebu_clk_gating_setup(np, armada_38x_gating_desc); 165 } 166 CLK_OF_DECLARE(armada_38x_clk_gating, "marvell,armada-380-gating-clock", 167 armada_38x_clk_gating_init); 168