1/* 2 * Marvell Armada 370 SoC clocks 3 * 4 * Copyright (C) 2012 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 * Core Clocks 23 */ 24 25#define SARL 0 /* Low part [0:31] */ 26#define SARL_A370_SSCG_ENABLE BIT(10) 27#define SARL_A370_PCLK_FREQ_OPT 11 28#define SARL_A370_PCLK_FREQ_OPT_MASK 0xF 29#define SARL_A370_FAB_FREQ_OPT 15 30#define SARL_A370_FAB_FREQ_OPT_MASK 0x1F 31#define SARL_A370_TCLK_FREQ_OPT 20 32#define SARL_A370_TCLK_FREQ_OPT_MASK 0x1 33 34enum { A370_CPU_TO_NBCLK, A370_CPU_TO_HCLK, A370_CPU_TO_DRAMCLK }; 35 36static const struct coreclk_ratio a370_coreclk_ratios[] __initconst = { 37 { .id = A370_CPU_TO_NBCLK, .name = "nbclk" }, 38 { .id = A370_CPU_TO_HCLK, .name = "hclk" }, 39 { .id = A370_CPU_TO_DRAMCLK, .name = "dramclk" }, 40}; 41 42static const u32 a370_tclk_freqs[] __initconst = { 43 166000000, 44 200000000, 45}; 46 47static u32 __init a370_get_tclk_freq(void __iomem *sar) 48{ 49 u8 tclk_freq_select = 0; 50 51 tclk_freq_select = ((readl(sar) >> SARL_A370_TCLK_FREQ_OPT) & 52 SARL_A370_TCLK_FREQ_OPT_MASK); 53 return a370_tclk_freqs[tclk_freq_select]; 54} 55 56static const u32 a370_cpu_freqs[] __initconst = { 57 400000000, 58 533000000, 59 667000000, 60 800000000, 61 1000000000, 62 1067000000, 63 1200000000, 64}; 65 66static u32 __init a370_get_cpu_freq(void __iomem *sar) 67{ 68 u32 cpu_freq; 69 u8 cpu_freq_select = 0; 70 71 cpu_freq_select = ((readl(sar) >> SARL_A370_PCLK_FREQ_OPT) & 72 SARL_A370_PCLK_FREQ_OPT_MASK); 73 if (cpu_freq_select >= ARRAY_SIZE(a370_cpu_freqs)) { 74 pr_err("CPU freq select unsupported %d\n", cpu_freq_select); 75 cpu_freq = 0; 76 } else 77 cpu_freq = a370_cpu_freqs[cpu_freq_select]; 78 79 return cpu_freq; 80} 81 82static const int a370_nbclk_ratios[32][2] __initconst = { 83 {0, 1}, {1, 2}, {2, 2}, {2, 2}, 84 {1, 2}, {1, 2}, {1, 1}, {2, 3}, 85 {0, 1}, {1, 2}, {2, 4}, {0, 1}, 86 {1, 2}, {0, 1}, {0, 1}, {2, 2}, 87 {0, 1}, {0, 1}, {0, 1}, {1, 1}, 88 {2, 3}, {0, 1}, {0, 1}, {0, 1}, 89 {0, 1}, {0, 1}, {0, 1}, {1, 1}, 90 {0, 1}, {0, 1}, {0, 1}, {0, 1}, 91}; 92 93static const int a370_hclk_ratios[32][2] __initconst = { 94 {0, 1}, {1, 2}, {2, 6}, {2, 3}, 95 {1, 3}, {1, 4}, {1, 2}, {2, 6}, 96 {0, 1}, {1, 6}, {2, 10}, {0, 1}, 97 {1, 4}, {0, 1}, {0, 1}, {2, 5}, 98 {0, 1}, {0, 1}, {0, 1}, {1, 2}, 99 {2, 6}, {0, 1}, {0, 1}, {0, 1}, 100 {0, 1}, {0, 1}, {0, 1}, {1, 1}, 101 {0, 1}, {0, 1}, {0, 1}, {0, 1}, 102}; 103 104static const int a370_dramclk_ratios[32][2] __initconst = { 105 {0, 1}, {1, 2}, {2, 3}, {2, 3}, 106 {1, 3}, {1, 2}, {1, 2}, {2, 6}, 107 {0, 1}, {1, 3}, {2, 5}, {0, 1}, 108 {1, 4}, {0, 1}, {0, 1}, {2, 5}, 109 {0, 1}, {0, 1}, {0, 1}, {1, 1}, 110 {2, 3}, {0, 1}, {0, 1}, {0, 1}, 111 {0, 1}, {0, 1}, {0, 1}, {1, 1}, 112 {0, 1}, {0, 1}, {0, 1}, {0, 1}, 113}; 114 115static void __init a370_get_clk_ratio( 116 void __iomem *sar, int id, int *mult, int *div) 117{ 118 u32 opt = ((readl(sar) >> SARL_A370_FAB_FREQ_OPT) & 119 SARL_A370_FAB_FREQ_OPT_MASK); 120 121 switch (id) { 122 case A370_CPU_TO_NBCLK: 123 *mult = a370_nbclk_ratios[opt][0]; 124 *div = a370_nbclk_ratios[opt][1]; 125 break; 126 case A370_CPU_TO_HCLK: 127 *mult = a370_hclk_ratios[opt][0]; 128 *div = a370_hclk_ratios[opt][1]; 129 break; 130 case A370_CPU_TO_DRAMCLK: 131 *mult = a370_dramclk_ratios[opt][0]; 132 *div = a370_dramclk_ratios[opt][1]; 133 break; 134 } 135} 136 137static bool a370_is_sscg_enabled(void __iomem *sar) 138{ 139 return !(readl(sar) & SARL_A370_SSCG_ENABLE); 140} 141 142static const struct coreclk_soc_desc a370_coreclks = { 143 .get_tclk_freq = a370_get_tclk_freq, 144 .get_cpu_freq = a370_get_cpu_freq, 145 .get_clk_ratio = a370_get_clk_ratio, 146 .is_sscg_enabled = a370_is_sscg_enabled, 147 .fix_sscg_deviation = kirkwood_fix_sscg_deviation, 148 .ratios = a370_coreclk_ratios, 149 .num_ratios = ARRAY_SIZE(a370_coreclk_ratios), 150}; 151 152/* 153 * Clock Gating Control 154 */ 155 156static const struct clk_gating_soc_desc a370_gating_desc[] __initconst = { 157 { "audio", NULL, 0, 0 }, 158 { "pex0_en", NULL, 1, 0 }, 159 { "pex1_en", NULL, 2, 0 }, 160 { "ge1", NULL, 3, 0 }, 161 { "ge0", NULL, 4, 0 }, 162 { "pex0", "pex0_en", 5, 0 }, 163 { "pex1", "pex1_en", 9, 0 }, 164 { "sata0", NULL, 15, 0 }, 165 { "sdio", NULL, 17, 0 }, 166 { "tdm", NULL, 25, 0 }, 167 { "ddr", NULL, 28, CLK_IGNORE_UNUSED }, 168 { "sata1", NULL, 30, 0 }, 169 { } 170}; 171 172static void __init a370_clk_init(struct device_node *np) 173{ 174 struct device_node *cgnp = 175 of_find_compatible_node(NULL, NULL, "marvell,armada-370-gating-clock"); 176 177 mvebu_coreclk_setup(np, &a370_coreclks); 178 179 if (cgnp) 180 mvebu_clk_gating_setup(cgnp, a370_gating_desc); 181} 182CLK_OF_DECLARE(a370_clk, "marvell,armada-370-core-clock", a370_clk_init); 183 184