blob: cf72609b42ffde371ea5a2b05ab7de90df4400fc [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
33/* PLL min/max specifications */
TsiChungLiew2ce14b72007-07-05 23:05:31 -050034#define MAX_FVCO 500000 /* KHz */
35#define MAX_FSYS 80000 /* KHz */
36#define MIN_FSYS 58333 /* KHz */
37#define FREF 16000 /* KHz */
38#define MAX_MFD 135 /* Multiplier */
39#define MIN_MFD 88 /* Multiplier */
40#define BUSDIV 6 /* Divider */
TsiChung Liewf6afe722007-06-18 13:50:13 -050041/*
42 * Low Power Divider specifications
43 */
TsiChungLiew2ce14b72007-07-05 23:05:31 -050044#define MIN_LPD (1 << 0) /* Divider (not encoded) */
45#define MAX_LPD (1 << 15) /* Divider (not encoded) */
46#define DEFAULT_LPD (1 << 1) /* Divider (not encoded) */
TsiChung Liewf6afe722007-06-18 13:50:13 -050047
48/*
49 * Get the value of the current system clock
50 *
51 * Parameters:
52 * none
53 *
54 * Return Value:
55 * The current output system frequency
56 */
57int get_sys_clock(void)
58{
59 volatile ccm_t *ccm = (volatile ccm_t *)(MMAP_CCM);
60 volatile pll_t *pll = (volatile pll_t *)(MMAP_PLL);
61 int divider;
62
63 /* Test to see if device is in LIMP mode */
64 if (ccm->misccr & CCM_MISCCR_LIMP) {
65 divider = ccm->cdr & CCM_CDR_LPDIV(0xF);
66 return (FREF / (2 << divider));
67 } else {
68 return ((FREF * pll->pfdr) / (BUSDIV * 4));
69 }
70}
71
72/*
73 * Initialize the Low Power Divider circuit
74 *
75 * Parameters:
76 * div Desired system frequency divider
77 *
78 * Return Value:
79 * The resulting output system frequency
80 */
81int clock_limp(int div)
82{
83 volatile ccm_t *ccm = (volatile ccm_t *)(MMAP_CCM);
84 u32 temp;
85
86 /* Check bounds of divider */
87 if (div < MIN_LPD)
88 div = MIN_LPD;
89 if (div > MAX_LPD)
90 div = MAX_LPD;
91
92 /* Save of the current value of the SSIDIV so we don't overwrite the value */
93 temp = (ccm->cdr & CCM_CDR_SSIDIV(0xF));
94
95 /* Apply the divider to the system clock */
96 ccm->cdr = (CCM_CDR_LPDIV(div) | CCM_CDR_SSIDIV(temp));
97
98 ccm->misccr |= CCM_MISCCR_LIMP;
99
100 return (FREF / (3 * (1 << div)));
101}
102
103/*
104 * Exit low power LIMP mode
105 *
106 * Parameters:
107 * div Desired system frequency divider
108 *
109 * Return Value:
110 * The resulting output system frequency
111 */
112int clock_exit_limp(void)
113{
114 volatile ccm_t *ccm = (volatile ccm_t *)(MMAP_CCM);
115 int fout;
116
117 /* Exit LIMP mode */
118 ccm->misccr &= (~CCM_MISCCR_LIMP);
119
120 /* Wait for PLL to lock */
121 while (!(ccm->misccr & CCM_MISCCR_PLL_LOCK)) ;
122
123 fout = get_sys_clock();
124
125 return fout;
126}
127
128/* Initialize the PLL
129 *
130 * Parameters:
131 * fref PLL reference clock frequency in KHz
132 * fsys Desired PLL output frequency in KHz
133 * flags Operating parameters
134 *
135 * Return Value:
136 * The resulting output system frequency
137 */
138int clock_pll(int fsys, int flags)
139{
140 volatile u32 *sdram_workaround = (volatile u32 *)(MMAP_SDRAM + 0x80);
141 volatile pll_t *pll = (volatile pll_t *)(MMAP_PLL);
142 int fref, temp, fout, mfd;
143 u32 i;
144
145 fref = FREF;
146
147 if (fsys == 0) {
148 /* Return current PLL output */
149 mfd = pll->pfdr;
150
151 return (fref * mfd / (BUSDIV * 4));
152 }
153
154 /* Check bounds of requested system clock */
155 if (fsys > MAX_FSYS)
156 fsys = MAX_FSYS;
157
158 if (fsys < MIN_FSYS)
159 fsys = MIN_FSYS;
160
161 /* Multiplying by 100 when calculating the temp value,
162 and then dividing by 100 to calculate the mfd allows
163 for exact values without needing to include floating
164 point libraries. */
165 temp = (100 * fsys) / fref;
166 mfd = (4 * BUSDIV * temp) / 100;
167
168 /* Determine the output frequency for selected values */
169 fout = ((fref * mfd) / (BUSDIV * 4));
170
171 /*
172 * Check to see if the SDRAM has already been initialized.
173 * If it has then the SDRAM needs to be put into self refresh
174 * mode before reprogramming the PLL.
175 */
TsiChung Liewf6afe722007-06-18 13:50:13 -0500176
177 /*
178 * Initialize the PLL to generate the new system clock frequency.
179 * The device must be put into LIMP mode to reprogram the PLL.
180 */
181
182 /* Enter LIMP mode */
183 clock_limp(DEFAULT_LPD);
184
185 /* Reprogram PLL for desired fsys */
186 pll->podr = (PLL_PODR_CPUDIV(BUSDIV / 3) | PLL_PODR_BUSDIV(BUSDIV));
187
188 pll->pfdr = mfd;
189
190 /* Exit LIMP mode */
191 clock_exit_limp();
192
193 /*
194 * Return the SDRAM to normal operation if it is in use.
195 */
TsiChung Liewf6afe722007-06-18 13:50:13 -0500196
197 /* software workaround for SDRAM opeartion after exiting LIMP mode errata */
198 *sdram_workaround = CFG_SDRAM_BASE;
TsiChungLiew2ce14b72007-07-05 23:05:31 -0500199
TsiChung Liewf6afe722007-06-18 13:50:13 -0500200 /* wait for DQS logic to relock */
201 for (i = 0; i < 0x200; i++) ;
202
203 return fout;
204}
205
206/*
207 * get_clocks() fills in gd->cpu_clock and gd->bus_clk
208 */
209int get_clocks(void)
210{
211 DECLARE_GLOBAL_DATA_PTR;
212
213 gd->bus_clk = clock_pll(CFG_CLK / 1000, 0) * 1000;
214 gd->cpu_clk = (gd->bus_clk * 3);
215 return (0);
216}