blob: 001b9f42d64f3bf2e6f8fd03e30d0e914b1ca8f6 [file] [log] [blame]
TsiChung Liewf6afe722007-06-18 13:50:13 -05001/*
2 *
3 * (C) Copyright 2000-2003
4 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
5 *
6 * Copyright (C) 2004-2007 Freescale Semiconductor, Inc.
7 * TsiChung Liew (Tsi-Chung.Liew@freescale.com)
8 *
9 * See file CREDITS for list of people who contributed to this
10 * project.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License as
14 * published by the Free Software Foundation; either version 2 of
15 * the License, or (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
25 * MA 02111-1307 USA
26 */
27
28#include <common.h>
29#include <asm/processor.h>
30
TsiChungLiew2ce14b72007-07-05 23:05:31 -050031#include <asm/immap.h>
TsiChung Liewf6afe722007-06-18 13:50:13 -050032
Wolfgang Denkd112a2c2007-09-15 20:48:41 +020033DECLARE_GLOBAL_DATA_PTR;
34
TsiChung Liewf6afe722007-06-18 13:50:13 -050035/* PLL min/max specifications */
TsiChungLiew2ce14b72007-07-05 23:05:31 -050036#define MAX_FVCO 500000 /* KHz */
37#define MAX_FSYS 80000 /* KHz */
38#define MIN_FSYS 58333 /* KHz */
39#define FREF 16000 /* KHz */
40#define MAX_MFD 135 /* Multiplier */
41#define MIN_MFD 88 /* Multiplier */
42#define BUSDIV 6 /* Divider */
TsiChung Liewf6afe722007-06-18 13:50:13 -050043/*
44 * Low Power Divider specifications
45 */
TsiChungLiew2ce14b72007-07-05 23:05:31 -050046#define MIN_LPD (1 << 0) /* Divider (not encoded) */
47#define MAX_LPD (1 << 15) /* Divider (not encoded) */
48#define DEFAULT_LPD (1 << 1) /* Divider (not encoded) */
TsiChung Liewf6afe722007-06-18 13:50:13 -050049
50/*
51 * Get the value of the current system clock
52 *
53 * Parameters:
54 * none
55 *
56 * Return Value:
57 * The current output system frequency
58 */
59int get_sys_clock(void)
60{
61 volatile ccm_t *ccm = (volatile ccm_t *)(MMAP_CCM);
62 volatile pll_t *pll = (volatile pll_t *)(MMAP_PLL);
63 int divider;
64
65 /* Test to see if device is in LIMP mode */
66 if (ccm->misccr & CCM_MISCCR_LIMP) {
67 divider = ccm->cdr & CCM_CDR_LPDIV(0xF);
68 return (FREF / (2 << divider));
69 } else {
70 return ((FREF * pll->pfdr) / (BUSDIV * 4));
71 }
72}
73
74/*
75 * Initialize the Low Power Divider circuit
76 *
77 * Parameters:
78 * div Desired system frequency divider
79 *
80 * Return Value:
81 * The resulting output system frequency
82 */
83int clock_limp(int div)
84{
85 volatile ccm_t *ccm = (volatile ccm_t *)(MMAP_CCM);
86 u32 temp;
87
88 /* Check bounds of divider */
89 if (div < MIN_LPD)
90 div = MIN_LPD;
91 if (div > MAX_LPD)
92 div = MAX_LPD;
93
94 /* Save of the current value of the SSIDIV so we don't overwrite the value */
95 temp = (ccm->cdr & CCM_CDR_SSIDIV(0xF));
96
97 /* Apply the divider to the system clock */
98 ccm->cdr = (CCM_CDR_LPDIV(div) | CCM_CDR_SSIDIV(temp));
99
100 ccm->misccr |= CCM_MISCCR_LIMP;
101
102 return (FREF / (3 * (1 << div)));
103}
104
105/*
106 * Exit low power LIMP mode
107 *
108 * Parameters:
109 * div Desired system frequency divider
110 *
111 * Return Value:
112 * The resulting output system frequency
113 */
114int clock_exit_limp(void)
115{
116 volatile ccm_t *ccm = (volatile ccm_t *)(MMAP_CCM);
117 int fout;
118
119 /* Exit LIMP mode */
120 ccm->misccr &= (~CCM_MISCCR_LIMP);
121
122 /* Wait for PLL to lock */
123 while (!(ccm->misccr & CCM_MISCCR_PLL_LOCK)) ;
124
125 fout = get_sys_clock();
126
127 return fout;
128}
129
130/* Initialize the PLL
131 *
132 * Parameters:
133 * fref PLL reference clock frequency in KHz
134 * fsys Desired PLL output frequency in KHz
135 * flags Operating parameters
136 *
137 * Return Value:
138 * The resulting output system frequency
139 */
140int clock_pll(int fsys, int flags)
141{
142 volatile u32 *sdram_workaround = (volatile u32 *)(MMAP_SDRAM + 0x80);
143 volatile pll_t *pll = (volatile pll_t *)(MMAP_PLL);
144 int fref, temp, fout, mfd;
145 u32 i;
146
147 fref = FREF;
148
149 if (fsys == 0) {
150 /* Return current PLL output */
151 mfd = pll->pfdr;
152
153 return (fref * mfd / (BUSDIV * 4));
154 }
155
156 /* Check bounds of requested system clock */
157 if (fsys > MAX_FSYS)
158 fsys = MAX_FSYS;
159
160 if (fsys < MIN_FSYS)
161 fsys = MIN_FSYS;
162
163 /* Multiplying by 100 when calculating the temp value,
164 and then dividing by 100 to calculate the mfd allows
165 for exact values without needing to include floating
166 point libraries. */
167 temp = (100 * fsys) / fref;
168 mfd = (4 * BUSDIV * temp) / 100;
169
170 /* Determine the output frequency for selected values */
171 fout = ((fref * mfd) / (BUSDIV * 4));
172
173 /*
174 * Check to see if the SDRAM has already been initialized.
175 * If it has then the SDRAM needs to be put into self refresh
176 * mode before reprogramming the PLL.
177 */
TsiChung Liewf6afe722007-06-18 13:50:13 -0500178
179 /*
180 * Initialize the PLL to generate the new system clock frequency.
181 * The device must be put into LIMP mode to reprogram the PLL.
182 */
183
184 /* Enter LIMP mode */
185 clock_limp(DEFAULT_LPD);
186
187 /* Reprogram PLL for desired fsys */
188 pll->podr = (PLL_PODR_CPUDIV(BUSDIV / 3) | PLL_PODR_BUSDIV(BUSDIV));
189
190 pll->pfdr = mfd;
191
192 /* Exit LIMP mode */
193 clock_exit_limp();
194
195 /*
196 * Return the SDRAM to normal operation if it is in use.
197 */
TsiChung Liewf6afe722007-06-18 13:50:13 -0500198
199 /* software workaround for SDRAM opeartion after exiting LIMP mode errata */
200 *sdram_workaround = CFG_SDRAM_BASE;
TsiChungLiew2ce14b72007-07-05 23:05:31 -0500201
TsiChung Liewf6afe722007-06-18 13:50:13 -0500202 /* wait for DQS logic to relock */
203 for (i = 0; i < 0x200; i++) ;
204
205 return fout;
206}
207
208/*
209 * get_clocks() fills in gd->cpu_clock and gd->bus_clk
210 */
211int get_clocks(void)
212{
TsiChung Liewf6afe722007-06-18 13:50:13 -0500213 gd->bus_clk = clock_pll(CFG_CLK / 1000, 0) * 1000;
214 gd->cpu_clk = (gd->bus_clk * 3);
215 return (0);
216}