diff options
Diffstat (limited to 'cpu/ppc4xx')
-rw-r--r-- | cpu/ppc4xx/405gp_enet.c | 867 | ||||
-rw-r--r-- | cpu/ppc4xx/405gp_pci.c | 502 | ||||
-rw-r--r-- | cpu/ppc4xx/cpu.c | 253 | ||||
-rw-r--r-- | cpu/ppc4xx/i2c.c | 417 | ||||
-rw-r--r-- | cpu/ppc4xx/sdram.c | 191 | ||||
-rw-r--r-- | cpu/ppc4xx/speed.c | 308 | ||||
-rw-r--r-- | cpu/ppc4xx/vecnum.h | 100 |
7 files changed, 2638 insertions, 0 deletions
diff --git a/cpu/ppc4xx/405gp_enet.c b/cpu/ppc4xx/405gp_enet.c new file mode 100644 index 0000000000..49e9e424fd --- /dev/null +++ b/cpu/ppc4xx/405gp_enet.c @@ -0,0 +1,867 @@ +/*-----------------------------------------------------------------------------+ + * + * This source code has been made available to you by IBM on an AS-IS + * basis. Anyone receiving this source is licensed under IBM + * copyrights to use it in any way he or she deems fit, including + * copying it, modifying it, compiling it, and redistributing it either + * with or without modifications. No license under IBM patents or + * patent applications is to be implied by the copyright license. + * + * Any user of this software should understand that IBM cannot provide + * technical support for this software and will not be responsible for + * any consequences resulting from the use of this software. + * + * Any person who transfers this source code or any derivative work + * must include the IBM copyright notice, this paragraph, and the + * preceding two paragraphs in the transferred software. + * + * COPYRIGHT I B M CORPORATION 1995 + * LICENSED MATERIAL - PROGRAM PROPERTY OF I B M + *-----------------------------------------------------------------------------*/ +/*-----------------------------------------------------------------------------+ + * + * File Name: enetemac.c + * + * Function: Device driver for the ethernet EMAC3 macro on the 405GP. + * + * Author: Mark Wisner + * + * Change Activity- + * + * Date Description of Change BY + * --------- --------------------- --- + * 05-May-99 Created MKW + * 27-Jun-99 Clean up JWB + * 16-Jul-99 Added MAL error recovery and better IP packet handling MKW + * 29-Jul-99 Added Full duplex support MKW + * 06-Aug-99 Changed names for Mal CR reg MKW + * 23-Aug-99 Turned off SYE when running at 10Mbs MKW + * 24-Aug-99 Marked descriptor empty after call_xlc MKW + * 07-Sep-99 Set MAL RX buffer size reg to ENET_MAX_MTU_ALIGNED / 16 MCG + * to avoid chaining maximum sized packets. Push starting + * RX descriptor address up to the next cache line boundary. + * 16-Jan-00 Added support for booting with IP of 0x0 MKW + * 15-Mar-00 Updated enetInit() to enable broadcast addresses in the + * EMAC_RXM register. JWB + * 12-Mar-01 anne-sophie.harnois@nextream.fr + * - Variables are compatible with those already defined in + * include/net.h + * - Receive buffer descriptor ring is used to send buffers + * to the user + * - Info print about send/received/handled packet number if + * INFO_405_ENET is set + * 17-Apr-01 stefan.roese@esd-electronics.com + * - MAL reset in "eth_halt" included + * - Enet speed and duplex output now in one line + * 08-May-01 stefan.roese@esd-electronics.com + * - MAL error handling added (eth_init called again) + * 13-Nov-01 stefan.roese@esd-electronics.com + * - Set IST bit in EMAC_M1 reg upon 100MBit or full duplex + * 04-Jan-02 stefan.roese@esd-electronics.com + * - Wait for PHY auto negotiation to complete added + * 06-Feb-02 stefan.roese@esd-electronics.com + * - Bug fixed in waiting for auto negotiation to complete + * 26-Feb-02 stefan.roese@esd-electronics.com + * - rx and tx buffer descriptors now allocated (no fixed address + * used anymore) + * 17-Jun-02 stefan.roese@esd-electronics.com + * - MAL error debug printf 'M' removed (rx de interrupt may + * occur upon many incoming packets with only 4 rx buffers). + *-----------------------------------------------------------------------------*/ + +#include <common.h> +#include <asm/processor.h> +#include <ppc4xx.h> +#include <commproc.h> +#include <405gp_enet.h> +#include <405_mal.h> +#include <miiphy.h> +#include <net.h> +#include <malloc.h> +#include "vecnum.h" + +#if defined(CONFIG_405GP) || defined(CONFIG_440) + +#define EMAC_RESET_TIMEOUT 1000 /* 1000 ms reset timeout */ +#define PHY_AUTONEGOTIATE_TIMEOUT 2000 /* 2000 ms autonegotiate timeout */ + +#define NUM_TX_BUFF 1 +/* AS.HARNOIS + * Use PKTBUFSRX (include/net.h) instead of setting NUM_RX_BUFF again + * These both variables are used to define the same thing! + * #define NUM_RX_BUFF 4 + */ +#define NUM_RX_BUFF PKTBUFSRX + +/* Ethernet Transmit and Receive Buffers */ +/* AS.HARNOIS + * In the same way ENET_MAX_MTU and ENET_MAX_MTU_ALIGNED are set from + * PKTSIZE and PKTSIZE_ALIGN (include/net.h) + */ +#define ENET_MAX_MTU PKTSIZE +#define ENET_MAX_MTU_ALIGNED PKTSIZE_ALIGN + +static char *txbuf_ptr; + +/* define the number of channels implemented */ +#define EMAC_RXCHL 1 +#define EMAC_TXCHL 1 + +/*-----------------------------------------------------------------------------+ + * Defines for MAL/EMAC interrupt conditions as reported in the UIC (Universal + * Interrupt Controller). + *-----------------------------------------------------------------------------*/ +#define MAL_UIC_ERR ( UIC_MAL_SERR | UIC_MAL_TXDE | UIC_MAL_RXDE) +#define MAL_UIC_DEF (UIC_MAL_RXEOB | MAL_UIC_ERR) +#define EMAC_UIC_DEF UIC_ENET + +/*-----------------------------------------------------------------------------+ + * Global variables. TX and RX descriptors and buffers. + *-----------------------------------------------------------------------------*/ +static volatile mal_desc_t *tx; +static volatile mal_desc_t *rx; +static mal_desc_t *alloc_tx_buf = NULL; +static mal_desc_t *alloc_rx_buf = NULL; + +/* IER globals */ +static unsigned long emac_ier; +static unsigned long mal_ier; + + +/* Statistic Areas */ +#define MAX_ERR_LOG 10 +struct emac_stats { + int data_len_err; + int rx_frames; + int rx; + int rx_prot_err; +}; + +static struct stats { /* Statistic Block */ + struct emac_stats emac; + int int_err; + short tx_err_log[MAX_ERR_LOG]; + short rx_err_log[MAX_ERR_LOG]; +} stats; + +static int first_init = 0; + +static int tx_err_index = 0; /* Transmit Error Index for tx_err_log */ +static int rx_err_index = 0; /* Receive Error Index for rx_err_log */ + +static int rx_slot = 0; /* MAL Receive Slot */ +static int rx_i_index = 0; /* Receive Interrupt Queue Index */ +static int rx_u_index = 0; /* Receive User Queue Index */ +static int rx_ready[NUM_RX_BUFF]; /* Receive Ready Queue */ + +static int tx_slot = 0; /* MAL Transmit Slot */ +static int tx_i_index = 0; /* Transmit Interrupt Queue Index */ +static int tx_u_index = 0; /* Transmit User Queue Index */ +static int tx_run[NUM_TX_BUFF]; /* Transmit Running Queue */ + +#undef INFO_405_ENET 1 +#ifdef INFO_405_ENET +static int packetSent = 0; +static int packetReceived = 0; +static int packetHandled = 0; +#endif + +static char emac_hwd_addr[ENET_ADDR_LENGTH]; + +static bd_t *bis_save = NULL; /* for eth_init upon mal error */ + +static int is_receiving = 0; /* sync with eth interrupt */ +static int print_speed = 1; /* print speed message upon start */ + +static void enet_rcv (unsigned long malisr); + +/*-----------------------------------------------------------------------------+ + * Prototypes and externals. + *-----------------------------------------------------------------------------*/ +void mal_err (unsigned long isr, unsigned long uic, unsigned long mal_def, + unsigned long mal_errr); +void emac_err (unsigned long isr); + + +void eth_halt (void) +{ + mtdcr (malier, 0x00000000); /* disable mal interrupts */ + out32 (EMAC_IER, 0x00000000); /* disable emac interrupts */ + + /* 1st reset MAL */ + mtdcr (malmcr, MAL_CR_MMSR); + + /* wait for reset */ + while (mfdcr (malmcr) & MAL_CR_MMSR) { + }; + + /* EMAC RESET */ + out32 (EMAC_M0, EMAC_M0_SRST); + + print_speed = 1; /* print speed message again next time */ +} + + +int eth_init (bd_t * bis) +{ + int i; + unsigned long reg; + unsigned long msr; + unsigned long speed; + unsigned long duplex; + unsigned mode_reg; + unsigned short reg_short; + + msr = mfmsr (); + mtmsr (msr & ~(MSR_EE)); /* disable interrupts */ + +#ifdef INFO_405_ENET + /* AS.HARNOIS + * We should have : + * packetHandled <= packetReceived <= packetHandled+PKTBUFSRX + * In the most cases packetHandled = packetReceived, but it + * is possible that new packets (without relationship with + * current transfer) have got the time to arrived before + * netloop calls eth_halt + */ + printf ("About preceeding transfer:\n" + "- Sent packet number %d\n" + "- Received packet number %d\n" + "- Handled packet number %d\n", + packetSent, packetReceived, packetHandled); + packetSent = 0; + packetReceived = 0; + packetHandled = 0; +#endif + + /* MAL RESET */ + mtdcr (malmcr, MAL_CR_MMSR); + /* wait for reset */ + while (mfdcr (malmcr) & MAL_CR_MMSR) { + }; + + tx_err_index = 0; /* Transmit Error Index for tx_err_log */ + rx_err_index = 0; /* Receive Error Index for rx_err_log */ + + rx_slot = 0; /* MAL Receive Slot */ + rx_i_index = 0; /* Receive Interrupt Queue Index */ + rx_u_index = 0; /* Receive User Queue Index */ + + tx_slot = 0; /* MAL Transmit Slot */ + tx_i_index = 0; /* Transmit Interrupt Queue Index */ + tx_u_index = 0; /* Transmit User Queue Index */ + +#if defined(CONFIG_440) + /* set RMII mode */ + out32 (ZMII_FER, ZMII_RMII | ZMII_MDI0); +#endif /* CONFIG_440 */ + + /* EMAC RESET */ + out32 (EMAC_M0, EMAC_M0_SRST); + + /* wait for PHY to complete auto negotiation */ + reg_short = 0; +#ifndef CONFIG_CS8952_PHY + miiphy_read (CONFIG_PHY_ADDR, PHY_BMSR, ®_short); + + /* + * Wait if PHY is able of autonegotiation and autonegotiation is not complete + */ + if ((reg_short & PHY_BMSR_AUTN_ABLE) + && !(reg_short & PHY_BMSR_AUTN_COMP)) { + puts ("Waiting for PHY auto negotiation to complete"); + i = 0; + while (!(reg_short & PHY_BMSR_AUTN_COMP)) { + if ((i++ % 100) == 0) + putc ('.'); + udelay (10000); /* 10 ms */ + miiphy_read (CONFIG_PHY_ADDR, PHY_BMSR, ®_short); + + /* + * Timeout reached ? + */ + if (i * 10 > PHY_AUTONEGOTIATE_TIMEOUT) { + puts (" TIMEOUT !\n"); + break; + } + } + puts (" done\n"); + udelay (500000); /* another 500 ms (results in faster booting) */ + } +#endif + speed = miiphy_speed (CONFIG_PHY_ADDR); + duplex = miiphy_duplex (CONFIG_PHY_ADDR); + if (print_speed) { + print_speed = 0; + printf ("ENET Speed is %d Mbps - %s duplex connection\n", + (int) speed, (duplex == HALF) ? "HALF" : "FULL"); + } + + /* set the Mal configuration reg */ +#if defined(CONFIG_440) + /* Errata 1.12: MAL_1 -- Disable MAL bursting */ + if( get_pvr() == PVR_440GP_RB ) + mtdcr (malmcr, MAL_CR_OPBBL | MAL_CR_LEA | MAL_CR_PLBLT_DEFAULT); + else +#else + mtdcr (malmcr, MAL_CR_PLBB | MAL_CR_OPBBL | MAL_CR_LEA | MAL_CR_PLBLT_DEFAULT); +#endif + + /* Free "old" buffers */ + if (alloc_tx_buf) free(alloc_tx_buf); + if (alloc_rx_buf) free(alloc_rx_buf); + + /* + * Malloc MAL buffer desciptors, make sure they are + * aligned on cache line boundary size + * (401/403/IOP480 = 16, 405 = 32) + * and doesn't cross cache block boundaries. + */ + alloc_tx_buf = (mal_desc_t *)malloc((sizeof(mal_desc_t) * NUM_TX_BUFF) + + ((2 * CFG_CACHELINE_SIZE) - 2)); + if (((int)alloc_tx_buf & CACHELINE_MASK) != 0) { + tx = (mal_desc_t *)((int)alloc_tx_buf + CFG_CACHELINE_SIZE - + ((int)alloc_tx_buf & CACHELINE_MASK)); + } else { + tx = alloc_tx_buf; + } + + alloc_rx_buf = (mal_desc_t *)malloc((sizeof(mal_desc_t) * NUM_RX_BUFF) + + ((2 * CFG_CACHELINE_SIZE) - 2)); + if (((int)alloc_rx_buf & CACHELINE_MASK) != 0) { + rx = (mal_desc_t *)((int)alloc_rx_buf + CFG_CACHELINE_SIZE - + ((int)alloc_rx_buf & CACHELINE_MASK)); + } else { + rx = alloc_rx_buf; + } + + for (i = 0; i < NUM_TX_BUFF; i++) { + tx[i].ctrl = 0; + tx[i].data_len = 0; + if (first_init == 0) + txbuf_ptr = (char *) malloc (ENET_MAX_MTU_ALIGNED); + tx[i].data_ptr = txbuf_ptr; + if ((NUM_TX_BUFF - 1) == i) + tx[i].ctrl |= MAL_TX_CTRL_WRAP; + tx_run[i] = -1; +#if 0 + printf ("TX_BUFF %d @ 0x%08lx\n", i, (ulong) tx[i].data_ptr); +#endif + } + + for (i = 0; i < NUM_RX_BUFF; i++) { + rx[i].ctrl = 0; + rx[i].data_len = 0; + /* rx[i].data_ptr = (char *) &rx_buff[i]; */ + rx[i].data_ptr = (char *) NetRxPackets[i]; + if ((NUM_RX_BUFF - 1) == i) + rx[i].ctrl |= MAL_RX_CTRL_WRAP; + rx[i].ctrl |= MAL_RX_CTRL_EMPTY | MAL_RX_CTRL_INTR; + rx_ready[i] = -1; +#if 0 + printf ("RX_BUFF %d @ 0x%08lx\n", i, (ulong) rx[i].data_ptr); +#endif + } + + memcpy (emac_hwd_addr, bis->bi_enetaddr, ENET_ADDR_LENGTH); + + reg = 0x00000000; + + reg |= emac_hwd_addr[0]; /* set high address */ + reg = reg << 8; + reg |= emac_hwd_addr[1]; + + out32 (EMAC_IAH, reg); + + reg = 0x00000000; + reg |= emac_hwd_addr[2]; /* set low address */ + reg = reg << 8; + reg |= emac_hwd_addr[3]; + reg = reg << 8; + reg |= emac_hwd_addr[4]; + reg = reg << 8; + reg |= emac_hwd_addr[5]; + + out32 (EMAC_IAL, reg); + + /* setup MAL tx & rx channel pointers */ + mtdcr (maltxctp0r, tx); + mtdcr (malrxctp0r, rx); + + /* Reset transmit and receive channels */ + mtdcr (malrxcarr, 0x80000000); /* 2 channels */ + mtdcr (maltxcarr, 0x80000000); /* 2 channels */ + + /* Enable MAL transmit and receive channels */ + mtdcr (maltxcasr, 0x80000000); /* 1 channel */ + mtdcr (malrxcasr, 0x80000000); /* 1 channel */ + + /* set RX buffer size */ + mtdcr (malrcbs0, ENET_MAX_MTU_ALIGNED / 16); + + /* set transmit enable & receive enable */ + out32 (EMAC_M0, EMAC_M0_TXE | EMAC_M0_RXE); + + /* set receive fifo to 4k and tx fifo to 2k */ + mode_reg = EMAC_M1_RFS_4K | EMAC_M1_TX_FIFO_2K; + + /* set speed */ + if (speed == _100BASET) + mode_reg = mode_reg | EMAC_M1_MF_100MBPS | EMAC_M1_IST; + else + mode_reg = mode_reg & ~0x00C00000; /* 10 MBPS */ + if (duplex == FULL) + mode_reg = mode_reg | 0x80000000 | EMAC_M1_IST; + + out32 (EMAC_M1, mode_reg); + + /* Enable broadcast and indvidual address */ + out32 (EMAC_RXM, EMAC_RMR_BAE | EMAC_RMR_IAE + /*| EMAC_RMR_ARRP| EMAC_RMR_SFCS | EMAC_RMR_SP */ ); + + /* we probably need to set the tx mode1 reg? maybe at tx time */ + + /* set transmit request threshold register */ + out32 (EMAC_TRTR, 0x18000000); /* 256 byte threshold */ + + /* set receive low/high water mark register */ +#if defined(CONFIG_440) + /* 440GP has a 64 byte burst length */ + out32 (EMAC_RX_HI_LO_WMARK, 0x80009000); + out32 (EMAC_TXM1, 0xf8640000); +#else /* CONFIG_440 */ + /* 405s have a 16 byte burst length */ + out32 (EMAC_RX_HI_LO_WMARK, 0x0f002000); +#endif /* CONFIG_440 */ + + /* Frame gap set */ + out32 (EMAC_I_FRAME_GAP_REG, 0x00000008); + + if (first_init == 0) { + /* + * Connect interrupt service routines + */ + irq_install_handler (VECNUM_EWU0, (interrupt_handler_t *) enetInt, NULL); + irq_install_handler (VECNUM_MS, (interrupt_handler_t *) enetInt, NULL); + irq_install_handler (VECNUM_MTE, (interrupt_handler_t *) enetInt, NULL); + irq_install_handler (VECNUM_MRE, (interrupt_handler_t *) enetInt, NULL); + irq_install_handler (VECNUM_TXDE, (interrupt_handler_t *) enetInt, NULL); + irq_install_handler (VECNUM_RXDE, (interrupt_handler_t *) enetInt, NULL); + irq_install_handler (VECNUM_ETH0, (interrupt_handler_t *) enetInt, NULL); + } + + /* set up interrupt handler */ + /* setup interrupt controler to take interrupts from the MAL & + EMAC */ + mtdcr (uicsr, 0xffffffff); /* clear pending interrupts */ + mtdcr (uicer, mfdcr (uicer) | MAL_UIC_DEF | EMAC_UIC_DEF); + + /* set the MAL IER ??? names may change with new spec ??? */ + mal_ier = MAL_IER_DE | MAL_IER_NE | MAL_IER_TE | MAL_IER_OPBE | + MAL_IER_PLBE; + mtdcr (malesr, 0xffffffff); /* clear pending interrupts */ + mtdcr (maltxdeir, 0xffffffff); /* clear pending interrupts */ + mtdcr (malrxdeir, 0xffffffff); /* clear pending interrupts */ + mtdcr (malier, mal_ier); + + /* Set EMAC IER */ + emac_ier = EMAC_ISR_PTLE | EMAC_ISR_BFCS | + EMAC_ISR_PTLE | EMAC_ISR_ORE | EMAC_ISR_IRE; + if (speed == _100BASET) + emac_ier = emac_ier | EMAC_ISR_SYE; + + out32 (EMAC_ISR, 0xffffffff); /* clear pending interrupts */ + out32 (EMAC_IER, emac_ier); + + mtmsr (msr); /* enable interrupts again */ + + bis_save = bis; + first_init = 1; + + return (0); +} + + +int eth_send (volatile void *ptr, int len) +{ + struct enet_frame *ef_ptr; + ulong time_start, time_now; + unsigned long temp_txm0; + + ef_ptr = (struct enet_frame *) ptr; + + /*-----------------------------------------------------------------------+ + * Copy in our address into the frame. + *-----------------------------------------------------------------------*/ + (void) memcpy (ef_ptr->source_addr, emac_hwd_addr, ENET_ADDR_LENGTH); + + /*-----------------------------------------------------------------------+ + * If frame is too long or too short, modify length. + *-----------------------------------------------------------------------*/ + if (len > ENET_MAX_MTU) + len = ENET_MAX_MTU; + + /* memcpy ((void *) &tx_buff[tx_slot], (const void *) ptr, len); */ + memcpy ((void *) txbuf_ptr, (const void *) ptr, len); + + /*-----------------------------------------------------------------------+ + * set TX Buffer busy, and send it + *-----------------------------------------------------------------------*/ + tx[tx_slot].ctrl = (MAL_TX_CTRL_LAST | + EMAC_TX_CTRL_GFCS | EMAC_TX_CTRL_GP) & + ~(EMAC_TX_CTRL_ISA | EMAC_TX_CTRL_RSA); + if ((NUM_TX_BUFF - 1) == tx_slot) + tx[tx_slot].ctrl |= MAL_TX_CTRL_WRAP; + + tx[tx_slot].data_len = (short) len; + tx[tx_slot].ctrl |= MAL_TX_CTRL_READY; + + __asm__ volatile ("eieio"); + out32 (EMAC_TXM0, in32 (EMAC_TXM0) | EMAC_TXM0_GNP0); +#ifdef INFO_405_ENET + packetSent++; +#endif + + /*-----------------------------------------------------------------------+ + * poll unitl the packet is sent and then make sure it is OK + *-----------------------------------------------------------------------*/ + time_start = get_timer (0); + while (1) { + temp_txm0 = in32 (EMAC_TXM0); + /* loop until either TINT turns on or 3 seconds elapse */ + if ((temp_txm0 & EMAC_TXM0_GNP0) != 0) { + /* transmit is done, so now check for errors + * If there is an error, an interrupt should + * happen when we return + */ + time_now = get_timer (0); + if ((time_now - time_start) > 3000) { + return (-1); + } + } else { + return (0); + } + } +} + + +#if defined(CONFIG_440) +/*-----------------------------------------------------------------------------+ +| EnetInt. +| EnetInt is the interrupt handler. It will determine the +| cause of the interrupt and call the apporpriate servive +| routine. ++-----------------------------------------------------------------------------*/ +int enetInt () +{ + int serviced; + int rc = -1; /* default to not us */ + unsigned long mal_isr; + unsigned long emac_isr = 0; + unsigned long mal_rx_eob; + unsigned long my_uic0msr, my_uic1msr; + + /* enter loop that stays in interrupt code until nothing to service */ + do { + serviced = 0; + + my_uic0msr = mfdcr (uic0msr); + my_uic1msr = mfdcr (uic1msr); + + if (!(my_uic0msr & UIC_MRE) + && !(my_uic1msr & (UIC_ETH0 | UIC_MS | UIC_MTDE | UIC_MRDE))) { + /* not for us */ + return (rc); + } + + /* get and clear controller status interrupts */ + /* look at Mal and EMAC interrupts */ + if ((my_uic0msr & UIC_MRE) + || (my_uic1msr & (UIC_MS | UIC_MTDE | UIC_MRDE))) { + /* we have a MAL interrupt */ + mal_isr = mfdcr (malesr); + /* look for mal error */ + if (my_uic1msr & (UIC_MS | UIC_MTDE | UIC_MRDE)) { + mal_err (mal_isr, my_uic0msr, MAL_UIC_DEF, MAL_UIC_ERR); + serviced = 1; + rc = 0; + } + } + if (UIC_ETH0 & my_uic1msr) { /* look for EMAC errors */ + emac_isr = in32 (EMAC_ISR); + if ((emac_ier & emac_isr) != 0) { + emac_err (emac_isr); + serviced = 1; + rc = 0; + } + } + if ((emac_ier & emac_isr) + || (my_uic1msr & (UIC_MS | UIC_MTDE | UIC_MRDE))) { + mtdcr (uic0sr, UIC_MRE); /* Clear */ + mtdcr (uic1sr, UIC_ETH0 | UIC_MS | UIC_MTDE | UIC_MRDE); /* Clear */ + return (rc); /* we had errors so get out */ + } + + /* handle MAL RX EOB interupt from a receive */ + /* check for EOB on valid channels */ + if (my_uic0msr & UIC_MRE) { + mal_rx_eob = mfdcr (malrxeobisr); + if ((mal_rx_eob & 0x80000000) != 0) { /* call emac routine for channel 0 */ + /* clear EOB + mtdcr(malrxeobisr, mal_rx_eob); */ + enet_rcv (emac_isr); + /* indicate that we serviced an interrupt */ + serviced = 1; + rc = 0; + } + } + mtdcr (uic0sr, UIC_MRE); /* Clear */ + mtdcr (uic1sr, UIC_ETH0 | UIC_MS | UIC_MTDE | UIC_MRDE); /* Clear */ + } while (serviced); + + return (rc); +} +#else /* CONFIG_440 */ +/*-----------------------------------------------------------------------------+ + * EnetInt. + * EnetInt is the interrupt handler. It will determine the + * cause of the interrupt and call the apporpriate servive + * routine. + *-----------------------------------------------------------------------------*/ +int enetInt () +{ + int serviced; + int rc = -1; /* default to not us */ + unsigned long mal_isr; + unsigned long emac_isr = 0; + unsigned long mal_rx_eob; + unsigned long my_uicmsr; + + /* enter loop that stays in interrupt code until nothing to service */ + do { + serviced = 0; + + my_uicmsr = mfdcr (uicmsr); + if ((my_uicmsr & (MAL_UIC_DEF | EMAC_UIC_DEF)) == 0) { /* not for us */ + return (rc); + } + + + /* get and clear controller status interrupts */ + /* look at Mal and EMAC interrupts */ + if ((MAL_UIC_DEF & my_uicmsr) != 0) { /* we have a MAL interrupt */ + mal_isr = mfdcr (malesr); + /* look for mal error */ + if ((my_uicmsr & MAL_UIC_ERR) != 0) { + mal_err (mal_isr, my_uicmsr, MAL_UIC_DEF, MAL_UIC_ERR); + serviced = 1; + rc = 0; + } + } + if ((EMAC_UIC_DEF & my_uicmsr) != 0) { /* look for EMAC errors */ + emac_isr = in32 (EMAC_ISR); + if ((emac_ier & emac_isr) != 0) { + emac_err (emac_isr); + serviced = 1; + rc = 0; + } + } + if (((emac_ier & emac_isr) != 0) | ((MAL_UIC_ERR & my_uicmsr) != 0)) { + mtdcr (uicsr, MAL_UIC_DEF | EMAC_UIC_DEF); /* Clear */ + return (rc); /* we had errors so get out */ + } + + + /* handle MAL RX EOB interupt from a receive */ + /* check for EOB on valid channels */ + if ((my_uicmsr & UIC_MAL_RXEOB) != 0) { + mal_rx_eob = mfdcr (malrxeobisr); + if ((mal_rx_eob & 0x80000000) != 0) { /* call emac routine for channel 0 */ + /* clear EOB + mtdcr(malrxeobisr, mal_rx_eob); */ + enet_rcv (emac_isr); + /* indicate that we serviced an interrupt */ + serviced = 1; + rc = 0; + } + } + mtdcr (uicsr, MAL_UIC_DEF | EMAC_UIC_DEF); /* Clear */ + } + while (serviced); + + return (rc); +} +#endif /* CONFIG_440 */ + +/*-----------------------------------------------------------------------------+ + * MAL Error Routine + *-----------------------------------------------------------------------------*/ +void mal_err (unsigned long isr, unsigned long uic, unsigned long maldef, + unsigned long mal_errr) +{ + mtdcr (malesr, isr); /* clear interrupt */ + + /* clear DE interrupt */ + mtdcr (maltxdeir, 0xC0000000); + mtdcr (malrxdeir, 0x80000000); + +#if 1 /*sr */ + printf ("\nMAL error occured.... ISR = %lx UIC = = %lx MAL_DEF = %lx MAL_ERR= %lx \n", + isr, uic, maldef, mal_errr); +#else +#if 0 + /* + * MAL error is RX DE error (out of rx buffers)! This is OK here, upon + * many incoming packets with only 4 rx buffers. + */ + printf ("M"); /* just to see something upon mal error */ +#endif +#endif /*sr */ + + eth_init (bis_save); /* start again... */ +} + +/*-----------------------------------------------------------------------------+ + * EMAC Error Routine + *-----------------------------------------------------------------------------*/ +void emac_err (unsigned long isr) +{ + printf ("EMAC error occured.... ISR = %lx\n", isr); + out32 (EMAC_ISR, isr); +} + +/*-----------------------------------------------------------------------------+ + * enet_rcv() handles the ethernet receive data + *-----------------------------------------------------------------------------*/ +static void enet_rcv (unsigned long malisr) +{ + struct enet_frame *ef_ptr; + unsigned long data_len; + unsigned long rx_eob_isr; + + int handled = 0; + int i; + int loop_count = 0; + + rx_eob_isr = mfdcr (malrxeobisr); + if ((0x80000000 >> (EMAC_RXCHL - 1)) & rx_eob_isr) { + /* clear EOB */ + mtdcr (malrxeobisr, rx_eob_isr); + + /* EMAC RX done */ + while (1) { /* do all */ + i = rx_slot; + + if ((MAL_RX_CTRL_EMPTY & rx[i].ctrl) + || (loop_count >= NUM_RX_BUFF)) + break; + loop_count++; + rx_slot++; + if (NUM_RX_BUFF == rx_slot) + rx_slot = 0; + handled++; + data_len = (unsigned long) rx[i].data_len; /* Get len */ + if (data_len) { + if (data_len > ENET_MAX_MTU) /* Check len */ + data_len = 0; + else { + if (EMAC_RX_ERRORS & rx[i].ctrl) { /* Check Errors */ + data_len = 0; + stats.rx_err_log[rx_err_index] = rx[i].ctrl; + rx_err_index++; + if (rx_err_index == MAX_ERR_LOG) + rx_err_index = 0; + } /* emac_erros */ + } /* data_len < max mtu */ + } /* if data_len */ + if (!data_len) { /* no data */ + rx[i].ctrl |= MAL_RX_CTRL_EMPTY; /* Free Recv Buffer */ + + stats.emac.data_len_err++; /* Error at Rx */ + } + + /* !data_len */ + /* AS.HARNOIS */ + /* Check if user has already eaten buffer */ + /* if not => ERROR */ + else if (rx_ready[rx_i_index] != -1) { + if (is_receiving) + printf ("ERROR : Receive buffers are full!\n"); + break; + } else { + stats.emac.rx_frames++; + stats.emac.rx += data_len; + ef_ptr = (struct enet_frame *) rx[i].data_ptr; +#ifdef INFO_405_ENET + packetReceived++; +#endif + /* AS.HARNOIS + * use ring buffer + */ + rx_ready[rx_i_index] = i; + rx_i_index++; + if (NUM_RX_BUFF == rx_i_index) + rx_i_index = 0; + + /* printf("X"); /|* test-only *|/ */ + + /* AS.HARNOIS + * free receive buffer only when + * buffer has been handled (eth_rx) + rx[i].ctrl |= MAL_RX_CTRL_EMPTY; + */ + } /* if data_len */ + } /* while */ + } /* if EMACK_RXCHL */ +} + + +int eth_rx (void) +{ + int length; + int user_index; + unsigned long msr; + + is_receiving = 1; /* tell driver */ + + for (;;) { + /* AS.HARNOIS + * use ring buffer and + * get index from rx buffer desciptor queue + */ + user_index = rx_ready[rx_u_index]; + if (user_index == -1) { + length = -1; + break; /* nothing received - leave for() loop */ + } + + msr = mfmsr (); + mtmsr (msr & ~(MSR_EE)); + + length = rx[user_index].data_len; + + /* Pass the packet up to the protocol layers. */ + /* NetReceive(NetRxPackets[rxIdx], length - 4); */ + /* NetReceive(NetRxPackets[i], length); */ + NetReceive (NetRxPackets[user_index], length - 4); + /* Free Recv Buffer */ + rx[user_index].ctrl |= MAL_RX_CTRL_EMPTY; + /* Free rx buffer descriptor queue */ + rx_ready[rx_u_index] = -1; + rx_u_index++; + if (NUM_RX_BUFF == rx_u_index) + rx_u_index = 0; + +#ifdef INFO_405_ENET + packetHandled++; +#endif + + mtmsr (msr); /* Enable IRQ's */ + } + + is_receiving = 0; /* tell driver */ + + return length; +} + +#endif /* CONFIG_405GP */ diff --git a/cpu/ppc4xx/405gp_pci.c b/cpu/ppc4xx/405gp_pci.c new file mode 100644 index 0000000000..1c2c59b9bd --- /dev/null +++ b/cpu/ppc4xx/405gp_pci.c @@ -0,0 +1,502 @@ +/*-----------------------------------------------------------------------------+ + * + * This source code has been made available to you by IBM on an AS-IS + * basis. Anyone receiving this source is licensed under IBM + * copyrights to use it in any way he or she deems fit, including + * copying it, modifying it, compiling it, and redistributing it either + * with or without modifications. No license under IBM patents or + * patent applications is to be implied by the copyright license. + * + * Any user of this software should understand that IBM cannot provide + * technical support for this software and will not be responsible for + * any consequences resulting from the use of this software. + * + * Any person who transfers this source code or any derivative work + * must include the IBM copyright notice, this paragraph, and the + * preceding two paragraphs in the transferred software. + * + * COPYRIGHT I B M CORPORATION 1995 + * LICENSED MATERIAL - PROGRAM PROPERTY OF I B M + *-----------------------------------------------------------------------------*/ +/*----------------------------------------------------------------------------+ + * + * File Name: 405gp_pci.c + * + * Function: Initialization code for the 405GP PCI Configuration regs. + * + * Author: Mark Game + * + * Change Activity- + * + * Date Description of Change BY + * --------- --------------------- --- + * 09-Sep-98 Created MCG + * 02-Nov-98 Removed External arbiter selected message JWB + * 27-Nov-98 Zero out PTMBAR2 and disable in PTM2MS JWB + * 04-Jan-99 Zero out other unused PMM and PTM regs. Change bus scan MCG + * from (0 to n) to (1 to n). + * 17-May-99 Port to Walnut JWB + * 17-Jun-99 Updated for VGA support JWB + * 21-Jun-99 Updated to allow SRAM region to be a target from PCI bus JWB + * 19-Jul-99 Updated for 405GP pass 1 errata #26 (Low PCI subsequent MCG + * target latency timer values are not supported). + * Should be fixed in pass 2. + * 09-Sep-99 Removed use of PTM2 since the SRAM region no longer needs JWB + * to be a PCI target. Zero out PTMBAR2 and disable in PTM2MS. + * 10-Dec-99 Updated PCI_Write_CFG_Reg for pass2 errata #6 JWB + * 11-Jan-00 Ensure PMMxMAs disabled before setting PMMxLAs. This is not + * really required after a reset since PMMxMAs are already + * disabled but is a good practice nonetheless. JWB + * 12-Jun-01 stefan.roese@esd-electronics.com + * - PCI host/adapter handling reworked + * 09-Jul-01 stefan.roese@esd-electronics.com + * - PCI host now configures from device 0 (not 1) to max_dev, + * (host configures itself) + * - On CPCI-405 pci base address and size is generated from + * SDRAM and FLASH size (CFG regs not used anymore) + * - Some minor changes for CPCI-405-A (adapter version) + * 14-Sep-01 stefan.roese@esd-electronics.com + * - CONFIG_PCI_SCAN_SHOW added to print pci devices upon startup + * 28-Sep-01 stefan.roese@esd-electronics.com + * - Changed pci master configuration for linux compatibility + * (no need for bios_fixup() anymore) + * 26-Feb-02 stefan.roese@esd-electronics.com + * - Bug fixed in pci configuration (Andrew May) + * - Removed pci class code init for CPCI405 board + * 15-May-02 stefan.roese@esd-electronics.com + * - New vga device handling + * 29-May-02 stefan.roese@esd-electronics.com + * - PCI class code init added (if defined) + *----------------------------------------------------------------------------*/ + +#include <common.h> +#include <command.h> +#include <cmd_boot.h> +#if !defined(CONFIG_440) +#include <405gp_pci.h> +#endif +#include <asm/processor.h> +#include <pci.h> + +#if defined(CONFIG_405GP) + +#ifdef CONFIG_PCI + +/*#define DEBUG*/ + +/*-----------------------------------------------------------------------------+ + * pci_init. Initializes the 405GP PCI Configuration regs. + *-----------------------------------------------------------------------------*/ +void pci_405gp_init(struct pci_controller *hose) +{ + DECLARE_GLOBAL_DATA_PTR; + + int i, reg_num = 0; + bd_t *bd = gd->bd; + + unsigned short temp_short; + unsigned long ptmpcila[2] = {CFG_PCI_PTM1PCI, CFG_PCI_PTM2PCI}; +#if defined(CONFIG_CPCI405) + unsigned long ptmla[2] = {bd->bi_memstart, bd->bi_flashstart}; + unsigned long ptmms[2] = {~(bd->bi_memsize - 1) | 1, ~(bd->bi_flashsize - 1) | 1}; +#else + unsigned long ptmla[2] = {CFG_PCI_PTM1LA, CFG_PCI_PTM2LA}; + unsigned long ptmms[2] = {CFG_PCI_PTM1MS, CFG_PCI_PTM2MS}; +#endif +#if defined(CONFIG_PIP405) || defined (CONFIG_MIP405) + unsigned long pmmla[3] = {0x80000000, 0xA0000000, 0}; + unsigned long pmmma[3] = {0xE0000001, 0xE0000001, 0}; + unsigned long pmmpcila[3] = {0x80000000, 0x00000000, 0}; + unsigned long pmmpciha[3] = {0x00000000, 0x00000000, 0}; +#else + unsigned long pmmla[3] = {0x80000000, 0,0}; + unsigned long pmmma[3] = {0xC0000001, 0,0}; + unsigned long pmmpcila[3] = {0x80000000, 0,0}; + unsigned long pmmpciha[3] = {0x00000000, 0,0}; +#endif + + /* + * Register the hose + */ + hose->first_busno = 0; + hose->last_busno = 0xff; + + /* ISA/PCI I/O space */ + pci_set_region(hose->regions + reg_num++, + MIN_PCI_PCI_IOADDR, + MIN_PLB_PCI_IOADDR, + 0x10000, + PCI_REGION_IO); + + /* PCI I/O space */ + pci_set_region(hose->regions + reg_num++, + 0x00800000, + 0xe8800000, + 0x03800000, + PCI_REGION_IO); + + reg_num = 2; + + /* Memory spaces */ + for (i=0; i<2; i++) + if (ptmms[i] & 1) + { + if (!i) hose->pci_fb = hose->regions + reg_num; + + pci_set_region(hose->regions + reg_num++, + ptmpcila[i], ptmla[i], + ~(ptmms[i] & 0xfffff000) + 1, + PCI_REGION_MEM | + PCI_REGION_MEMORY); + } + + /* PCI memory spaces */ + for (i=0; i<3; i++) + if (pmmma[i] & 1) + { + pci_set_region(hose->regions + reg_num++, + pmmpcila[i], pmmla[i], + ~(pmmma[i] & 0xfffff000) + 1, + PCI_REGION_MEM); + } + + hose->region_count = reg_num; + + pci_setup_indirect(hose, + PCICFGADR, + PCICFGDATA); + + if (hose->pci_fb) + pciauto_region_init(hose->pci_fb); + + pci_register_hose(hose); + + /*--------------------------------------------------------------------------+ + * 405GP PCI Master configuration. + * Map one 512 MB range of PLB/processor addresses to PCI memory space. + * PLB address 0x80000000-0xBFFFFFFF ==> PCI address 0x80000000-0xBFFFFFFF + * Use byte reversed out routines to handle endianess. + *--------------------------------------------------------------------------*/ + out32r(PMM0MA, pmmma[0]); /* ensure disabled b4 setting PMM0LA */ + out32r(PMM0LA, pmmla[0]); + out32r(PMM0PCILA, pmmpcila[0]); + out32r(PMM0PCIHA, pmmpciha[0]); + out32r(PMM0MA, pmmma[0]); + + /*--------------------------------------------------------------------------+ + * PMM1 is not used. Initialize them to zero. + *--------------------------------------------------------------------------*/ + out32r(PMM1MA, pmmma[1]); /* ensure disabled b4 setting PMM2LA */ + out32r(PMM1LA, pmmla[1]); + out32r(PMM1PCILA, pmmpcila[1]); + out32r(PMM1PCIHA, pmmpciha[1]); + out32r(PMM1MA, pmmma[1]); + + /*--------------------------------------------------------------------------+ + * PMM2 is not used. Initialize them to zero. + *--------------------------------------------------------------------------*/ + out32r(PMM2MA, pmmma[2]); /* ensure disabled b4 setting PMM2LA */ + out32r(PMM2LA, pmmla[2]); + out32r(PMM2PCILA, pmmpcila[2]); + out32r(PMM2PCIHA, pmmpciha[2]); + out32r(PMM2MA, pmmma[2]); + + /*--------------------------------------------------------------------------+ + * 405GP PCI Target configuration. (PTM1) + * Note: PTM1MS is hardwire enabled but we set the enable bit anyway. + *--------------------------------------------------------------------------*/ + out32r(PTM1LA, ptmla[0]); /* insert address */ + out32r(PTM1MS, ptmms[0]); /* insert size, enable bit is 1 */ + + /*--------------------------------------------------------------------------+ + * 405GP PCI Target configuration. (PTM2) + *--------------------------------------------------------------------------*/ + out32r(PTM2LA, ptmla[1]); /* insert address */ + if (ptmms[1] == 0) + { + out32r(PTM2MS, 0x00000001); /* set enable bit */ + pci_write_config_dword(PCIDEVID_405GP, PCI_BASE_ADDRESS_2, 0x00000000); + out32r(PTM2MS, 0x00000000); /* disable */ + } + else + { + out32r(PTM2MS, ptmms[1]); /* insert size, enable bit is 1 */ + } + + /* + * Insert Subsystem Vendor and Device ID + */ + pci_write_config_word(PCIDEVID_405GP, PCI_SUBSYSTEM_VENDOR_ID, CFG_PCI_SUBSYS_VENDORID); +#ifdef CONFIG_CPCI405 + if (mfdcr(strap) & PSR_PCI_ARBIT_EN) + pci_write_config_word(PCIDEVID_405GP, PCI_SUBSYSTEM_ID, CFG_PCI_SUBSYS_DEVICEID); + else + pci_write_config_word(PCIDEVID_405GP, PCI_SUBSYSTEM_ID, CFG_PCI_SUBSYS_DEVICEID2); +#else + pci_write_config_word(PCIDEVID_405GP, PCI_SUBSYSTEM_ID, CFG_PCI_SUBSYS_DEVICEID); +#endif + + /* + * Insert Class-code + */ +#ifdef CFG_PCI_CLASSCODE + pci_write_config_word(PCIDEVID_405GP, PCI_CLASS_SUB_CODE, CFG_PCI_CLASSCODE); +#endif /* CFG_PCI_CLASSCODE */ + + /*--------------------------------------------------------------------------+ + * If PCI speed = 66Mhz, set 66Mhz capable bit. + *--------------------------------------------------------------------------*/ + if (bd->bi_pci_busfreq >= 66000000) { + pci_read_config_word(PCIDEVID_405GP, PCI_STATUS, &temp_short); + pci_write_config_word(PCIDEVID_405GP,PCI_STATUS,(temp_short|PCI_STATUS_66MHZ)); + } + +#if (CONFIG_PCI_HOST != PCI_HOST_ADAPTER) +#if (CONFIG_PCI_HOSE == PCI_HOST_AUTO) + if (mfdcr(strap) & PSR_PCI_ARBIT_EN) +#endif + { + /*--------------------------------------------------------------------------+ + * Write the 405GP PCI Configuration regs. + * Enable 405GP to be a master on the PCI bus (PMM). + * Enable 405GP to act as a PCI memory target (PTM). + *--------------------------------------------------------------------------*/ + pci_read_config_word(PCIDEVID_405GP, PCI_COMMAND, &temp_short); + pci_write_config_word(PCIDEVID_405GP, PCI_COMMAND, temp_short | + PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY); + } +#endif + + /* + * Set HCE bit (Host Configuration Enabled) + */ + pci_read_config_word(PCIDEVID_405GP, PCIBRDGOPT2, &temp_short); + pci_write_config_word(PCIDEVID_405GP, PCIBRDGOPT2, (temp_short | 0x0001)); + +#ifdef CONFIG_PCI_PNP + /*--------------------------------------------------------------------------+ + * Scan the PCI bus and configure devices found. + *--------------------------------------------------------------------------*/ +#if (CONFIG_PCI_HOST == PCI_HOST_AUTO) + if (mfdcr(strap) & PSR_PCI_ARBIT_EN) +#endif + { +#ifdef CONFIG_PCI_SCAN_SHOW + printf("PCI: Bus Dev VenId DevId Class Int\n"); +#endif + + hose->last_busno = pci_hose_scan(hose); + } +#endif /* CONFIG_PCI_PNP */ + +} + +/* + * drivers/pci.c skips every host bridge but the 405GP since it could + * be set as an Adapter. + * + * I (Andrew May) don't know what we should do here, but I don't want + * the auto setup of a PCI device disabling what is done pci_405gp_init + * as has happened before. + */ +void pci_405gp_setup_bridge(struct pci_controller *hose, pci_dev_t dev, + struct pci_config_table *entry) +{ +#ifdef DEBUG + printf("405gp_setup_bridge\n"); +#endif +} + +/* + * + */ + +void pci_405gp_fixup_irq(struct pci_controller *hose, pci_dev_t dev) +{ + unsigned char int_line = 0xff; + + /* + * Write pci interrupt line register (cpci405 specific) + */ + switch (PCI_DEV(dev) & 0x03) + { + case 0: + int_line = 27 + 2; + break; + case 1: + int_line = 27 + 3; + break; + case 2: + int_line = 27 + 0; + break; + case 3: + int_line = 27 + 1; + break; + } + + pci_hose_write_config_byte(hose, dev, PCI_INTERRUPT_LINE, int_line); +} + +void pci_405gp_setup_vga(struct pci_controller *hose, pci_dev_t dev, + struct pci_config_table *entry) +{ + unsigned int cmdstat = 0; + + pciauto_setup_device(hose, dev, 6, hose->pci_mem, hose->pci_io); + + /* always enable io space on vga boards */ + pci_hose_read_config_dword(hose, dev, PCI_COMMAND, &cmdstat); + cmdstat |= PCI_COMMAND_IO; + pci_hose_write_config_dword(hose, dev, PCI_COMMAND, cmdstat); +} + +#if !(defined(CONFIG_PIP405) || defined (CONFIG_MIP405)) + +/* + *As is these functs get called out of flash Not a horrible + *thing, but something to keep in mind. (no statics?) + */ +static struct pci_config_table pci_405gp_config_table[] = { +/*if VendID is 0 it terminates the table search (ie Walnut)*/ +#if CFG_PCI_SUBSYS_VENDORID + {CFG_PCI_SUBSYS_VENDORID, PCI_ANY_ID, PCI_CLASS_BRIDGE_HOST, + PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, pci_405gp_setup_bridge}, +#endif + {PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_DISPLAY_VGA, + PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, pci_405gp_setup_vga}, + + {PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NOT_DEFINED_VGA, + PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, pci_405gp_setup_vga}, + + { } +}; + +static struct pci_controller hose = { + fixup_irq: pci_405gp_fixup_irq, + config_table: pci_405gp_config_table, +}; + +void pci_init(void) +{ + /*we want the ptrs to RAM not flash (ie don't use init list)*/ + hose.fixup_irq = pci_405gp_fixup_irq; + hose.config_table = pci_405gp_config_table; + pci_405gp_init(&hose); +} + +#endif + +#endif /* CONFIG_PCI */ + +#endif /* CONFIG_405GP */ + +/*-----------------------------------------------------------------------------+ + * CONFIG_440 + *-----------------------------------------------------------------------------*/ +#if defined(CONFIG_440) && defined(CONFIG_PCI) + +static struct pci_controller ppc440_hose = {0}; + + +void pci_440_init (struct pci_controller *hose) +{ + int reg_num = 0; + unsigned long strap; + + /*--------------------------------------------------------------------------+ + * The PCI initialization sequence enable bit must be set ... if not abort + * pci setup since updating the bit requires chip reset. + *--------------------------------------------------------------------------*/ + strap = mfdcr(cpc0_strp1); + if( (strap & 0x00040000) == 0 ){ + printf("PCI: CPC0_STRP1[PISE] not set.\n"); + printf("PCI: Configuration aborted.\n"); + return; + } + + /*--------------------------------------------------------------------------+ + * PCI controller init + *--------------------------------------------------------------------------*/ + hose->first_busno = 0; + hose->last_busno = 0xff; + + pci_set_region(hose->regions + reg_num++, + 0x00000000, + PCIX0_IOBASE, + 0x10000, + PCI_REGION_IO); + + pci_set_region(hose->regions + reg_num++, + CFG_PCI_TARGBASE, + CFG_PCI_MEMBASE, + 0x10000000, + PCI_REGION_MEM ); + hose->region_count = reg_num; + + pci_setup_indirect(hose, PCIX0_CFGADR, PCIX0_CFGDATA); + +#if defined(CFG_PCI_PRE_INIT) + /* Let board change/modify hose & do initial checks */ + if( pci_pre_init (hose) == 0 ){ + printf("PCI: Board-specific initialization failed.\n"); + printf("PCI: Configuration aborted.\n"); + return; + } +#endif + + pci_register_hose( hose ); + + /*--------------------------------------------------------------------------+ + * PCI target init + *--------------------------------------------------------------------------*/ +#if defined(CFG_PCI_TARGET_INIT) + pci_target_init(hose); /* Let board setup pci target */ +#else + out16r( PCIX0_SBSYSVID, CFG_PCI_SUBSYS_VENDORID ); + out16r( PCIX0_SBSYSID, CFG_PCI_SUBSYS_ID ); + out16r( PCIX0_CLS, 0x00060000 ); /* Bridge, host bridge */ +#endif + + out32r( PCIX0_BRDGOPT1, 0x10000060 ); /* PLB Rq pri highest */ + out32r( PCIX0_BRDGOPT2, in32(PCIX0_BRDGOPT2) | 1 ); /* Enable host config */ + + /*--------------------------------------------------------------------------+ + * PCI master init: default is one 256MB region for PCI memory: + * 0x3_00000000 - 0x3_0FFFFFFF ==> CFG_PCI_MEMBASE + *--------------------------------------------------------------------------*/ +#if defined(CFG_PCI_MASTER_INIT) + pci_master_init(hose); /* Let board setup pci master */ +#else + out32r( PCIX0_POM0SA, 0 ); /* disable */ + out32r( PCIX0_POM1SA, 0 ); /* disable */ + out32r( PCIX0_POM2SA, 0 ); /* disable */ + out32r( PCIX0_POM0LAL, 0x00000000 ); + out32r( PCIX0_POM0LAH, 0x00000003 ); + out32r( PCIX0_POM0PCIAL, CFG_PCI_MEMBASE ); + out32r( PCIX0_POM0PCIAH, 0x00000000 ); + out32r( PCIX0_POM0SA, 0xf0000001 ); /* 256MB, enabled */ + out32r( PCIX0_STS, in32r( PCIX0_STS ) & ~0x0000fff8 ); +#endif + + /*--------------------------------------------------------------------------+ + * PCI host configuration -- we don't make any assumptions here ... the + * _board_must_indicate_ what to do -- there's just too many runtime + * scenarios in environments like cPCI, PPMC, etc. to make a determination + * based on hard-coded values or state of arbiter enable. + *--------------------------------------------------------------------------*/ + if( is_pci_host(hose) ){ +#ifdef CONFIG_PCI_SCAN_SHOW + printf("PCI: Bus Dev VenId DevId Class Int\n"); +#endif + out16r( PCIX0_CMD, in16r( PCIX0_CMD ) | PCI_COMMAND_MASTER); + hose->last_busno = pci_hose_scan(hose); + } +} + + +void pci_init(void) +{ + pci_440_init (&ppc440_hose); +} + +#endif /* CONFIG_440 & CONFIG_PCI */ diff --git a/cpu/ppc4xx/cpu.c b/cpu/ppc4xx/cpu.c new file mode 100644 index 0000000000..8aaffb1c19 --- /dev/null +++ b/cpu/ppc4xx/cpu.c @@ -0,0 +1,253 @@ +/* + * (C) Copyright 2000 + * 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 + */ + +/* + * m8xx.c + * + * CPU specific code + * + * written or collected and sometimes rewritten by + * Magnus Damm <damm@bitsmart.com> + * + * minor modifications by + * Wolfgang Denk <wd@denx.de> + */ + +#include <common.h> +#include <watchdog.h> +#include <command.h> +#include <asm/cache.h> +#include <ppc4xx.h> + + +#if defined(CONFIG_440) +static int do_chip_reset( unsigned long sys0, unsigned long sys1 ); +#endif + +/* ------------------------------------------------------------------------- */ + +int checkcpu (void) +{ +#if defined(CONFIG_405GP) || defined(CONFIG_405CR) || defined(CONFIG_IOP480) || defined(CONFIG_440) + uint pvr = get_pvr(); +#endif +#if defined(CONFIG_405GP) || defined(CONFIG_405CR) || defined(CONFIG_IOP480) + DECLARE_GLOBAL_DATA_PTR; + + ulong clock = gd->cpu_clk; + char buf[32]; +#endif + +#if defined(CONFIG_405GP) || defined(CONFIG_405CR) + PPC405_SYS_INFO sys_info; + + puts ("CPU: "); + + get_sys_info(&sys_info); + +#if CONFIG_405GP + puts("IBM PowerPC 405GP"); + if (pvr == PVR_405GPR_RA) { + putc('r'); + } + puts(" Rev. "); +#endif +#if CONFIG_405CR + puts("IBM PowerPC 405CR Rev. "); +#endif + switch (pvr) { + case PVR_405GP_RB: + putc('B'); + break; + case PVR_405GP_RC: +#if CONFIG_405CR + case PVR_405CR_RC: +#endif + putc('C'); + break; + case PVR_405GP_RD: + putc('D'); + break; +#if CONFIG_405GP + case PVR_405GP_RE: + putc('E'); + break; +#endif + case PVR_405CR_RA: + case PVR_405GPR_RA: + putc('A'); + break; + case PVR_405CR_RB: + putc('B'); + break; + default: + printf("? (PVR=%08x)", pvr); + break; + } + + printf(" at %s MHz (PLB=%lu, OPB=%lu, EBC=%lu MHz)\n", strmhz(buf, clock), + sys_info.freqPLB / 1000000, + sys_info.freqPLB / sys_info.pllOpbDiv / 1000000, + sys_info.freqPLB / sys_info.pllExtBusDiv / 1000000); + +#if CONFIG_405GP + if (mfdcr(strap) & PSR_PCI_ASYNC_EN) + printf(" PCI async ext clock used, "); + else + printf(" PCI sync clock at %lu MHz, ", + sys_info.freqPLB / sys_info.pllPciDiv / 1000000); + if (mfdcr(strap) & PSR_PCI_ARBIT_EN) + printf("internal PCI arbiter enabled\n"); + else + printf("external PCI arbiter enabled\n"); +#endif + + if ((pvr | 0x00000001) == PVR_405GPR_RA) { + printf(" 16 kB I-Cache 16 kB D-Cache"); + } else { + printf(" 16 kB I-Cache 8 kB D-Cache"); + } + + +#endif /* defined(CONFIG_405GP) || defined(CONFIG_405CR) */ + +#ifdef CONFIG_IOP480 + printf("PLX IOP480 (PVR=%08x)", pvr); + printf(" at %s MHz:", strmhz(buf, clock)); + printf(" %u kB I-Cache", 4); + printf(" %u kB D-Cache", 2); +#endif + +#if defined(CONFIG_440) + puts("IBM PowerPC 440 Rev. "); + switch(pvr) + { + case PVR_440GP_RB: + putc('B'); + /* See errata 1.12: CHIP_4 */ + if( ( mfdcr(cpc0_sys0) != mfdcr(cpc0_strp0) ) + ||( mfdcr(cpc0_sys1) != mfdcr(cpc0_strp1) ) ){ + puts("\n\t CPC0_SYSx DCRs corrupted. Resetting chip ...\n"); + udelay( 1000 * 1000 ); /* Give time for serial buf to clear */ + do_chip_reset( mfdcr(cpc0_strp0), mfdcr(cpc0_strp1) ); + } + break; + case PVR_440GP_RC: + putc('C'); + break; + default: + printf("UNKNOWN (PVR=%08x)", pvr); + break; + } +#endif + + printf("\n"); + + return 0; +} + + +/* ------------------------------------------------------------------------- */ + +int do_reset (cmd_tbl_t *cmdtp, bd_t *bd, int flag, int argc, char *argv[]) +{ + /* + * Initiate system reset in debug control register DBCR + */ + __asm__ __volatile__("lis 3, 0x3000" ::: "r3"); +#if defined(CONFIG_440) + __asm__ __volatile__("mtspr 0x134, 3"); +#else + __asm__ __volatile__("mtspr 0x3f2, 3"); +#endif + return 1; +} + +#if defined(CONFIG_440) +static +int do_chip_reset( unsigned long sys0, unsigned long sys1 ) +{ + /* Changes to cpc0_sys0 and cpc0_sys1 require chip + * reset. + */ + mtdcr( cntrl0, mfdcr(cntrl0) | 0x80000000 ); /* Set SWE */ + mtdcr( cpc0_sys0, sys0 ); + mtdcr( cpc0_sys1, sys1 ); + mtdcr( cntrl0, mfdcr(cntrl0) & ~0x80000000 ); /* Clr SWE */ + mtspr( dbcr0, 0x20000000); /* Reset the chip */ + + return 1; +} +#endif + + +/* + * Get timebase clock frequency + */ +unsigned long get_tbclk (void) +{ +#if defined(CONFIG_440) + + sys_info_t sys_info; + + get_sys_info(&sys_info); + return (sys_info.freqProcessor); + +#elif defined(CONFIG_405GP) || defined(CONFIG_405CR) || defined(CONFIG_405) + + PPC405_SYS_INFO sys_info; + + get_sys_info(&sys_info); + return (sys_info.freqProcessor); + +#elif defined(CONFIG_IOP480) + + return (66000000); + +#else + +# error get_tbclk() not implemented + +#endif + +} + + +#if defined(CONFIG_WATCHDOG) +void +watchdog_reset(void) +{ + int re_enable = disable_interrupts(); + reset_4xx_watchdog(); + if (re_enable) enable_interrupts(); +} + +void +reset_4xx_watchdog(void) +{ + /* + * Clear TSR(WIS) bit + */ + mtspr(tsr, 0x40000000); +} +#endif /* CONFIG_WATCHDOG */ diff --git a/cpu/ppc4xx/i2c.c b/cpu/ppc4xx/i2c.c new file mode 100644 index 0000000000..68af057d1e --- /dev/null +++ b/cpu/ppc4xx/i2c.c @@ -0,0 +1,417 @@ +/*****************************************************************************/ +/* I2C Bus interface initialisation and I2C Commands */ +/* for PPC405GP */ +/* Author : AS HARNOIS */ +/* Date : 13.Dec.00 */ +/*****************************************************************************/ + +#include <common.h> +#include <ppc4xx.h> +#if defined(CONFIG_440) +# include <440_i2c.h> +#else +# include <405gp_i2c.h> +#endif +#include <i2c.h> + +#ifdef CONFIG_HARD_I2C + +#define IIC_OK 0 +#define IIC_NOK 1 +#define IIC_NOK_LA 2 /* Lost arbitration */ +#define IIC_NOK_ICT 3 /* Incomplete transfer */ +#define IIC_NOK_XFRA 4 /* Transfer aborted */ +#define IIC_NOK_DATA 5 /* No data in buffer */ +#define IIC_NOK_TOUT 6 /* Transfer timeout */ + +#define IIC_TIMEOUT 1 /* 1 seconde */ + + +static void _i2c_bus_reset (void) +{ + int i, status; + + /* Reset status register */ + /* write 1 in SCMP and IRQA to clear these fields */ + out8 (IIC_STS, 0x0A); + + /* write 1 in IRQP IRQD LA ICT XFRA to clear these fields */ + out8 (IIC_EXTSTS, 0x8F); + __asm__ volatile ("eieio"); + + /* + * Get current state, reset bus + * only if no transfers are pending. + */ + i = 10; + do { + /* Get status */ + status = in8 (IIC_STS); + udelay (500); /* 500us */ + i--; + } while ((status & IIC_STS_PT) && (i > 0)); + /* Soft reset controller */ + status = in8 (IIC_XTCNTLSS); + out8 (IIC_XTCNTLSS, (status | IIC_XTCNTLSS_SRST)); + __asm__ volatile ("eieio"); + + /* make sure where in initial state, data hi, clock hi */ + out8 (IIC_DIRECTCNTL, 0xC); + for (i = 0; i < 10; i++) { + if ((in8 (IIC_DIRECTCNTL) & 0x3) != 0x3) { + /* clock until we get to known state */ + out8 (IIC_DIRECTCNTL, 0x8); /* clock lo */ + udelay (100); /* 100us */ + out8 (IIC_DIRECTCNTL, 0xC); /* clock hi */ + udelay (100); /* 100us */ + } else { + break; + } + } + /* send start condition */ + out8 (IIC_DIRECTCNTL, 0x4); + udelay (1000); /* 1ms */ + /* send stop condition */ + out8 (IIC_DIRECTCNTL, 0xC); + udelay (1000); /* 1ms */ + /* Unreset controller */ + out8 (IIC_XTCNTLSS, (status & ~IIC_XTCNTLSS_SRST)); + udelay (1000); /* 1ms */ +} + +void i2c_init (int speed, int slaveadd) +{ + sys_info_t sysInfo; + unsigned long freqOPB; + int val, divisor; + + /* Handle possible failed I2C state */ + _i2c_bus_reset (); + + /* clear lo master address */ + out8 (IIC_LMADR, 0); + + /* clear hi master address */ + out8 (IIC_HMADR, 0); + + /* clear lo slave address */ + out8 (IIC_LSADR, 0); + + /* clear hi slave address */ + out8 (IIC_HSADR, 0); + + /* Clock divide Register */ + /* get OPB frequency */ + get_sys_info (&sysInfo); + freqOPB = sysInfo.freqPLB / sysInfo.pllOpbDiv; + /* set divisor according to freqOPB */ + divisor = (freqOPB - 1) / 10000000; + if (divisor == 0) + divisor = 1; + out8 (IIC_CLKDIV, divisor); + + /* no interrupts */ + out8 (IIC_INTRMSK, 0); + + /* clear transfer count */ + out8 (IIC_XFRCNT, 0); + + /* clear extended control & stat */ + /* write 1 in SRC SRS SWC SWS to clear these fields */ + out8 (IIC_XTCNTLSS, 0xF0); + + /* Mode Control Register + Flush Slave/Master data buffer */ + out8 (IIC_MDCNTL, IIC_MDCNTL_FSDB | IIC_MDCNTL_FMDB); + __asm__ volatile ("eieio"); + + + val = in8(IIC_MDCNTL); + __asm__ volatile ("eieio"); + + /* Ignore General Call, slave transfers are ignored, + disable interrupts, exit unknown bus state, enable hold + SCL + 100kHz normaly or FastMode for 400kHz and above + */ + + val |= IIC_MDCNTL_EUBS|IIC_MDCNTL_HSCL; + if( speed >= 400000 ){ + val |= IIC_MDCNTL_FSM; + } + out8 (IIC_MDCNTL, val); + + /* clear control reg */ + out8 (IIC_CNTL, 0x00); + __asm__ volatile ("eieio"); + +} + +/* + This code tries to use the features of the 405GP i2c + controller. It will transfer up to 4 bytes in one pass + on the loop. It only does out8(lbz) to the buffer when it + is possible to do out16(lhz) transfers. + + cmd_type is 0 for write 1 for read. + + addr_len can take any value from 0-255, it is only limited + by the char, we could make it larger if needed. If it is + 0 we skip the address write cycle. + + Typical case is a Write of an addr followd by a Read. The + IBM FAQ does not cover this. On the last byte of the write + we don't set the creg CHT bit, and on the first bytes of the + read we set the RPST bit. + + It does not support address only transfers, there must be + a data part. If you want to write the address yourself, put + it in the data pointer. + + It does not support transfer to/from address 0. + + It does not check XFRCNT. +*/ +static +int i2c_transfer(unsigned char cmd_type, + unsigned char chip, + unsigned char addr[], + unsigned char addr_len, + unsigned char data[], + unsigned short data_len ) +{ + unsigned char* ptr; + int reading; + int tran,cnt; + int result; + int status; + int i; + uchar creg; + + if( data == 0 || data_len == 0 ){ + /*Don't support data transfer of no length or to address 0*/ + printf( "i2c_transfer: bad call\n" ); + return IIC_NOK; + } + if( addr && addr_len ){ + ptr = addr; + cnt = addr_len; + reading = 0; + }else{ + ptr = data; + cnt = data_len; + reading = cmd_type; + } + + /*Clear Stop Complete Bit*/ + out8(IIC_STS,IIC_STS_SCMP); + /* Check init */ + i=10; + do { + /* Get status */ + status = in8(IIC_STS); + __asm__ volatile("eieio"); + i--; + } while ((status & IIC_STS_PT) && (i>0)); + + if (status & IIC_STS_PT) { + result = IIC_NOK_TOUT; + return(result); + } + /*flush the Master/Slave Databuffers*/ + out8(IIC_MDCNTL, ((in8(IIC_MDCNTL))|IIC_MDCNTL_FMDB|IIC_MDCNTL_FSDB)); + /*need to wait 4 OPB clocks? code below should take that long*/ + + /* 7-bit adressing */ + out8(IIC_HMADR,0); + out8(IIC_LMADR, chip); + __asm__ volatile("eieio"); + + tran = 0; + result = IIC_OK; + creg = 0; + + while ( tran != cnt && (result == IIC_OK)) { + int bc,j; + + /* Control register = + Normal transfer, 7-bits adressing, Transfer up to bc bytes, Normal start, + Transfer is a sequence of transfers + */ + creg |= IIC_CNTL_PT; + + bc = (cnt - tran) > 4 ? 4 : + cnt - tran; + creg |= (bc-1)<<4; + /* if the real cmd type is write continue trans*/ + if ( (!cmd_type && (ptr == addr)) || ((tran+bc) != cnt) ) + creg |= IIC_CNTL_CHT; + + if (reading) + creg |= IIC_CNTL_READ; + else { + for(j=0; j<bc; j++) { + /* Set buffer */ + out8(IIC_MDBUF,ptr[tran+j]); + __asm__ volatile("eieio"); + } + } + out8(IIC_CNTL, creg ); + __asm__ volatile("eieio"); + + /* Transfer is in progress + we have to wait for upto 5 bytes of data + 1 byte chip address+r/w bit then bc bytes + of data. + udelay(10) is 1 bit time at 100khz + Doubled for slop. 20 is too small. + */ + i=2*5*8; + do { + /* Get status */ + status = in8(IIC_STS); + __asm__ volatile("eieio"); + udelay (10); + i--; + } while ((status & IIC_STS_PT) && !(status & IIC_STS_ERR) + && (i>0)); + + if (status & IIC_STS_ERR) { + result = IIC_NOK; + status = in8 (IIC_EXTSTS); + /* Lost arbitration? */ + if (status & IIC_EXTSTS_LA) + result = IIC_NOK_LA; + /* Incomplete transfer? */ + if (status & IIC_EXTSTS_ICT) + result = IIC_NOK_ICT; + /* Transfer aborted? */ + if (status & IIC_EXTSTS_XFRA) + result = IIC_NOK_XFRA; + } else if ( status & IIC_STS_PT) { + result = IIC_NOK_TOUT; + } + /* Command is reading => get buffer */ + if ((reading) && (result == IIC_OK)) { + /* Are there data in buffer */ + if (status & IIC_STS_MDBS) { + /* + even if we have data we have to wait 4OPB clocks + for it to hit the front of the FIFO, after that + we can just read. We should check XFCNT here and + if the FIFO is full there is no need to wait. + */ + udelay (1); + for(j=0;j<bc;j++) { + ptr[tran+j] = in8(IIC_MDBUF); + __asm__ volatile("eieio"); + } + } else + result = IIC_NOK_DATA; + } + creg = 0; + tran+=bc; + if( ptr == addr && tran == cnt ) { + ptr = data; + cnt = data_len; + tran = 0; + reading = cmd_type; + if( reading ) + creg = IIC_CNTL_RPST; + } + } + return (result); +} + +int i2c_probe (uchar chip) +{ + uchar buf[1]; + + buf[0] = 0; + + /* + * What is needed is to send the chip address and verify that the + * address was <ACK>ed (i.e. there was a chip at that address which + * drove the data line low). + */ + return(i2c_transfer (1, chip << 1, 0,0, buf, 1) != 0); +} + + + +int i2c_read (uchar chip, uint addr, int alen, uchar * buffer, int len) +{ + uchar xaddr[4]; + int ret; + + if ( alen > 4 ) { + printf ("I2C read: addr len %d not supported\n", alen); + return 1; + } + + if ( alen > 0 ) { + xaddr[0] = (addr >> 24) & 0xFF; + xaddr[1] = (addr >> 16) & 0xFF; + xaddr[2] = (addr >> 8) & 0xFF; + xaddr[3] = addr & 0xFF; + } + + +#ifdef CFG_I2C_EEPROM_ADDR_OVERFLOW + /* + * EEPROM chips that implement "address overflow" are ones + * like Catalyst 24WC04/08/16 which has 9/10/11 bits of + * address and the extra bits end up in the "chip address" + * bit slots. This makes a 24WC08 (1Kbyte) chip look like + * four 256 byte chips. + * + * Note that we consider the length of the address field to + * still be one byte because the extra address bits are + * hidden in the chip address. + */ + if( alen > 0 ) + chip |= ((addr >> (alen * 8)) & CFG_I2C_EEPROM_ADDR_OVERFLOW); +#endif + if( (ret = i2c_transfer( 1, chip<<1, &xaddr[4-alen], alen, buffer, len )) != 0) { + printf( "I2c read: failed %d\n", ret); + return 1; + } + return 0; +} + +int i2c_write (uchar chip, uint addr, int alen, uchar * buffer, int len) +{ + uchar xaddr[4]; + + if ( alen > 4 ) { + printf ("I2C write: addr len %d not supported\n", alen); + return 1; + + } + if ( alen > 0 ) { + xaddr[0] = (addr >> 24) & 0xFF; + xaddr[1] = (addr >> 16) & 0xFF; + xaddr[2] = (addr >> 8) & 0xFF; + xaddr[3] = addr & 0xFF; + } + +#ifdef CFG_I2C_EEPROM_ADDR_OVERFLOW + /* + * EEPROM chips that implement "address overflow" are ones + * like Catalyst 24WC04/08/16 which has 9/10/11 bits of + * address and the extra bits end up in the "chip address" + * bit slots. This makes a 24WC08 (1Kbyte) chip look like + * four 256 byte chips. + * + * Note that we consider the length of the address field to + * still be one byte because the extra address bits are + * hidden in the chip address. + */ + if( alen > 0 ) + chip |= ((addr >> (alen * 8)) & CFG_I2C_EEPROM_ADDR_OVERFLOW); +#endif + + return (i2c_transfer( 0, chip<<1, &xaddr[4-alen], alen, buffer, len ) != 0); +} + +#endif /* CONFIG_HARD_I2C */ diff --git a/cpu/ppc4xx/sdram.c b/cpu/ppc4xx/sdram.c new file mode 100644 index 0000000000..d64bf96f60 --- /dev/null +++ b/cpu/ppc4xx/sdram.c @@ -0,0 +1,191 @@ +/* + * (C) Copyright 2002 + * Stefan Roese, esd gmbh germany, stefan.roese@esd-electronics.com + * + * 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 <ppc4xx.h> +#include <asm/processor.h> +#include <pci.h> + + +#ifdef CONFIG_SDRAM_BANK0 + + +#define MAGIC0 0x00000000 +#define MAGIC1 0x11111111 +#define MAGIC2 0x22222222 +#define MAGIC3 0x33333333 +#define MAGIC4 0x44444444 + +#define ADDR_ZERO 0x00000000 +#define ADDR_400 0x00000400 +#define ADDR_08MB 0x00800000 +#define ADDR_16MB 0x01000000 +#define ADDR_32MB 0x02000000 +#define ADDR_64MB 0x04000000 + +#define mtsdram0(reg, data) mtdcr(memcfga,reg);mtdcr(memcfgd,data) + + +/*----------------------------------------------------------------------- + */ +void sdram_init(void) +{ + ulong speed; + ulong sdtr1; + ulong rtr; + + /* + * Determine SDRAM speed + */ + speed = get_bus_freq(0); /* parameter not used on ppc4xx */ + + /* + * Support for 100MHz and 133MHz SDRAM + */ + if (speed > 100000000) { + /* + * 133 MHz SDRAM + */ + sdtr1 = 0x01074015; + rtr = 0x07f00000; + } else { + /* + * default: 100 MHz SDRAM + */ + sdtr1 = 0x0086400d; + rtr = 0x05f00000; + } + + /* + * Set MB0CF for bank 0. (0-64MB) Address Mode 3 since 13x9(4) + */ + mtsdram0(mem_mb0cf, 0x00084001); + + mtsdram0(mem_sdtr1, sdtr1); + mtsdram0(mem_rtr, rtr); + + /* + * Wait for 200us + */ + udelay(200); + + /* + * Set memory controller options reg, MCOPT1. + * Set DC_EN to '1' and BRD_PRF to '01' for 16 byte PLB Burst + * read/prefetch. + */ + mtsdram0(mem_mcopt1, 0x80800000); + + /* + * Wait for 10ms + */ + udelay(10000); + + /* + * Test if 64 MByte are equipped (mirror test) + */ + *(volatile ulong *)ADDR_ZERO = MAGIC0; + *(volatile ulong *)ADDR_08MB = MAGIC1; + *(volatile ulong *)ADDR_16MB = MAGIC2; + *(volatile ulong *)ADDR_32MB = MAGIC3; + + if ((*(volatile ulong *)ADDR_ZERO == MAGIC0) && + (*(volatile ulong *)ADDR_08MB == MAGIC1) && + (*(volatile ulong *)ADDR_16MB == MAGIC2)) { + /* + * OK, 64MB detected -> all done + */ + return; + } + + /* + * Now test for 32 MByte... + */ + + /* + * Disable memory controller. + */ + mtsdram0(mem_mcopt1, 0x00000000); + + /* + * Set MB0CF for bank 0. (0-32MB) Address Mode 2 since 12x9(4) + */ + mtsdram0(mem_mb0cf, 0x00062001); + + /* + * Set memory controller options reg, MCOPT1. + * Set DC_EN to '1' and BRD_PRF to '01' for 16 byte PLB Burst + * read/prefetch. + */ + mtsdram0(mem_mcopt1, 0x80800000); + + /* + * Wait for 10ms + */ + udelay(10000); + + /* + * Test if 32 MByte are equipped (mirror test) + */ + *(volatile ulong *)ADDR_ZERO = MAGIC0; + *(volatile ulong *)ADDR_400 = MAGIC1; + *(volatile ulong *)ADDR_08MB = MAGIC2; + *(volatile ulong *)ADDR_16MB = MAGIC3; + + if ((*(volatile ulong *)ADDR_ZERO == MAGIC0) && + (*(volatile ulong *)ADDR_400 == MAGIC1) && + (*(volatile ulong *)ADDR_08MB == MAGIC2)) { + /* + * OK, 32MB detected -> all done + */ + return; + } + + /* + * Setup for 16 MByte... + */ + + /* + * Disable memory controller. + */ + mtsdram0(mem_mcopt1, 0x00000000); + + /* + * Set MB0CF for bank 0. (0-16MB) Address Mode 4 since 12x8(4) + */ + mtsdram0(mem_mb0cf, 0x00046001); + + /* + * Set memory controller options reg, MCOPT1. + * Set DC_EN to '1' and BRD_PRF to '01' for 16 byte PLB Burst + * read/prefetch. + */ + mtsdram0(mem_mcopt1, 0x80800000); + + /* + * Wait for 10ms + */ + udelay(10000); +} + +#endif /* CONFIG_SDRAM_BANK0 */ diff --git a/cpu/ppc4xx/speed.c b/cpu/ppc4xx/speed.c new file mode 100644 index 0000000000..45415292c3 --- /dev/null +++ b/cpu/ppc4xx/speed.c @@ -0,0 +1,308 @@ +/* + * (C) Copyright 2000 + * 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 <ppc_asm.tmpl> +#include <ppc4xx.h> +#include <asm/processor.h> + +/* ------------------------------------------------------------------------- */ + +#define ONE_BILLION 1000000000 + + +#if defined(CONFIG_405GP) || defined(CONFIG_405CR) + +void get_sys_info (PPC405_SYS_INFO * sysInfo) +{ + unsigned long pllmr; + unsigned long sysClkPeriodPs = ONE_BILLION / (CONFIG_SYS_CLK_FREQ / 1000); + uint pvr = get_pvr(); + unsigned long psr; + unsigned long m; + + /* + * Read PLL Mode register + */ + pllmr = mfdcr (pllmd); + + /* + * Read Pin Strapping register + */ + psr = mfdcr (strap); + + /* + * Determine FWD_DIV. + */ + sysInfo->pllFwdDiv = 8 - ((pllmr & PLLMR_FWD_DIV_MASK) >> 29); + + /* + * Determine FBK_DIV. + */ + sysInfo->pllFbkDiv = ((pllmr & PLLMR_FB_DIV_MASK) >> 25); + if (sysInfo->pllFbkDiv == 0) { + sysInfo->pllFbkDiv = 16; + } + + /* + * Determine PLB_DIV. + */ + sysInfo->pllPlbDiv = ((pllmr & PLLMR_CPU_TO_PLB_MASK) >> 17) + 1; + + /* + * Determine PCI_DIV. + */ + sysInfo->pllPciDiv = ((pllmr & PLLMR_PCI_TO_PLB_MASK) >> 13) + 1; + + /* + * Determine EXTBUS_DIV. + */ + sysInfo->pllExtBusDiv = ((pllmr & PLLMR_EXB_TO_PLB_MASK) >> 11) + 2; + + /* + * Determine OPB_DIV. + */ + sysInfo->pllOpbDiv = ((pllmr & PLLMR_OPB_TO_PLB_MASK) >> 15) + 1; + + /* + * Check if PPC405GPr used (mask minor revision field) + */ + if ((pvr & 0xfffffff0) == (PVR_405GPR_RA & 0xfffffff0)) { + /* + * Determine FWD_DIV B (only PPC405GPr with new mode strapping). + */ + sysInfo->pllFwdDivB = 8 - (pllmr & PLLMR_FWDB_DIV_MASK); + + /* + * Determine factor m depending on PLL feedback clock source + */ + if (!(psr & PSR_PCI_ASYNC_EN)) { + if (psr & PSR_NEW_MODE_EN) { + /* + * sync pci clock used as feedback (new mode) + */ + m = 1 * sysInfo->pllFwdDivB * 2 * sysInfo->pllPciDiv; + } else { + /* + * sync pci clock used as feedback (legacy mode) + */ + m = 1 * sysInfo->pllFwdDivB * sysInfo->pllPlbDiv * sysInfo->pllPciDiv; + } + } else if (psr & PSR_NEW_MODE_EN) { + if (psr & PSR_PERCLK_SYNC_MODE_EN) { + /* + * PerClk used as feedback (new mode) + */ + m = 1 * sysInfo->pllFwdDivB * 2 * sysInfo->pllExtBusDiv; + } else { + /* + * CPU clock used as feedback (new mode) + */ + m = sysInfo->pllFbkDiv * sysInfo->pllFwdDiv; + } + } else if (sysInfo->pllExtBusDiv == sysInfo->pllFbkDiv) { + /* + * PerClk used as feedback (legacy mode) + */ + m = 1 * sysInfo->pllFwdDivB * sysInfo->pllPlbDiv * sysInfo->pllExtBusDiv; + } else { + /* + * PLB clock used as feedback (legacy mode) + */ + m = sysInfo->pllFbkDiv * sysInfo->pllFwdDivB * sysInfo->pllPlbDiv; + } + + sysInfo->freqVCOMhz = (1000000 * m) / sysClkPeriodPs; + sysInfo->freqProcessor = (sysInfo->freqVCOMhz * 1000000) / sysInfo->pllFwdDiv; + sysInfo->freqPLB = (sysInfo->freqVCOMhz * 1000000) / + (sysInfo->pllFwdDivB * sysInfo->pllPlbDiv); + } else { + /* + * Check pllFwdDiv to see if running in bypass mode where the CPU speed + * is equal to the 405GP SYS_CLK_FREQ. If not in bypass mode, check VCO + * to make sure it is within the proper range. + * spec: VCO = SYS_CLOCK x FBKDIV x PLBDIV x FWDDIV + * Note freqVCO is calculated in Mhz to avoid errors introduced by rounding. + */ + if (sysInfo->pllFwdDiv == 1) { + sysInfo->freqProcessor = CONFIG_SYS_CLK_FREQ; + sysInfo->freqPLB = CONFIG_SYS_CLK_FREQ / sysInfo->pllPlbDiv; + } else { + sysInfo->freqVCOMhz = ( 1000000 * + sysInfo->pllFwdDiv * + sysInfo->pllFbkDiv * + sysInfo->pllPlbDiv + ) / sysClkPeriodPs; + if (sysInfo->freqVCOMhz >= VCO_MIN + && sysInfo->freqVCOMhz <= VCO_MAX) { + sysInfo->freqPLB = (ONE_BILLION / + ((sysClkPeriodPs * 10) / + sysInfo->pllFbkDiv)) * 10000; + sysInfo->freqProcessor = sysInfo->freqPLB * sysInfo->pllPlbDiv; + } else { + printf ("\nInvalid VCO frequency calculated : %ld MHz \a\n", + sysInfo->freqVCOMhz); + printf ("It must be between %d-%d MHz \a\n", + VCO_MIN, VCO_MAX); + printf ("PLL Mode reg : %8.8lx\a\n", + pllmr); + hang (); + } + } + } +} + + +/******************************************** + * get_OPB_freq + * return OPB bus freq in Hz + *********************************************/ +ulong get_OPB_freq (void) +{ + ulong val = 0; + + PPC405_SYS_INFO sys_info; + + get_sys_info (&sys_info); + val = sys_info.freqPLB / sys_info.pllOpbDiv; + + return val; +} + + +/******************************************** + * get_PCI_freq + * return PCI bus freq in Hz + *********************************************/ +ulong get_PCI_freq (void) +{ + ulong val; + PPC405_SYS_INFO sys_info; + + get_sys_info (&sys_info); + val = sys_info.freqPLB / sys_info.pllPciDiv; + return val; +} + + +#elif defined(CONFIG_440) +void get_sys_info (sys_info_t * sysInfo) +{ + unsigned long strp0; + unsigned long temp; + unsigned long m; + + /* Extract configured divisors */ + strp0 = mfdcr( cpc0_strp0 ); + sysInfo->pllFwdDivA = 8 - ((strp0 & PLLSYS0_FWD_DIV_A_MASK) >> 15); + sysInfo->pllFwdDivB = 8 - ((strp0 & PLLSYS0_FWD_DIV_B_MASK) >> 12); + temp = (strp0 & PLLSYS0_FB_DIV_MASK) >> 18; + sysInfo->pllFbkDiv = temp ? temp : 16; + sysInfo->pllOpbDiv = 1 + ((strp0 & PLLSYS0_OPB_DIV_MASK) >> 10); + sysInfo->pllExtBusDiv = 1 + ((strp0 & PLLSYS0_EPB_DIV_MASK) >> 8); + + /* Calculate 'M' based on feedback source */ + if( strp0 & PLLSYS0_EXTSL_MASK ) + m = sysInfo->pllExtBusDiv * sysInfo->pllOpbDiv * sysInfo->pllFwdDivB; + else + m = sysInfo->pllFbkDiv * sysInfo->pllFwdDivA; + + /* Now calculate the individual clocks */ + sysInfo->freqVCOMhz = (m * CONFIG_SYS_CLK_FREQ) + (m>>1); + sysInfo->freqProcessor = sysInfo->freqVCOMhz/sysInfo->pllFwdDivA; + sysInfo->freqPLB = sysInfo->freqVCOMhz/sysInfo->pllFwdDivB; + if( get_pvr() == PVR_440GP_RB ) /* Rev B divs an extra 2 -- geez! */ + sysInfo->freqPLB >>= 1; + sysInfo->freqOPB = sysInfo->freqPLB/sysInfo->pllOpbDiv; + sysInfo->freqEPB = sysInfo->freqOPB/sysInfo->pllExtBusDiv; + +} + +ulong get_OPB_freq (void) +{ + + sys_info_t sys_info; + get_sys_info (&sys_info); + return sys_info.freqOPB; +} + +#elif defined(CONFIG_405) + +void get_sys_info (sys_info_t * sysInfo) { + + sysInfo->freqVCOMhz=3125000; + sysInfo->freqProcessor=12*1000*1000; + sysInfo->freqPLB=50*1000*1000; + sysInfo->freqPCI=66*1000*1000; + +} + +#endif + +int get_clocks (void) +{ +#if defined(CONFIG_405GP) || defined(CONFIG_405CR) || defined(CONFIG_440) || defined(CONFIG_405) + DECLARE_GLOBAL_DATA_PTR; + + sys_info_t sys_info; + + get_sys_info (&sys_info); + gd->cpu_clk = sys_info.freqProcessor; + gd->bus_clk = sys_info.freqPLB; + +#endif /* defined(CONFIG_405GP) || defined(CONFIG_405CR) */ + +#ifdef CONFIG_IOP480 + DECLARE_GLOBAL_DATA_PTR; + + gd->cpu_clk = 66000000; + gd->bus_clk = 66000000; +#endif + return (0); +} + + +/******************************************** + * get_bus_freq + * return PLB bus freq in Hz + *********************************************/ +ulong get_bus_freq (ulong dummy) +{ + ulong val; + +#if defined(CONFIG_405GP) || defined(CONFIG_405CR) || defined(CONFIG_405) || defined(CONFIG_440) + sys_info_t sys_info; + + get_sys_info (&sys_info); + val = sys_info.freqPLB; + +#elif defined(CONFIG_IOP480) + + val = 66; + +#else +# error get_bus_freq() not implemented +#endif + + return val; +} diff --git a/cpu/ppc4xx/vecnum.h b/cpu/ppc4xx/vecnum.h new file mode 100644 index 0000000000..d493a5dd8f --- /dev/null +++ b/cpu/ppc4xx/vecnum.h @@ -0,0 +1,100 @@ +/* +* Copyright (C) 2002 Scott McNutt <smcnutt@artesyncp.com> +* +* 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 +*/ + +/* + * Interrupt vector number definitions to ease the + * 405 -- 440 porting pain ;-) + * + * NOTE: They're not all here yet ... update as needed. + * + */ + +#ifndef _VECNUMS_H_ +#define _VECNUMS_H_ + +#if defined(CONFIG_440) + +/* UIC 0 */ +#define VECNUM_U0 0 /* UART0 */ +#define VECNUM_U1 1 /* UART1 */ +#define VECNUM_IIC0 2 /* IIC0 */ +#define VECNUM_IIC1 3 /* IIC1 */ +#define VECNUM_PIM 4 /* PCI inbound message */ +#define VECNUM_PCRW 5 /* PCI command reg write */ +#define VECNUM_PPM 6 /* PCI power management */ +#define VECNUM_MSI0 7 /* PCI MSI level 0 */ +#define VECNUM_MSI1 8 /* PCI MSI level 0 */ +#define VECNUM_MSI2 9 /* PCI MSI level 0 */ +#define VECNUM_MTE 10 /* MAL TXEOB */ +#define VECNUM_MRE 11 /* MAL RXEOB */ +#define VECNUM_D0 12 /* DMA channel 0 */ +#define VECNUM_D1 13 /* DMA channel 1 */ +#define VECNUM_D2 14 /* DMA channel 2 */ +#define VECNUM_D3 15 /* DMA channel 3 */ +#define VECNUM_CT0 18 /* GPT compare timer 0 */ +#define VECNUM_CT1 19 /* GPT compare timer 1 */ +#define VECNUM_CT2 20 /* GPT compare timer 2 */ +#define VECNUM_CT3 21 /* GPT compare timer 3 */ +#define VECNUM_CT4 22 /* GPT compare timer 4 */ +#define VECNUM_EIR0 23 /* External interrupt 0 */ +#define VECNUM_EIR1 24 /* External interrupt 1 */ +#define VECNUM_EIR2 25 /* External interrupt 2 */ +#define VECNUM_EIR3 26 /* External interrupt 3 */ +#define VECNUM_EIR4 27 /* External interrupt 4 */ +#define VECNUM_EIR5 28 /* External interrupt 5 */ +#define VECNUM_EIR6 29 /* External interrupt 6 */ +#define VECNUM_UIC1NC 30 /* UIC1 non-critical interrupt */ +#define VECNUM_UIC1C 31 /* UIC1 critical interrupt */ + +/* UIC 1 */ +#define VECNUM_MS (32 + 0 ) /* MAL SERR */ +#define VECNUM_TXDE (32 + 1 ) /* MAL TXDE */ +#define VECNUM_RXDE (32 + 2 ) /* MAL RXDE */ +#define VECNUM_ETH0 (32 + 28) /* Ethernet 0 interrupt status */ +#define VECNUM_EWU0 (32 + 29) /* Ethernet 0 wakeup */ + +#else /* !defined(CONFIG_440) */ + +#define VECNUM_U0 0 /* UART0 */ +#define VECNUM_U1 1 /* UART1 */ +#define VECNUM_D0 5 /* DMA channel 0 */ +#define VECNUM_D1 6 /* DMA channel 1 */ +#define VECNUM_D2 7 /* DMA channel 2 */ +#define VECNUM_D3 8 /* DMA channel 3 */ +#define VECNUM_EWU0 9 /* Ethernet wakeup */ +#define VECNUM_MS 10 /* MAL SERR */ +#define VECNUM_MTE 11 /* MAL TXEOB */ +#define VECNUM_MRE 12 /* MAL RXEOB */ +#define VECNUM_TXDE 13 /* MAL TXDE */ +#define VECNUM_RXDE 14 /* MAL RXDE */ +#define VECNUM_ETH0 15 /* Ethernet interrupt status */ +#define VECNUM_EIR0 25 /* External interrupt 0 */ +#define VECNUM_EIR1 26 /* External interrupt 1 */ +#define VECNUM_EIR2 27 /* External interrupt 2 */ +#define VECNUM_EIR3 28 /* External interrupt 3 */ +#define VECNUM_EIR4 29 /* External interrupt 4 */ +#define VECNUM_EIR5 30 /* External interrupt 5 */ +#define VECNUM_EIR6 31 /* External interrupt 6 */ + +#endif /* defined(CONFIG_440) */ + +#endif /* _VECNUMS_H_ */ |