1 /* 2 * Copyright (C) 2011 by Vladimir Zapolskiy <vz@mleia.com> 3 * 4 * This program is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU General Public License 6 * as published by the Free Software Foundation; either version 2 7 * of the License, or (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, write to the Free Software 16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 17 * MA 02110-1301, USA. 18 */ 19 20 #include <common.h> 21 #include <div64.h> 22 #include <asm/arch/cpu.h> 23 #include <asm/arch/clk.h> 24 #include <asm/io.h> 25 26 static struct clk_pm_regs *clk = (struct clk_pm_regs *)CLK_PM_BASE; 27 28 unsigned int get_sys_clk_rate(void) 29 { 30 if (readl(&clk->sysclk_ctrl) & CLK_SYSCLK_PLL397) 31 return RTC_CLK_FREQUENCY * 397; 32 else 33 return OSC_CLK_FREQUENCY; 34 } 35 36 unsigned int get_hclk_pll_rate(void) 37 { 38 unsigned long long fin, fref, fcco, fout; 39 u32 val, m_div, n_div, p_div; 40 41 /* 42 * Valid frequency ranges: 43 * 1 * 10^6 <= Fin <= 20 * 10^6 44 * 1 * 10^6 <= Fref <= 27 * 10^6 45 * 156 * 10^6 <= Fcco <= 320 * 10^6 46 */ 47 48 fref = fin = get_sys_clk_rate(); 49 if (fin > 20000000ULL || fin < 1000000ULL) 50 return 0; 51 52 val = readl(&clk->hclkpll_ctrl); 53 m_div = ((val & CLK_HCLK_PLL_FEEDBACK_DIV_MASK) >> 1) + 1; 54 n_div = ((val & CLK_HCLK_PLL_PREDIV_MASK) >> 9) + 1; 55 if (val & CLK_HCLK_PLL_DIRECT) 56 p_div = 0; 57 else 58 p_div = ((val & CLK_HCLK_PLL_POSTDIV_MASK) >> 11) + 1; 59 p_div = 1 << p_div; 60 61 if (val & CLK_HCLK_PLL_BYPASS) { 62 do_div(fin, p_div); 63 return fin; 64 } 65 66 do_div(fref, n_div); 67 if (fref > 27000000ULL || fref < 1000000ULL) 68 return 0; 69 70 fout = fref * m_div; 71 if (val & CLK_HCLK_PLL_FEEDBACK) { 72 fcco = fout; 73 do_div(fout, p_div); 74 } else 75 fcco = fout * p_div; 76 77 if (fcco > 320000000ULL || fcco < 156000000ULL) 78 return 0; 79 80 return fout; 81 } 82 83 unsigned int get_hclk_clk_div(void) 84 { 85 u32 val; 86 87 val = readl(&clk->hclkdiv_ctrl) & CLK_HCLK_ARM_PLL_DIV_MASK; 88 89 return 1 << val; 90 } 91 92 unsigned int get_hclk_clk_rate(void) 93 { 94 return get_hclk_pll_rate() / get_hclk_clk_div(); 95 } 96 97 unsigned int get_periph_clk_div(void) 98 { 99 u32 val; 100 101 val = readl(&clk->hclkdiv_ctrl) & CLK_HCLK_PERIPH_DIV_MASK; 102 103 return (val >> 2) + 1; 104 } 105 106 unsigned int get_periph_clk_rate(void) 107 { 108 if (!(readl(&clk->pwr_ctrl) & CLK_PWR_NORMAL_RUN)) 109 return get_sys_clk_rate(); 110 111 return get_hclk_pll_rate() / get_periph_clk_div(); 112 } 113 114 int get_serial_clock(void) 115 { 116 return get_periph_clk_rate(); 117 } 118