Merge with /home/stefan/git/u-boot/u-boot-ppc4xx
[platform/kernel/u-boot.git] / cpu / mcf532x / speed.c
1 /*
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
31 #include <asm/immap.h>
32
33 /* PLL min/max specifications */
34 #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 */
41 /*
42  * Low Power Divider specifications
43  */
44 #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) */
47
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  */
57 int 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  */
81 int 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  */
112 int 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  */
138 int 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          */
176
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          */
196
197         /* software workaround for SDRAM opeartion after exiting LIMP mode errata */
198         *sdram_workaround = CFG_SDRAM_BASE;
199
200         /* 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  */
209 int 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 }