]>
Commit | Line | Data |
---|---|---|
f046ccd1 | 1 | /* |
f6eda7f8 | 2 | * Copyright (C) 2004-2006 Freescale Semiconductor, Inc. |
f046ccd1 EL |
3 | * |
4 | * See file CREDITS for list of people who contributed to this | |
5 | * project. | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or | |
8 | * modify it under the terms of the GNU General Public License as | |
9 | * published by the Free Software Foundation; either version 2 of | |
10 | * the License, or (at your option) any later version. | |
11 | * | |
12 | * This program is distributed in the hope that it will be useful, | |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | * GNU General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU General Public License | |
18 | * along with this program; if not, write to the Free Software | |
19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, | |
20 | * MA 02111-1307 USA | |
21 | * | |
22 | * Change log: | |
23 | * | |
24 | * 20050101: Eran Liberty ([email protected]) | |
07a2505f | 25 | * Initial file creating (porting from 85XX & 8260) |
f046ccd1 EL |
26 | */ |
27 | ||
28 | /* | |
29 | * CPU specific code for the MPC83xx family. | |
30 | * | |
31 | * Derived from the MPC8260 and MPC85xx. | |
32 | */ | |
33 | ||
34 | #include <common.h> | |
35 | #include <watchdog.h> | |
36 | #include <command.h> | |
37 | #include <mpc83xx.h> | |
62ec6418 | 38 | #include <ft_build.h> |
f046ccd1 EL |
39 | #include <asm/processor.h> |
40 | ||
d87080b7 WD |
41 | DECLARE_GLOBAL_DATA_PTR; |
42 | ||
f046ccd1 EL |
43 | |
44 | int checkcpu(void) | |
45 | { | |
f046ccd1 EL |
46 | ulong clock = gd->cpu_clk; |
47 | u32 pvr = get_pvr(); | |
48 | char buf[32]; | |
49 | ||
50 | if ((pvr & 0xFFFF0000) != PVR_83xx) { | |
51 | puts("Not MPC83xx Family!!!\n"); | |
52 | return -1; | |
53 | } | |
54 | ||
e6f2e902 | 55 | puts("CPU: MPC83xx, "); |
f046ccd1 EL |
56 | switch(pvr) { |
57 | case PVR_8349_REV10: | |
58 | break; | |
59 | case PVR_8349_REV11: | |
60 | break; | |
61 | default: | |
62 | puts("Rev: Unknown\n"); | |
63 | return -1; /* Not sure what this is */ | |
64 | } | |
6902df56 RJ |
65 | printf("Rev: %d.%d at %s MHz\n", (pvr & 0xf0) >> 4, |
66 | (pvr & 0x0f), strmhz(buf, clock)); | |
67 | ||
f046ccd1 EL |
68 | return 0; |
69 | } | |
70 | ||
71 | ||
2ad6b513 TT |
72 | /** |
73 | * Program a UPM with the code supplied in the table. | |
74 | * | |
75 | * The 'dummy' variable is used to increment the MAD. 'dummy' is | |
76 | * supposed to be a pointer to the memory of the device being | |
77 | * programmed by the UPM. The data in the MDR is written into | |
78 | * memory and the MAD is incremented every time there's a read | |
79 | * from 'dummy'. Unfortunately, the current prototype for this | |
80 | * function doesn't allow for passing the address of this | |
81 | * device, and changing the prototype will break a number lots | |
82 | * of other code, so we need to use a round-about way of finding | |
83 | * the value for 'dummy'. | |
84 | * | |
85 | * The value can be extracted from the base address bits of the | |
86 | * Base Register (BR) associated with the specific UPM. To find | |
87 | * that BR, we need to scan all 8 BRs until we find the one that | |
88 | * has its MSEL bits matching the UPM we want. Once we know the | |
89 | * right BR, we can extract the base address bits from it. | |
90 | * | |
91 | * The MxMR and the BR and OR of the chosen bank should all be | |
92 | * configured before calling this function. | |
93 | * | |
94 | * Parameters: | |
95 | * upm: 0=UPMA, 1=UPMB, 2=UPMC | |
96 | * table: Pointer to an array of values to program | |
97 | * size: Number of elements in the array. Must be 64 or less. | |
98 | */ | |
f046ccd1 EL |
99 | void upmconfig (uint upm, uint *table, uint size) |
100 | { | |
2ad6b513 TT |
101 | #if defined(CONFIG_MPC834X) |
102 | volatile immap_t *immap = (immap_t *) CFG_IMMRBAR; | |
103 | volatile lbus83xx_t *lbus = &immap->lbus; | |
104 | volatile uchar *dummy = NULL; | |
105 | const u32 msel = (upm + 4) << BR_MSEL_SHIFT; /* What the MSEL field in BRn should be */ | |
106 | volatile u32 *mxmr = &lbus->mamr + upm; /* Pointer to mamr, mbmr, or mcmr */ | |
107 | uint i; | |
108 | ||
109 | /* Scan all the banks to determine the base address of the device */ | |
110 | for (i = 0; i < 8; i++) { | |
111 | if ((lbus->bank[i].br & BR_MSEL) == msel) { | |
112 | dummy = (uchar *) (lbus->bank[i].br & BR_BA); | |
113 | break; | |
114 | } | |
115 | } | |
116 | ||
117 | if (!dummy) { | |
118 | printf("Error: %s() could not find matching BR\n", __FUNCTION__); | |
119 | hang(); | |
120 | } | |
121 | ||
122 | /* Set the OP field in the MxMR to "write" and the MAD field to 000000 */ | |
123 | *mxmr = (*mxmr & 0xCFFFFFC0) | 0x10000000; | |
124 | ||
125 | for (i = 0; i < size; i++) { | |
126 | lbus->mdr = table[i]; | |
127 | __asm__ __volatile__ ("sync"); | |
128 | *dummy; /* Write the value to memory and increment MAD */ | |
129 | __asm__ __volatile__ ("sync"); | |
130 | } | |
131 | ||
132 | /* Set the OP field in the MxMR to "normal" and the MAD field to 000000 */ | |
133 | *mxmr &= 0xCFFFFFC0; | |
134 | #else | |
135 | printf("Error: %s() not defined for this configuration.\n", __FUNCTION__); | |
136 | hang(); | |
137 | #endif | |
f046ccd1 EL |
138 | } |
139 | ||
140 | ||
141 | int | |
142 | do_reset (cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) | |
143 | { | |
07a2505f WD |
144 | ulong msr; |
145 | #ifndef MPC83xx_RESET | |
146 | ulong addr; | |
147 | #endif | |
f046ccd1 EL |
148 | |
149 | volatile immap_t *immap = (immap_t *) CFG_IMMRBAR; | |
150 | ||
151 | #ifdef MPC83xx_RESET | |
152 | /* Interrupts and MMU off */ | |
153 | __asm__ __volatile__ ("mfmsr %0":"=r" (msr):); | |
154 | ||
155 | msr &= ~( MSR_EE | MSR_IR | MSR_DR); | |
156 | __asm__ __volatile__ ("mtmsr %0"::"r" (msr)); | |
157 | ||
158 | /* enable Reset Control Reg */ | |
159 | immap->reset.rpr = 0x52535445; | |
6d8ae5ab MB |
160 | __asm__ __volatile__ ("sync"); |
161 | __asm__ __volatile__ ("isync"); | |
f046ccd1 EL |
162 | |
163 | /* confirm Reset Control Reg is enabled */ | |
164 | while(!((immap->reset.rcer) & RCER_CRE)); | |
165 | ||
166 | printf("Resetting the board."); | |
167 | printf("\n"); | |
168 | ||
169 | udelay(200); | |
170 | ||
171 | /* perform reset, only one bit */ | |
07a2505f WD |
172 | immap->reset.rcr = RCR_SWHR; |
173 | ||
174 | #else /* ! MPC83xx_RESET */ | |
f046ccd1 | 175 | |
07a2505f WD |
176 | immap->reset.rmr = RMR_CSRE; /* Checkstop Reset enable */ |
177 | ||
178 | /* Interrupts and MMU off */ | |
179 | __asm__ __volatile__ ("mfmsr %0":"=r" (msr):); | |
f046ccd1 EL |
180 | |
181 | msr &= ~(MSR_ME | MSR_EE | MSR_IR | MSR_DR); | |
182 | __asm__ __volatile__ ("mtmsr %0"::"r" (msr)); | |
183 | ||
184 | /* | |
185 | * Trying to execute the next instruction at a non-existing address | |
186 | * should cause a machine check, resulting in reset | |
187 | */ | |
188 | addr = CFG_RESET_ADDRESS; | |
189 | ||
190 | printf("resetting the board."); | |
191 | printf("\n"); | |
192 | ((void (*)(void)) addr) (); | |
07a2505f WD |
193 | #endif /* MPC83xx_RESET */ |
194 | ||
f046ccd1 EL |
195 | return 1; |
196 | } | |
197 | ||
198 | ||
199 | /* | |
200 | * Get timebase clock frequency (like cpu_clk in Hz) | |
201 | */ | |
202 | ||
203 | unsigned long get_tbclk(void) | |
204 | { | |
f046ccd1 EL |
205 | ulong tbclk; |
206 | ||
207 | tbclk = (gd->bus_clk + 3L) / 4L; | |
208 | ||
209 | return tbclk; | |
210 | } | |
211 | ||
212 | ||
213 | #if defined(CONFIG_WATCHDOG) | |
214 | void watchdog_reset (void) | |
215 | { | |
2ad6b513 TT |
216 | #ifdef CONFIG_MPC834X |
217 | int re_enable = disable_interrupts(); | |
218 | ||
219 | /* Reset the 83xx watchdog */ | |
220 | volatile immap_t *immr = (immap_t *) CFG_IMMRBAR; | |
221 | immr->wdt.swsrr = 0x556c; | |
222 | immr->wdt.swsrr = 0xaa39; | |
223 | ||
224 | if (re_enable) | |
225 | enable_interrupts (); | |
226 | #else | |
227 | hang(); | |
228 | #endif | |
f046ccd1 | 229 | } |
2ad6b513 | 230 | #endif |
62ec6418 KG |
231 | |
232 | #if defined(CONFIG_OF_FLAT_TREE) | |
233 | void | |
234 | ft_cpu_setup(void *blob, bd_t *bd) | |
235 | { | |
236 | u32 *p; | |
237 | int len; | |
238 | ulong clock; | |
239 | ||
240 | clock = bd->bi_busfreq; | |
241 | p = ft_get_prop(blob, "/cpus/" OF_CPU "/bus-frequency", &len); | |
242 | if (p != NULL) | |
243 | *p = cpu_to_be32(clock); | |
244 | ||
245 | p = ft_get_prop(blob, "/" OF_SOC "/bus-frequency", &len); | |
246 | if (p != NULL) | |
247 | *p = cpu_to_be32(clock); | |
248 | ||
249 | p = ft_get_prop(blob, "/" OF_SOC "/serial@4500/clock-frequency", &len); | |
250 | if (p != NULL) | |
251 | *p = cpu_to_be32(clock); | |
252 | ||
253 | p = ft_get_prop(blob, "/" OF_SOC "/serial@4600/clock-frequency", &len); | |
254 | if (p != NULL) | |
255 | *p = cpu_to_be32(clock); | |
256 | ||
257 | #ifdef CONFIG_MPC83XX_TSEC1 | |
258 | p = ft_get_prop(blob, "/" OF_SOC "/ethernet@24000/address", &len); | |
259 | memcpy(p, bd->bi_enetaddr, 6); | |
260 | #endif | |
261 | ||
262 | #ifdef CONFIG_MPC83XX_TSEC2 | |
263 | p = ft_get_prop(blob, "/" OF_SOC "/ethernet@25000/address", &len); | |
264 | memcpy(p, bd->bi_enet1addr, 6); | |
265 | #endif | |
266 | } | |
267 | #endif | |
61f25155 MB |
268 | |
269 | #if defined(CONFIG_DDR_ECC) | |
270 | void dma_init(void) | |
271 | { | |
272 | volatile immap_t *immap = (immap_t *)CFG_IMMRBAR; | |
f6eda7f8 | 273 | volatile dma83xx_t *dma = &immap->dma; |
61f25155 MB |
274 | volatile u32 status = swab32(dma->dmasr0); |
275 | volatile u32 dmamr0 = swab32(dma->dmamr0); | |
276 | ||
277 | debug("DMA-init\n"); | |
278 | ||
279 | /* initialize DMASARn, DMADAR and DMAABCRn */ | |
280 | dma->dmadar0 = (u32)0; | |
281 | dma->dmasar0 = (u32)0; | |
282 | dma->dmabcr0 = 0; | |
283 | ||
284 | __asm__ __volatile__ ("sync"); | |
285 | __asm__ __volatile__ ("isync"); | |
286 | ||
287 | /* clear CS bit */ | |
288 | dmamr0 &= ~DMA_CHANNEL_START; | |
289 | dma->dmamr0 = swab32(dmamr0); | |
290 | __asm__ __volatile__ ("sync"); | |
291 | __asm__ __volatile__ ("isync"); | |
292 | ||
293 | /* while the channel is busy, spin */ | |
294 | while(status & DMA_CHANNEL_BUSY) { | |
295 | status = swab32(dma->dmasr0); | |
296 | } | |
297 | ||
298 | debug("DMA-init end\n"); | |
299 | } | |
300 | ||
301 | uint dma_check(void) | |
302 | { | |
303 | volatile immap_t *immap = (immap_t *)CFG_IMMRBAR; | |
f6eda7f8 | 304 | volatile dma83xx_t *dma = &immap->dma; |
61f25155 MB |
305 | volatile u32 status = swab32(dma->dmasr0); |
306 | volatile u32 byte_count = swab32(dma->dmabcr0); | |
307 | ||
308 | /* while the channel is busy, spin */ | |
309 | while (status & DMA_CHANNEL_BUSY) { | |
310 | status = swab32(dma->dmasr0); | |
311 | } | |
312 | ||
313 | if (status & DMA_CHANNEL_TRANSFER_ERROR) { | |
314 | printf ("DMA Error: status = %x @ %d\n", status, byte_count); | |
315 | } | |
316 | ||
317 | return status; | |
318 | } | |
319 | ||
320 | int dma_xfer(void *dest, u32 count, void *src) | |
321 | { | |
322 | volatile immap_t *immap = (immap_t *)CFG_IMMRBAR; | |
f6eda7f8 | 323 | volatile dma83xx_t *dma = &immap->dma; |
61f25155 MB |
324 | volatile u32 dmamr0; |
325 | ||
326 | /* initialize DMASARn, DMADAR and DMAABCRn */ | |
327 | dma->dmadar0 = swab32((u32)dest); | |
328 | dma->dmasar0 = swab32((u32)src); | |
329 | dma->dmabcr0 = swab32(count); | |
330 | ||
331 | __asm__ __volatile__ ("sync"); | |
332 | __asm__ __volatile__ ("isync"); | |
333 | ||
334 | /* init direct transfer, clear CS bit */ | |
335 | dmamr0 = (DMA_CHANNEL_TRANSFER_MODE_DIRECT | | |
336 | DMA_CHANNEL_SOURCE_ADDRESS_HOLD_8B | | |
337 | DMA_CHANNEL_SOURCE_ADRESSS_HOLD_EN); | |
cf48eb9a | 338 | |
61f25155 MB |
339 | dma->dmamr0 = swab32(dmamr0); |
340 | ||
341 | __asm__ __volatile__ ("sync"); | |
342 | __asm__ __volatile__ ("isync"); | |
343 | ||
344 | /* set CS to start DMA transfer */ | |
345 | dmamr0 |= DMA_CHANNEL_START; | |
346 | dma->dmamr0 = swab32(dmamr0); | |
347 | __asm__ __volatile__ ("sync"); | |
348 | __asm__ __volatile__ ("isync"); | |
349 | ||
350 | return ((int)dma_check()); | |
351 | } | |
352 | #endif /*CONFIG_DDR_ECC*/ |