1 /* 2 * Freescale i.MX28 clock setup code 3 * 4 * Copyright (C) 2011 Marek Vasut <marek.vasut@gmail.com> 5 * on behalf of DENX Software Engineering GmbH 6 * 7 * Based on code from LTIB: 8 * Copyright (C) 2010 Freescale Semiconductor, Inc. 9 * 10 * See file CREDITS for list of people who contributed to this 11 * project. 12 * 13 * This program is free software; you can redistribute it and/or 14 * modify it under the terms of the GNU General Public License as 15 * published by the Free Software Foundation; either version 2 of 16 * the License, or (at your option) any later version. 17 * 18 * This program is distributed in the hope that it will be useful, 19 * but WITHOUT ANY WARRANTY; without even the implied warranty of 20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 * GNU General Public License for more details. 22 * 23 * You should have received a copy of the GNU General Public License 24 * along with this program; if not, write to the Free Software 25 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, 26 * MA 02111-1307 USA 27 */ 28 29 #include <common.h> 30 #include <asm/errno.h> 31 #include <asm/io.h> 32 #include <asm/arch/clock.h> 33 #include <asm/arch/imx-regs.h> 34 35 /* The PLL frequency is always 480MHz, see section 10.2 in iMX28 datasheet. */ 36 #define PLL_FREQ_KHZ 480000 37 #define PLL_FREQ_COEF 18 38 /* The XTAL frequency is always 24MHz, see section 10.2 in iMX28 datasheet. */ 39 #define XTAL_FREQ_KHZ 24000 40 41 #define PLL_FREQ_MHZ (PLL_FREQ_KHZ / 1000) 42 #define XTAL_FREQ_MHZ (XTAL_FREQ_KHZ / 1000) 43 44 static uint32_t mx28_get_pclk(void) 45 { 46 struct mxs_clkctrl_regs *clkctrl_regs = 47 (struct mxs_clkctrl_regs *)MXS_CLKCTRL_BASE; 48 49 uint32_t clkctrl, clkseq, div; 50 uint8_t clkfrac, frac; 51 52 clkctrl = readl(&clkctrl_regs->hw_clkctrl_cpu); 53 54 /* No support of fractional divider calculation */ 55 if (clkctrl & 56 (CLKCTRL_CPU_DIV_XTAL_FRAC_EN | CLKCTRL_CPU_DIV_CPU_FRAC_EN)) { 57 return 0; 58 } 59 60 clkseq = readl(&clkctrl_regs->hw_clkctrl_clkseq); 61 62 /* XTAL Path */ 63 if (clkseq & CLKCTRL_CLKSEQ_BYPASS_CPU) { 64 div = (clkctrl & CLKCTRL_CPU_DIV_XTAL_MASK) >> 65 CLKCTRL_CPU_DIV_XTAL_OFFSET; 66 return XTAL_FREQ_MHZ / div; 67 } 68 69 /* REF Path */ 70 clkfrac = readb(&clkctrl_regs->hw_clkctrl_frac0[CLKCTRL_FRAC0_CPU]); 71 frac = clkfrac & CLKCTRL_FRAC_FRAC_MASK; 72 div = clkctrl & CLKCTRL_CPU_DIV_CPU_MASK; 73 return (PLL_FREQ_MHZ * PLL_FREQ_COEF / frac) / div; 74 } 75 76 static uint32_t mx28_get_hclk(void) 77 { 78 struct mxs_clkctrl_regs *clkctrl_regs = 79 (struct mxs_clkctrl_regs *)MXS_CLKCTRL_BASE; 80 81 uint32_t div; 82 uint32_t clkctrl; 83 84 clkctrl = readl(&clkctrl_regs->hw_clkctrl_hbus); 85 86 /* No support of fractional divider calculation */ 87 if (clkctrl & CLKCTRL_HBUS_DIV_FRAC_EN) 88 return 0; 89 90 div = clkctrl & CLKCTRL_HBUS_DIV_MASK; 91 return mx28_get_pclk() / div; 92 } 93 94 static uint32_t mx28_get_emiclk(void) 95 { 96 struct mxs_clkctrl_regs *clkctrl_regs = 97 (struct mxs_clkctrl_regs *)MXS_CLKCTRL_BASE; 98 99 uint32_t clkctrl, clkseq, div; 100 uint8_t clkfrac, frac; 101 102 clkseq = readl(&clkctrl_regs->hw_clkctrl_clkseq); 103 clkctrl = readl(&clkctrl_regs->hw_clkctrl_emi); 104 105 /* XTAL Path */ 106 if (clkseq & CLKCTRL_CLKSEQ_BYPASS_EMI) { 107 div = (clkctrl & CLKCTRL_EMI_DIV_XTAL_MASK) >> 108 CLKCTRL_EMI_DIV_XTAL_OFFSET; 109 return XTAL_FREQ_MHZ / div; 110 } 111 112 /* REF Path */ 113 clkfrac = readb(&clkctrl_regs->hw_clkctrl_frac0[CLKCTRL_FRAC0_EMI]); 114 frac = clkfrac & CLKCTRL_FRAC_FRAC_MASK; 115 div = clkctrl & CLKCTRL_EMI_DIV_EMI_MASK; 116 return (PLL_FREQ_MHZ * PLL_FREQ_COEF / frac) / div; 117 } 118 119 static uint32_t mx28_get_gpmiclk(void) 120 { 121 struct mxs_clkctrl_regs *clkctrl_regs = 122 (struct mxs_clkctrl_regs *)MXS_CLKCTRL_BASE; 123 124 uint32_t clkctrl, clkseq, div; 125 uint8_t clkfrac, frac; 126 127 clkseq = readl(&clkctrl_regs->hw_clkctrl_clkseq); 128 clkctrl = readl(&clkctrl_regs->hw_clkctrl_gpmi); 129 130 /* XTAL Path */ 131 if (clkseq & CLKCTRL_CLKSEQ_BYPASS_GPMI) { 132 div = clkctrl & CLKCTRL_GPMI_DIV_MASK; 133 return XTAL_FREQ_MHZ / div; 134 } 135 136 /* REF Path */ 137 clkfrac = readb(&clkctrl_regs->hw_clkctrl_frac1[CLKCTRL_FRAC1_GPMI]); 138 frac = clkfrac & CLKCTRL_FRAC_FRAC_MASK; 139 div = clkctrl & CLKCTRL_GPMI_DIV_MASK; 140 return (PLL_FREQ_MHZ * PLL_FREQ_COEF / frac) / div; 141 } 142 143 /* 144 * Set IO clock frequency, in kHz 145 */ 146 void mx28_set_ioclk(enum mxs_ioclock io, uint32_t freq) 147 { 148 struct mxs_clkctrl_regs *clkctrl_regs = 149 (struct mxs_clkctrl_regs *)MXS_CLKCTRL_BASE; 150 uint32_t div; 151 int io_reg; 152 153 if (freq == 0) 154 return; 155 156 if ((io < MXC_IOCLK0) || (io > MXC_IOCLK1)) 157 return; 158 159 div = (PLL_FREQ_KHZ * PLL_FREQ_COEF) / freq; 160 161 if (div < 18) 162 div = 18; 163 164 if (div > 35) 165 div = 35; 166 167 io_reg = CLKCTRL_FRAC0_IO0 - io; /* Register order is reversed */ 168 writeb(CLKCTRL_FRAC_CLKGATE, 169 &clkctrl_regs->hw_clkctrl_frac0_set[io_reg]); 170 writeb(CLKCTRL_FRAC_CLKGATE | (div & CLKCTRL_FRAC_FRAC_MASK), 171 &clkctrl_regs->hw_clkctrl_frac0[io_reg]); 172 writeb(CLKCTRL_FRAC_CLKGATE, 173 &clkctrl_regs->hw_clkctrl_frac0_clr[io_reg]); 174 } 175 176 /* 177 * Get IO clock, returns IO clock in kHz 178 */ 179 static uint32_t mx28_get_ioclk(enum mxs_ioclock io) 180 { 181 struct mxs_clkctrl_regs *clkctrl_regs = 182 (struct mxs_clkctrl_regs *)MXS_CLKCTRL_BASE; 183 uint8_t ret; 184 int io_reg; 185 186 if ((io < MXC_IOCLK0) || (io > MXC_IOCLK1)) 187 return 0; 188 189 io_reg = CLKCTRL_FRAC0_IO0 - io; /* Register order is reversed */ 190 191 ret = readb(&clkctrl_regs->hw_clkctrl_frac0[io_reg]) & 192 CLKCTRL_FRAC_FRAC_MASK; 193 194 return (PLL_FREQ_KHZ * PLL_FREQ_COEF) / ret; 195 } 196 197 /* 198 * Configure SSP clock frequency, in kHz 199 */ 200 void mx28_set_sspclk(enum mxs_sspclock ssp, uint32_t freq, int xtal) 201 { 202 struct mxs_clkctrl_regs *clkctrl_regs = 203 (struct mxs_clkctrl_regs *)MXS_CLKCTRL_BASE; 204 uint32_t clk, clkreg; 205 206 if (ssp > MXC_SSPCLK3) 207 return; 208 209 clkreg = (uint32_t)(&clkctrl_regs->hw_clkctrl_ssp0) + 210 (ssp * sizeof(struct mxs_register_32)); 211 212 clrbits_le32(clkreg, CLKCTRL_SSP_CLKGATE); 213 while (readl(clkreg) & CLKCTRL_SSP_CLKGATE) 214 ; 215 216 if (xtal) 217 clk = XTAL_FREQ_KHZ; 218 else 219 clk = mx28_get_ioclk(ssp >> 1); 220 221 if (freq > clk) 222 return; 223 224 /* Calculate the divider and cap it if necessary */ 225 clk /= freq; 226 if (clk > CLKCTRL_SSP_DIV_MASK) 227 clk = CLKCTRL_SSP_DIV_MASK; 228 229 clrsetbits_le32(clkreg, CLKCTRL_SSP_DIV_MASK, clk); 230 while (readl(clkreg) & CLKCTRL_SSP_BUSY) 231 ; 232 233 if (xtal) 234 writel(CLKCTRL_CLKSEQ_BYPASS_SSP0 << ssp, 235 &clkctrl_regs->hw_clkctrl_clkseq_set); 236 else 237 writel(CLKCTRL_CLKSEQ_BYPASS_SSP0 << ssp, 238 &clkctrl_regs->hw_clkctrl_clkseq_clr); 239 } 240 241 /* 242 * Return SSP frequency, in kHz 243 */ 244 static uint32_t mx28_get_sspclk(enum mxs_sspclock ssp) 245 { 246 struct mxs_clkctrl_regs *clkctrl_regs = 247 (struct mxs_clkctrl_regs *)MXS_CLKCTRL_BASE; 248 uint32_t clkreg; 249 uint32_t clk, tmp; 250 251 if (ssp > MXC_SSPCLK3) 252 return 0; 253 254 tmp = readl(&clkctrl_regs->hw_clkctrl_clkseq); 255 if (tmp & (CLKCTRL_CLKSEQ_BYPASS_SSP0 << ssp)) 256 return XTAL_FREQ_KHZ; 257 258 clkreg = (uint32_t)(&clkctrl_regs->hw_clkctrl_ssp0) + 259 (ssp * sizeof(struct mxs_register_32)); 260 261 tmp = readl(clkreg) & CLKCTRL_SSP_DIV_MASK; 262 263 if (tmp == 0) 264 return 0; 265 266 clk = mx28_get_ioclk(ssp >> 1); 267 268 return clk / tmp; 269 } 270 271 /* 272 * Set SSP/MMC bus frequency, in kHz) 273 */ 274 void mx28_set_ssp_busclock(unsigned int bus, uint32_t freq) 275 { 276 struct mxs_ssp_regs *ssp_regs; 277 const uint32_t sspclk = mx28_get_sspclk(bus); 278 uint32_t reg; 279 uint32_t divide, rate, tgtclk; 280 281 ssp_regs = (struct mxs_ssp_regs *)(MXS_SSP0_BASE + (bus * 0x2000)); 282 283 /* 284 * SSP bit rate = SSPCLK / (CLOCK_DIVIDE * (1 + CLOCK_RATE)), 285 * CLOCK_DIVIDE has to be an even value from 2 to 254, and 286 * CLOCK_RATE could be any integer from 0 to 255. 287 */ 288 for (divide = 2; divide < 254; divide += 2) { 289 rate = sspclk / freq / divide; 290 if (rate <= 256) 291 break; 292 } 293 294 tgtclk = sspclk / divide / rate; 295 while (tgtclk > freq) { 296 rate++; 297 tgtclk = sspclk / divide / rate; 298 } 299 if (rate > 256) 300 rate = 256; 301 302 /* Always set timeout the maximum */ 303 reg = SSP_TIMING_TIMEOUT_MASK | 304 (divide << SSP_TIMING_CLOCK_DIVIDE_OFFSET) | 305 ((rate - 1) << SSP_TIMING_CLOCK_RATE_OFFSET); 306 writel(reg, &ssp_regs->hw_ssp_timing); 307 308 debug("SPI%d: Set freq rate to %d KHz (requested %d KHz)\n", 309 bus, tgtclk, freq); 310 } 311 312 uint32_t mxc_get_clock(enum mxc_clock clk) 313 { 314 switch (clk) { 315 case MXC_ARM_CLK: 316 return mx28_get_pclk() * 1000000; 317 case MXC_GPMI_CLK: 318 return mx28_get_gpmiclk() * 1000000; 319 case MXC_AHB_CLK: 320 case MXC_IPG_CLK: 321 return mx28_get_hclk() * 1000000; 322 case MXC_EMI_CLK: 323 return mx28_get_emiclk(); 324 case MXC_IO0_CLK: 325 return mx28_get_ioclk(MXC_IOCLK0); 326 case MXC_IO1_CLK: 327 return mx28_get_ioclk(MXC_IOCLK1); 328 case MXC_SSP0_CLK: 329 return mx28_get_sspclk(MXC_SSPCLK0); 330 case MXC_SSP1_CLK: 331 return mx28_get_sspclk(MXC_SSPCLK1); 332 case MXC_SSP2_CLK: 333 return mx28_get_sspclk(MXC_SSPCLK2); 334 case MXC_SSP3_CLK: 335 return mx28_get_sspclk(MXC_SSPCLK3); 336 } 337 338 return 0; 339 } 340