mirror of
https://github.com/brain-hackers/u-boot-brain
synced 2024-09-03 11:23:22 +09:00
![Stefan Roese](/assets/img/avatar_default.png)
This patch reworks the 440GX interrupt handling so that the common 4xx code can be used. The 440GX is an exception to all other 4xx variants by having the cascading interrupt vectors not on UIC0 but on a special UIC named UICB0 (UIC Base 0). With this patch now, U-Boot references the 440GX UICB0 when UIC0 is selected. And the common 4xx interrupt handling is simpler without any 440GX special cases. Also some additional cleanup to cpu/ppc4xx/interrupt.c is done. Signed-off-by: Stefan Roese <sr@denx.de>
350 lines
9.4 KiB
C
350 lines
9.4 KiB
C
/*
|
|
* (C) Copyright 2000-2007
|
|
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
|
|
*
|
|
* See file CREDITS for list of people who contributed to this
|
|
* project.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License as
|
|
* published by the Free Software Foundation; either version 2 of
|
|
* the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
|
|
* MA 02111-1307 USA
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <watchdog.h>
|
|
#include <ppc4xx_enet.h>
|
|
#include <asm/processor.h>
|
|
#include <asm/gpio.h>
|
|
#include <ppc4xx.h>
|
|
|
|
#if defined(CONFIG_405GP) || defined(CONFIG_405EP)
|
|
DECLARE_GLOBAL_DATA_PTR;
|
|
#endif
|
|
|
|
#ifndef CFG_PLL_RECONFIG
|
|
#define CFG_PLL_RECONFIG 0
|
|
#endif
|
|
|
|
void reconfigure_pll(u32 new_cpu_freq)
|
|
{
|
|
#if defined(CONFIG_440EPX)
|
|
int reset_needed = 0;
|
|
u32 reg, temp;
|
|
u32 prbdv0, target_prbdv0, /* CLK_PRIMBD */
|
|
fwdva, target_fwdva, fwdvb, target_fwdvb, /* CLK_PLLD */
|
|
fbdv, target_fbdv, lfbdv, target_lfbdv,
|
|
perdv0, target_perdv0, /* CLK_PERD */
|
|
spcid0, target_spcid0; /* CLK_SPCID */
|
|
|
|
/* Reconfigure clocks if necessary.
|
|
* See PPC440EPx User's Manual, sections 8.2 and 14 */
|
|
if (new_cpu_freq == 667) {
|
|
target_prbdv0 = 2;
|
|
target_fwdva = 2;
|
|
target_fwdvb = 4;
|
|
target_fbdv = 20;
|
|
target_lfbdv = 1;
|
|
target_perdv0 = 4;
|
|
target_spcid0 = 4;
|
|
|
|
mfcpr(clk_primbd, reg);
|
|
temp = (reg & PRBDV_MASK) >> 24;
|
|
prbdv0 = temp ? temp : 8;
|
|
if (prbdv0 != target_prbdv0) {
|
|
reg &= ~PRBDV_MASK;
|
|
reg |= ((target_prbdv0 == 8 ? 0 : target_prbdv0) << 24);
|
|
mtcpr(clk_primbd, reg);
|
|
reset_needed = 1;
|
|
}
|
|
|
|
mfcpr(clk_plld, reg);
|
|
|
|
temp = (reg & PLLD_FWDVA_MASK) >> 16;
|
|
fwdva = temp ? temp : 16;
|
|
|
|
temp = (reg & PLLD_FWDVB_MASK) >> 8;
|
|
fwdvb = temp ? temp : 8;
|
|
|
|
temp = (reg & PLLD_FBDV_MASK) >> 24;
|
|
fbdv = temp ? temp : 32;
|
|
|
|
temp = (reg & PLLD_LFBDV_MASK);
|
|
lfbdv = temp ? temp : 64;
|
|
|
|
if (fwdva != target_fwdva || fbdv != target_fbdv || lfbdv != target_lfbdv) {
|
|
reg &= ~(PLLD_FWDVA_MASK | PLLD_FWDVB_MASK |
|
|
PLLD_FBDV_MASK | PLLD_LFBDV_MASK);
|
|
reg |= ((target_fwdva == 16 ? 0 : target_fwdva) << 16) |
|
|
((target_fwdvb == 8 ? 0 : target_fwdvb) << 8) |
|
|
((target_fbdv == 32 ? 0 : target_fbdv) << 24) |
|
|
(target_lfbdv == 64 ? 0 : target_lfbdv);
|
|
mtcpr(clk_plld, reg);
|
|
reset_needed = 1;
|
|
}
|
|
|
|
mfcpr(clk_perd, reg);
|
|
perdv0 = (reg & CPR0_PERD_PERDV0_MASK) >> 24;
|
|
if (perdv0 != target_perdv0) {
|
|
reg &= ~CPR0_PERD_PERDV0_MASK;
|
|
reg |= (target_perdv0 << 24);
|
|
mtcpr(clk_perd, reg);
|
|
reset_needed = 1;
|
|
}
|
|
|
|
mfcpr(clk_spcid, reg);
|
|
temp = (reg & CPR0_SPCID_SPCIDV0_MASK) >> 24;
|
|
spcid0 = temp ? temp : 4;
|
|
if (spcid0 != target_spcid0) {
|
|
reg &= ~CPR0_SPCID_SPCIDV0_MASK;
|
|
reg |= ((target_spcid0 == 4 ? 0 : target_spcid0) << 24);
|
|
mtcpr(clk_spcid, reg);
|
|
reset_needed = 1;
|
|
}
|
|
|
|
/* Set reload inhibit so configuration will persist across
|
|
* processor resets */
|
|
mfcpr(clk_icfg, reg);
|
|
reg &= ~CPR0_ICFG_RLI_MASK;
|
|
reg |= 1 << 31;
|
|
mtcpr(clk_icfg, reg);
|
|
}
|
|
|
|
/* Reset processor if configuration changed */
|
|
if (reset_needed) {
|
|
__asm__ __volatile__ ("sync; isync");
|
|
mtspr(dbcr0, 0x20000000);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Breath some life into the CPU...
|
|
*
|
|
* Reconfigure PLL if necessary,
|
|
* set up the memory map,
|
|
* initialize a bunch of registers
|
|
*/
|
|
void
|
|
cpu_init_f (void)
|
|
{
|
|
#if defined(CONFIG_WATCHDOG) || defined(CONFIG_440GX) || defined(CONFIG_460EX)
|
|
u32 val;
|
|
#endif
|
|
|
|
reconfigure_pll(CFG_PLL_RECONFIG);
|
|
|
|
#if (defined(CONFIG_405EP) || defined (CONFIG_405EX)) && !defined(CFG_4xx_GPIO_TABLE)
|
|
/*
|
|
* GPIO0 setup (select GPIO or alternate function)
|
|
*/
|
|
#if defined(CFG_GPIO0_OR)
|
|
out32(GPIO0_OR, CFG_GPIO0_OR); /* set initial state of output pins */
|
|
#endif
|
|
#if defined(CFG_GPIO0_ODR)
|
|
out32(GPIO0_ODR, CFG_GPIO0_ODR); /* open-drain select */
|
|
#endif
|
|
out32(GPIO0_OSRH, CFG_GPIO0_OSRH); /* output select */
|
|
out32(GPIO0_OSRL, CFG_GPIO0_OSRL);
|
|
out32(GPIO0_ISR1H, CFG_GPIO0_ISR1H); /* input select */
|
|
out32(GPIO0_ISR1L, CFG_GPIO0_ISR1L);
|
|
out32(GPIO0_TSRH, CFG_GPIO0_TSRH); /* three-state select */
|
|
out32(GPIO0_TSRL, CFG_GPIO0_TSRL);
|
|
#if defined(CFG_GPIO0_ISR2H)
|
|
out32(GPIO0_ISR2H, CFG_GPIO0_ISR2H);
|
|
out32(GPIO0_ISR2L, CFG_GPIO0_ISR2L);
|
|
#endif
|
|
#if defined (CFG_GPIO0_TCR)
|
|
out32(GPIO0_TCR, CFG_GPIO0_TCR); /* enable output driver for outputs */
|
|
#endif
|
|
#endif /* CONFIG_405EP ... && !CFG_4xx_GPIO_TABLE */
|
|
|
|
#if defined (CONFIG_405EP)
|
|
/*
|
|
* Set EMAC noise filter bits
|
|
*/
|
|
mtdcr(cpc0_epctl, CPC0_EPRCSR_E0NFE | CPC0_EPRCSR_E1NFE);
|
|
|
|
/*
|
|
* Enable the internal PCI arbiter
|
|
*/
|
|
mtdcr(cpc0_pci, mfdcr(cpc0_pci) | CPC0_PCI_HOST_CFG_EN | CPC0_PCI_ARBIT_EN);
|
|
#endif /* CONFIG_405EP */
|
|
|
|
#if defined(CFG_4xx_GPIO_TABLE)
|
|
gpio_set_chip_configuration();
|
|
#endif /* CFG_4xx_GPIO_TABLE */
|
|
|
|
/*
|
|
* External Bus Controller (EBC) Setup
|
|
*/
|
|
#if (defined(CFG_EBC_PB0AP) && defined(CFG_EBC_PB0CR))
|
|
#if (defined(CONFIG_405GP) || defined(CONFIG_405CR) || \
|
|
defined(CONFIG_405EP) || defined(CONFIG_405EZ) || \
|
|
defined(CONFIG_405EX) || defined(CONFIG_405))
|
|
/*
|
|
* Move the next instructions into icache, since these modify the flash
|
|
* we are running from!
|
|
*/
|
|
asm volatile(" bl 0f" ::: "lr");
|
|
asm volatile("0: mflr 3" ::: "r3");
|
|
asm volatile(" addi 4, 0, 14" ::: "r4");
|
|
asm volatile(" mtctr 4" ::: "ctr");
|
|
asm volatile("1: icbt 0, 3");
|
|
asm volatile(" addi 3, 3, 32" ::: "r3");
|
|
asm volatile(" bdnz 1b" ::: "ctr", "cr0");
|
|
asm volatile(" addis 3, 0, 0x0" ::: "r3");
|
|
asm volatile(" ori 3, 3, 0xA000" ::: "r3");
|
|
asm volatile(" mtctr 3" ::: "ctr");
|
|
asm volatile("2: bdnz 2b" ::: "ctr", "cr0");
|
|
#endif
|
|
|
|
mtebc(pb0ap, CFG_EBC_PB0AP);
|
|
mtebc(pb0cr, CFG_EBC_PB0CR);
|
|
#endif
|
|
|
|
#if (defined(CFG_EBC_PB1AP) && defined(CFG_EBC_PB1CR) && !(CFG_INIT_DCACHE_CS == 1))
|
|
mtebc(pb1ap, CFG_EBC_PB1AP);
|
|
mtebc(pb1cr, CFG_EBC_PB1CR);
|
|
#endif
|
|
|
|
#if (defined(CFG_EBC_PB2AP) && defined(CFG_EBC_PB2CR) && !(CFG_INIT_DCACHE_CS == 2))
|
|
mtebc(pb2ap, CFG_EBC_PB2AP);
|
|
mtebc(pb2cr, CFG_EBC_PB2CR);
|
|
#endif
|
|
|
|
#if (defined(CFG_EBC_PB3AP) && defined(CFG_EBC_PB3CR) && !(CFG_INIT_DCACHE_CS == 3))
|
|
mtebc(pb3ap, CFG_EBC_PB3AP);
|
|
mtebc(pb3cr, CFG_EBC_PB3CR);
|
|
#endif
|
|
|
|
#if (defined(CFG_EBC_PB4AP) && defined(CFG_EBC_PB4CR) && !(CFG_INIT_DCACHE_CS == 4))
|
|
mtebc(pb4ap, CFG_EBC_PB4AP);
|
|
mtebc(pb4cr, CFG_EBC_PB4CR);
|
|
#endif
|
|
|
|
#if (defined(CFG_EBC_PB5AP) && defined(CFG_EBC_PB5CR) && !(CFG_INIT_DCACHE_CS == 5))
|
|
mtebc(pb5ap, CFG_EBC_PB5AP);
|
|
mtebc(pb5cr, CFG_EBC_PB5CR);
|
|
#endif
|
|
|
|
#if (defined(CFG_EBC_PB6AP) && defined(CFG_EBC_PB6CR) && !(CFG_INIT_DCACHE_CS == 6))
|
|
mtebc(pb6ap, CFG_EBC_PB6AP);
|
|
mtebc(pb6cr, CFG_EBC_PB6CR);
|
|
#endif
|
|
|
|
#if (defined(CFG_EBC_PB7AP) && defined(CFG_EBC_PB7CR) && !(CFG_INIT_DCACHE_CS == 7))
|
|
mtebc(pb7ap, CFG_EBC_PB7AP);
|
|
mtebc(pb7cr, CFG_EBC_PB7CR);
|
|
#endif
|
|
|
|
#if defined (CFG_EBC_CFG)
|
|
mtebc(EBC0_CFG, CFG_EBC_CFG);
|
|
#endif
|
|
|
|
#if defined(CONFIG_WATCHDOG)
|
|
val = mfspr(tcr);
|
|
#if defined(CONFIG_440EP) || defined(CONFIG_440GR)
|
|
val |= 0xb8000000; /* generate system reset after 1.34 seconds */
|
|
#elif defined(CONFIG_440EPX)
|
|
val |= 0xb0000000; /* generate system reset after 1.34 seconds */
|
|
#else
|
|
val |= 0xf0000000; /* generate system reset after 2.684 seconds */
|
|
#endif
|
|
#if defined(CFG_4xx_RESET_TYPE)
|
|
val &= ~0x30000000; /* clear WRC bits */
|
|
val |= CFG_4xx_RESET_TYPE << 28; /* set board specific WRC type */
|
|
#endif
|
|
mtspr(tcr, val);
|
|
|
|
val = mfspr(tsr);
|
|
val |= 0x80000000; /* enable watchdog timer */
|
|
mtspr(tsr, val);
|
|
|
|
reset_4xx_watchdog();
|
|
#endif /* CONFIG_WATCHDOG */
|
|
|
|
#if defined(CONFIG_440GX)
|
|
/* Take the GX out of compatibility mode
|
|
* Travis Sawyer, 9 Mar 2004
|
|
* NOTE: 440gx user manual inconsistency here
|
|
* Compatibility mode and Ethernet Clock select are not
|
|
* correct in the manual
|
|
*/
|
|
mfsdr(sdr_mfr, val);
|
|
val &= ~0x10000000;
|
|
mtsdr(sdr_mfr,val);
|
|
#endif /* CONFIG_440GX */
|
|
|
|
#if defined(CONFIG_460EX)
|
|
/*
|
|
* Set SDR0_AHB_CFG[A2P_INCR4] (bit 24) and
|
|
* clear SDR0_AHB_CFG[A2P_PROT2] (bit 25) for a new 460EX errata
|
|
* regarding concurrent use of AHB USB OTG, USB 2.0 host and SATA
|
|
*/
|
|
mfsdr(SDR0_AHB_CFG, val);
|
|
val |= 0x80;
|
|
val &= ~0x40;
|
|
mtsdr(SDR0_AHB_CFG, val);
|
|
mfsdr(SDR0_USB2HOST_CFG, val);
|
|
val &= ~0xf00;
|
|
val |= 0x400;
|
|
mtsdr(SDR0_USB2HOST_CFG, val);
|
|
#endif /* CONFIG_460EX */
|
|
}
|
|
|
|
/*
|
|
* initialize higher level parts of CPU like time base and timers
|
|
*/
|
|
int cpu_init_r (void)
|
|
{
|
|
#if defined(CONFIG_405GP) || defined(CONFIG_405EP)
|
|
bd_t *bd = gd->bd;
|
|
unsigned long reg;
|
|
#if defined(CONFIG_405GP)
|
|
uint pvr = get_pvr();
|
|
#endif
|
|
|
|
/*
|
|
* Write Ethernetaddress into on-chip register
|
|
*/
|
|
reg = 0x00000000;
|
|
reg |= bd->bi_enetaddr[0]; /* set high address */
|
|
reg = reg << 8;
|
|
reg |= bd->bi_enetaddr[1];
|
|
out32 (EMAC_IAH, reg);
|
|
|
|
reg = 0x00000000;
|
|
reg |= bd->bi_enetaddr[2]; /* set low address */
|
|
reg = reg << 8;
|
|
reg |= bd->bi_enetaddr[3];
|
|
reg = reg << 8;
|
|
reg |= bd->bi_enetaddr[4];
|
|
reg = reg << 8;
|
|
reg |= bd->bi_enetaddr[5];
|
|
out32 (EMAC_IAL, reg);
|
|
|
|
#if defined(CONFIG_405GP)
|
|
/*
|
|
* Set edge conditioning circuitry on PPC405GPr
|
|
* for compatibility to existing PPC405GP designs.
|
|
*/
|
|
if ((pvr & 0xfffffff0) == (PVR_405GPR_RB & 0xfffffff0)) {
|
|
mtdcr(ecr, 0x60606000);
|
|
}
|
|
#endif /* defined(CONFIG_405GP) */
|
|
#endif /* defined(CONFIG_405GP) || defined(CONFIG_405EP) */
|
|
|
|
return (0);
|
|
}
|