diff options
Diffstat (limited to 'cpu/ppc4xx')
-rwxr-xr-x | cpu/ppc4xx/405gp_pci.c | 553 | ||||
-rwxr-xr-x | cpu/ppc4xx/4xx_enet.c | 1564 | ||||
-rwxr-xr-x | cpu/ppc4xx/Makefile | 50 | ||||
-rwxr-xr-x | cpu/ppc4xx/bedbug_405.c | 308 | ||||
-rwxr-xr-x | cpu/ppc4xx/commproc.c | 69 | ||||
-rwxr-xr-x | cpu/ppc4xx/config.mk | 26 | ||||
-rwxr-xr-x | cpu/ppc4xx/cpu.c | 371 | ||||
-rwxr-xr-x | cpu/ppc4xx/cpu_init.c | 268 | ||||
-rwxr-xr-x | cpu/ppc4xx/dcr.S | 198 | ||||
-rwxr-xr-x | cpu/ppc4xx/i2c.c | 445 | ||||
-rwxr-xr-x | cpu/ppc4xx/interrupts.c | 571 | ||||
-rwxr-xr-x | cpu/ppc4xx/kgdb.S | 78 | ||||
-rwxr-xr-x | cpu/ppc4xx/miiphy.c | 260 | ||||
-rwxr-xr-x | cpu/ppc4xx/resetvec.S | 12 | ||||
-rwxr-xr-x | cpu/ppc4xx/sdram.c | 178 | ||||
-rwxr-xr-x | cpu/ppc4xx/serial.c | 1064 | ||||
-rwxr-xr-x | cpu/ppc4xx/spd_sdram.c | 1831 | ||||
-rwxr-xr-x | cpu/ppc4xx/speed.c | 568 | ||||
-rwxr-xr-x | cpu/ppc4xx/start.S | 1687 | ||||
-rwxr-xr-x | cpu/ppc4xx/traps.c | 290 | ||||
-rwxr-xr-x | cpu/ppc4xx/usb_ohci.c | 1642 | ||||
-rwxr-xr-x | cpu/ppc4xx/usb_ohci.h | 410 | ||||
-rwxr-xr-x | cpu/ppc4xx/usbdev.c | 214 | ||||
-rwxr-xr-x | cpu/ppc4xx/usbdev.h | 31 | ||||
-rwxr-xr-x | cpu/ppc4xx/vecnum.h | 129 |
25 files changed, 12817 insertions, 0 deletions
diff --git a/cpu/ppc4xx/405gp_pci.c b/cpu/ppc4xx/405gp_pci.c new file mode 100755 index 0000000..64431ab --- /dev/null +++ b/cpu/ppc4xx/405gp_pci.c @@ -0,0 +1,553 @@ +/*-----------------------------------------------------------------------------+ + * + * 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> +#if !defined(CONFIG_440) +#include <405gp_pci.h> +#endif +#include <asm/processor.h> +#include <pci.h> + +#if defined(CONFIG_405GP) || defined(CONFIG_405EP) + +#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) || defined(CONFIG_PMC405) + unsigned long ptmla[2] = {bd->bi_memstart, bd->bi_flashstart}; + unsigned long ptmms[2] = {~(bd->bi_memsize - 1) | 1, ~(bd->bi_flashsize - 1) | 1}; + char *ptmla_str, *ptmms_str; +#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 +#ifdef CONFIG_PCI_PNP +#if (CONFIG_PCI_HOST == PCI_HOST_AUTO) + char *s; +#endif +#endif + +#if defined(CONFIG_CPCI405) || defined(CONFIG_PMC405) + ptmla_str = getenv("ptm1la"); + ptmms_str = getenv("ptm1ms"); + if(NULL != ptmla_str && NULL != ptmms_str ) { + ptmla[0] = simple_strtoul (ptmla_str, NULL, 16); + ptmms[0] = simple_strtoul (ptmms_str, NULL, 16); + } + + ptmla_str = getenv("ptm2la"); + ptmms_str = getenv("ptm2ms"); + if(NULL != ptmla_str && NULL != ptmms_str ) { + ptmla[1] = simple_strtoul (ptmla_str, NULL, 16); + ptmms[1] = simple_strtoul (ptmms_str, NULL, 16); + } +#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]&~0x1)); /* disable, configure PMMxLA, PMMxPCILA first */ + 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]&~0x1)); + 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]&~0x1)); + 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 */ + pci_write_config_dword(PCIDEVID_405GP, PCI_BASE_ADDRESS_1, ptmpcila[0]); + + /*--------------------------------------------------------------------------+ + * 405GP PCI Target configuration. (PTM2) + *--------------------------------------------------------------------------*/ + out32r(PTM2LA, ptmla[1]); /* insert address */ + pci_write_config_dword(PCIDEVID_405GP, PCI_BASE_ADDRESS_2, ptmpcila[1]); + + 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_HOST == PCI_HOST_AUTO) + if ((mfdcr(strap) & PSR_PCI_ARBIT_EN) || + (((s = getenv("pciscan")) != NULL) && (strcmp(s, "yes") == 0))) +#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 + +#if defined(CONFIG_405EP) /* on ppc405ep vendor id is not set */ + pci_write_config_word(PCIDEVID_405GP, PCI_VENDOR_ID, 0x1014); /* IBM */ +#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) || + (((s = getenv("pciscan")) != NULL) && (strcmp(s, "yes") == 0))) +#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)*/ +#ifdef 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_board(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; + +#ifndef CONFIG_DISABLE_PISE_TEST + /*--------------------------------------------------------------------------+ + * The PCI initialization sequence enable bit must be set ... if not abort + * pci setup since updating the bit requires chip reset. + *--------------------------------------------------------------------------*/ +#if defined(CONFIG_440GX) || defined(CONFIG_440SP) + unsigned long strap; + + mfsdr(sdr_sdstp1,strap); + if ((strap & SDR0_SDSTP1_PISE_MASK) == 0) { + printf("PCI: SDR0_STRP1[PISE] not set.\n"); + printf("PCI: Configuration aborted.\n"); + return; + } +#elif defined(CONFIG_440GP) + unsigned long strap; + + strap = mfdcr(cpc0_strp1); + if ((strap & CPC0_STRP1_PISE_MASK) == 0) { + printf("PCI: CPC0_STRP1[PISE] not set.\n"); + printf("PCI: Configuration aborted.\n"); + return; + } +#endif +#endif /* CONFIG_DISABLE_PISE_TEST */ + + /*--------------------------------------------------------------------------+ + * 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 + +#if defined(CONFIG_440GX) + out32r( PCIX0_BRDGOPT1, 0x04000060 ); /* PLB Rq pri highest */ + out32r( PCIX0_BRDGOPT2, in32(PCIX0_BRDGOPT2) | 0x83 ); /* Enable host config, clear Timeout, ensure int src1 */ +#elif defined(PCIX0_BRDGOPT1) + out32r( PCIX0_BRDGOPT1, 0x10000060 ); /* PLB Rq pri highest */ + out32r( PCIX0_BRDGOPT2, in32(PCIX0_BRDGOPT2) | 1 ); /* Enable host config */ +#endif + + /*--------------------------------------------------------------------------+ + * 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 +#if !defined(CONFIG_440EP) && !defined(CONFIG_440GR) + out16r( PCIX0_CMD, in16r( PCIX0_CMD ) | PCI_COMMAND_MASTER); +#endif + hose->last_busno = pci_hose_scan(hose); + } +} + + +void pci_init_board(void) +{ + pci_440_init (&ppc440_hose); +} + +#endif /* CONFIG_440 & CONFIG_PCI */ diff --git a/cpu/ppc4xx/4xx_enet.c b/cpu/ppc4xx/4xx_enet.c new file mode 100755 index 0000000..86dc2d0 --- /dev/null +++ b/cpu/ppc4xx/4xx_enet.c @@ -0,0 +1,1564 @@ +/*-----------------------------------------------------------------------------+ + * + * 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). + *-----------------------------------------------------------------------------* + * 17-Nov-03 travis.sawyer@sandburst.com + * - ported from 405gp_enet.c to utilized upto 4 EMAC ports + * in the 440GX. This port should work with the 440GP + * (2 EMACs) also + * 15-Aug-05 sr@denx.de + * - merged 405gp_enet.c and 440gx_enet.c to generic 4xx_enet.c + now handling all 4xx cpu's. + *-----------------------------------------------------------------------------*/ + +#include <config.h> +#include <common.h> +#include <net.h> +#include <asm/processor.h> +#include <commproc.h> +#include <ppc4xx.h> +#include <ppc4xx_enet.h> +#include <405_mal.h> +#include <miiphy.h> +#include <malloc.h> +#include "vecnum.h" + +/* + * Only compile for platform with AMCC EMAC ethernet controller and + * network support enabled. + * Remark: CONFIG_405 describes Xilinx PPC405 FPGA without EMAC controller! + */ +#if (CONFIG_COMMANDS & CFG_CMD_NET) && !defined(CONFIG_405) && !defined(CONFIG_IOP480) + +#if !(defined(CONFIG_MII) || (CONFIG_COMMANDS & CFG_CMD_MII)) +#error "CONFIG_MII has to be defined!" +#endif + +#if defined(CONFIG_NETCONSOLE) && !defined(CONFIG_NET_MULTI) +#error "CONFIG_NET_MULTI has to be defined for NetConsole" +#endif + +#define EMAC_RESET_TIMEOUT 1000 /* 1000 ms reset timeout */ +#define PHY_AUTONEGOTIATE_TIMEOUT 4000 /* 4000 ms autonegotiate timeout */ + +/* 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 + +/*-----------------------------------------------------------------------------+ + * 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 +#define EMAC_UIC_DEF1 UIC_ENET1 +#define SEL_UIC_DEF(p) (p ? UIC_ENET1 : UIC_ENET ) + +#undef INFO_4XX_ENET + +#define BI_PHYMODE_NONE 0 +#define BI_PHYMODE_ZMII 1 +#define BI_PHYMODE_RGMII 2 + + +/*-----------------------------------------------------------------------------+ + * Global variables. TX and RX descriptors and buffers. + *-----------------------------------------------------------------------------*/ +/* IER globals */ +static uint32_t mal_ier; + +#if !defined(CONFIG_NET_MULTI) +struct eth_device *emac0_dev = NULL; +#endif + +/* + * Get count of EMAC devices (doesn't have to be the max. possible number + * supported by the cpu) + */ +#if defined(CONFIG_HAS_ETH3) +#define LAST_EMAC_NUM 4 +#elif defined(CONFIG_HAS_ETH2) +#define LAST_EMAC_NUM 3 +#elif defined(CONFIG_HAS_ETH1) +#define LAST_EMAC_NUM 2 +#else +#define LAST_EMAC_NUM 1 +#endif + +/*-----------------------------------------------------------------------------+ + * Prototypes and externals. + *-----------------------------------------------------------------------------*/ +static void enet_rcv (struct eth_device *dev, unsigned long malisr); + +int enetInt (struct eth_device *dev); +static void mal_err (struct eth_device *dev, unsigned long isr, + unsigned long uic, unsigned long maldef, + unsigned long mal_errr); +static void emac_err (struct eth_device *dev, unsigned long isr); + +extern int phy_setup_aneg (char *devname, unsigned char addr); +extern int emac4xx_miiphy_read (char *devname, unsigned char addr, + unsigned char reg, unsigned short *value); +extern int emac4xx_miiphy_write (char *devname, unsigned char addr, + unsigned char reg, unsigned short value); + +/*-----------------------------------------------------------------------------+ +| ppc_4xx_eth_halt +| Disable MAL channel, and EMACn ++-----------------------------------------------------------------------------*/ +static void ppc_4xx_eth_halt (struct eth_device *dev) +{ + EMAC_4XX_HW_PST hw_p = dev->priv; + uint32_t failsafe = 10000; + + out32 (EMAC_IER + hw_p->hw_addr, 0x00000000); /* disable emac interrupts */ + + /* 1st reset MAL channel */ + /* Note: writing a 0 to a channel has no effect */ +#if defined(CONFIG_405EP) || defined(CONFIG_440EP) || defined(CONFIG_440GR) + mtdcr (maltxcarr, (MAL_CR_MMSR >> (hw_p->devnum * 2))); +#else + mtdcr (maltxcarr, (MAL_CR_MMSR >> hw_p->devnum)); +#endif + mtdcr (malrxcarr, (MAL_CR_MMSR >> hw_p->devnum)); + + /* wait for reset */ + while (mfdcr (malrxcasr) & (MAL_CR_MMSR >> hw_p->devnum)) { + udelay (1000); /* Delay 1 MS so as not to hammer the register */ + failsafe--; + if (failsafe == 0) + break; + } + + /* EMAC RESET */ + out32 (EMAC_M0 + hw_p->hw_addr, EMAC_M0_SRST); + +#ifndef CONFIG_NETCONSOLE + hw_p->print_speed = 1; /* print speed message again next time */ +#endif + + return; +} + +#if defined (CONFIG_440GX) +int ppc_4xx_eth_setup_bridge(int devnum, bd_t * bis) +{ + unsigned long pfc1; + unsigned long zmiifer; + unsigned long rmiifer; + + mfsdr(sdr_pfc1, pfc1); + pfc1 = SDR0_PFC1_EPS_DECODE(pfc1); + + zmiifer = 0; + rmiifer = 0; + + switch (pfc1) { + case 1: + zmiifer |= ZMII_FER_RMII << ZMII_FER_V(0); + zmiifer |= ZMII_FER_RMII << ZMII_FER_V(1); + zmiifer |= ZMII_FER_RMII << ZMII_FER_V(2); + zmiifer |= ZMII_FER_RMII << ZMII_FER_V(3); + bis->bi_phymode[0] = BI_PHYMODE_ZMII; + bis->bi_phymode[1] = BI_PHYMODE_ZMII; + bis->bi_phymode[2] = BI_PHYMODE_ZMII; + bis->bi_phymode[3] = BI_PHYMODE_ZMII; + break; + case 2: + zmiifer = ZMII_FER_SMII << ZMII_FER_V(0); + zmiifer = ZMII_FER_SMII << ZMII_FER_V(1); + zmiifer = ZMII_FER_SMII << ZMII_FER_V(2); + zmiifer = ZMII_FER_SMII << ZMII_FER_V(3); + bis->bi_phymode[0] = BI_PHYMODE_ZMII; + bis->bi_phymode[1] = BI_PHYMODE_ZMII; + bis->bi_phymode[2] = BI_PHYMODE_ZMII; + bis->bi_phymode[3] = BI_PHYMODE_ZMII; + break; + case 3: + zmiifer |= ZMII_FER_RMII << ZMII_FER_V(0); + rmiifer |= RGMII_FER_RGMII << RGMII_FER_V(2); + bis->bi_phymode[0] = BI_PHYMODE_ZMII; + bis->bi_phymode[1] = BI_PHYMODE_NONE; + bis->bi_phymode[2] = BI_PHYMODE_RGMII; + bis->bi_phymode[3] = BI_PHYMODE_NONE; + break; + case 4: + zmiifer |= ZMII_FER_SMII << ZMII_FER_V(0); + zmiifer |= ZMII_FER_SMII << ZMII_FER_V(1); + rmiifer |= RGMII_FER_RGMII << RGMII_FER_V (2); + rmiifer |= RGMII_FER_RGMII << RGMII_FER_V (3); + bis->bi_phymode[0] = BI_PHYMODE_ZMII; + bis->bi_phymode[1] = BI_PHYMODE_ZMII; + bis->bi_phymode[2] = BI_PHYMODE_RGMII; + bis->bi_phymode[3] = BI_PHYMODE_RGMII; + break; + case 5: + zmiifer |= ZMII_FER_SMII << ZMII_FER_V (0); + zmiifer |= ZMII_FER_SMII << ZMII_FER_V (1); + zmiifer |= ZMII_FER_SMII << ZMII_FER_V (2); + rmiifer |= RGMII_FER_RGMII << RGMII_FER_V(3); + bis->bi_phymode[0] = BI_PHYMODE_ZMII; + bis->bi_phymode[1] = BI_PHYMODE_ZMII; + bis->bi_phymode[2] = BI_PHYMODE_ZMII; + bis->bi_phymode[3] = BI_PHYMODE_RGMII; + break; + case 6: + zmiifer |= ZMII_FER_SMII << ZMII_FER_V (0); + zmiifer |= ZMII_FER_SMII << ZMII_FER_V (1); + rmiifer |= RGMII_FER_RGMII << RGMII_FER_V(2); + bis->bi_phymode[0] = BI_PHYMODE_ZMII; + bis->bi_phymode[1] = BI_PHYMODE_ZMII; + bis->bi_phymode[2] = BI_PHYMODE_RGMII; + break; + case 0: + default: + zmiifer = ZMII_FER_MII << ZMII_FER_V(devnum); + rmiifer = 0x0; + bis->bi_phymode[0] = BI_PHYMODE_ZMII; + bis->bi_phymode[1] = BI_PHYMODE_ZMII; + bis->bi_phymode[2] = BI_PHYMODE_ZMII; + bis->bi_phymode[3] = BI_PHYMODE_ZMII; + break; + } + + /* Ensure we setup mdio for this devnum and ONLY this devnum */ + zmiifer |= (ZMII_FER_MDI) << ZMII_FER_V(devnum); + + out32 (ZMII_FER, zmiifer); + out32 (RGMII_FER, rmiifer); + + return ((int)pfc1); + +} +#endif + +static int ppc_4xx_eth_init (struct eth_device *dev, bd_t * bis) +{ + int i, j; + unsigned long reg = 0; + unsigned long msr; + unsigned long speed; + unsigned long duplex; + unsigned long failsafe; + unsigned mode_reg; + unsigned short devnum; + unsigned short reg_short; +#if defined(CONFIG_440GX) || defined(CONFIG_440SP) + sys_info_t sysinfo; +#if defined(CONFIG_440GX) + int ethgroup = -1; +#endif +#endif + + EMAC_4XX_HW_PST hw_p = dev->priv; + + /* before doing anything, figure out if we have a MAC address */ + /* if not, bail */ + if (memcmp (dev->enetaddr, "\0\0\0\0\0\0", 6) == 0) { + printf("ERROR: ethaddr not set!\n"); + return -1; + } + +#if defined(CONFIG_440GX) || defined(CONFIG_440SP) + /* Need to get the OPB frequency so we can access the PHY */ + get_sys_info (&sysinfo); +#endif + + msr = mfmsr (); + mtmsr (msr & ~(MSR_EE)); /* disable interrupts */ + + devnum = hw_p->devnum; + +#ifdef INFO_4XX_ENET + /* AS.HARNOIS + * We should have : + * hw_p->stats.pkts_handled <= hw_p->stats.pkts_rx <= hw_p->stats.pkts_handled+PKTBUFSRX + * In the most cases hw_p->stats.pkts_handled = hw_p->stats.pkts_rx, 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 (eth%d):\n" + "- Sent packet number %d\n" + "- Received packet number %d\n" + "- Handled packet number %d\n", + hw_p->devnum, + hw_p->stats.pkts_tx, + hw_p->stats.pkts_rx, hw_p->stats.pkts_handled); + + hw_p->stats.pkts_tx = 0; + hw_p->stats.pkts_rx = 0; + hw_p->stats.pkts_handled = 0; +#endif + + hw_p->tx_err_index = 0; /* Transmit Error Index for tx_err_log */ + hw_p->rx_err_index = 0; /* Receive Error Index for rx_err_log */ + + hw_p->rx_slot = 0; /* MAL Receive Slot */ + hw_p->rx_i_index = 0; /* Receive Interrupt Queue Index */ + hw_p->rx_u_index = 0; /* Receive User Queue Index */ + + hw_p->tx_slot = 0; /* MAL Transmit Slot */ + hw_p->tx_i_index = 0; /* Transmit Interrupt Queue Index */ + hw_p->tx_u_index = 0; /* Transmit User Queue Index */ + +#if defined(CONFIG_440) && !defined(CONFIG_440SP) + /* set RMII mode */ + /* NOTE: 440GX spec states that mode is mutually exclusive */ + /* NOTE: Therefore, disable all other EMACS, since we handle */ + /* NOTE: only one emac at a time */ + reg = 0; + out32 (ZMII_FER, 0); + udelay (100); + +#if defined(CONFIG_440EP) || defined(CONFIG_440GR) + out32 (ZMII_FER, (ZMII_FER_RMII | ZMII_FER_MDI) << ZMII_FER_V (devnum)); +#elif defined(CONFIG_440GX) + ethgroup = ppc_4xx_eth_setup_bridge(devnum, bis); +#elif defined(CONFIG_440GP) + /* set RMII mode */ + out32 (ZMII_FER, ZMII_RMII | ZMII_MDI0); +#else + if ((devnum == 0) || (devnum == 1)) { + out32 (ZMII_FER, (ZMII_FER_SMII | ZMII_FER_MDI) << ZMII_FER_V (devnum)); + } + else { /* ((devnum == 2) || (devnum == 3)) */ + out32 (ZMII_FER, ZMII_FER_MDI << ZMII_FER_V (devnum)); + out32 (RGMII_FER, ((RGMII_FER_RGMII << RGMII_FER_V (2)) | + (RGMII_FER_RGMII << RGMII_FER_V (3)))); + } +#endif + + out32 (ZMII_SSR, ZMII_SSR_SP << ZMII_SSR_V(devnum)); +#endif /* defined(CONFIG_440) && !defined(CONFIG_440SP) */ + + __asm__ volatile ("eieio"); + + /* reset emac so we have access to the phy */ + + out32 (EMAC_M0 + hw_p->hw_addr, EMAC_M0_SRST); + __asm__ volatile ("eieio"); + + failsafe = 1000; + while ((in32 (EMAC_M0 + hw_p->hw_addr) & (EMAC_M0_SRST)) && failsafe) { + udelay (1000); + failsafe--; + } + +#if defined(CONFIG_440GX) || defined(CONFIG_440SP) + /* Whack the M1 register */ + mode_reg = 0x0; + mode_reg &= ~0x00000038; + if (sysinfo.freqOPB <= 50000000); + else if (sysinfo.freqOPB <= 66666667) + mode_reg |= EMAC_M1_OBCI_66; + else if (sysinfo.freqOPB <= 83333333) + mode_reg |= EMAC_M1_OBCI_83; + else if (sysinfo.freqOPB <= 100000000) + mode_reg |= EMAC_M1_OBCI_100; + else + mode_reg |= EMAC_M1_OBCI_GT100; + + out32 (EMAC_M1 + hw_p->hw_addr, mode_reg); +#endif /* defined(CONFIG_440GX) || defined(CONFIG_440SP) */ + + /* wait for PHY to complete auto negotiation */ + reg_short = 0; +#ifndef CONFIG_CS8952_PHY + switch (devnum) { + case 0: + reg = CONFIG_PHY_ADDR; + break; +#if defined (CONFIG_PHY1_ADDR) + case 1: + reg = CONFIG_PHY1_ADDR; + break; +#endif +#if defined (CONFIG_440GX) + case 2: + reg = CONFIG_PHY2_ADDR; + break; + case 3: + reg = CONFIG_PHY3_ADDR; + break; +#endif + default: + reg = CONFIG_PHY_ADDR; + break; + } + + bis->bi_phynum[devnum] = reg; + +#if defined(CONFIG_PHY_RESET) + /* + * Reset the phy, only if its the first time through + * otherwise, just check the speeds & feeds + */ + if (hw_p->first_init == 0) { + miiphy_reset (dev->name, reg); + +#if defined(CONFIG_440GX) || defined(CONFIG_440SP) +#if defined(CONFIG_CIS8201_PHY) + /* + * Cicada 8201 PHY needs to have an extended register whacked + * for RGMII mode. + */ + if ( ((devnum == 2) || (devnum ==3)) && (4 == ethgroup) ) { +#if defined(CONFIG_CIS8201_SHORT_ETCH) + miiphy_write (dev->name, reg, 23, 0x1300); +#else + miiphy_write (dev->name, reg, 23, 0x1000); +#endif + /* + * Vitesse VSC8201/Cicada CIS8201 errata: + * Interoperability problem with Intel 82547EI phys + * This work around (provided by Vitesse) changes + * the default timer convergence from 8ms to 12ms + */ + miiphy_write (dev->name, reg, 0x1f, 0x2a30); + miiphy_write (dev->name, reg, 0x08, 0x0200); + miiphy_write (dev->name, reg, 0x1f, 0x52b5); + miiphy_write (dev->name, reg, 0x02, 0x0004); + miiphy_write (dev->name, reg, 0x01, 0x0671); + miiphy_write (dev->name, reg, 0x00, 0x8fae); + miiphy_write (dev->name, reg, 0x1f, 0x2a30); + miiphy_write (dev->name, reg, 0x08, 0x0000); + miiphy_write (dev->name, reg, 0x1f, 0x0000); + /* end Vitesse/Cicada errata */ + } +#endif +#endif + /* Start/Restart autonegotiation */ + phy_setup_aneg (dev->name, reg); + udelay (1000); + } +#endif /* defined(CONFIG_PHY_RESET) */ + + miiphy_read (dev->name, reg, PHY_BMSR, ®_short); + + /* + * Wait if PHY is capable 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)) { + /* + * Timeout reached ? + */ + if (i > PHY_AUTONEGOTIATE_TIMEOUT) { + puts (" TIMEOUT !\n"); + break; + } + + if ((i++ % 1000) == 0) { + putc ('.'); + } + udelay (1000); /* 1 ms */ + miiphy_read (dev->name, reg, PHY_BMSR, ®_short); + + } + puts (" done\n"); + udelay (500000); /* another 500 ms (results in faster booting) */ + } +#endif /* #ifndef CONFIG_CS8952_PHY */ + + speed = miiphy_speed (dev->name, reg); + duplex = miiphy_duplex (dev->name, reg); + + if (hw_p->print_speed) { + hw_p->print_speed = 0; + printf ("ENET Speed is %d Mbps - %s duplex connection\n", + (int) speed, (duplex == HALF) ? "HALF" : "FULL"); + } + +#if defined(CONFIG_440) && !defined(CONFIG_440SP) +#if defined(CONFIG_440EP) || defined(CONFIG_440GR) + mfsdr(sdr_mfr, reg); + if (speed == 100) { + reg = (reg & ~SDR0_MFR_ZMII_MODE_MASK) | SDR0_MFR_ZMII_MODE_RMII_100M; + } else { + reg = (reg & ~SDR0_MFR_ZMII_MODE_MASK) | SDR0_MFR_ZMII_MODE_RMII_10M; + } + mtsdr(sdr_mfr, reg); +#endif + + /* Set ZMII/RGMII speed according to the phy link speed */ + reg = in32 (ZMII_SSR); + if ( (speed == 100) || (speed == 1000) ) + out32 (ZMII_SSR, reg | (ZMII_SSR_SP << ZMII_SSR_V (devnum))); + else + out32 (ZMII_SSR, reg & (~(ZMII_SSR_SP << ZMII_SSR_V (devnum)))); + + if ((devnum == 2) || (devnum == 3)) { + if (speed == 1000) + reg = (RGMII_SSR_SP_1000MBPS << RGMII_SSR_V (devnum)); + else if (speed == 100) + reg = (RGMII_SSR_SP_100MBPS << RGMII_SSR_V (devnum)); + else + reg = (RGMII_SSR_SP_10MBPS << RGMII_SSR_V (devnum)); + + out32 (RGMII_SSR, reg); + } +#endif /* defined(CONFIG_440) && !defined(CONFIG_440SP) */ + + /* set the Mal configuration reg */ +#if defined(CONFIG_440GX) || defined(CONFIG_440SP) + mtdcr (malmcr, MAL_CR_PLBB | MAL_CR_OPBBL | MAL_CR_LEA | + MAL_CR_PLBLT_DEFAULT | MAL_CR_EOPIE | 0x00330000); +#else + mtdcr (malmcr, MAL_CR_PLBB | MAL_CR_OPBBL | MAL_CR_LEA | MAL_CR_PLBLT_DEFAULT); + /* Errata 1.12: MAL_1 -- Disable MAL bursting */ + if (get_pvr() == PVR_440GP_RB) { + mtdcr (malmcr, mfdcr(malmcr) & ~MAL_CR_PLBB); + } +#endif + + /* Free "old" buffers */ + if (hw_p->alloc_tx_buf) + free (hw_p->alloc_tx_buf); + if (hw_p->alloc_rx_buf) + free (hw_p->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. + */ + hw_p->alloc_tx_buf = + (mal_desc_t *) malloc ((sizeof (mal_desc_t) * NUM_TX_BUFF) + + ((2 * CFG_CACHELINE_SIZE) - 2)); + if (NULL == hw_p->alloc_tx_buf) + return -1; + if (((int) hw_p->alloc_tx_buf & CACHELINE_MASK) != 0) { + hw_p->tx = + (mal_desc_t *) ((int) hw_p->alloc_tx_buf + + CFG_CACHELINE_SIZE - + ((int) hw_p-> + alloc_tx_buf & CACHELINE_MASK)); + } else { + hw_p->tx = hw_p->alloc_tx_buf; + } + + hw_p->alloc_rx_buf = + (mal_desc_t *) malloc ((sizeof (mal_desc_t) * NUM_RX_BUFF) + + ((2 * CFG_CACHELINE_SIZE) - 2)); + if (NULL == hw_p->alloc_rx_buf) { + free(hw_p->alloc_tx_buf); + hw_p->alloc_tx_buf = NULL; + return -1; + } + + if (((int) hw_p->alloc_rx_buf & CACHELINE_MASK) != 0) { + hw_p->rx = + (mal_desc_t *) ((int) hw_p->alloc_rx_buf + + CFG_CACHELINE_SIZE - + ((int) hw_p-> + alloc_rx_buf & CACHELINE_MASK)); + } else { + hw_p->rx = hw_p->alloc_rx_buf; + } + + for (i = 0; i < NUM_TX_BUFF; i++) { + hw_p->tx[i].ctrl = 0; + hw_p->tx[i].data_len = 0; + if (hw_p->first_init == 0) { + hw_p->txbuf_ptr = + (char *) malloc (ENET_MAX_MTU_ALIGNED); + if (NULL == hw_p->txbuf_ptr) { + free(hw_p->alloc_rx_buf); + free(hw_p->alloc_tx_buf); + hw_p->alloc_rx_buf = NULL; + hw_p->alloc_tx_buf = NULL; + for(j = 0; j < i; j++) { + free(hw_p->tx[i].data_ptr); + hw_p->tx[i].data_ptr = NULL; + } + } + } + hw_p->tx[i].data_ptr = hw_p->txbuf_ptr; + if ((NUM_TX_BUFF - 1) == i) + hw_p->tx[i].ctrl |= MAL_TX_CTRL_WRAP; + hw_p->tx_run[i] = -1; +#if 0 + printf ("TX_BUFF %d @ 0x%08lx\n", i, + (ulong) hw_p->tx[i].data_ptr); +#endif + } + + for (i = 0; i < NUM_RX_BUFF; i++) { + hw_p->rx[i].ctrl = 0; + hw_p->rx[i].data_len = 0; + /* rx[i].data_ptr = (char *) &rx_buff[i]; */ + hw_p->rx[i].data_ptr = (char *) NetRxPackets[i]; + if ((NUM_RX_BUFF - 1) == i) + hw_p->rx[i].ctrl |= MAL_RX_CTRL_WRAP; + hw_p->rx[i].ctrl |= MAL_RX_CTRL_EMPTY | MAL_RX_CTRL_INTR; + hw_p->rx_ready[i] = -1; +#if 0 + printf ("RX_BUFF %d @ 0x%08lx\n", i, (ulong) rx[i].data_ptr); +#endif + } + + reg = 0x00000000; + + reg |= dev->enetaddr[0]; /* set high address */ + reg = reg << 8; + reg |= dev->enetaddr[1]; + + out32 (EMAC_IAH + hw_p->hw_addr, reg); + + reg = 0x00000000; + reg |= dev->enetaddr[2]; /* set low address */ + reg = reg << 8; + reg |= dev->enetaddr[3]; + reg = reg << 8; + reg |= dev->enetaddr[4]; + reg = reg << 8; + reg |= dev->enetaddr[5]; + + out32 (EMAC_IAL + hw_p->hw_addr, reg); + + switch (devnum) { + case 1: + /* setup MAL tx & rx channel pointers */ +#if defined (CONFIG_405EP) || defined (CONFIG_440EP) || defined (CONFIG_440GR) + mtdcr (maltxctp2r, hw_p->tx); +#else + mtdcr (maltxctp1r, hw_p->tx); +#endif +#if defined(CONFIG_440) + mtdcr (maltxbattr, 0x0); + mtdcr (malrxbattr, 0x0); +#endif + mtdcr (malrxctp1r, hw_p->rx); + /* set RX buffer size */ + mtdcr (malrcbs1, ENET_MAX_MTU_ALIGNED / 16); + break; +#if defined (CONFIG_440GX) + case 2: + /* setup MAL tx & rx channel pointers */ + mtdcr (maltxbattr, 0x0); + mtdcr (malrxbattr, 0x0); + mtdcr (maltxctp2r, hw_p->tx); + mtdcr (malrxctp2r, hw_p->rx); + /* set RX buffer size */ + mtdcr (malrcbs2, ENET_MAX_MTU_ALIGNED / 16); + break; + case 3: + /* setup MAL tx & rx channel pointers */ + mtdcr (maltxbattr, 0x0); + mtdcr (maltxctp3r, hw_p->tx); + mtdcr (malrxbattr, 0x0); + mtdcr (malrxctp3r, hw_p->rx); + /* set RX buffer size */ + mtdcr (malrcbs3, ENET_MAX_MTU_ALIGNED / 16); + break; +#endif /* CONFIG_440GX */ + case 0: + default: + /* setup MAL tx & rx channel pointers */ +#if defined(CONFIG_440) + mtdcr (maltxbattr, 0x0); + mtdcr (malrxbattr, 0x0); +#endif + mtdcr (maltxctp0r, hw_p->tx); + mtdcr (malrxctp0r, hw_p->rx); + /* set RX buffer size */ + mtdcr (malrcbs0, ENET_MAX_MTU_ALIGNED / 16); + break; + } + + /* Enable MAL transmit and receive channels */ +#if defined(CONFIG_405EP) || defined(CONFIG_440EP) || defined(CONFIG_440GR) + mtdcr (maltxcasr, (MAL_TXRX_CASR >> (hw_p->devnum*2))); +#else + mtdcr (maltxcasr, (MAL_TXRX_CASR >> hw_p->devnum)); +#endif + mtdcr (malrxcasr, (MAL_TXRX_CASR >> hw_p->devnum)); + + /* set transmit enable & receive enable */ + out32 (EMAC_M0 + hw_p->hw_addr, EMAC_M0_TXE | EMAC_M0_RXE); + + /* set receive fifo to 4k and tx fifo to 2k */ + mode_reg = in32 (EMAC_M1 + hw_p->hw_addr); + mode_reg |= EMAC_M1_RFS_4K | EMAC_M1_TX_FIFO_2K; + + /* set speed */ + if (speed == _1000BASET) { +#if defined(CONFIG_440SP) +#define SDR0_PFC1_EM_1000 0x00200000 + unsigned long pfc1; + mfsdr (sdr_pfc1, pfc1); + pfc1 |= SDR0_PFC1_EM_1000; + mtsdr (sdr_pfc1, pfc1); +#endif + mode_reg = mode_reg | EMAC_M1_MF_1000MBPS | EMAC_M1_IST; + } else 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 + hw_p->hw_addr, mode_reg); + + /* Enable broadcast and indvidual address */ + /* TBS: enabling runts as some misbehaved nics will send runts */ + out32 (EMAC_RXM + hw_p->hw_addr, EMAC_RMR_BAE | EMAC_RMR_IAE); + + /* we probably need to set the tx mode1 reg? maybe at tx time */ + + /* set transmit request threshold register */ + out32 (EMAC_TRTR + hw_p->hw_addr, 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 + hw_p->hw_addr, 0x80009000); +#else + /* 405s have a 16 byte burst length */ + out32 (EMAC_RX_HI_LO_WMARK + hw_p->hw_addr, 0x0f002000); +#endif /* defined(CONFIG_440) */ + out32 (EMAC_TXM1 + hw_p->hw_addr, 0xf8640000); + + /* Set fifo limit entry in tx mode 0 */ + out32 (EMAC_TXM0 + hw_p->hw_addr, 0x00000003); + /* Frame gap set */ + out32 (EMAC_I_FRAME_GAP_REG + hw_p->hw_addr, 0x00000008); + + /* Set EMAC IER */ + hw_p->emac_ier = EMAC_ISR_PTLE | EMAC_ISR_BFCS | EMAC_ISR_ORE | EMAC_ISR_IRE; + if (speed == _100BASET) + hw_p->emac_ier = hw_p->emac_ier | EMAC_ISR_SYE; + + out32 (EMAC_ISR + hw_p->hw_addr, 0xffffffff); /* clear pending interrupts */ + out32 (EMAC_IER + hw_p->hw_addr, hw_p->emac_ier); + + if (hw_p->first_init == 0) { + /* + * Connect interrupt service routines + */ + irq_install_handler (VECNUM_ETH0 + (hw_p->devnum * 2), + (interrupt_handler_t *) enetInt, dev); + } + + mtmsr (msr); /* enable interrupts again */ + + hw_p->bis = bis; + hw_p->first_init = 1; + + return (1); +} + + +static int ppc_4xx_eth_send (struct eth_device *dev, volatile void *ptr, + int len) +{ + struct enet_frame *ef_ptr; + ulong time_start, time_now; + unsigned long temp_txm0; + EMAC_4XX_HW_PST hw_p = dev->priv; + + ef_ptr = (struct enet_frame *) ptr; + + /*-----------------------------------------------------------------------+ + * Copy in our address into the frame. + *-----------------------------------------------------------------------*/ + (void) memcpy (ef_ptr->source_addr, dev->enetaddr, ENET_ADDR_LENGTH); + + /*-----------------------------------------------------------------------+ + * If frame is too long or too short, modify length. + *-----------------------------------------------------------------------*/ + /* TBS: where does the fragment go???? */ + if (len > ENET_MAX_MTU) + len = ENET_MAX_MTU; + + /* memcpy ((void *) &tx_buff[tx_slot], (const void *) ptr, len); */ + memcpy ((void *) hw_p->txbuf_ptr, (const void *) ptr, len); + + /*-----------------------------------------------------------------------+ + * set TX Buffer busy, and send it + *-----------------------------------------------------------------------*/ + hw_p->tx[hw_p->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) == hw_p->tx_slot) + hw_p->tx[hw_p->tx_slot].ctrl |= MAL_TX_CTRL_WRAP; + + hw_p->tx[hw_p->tx_slot].data_len = (short) len; + hw_p->tx[hw_p->tx_slot].ctrl |= MAL_TX_CTRL_READY; + + __asm__ volatile ("eieio"); + + out32 (EMAC_TXM0 + hw_p->hw_addr, + in32 (EMAC_TXM0 + hw_p->hw_addr) | EMAC_TXM0_GNP0); +#ifdef INFO_4XX_ENET + hw_p->stats.pkts_tx++; +#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 + hw_p->hw_addr); + /* 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 (len); + } + } +} + + +#if defined (CONFIG_440) + +#if defined(CONFIG_440SP) +/* + * Hack: On 440SP all enet irq sources are located on UIC1 + * Needs some cleanup. --sr + */ +#define UIC0MSR uic1msr +#define UIC0SR uic1sr +#else +#define UIC0MSR uic0msr +#define UIC0SR uic0sr +#endif + +int enetInt (struct eth_device *dev) +{ + 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; + +#if defined(CONFIG_440GX) + unsigned long my_uic2msr; +#endif + EMAC_4XX_HW_PST hw_p; + + /* + * Because the mal is generic, we need to get the current + * eth device + */ +#if defined(CONFIG_NET_MULTI) + dev = eth_get_dev(); +#else + dev = emac0_dev; +#endif + + hw_p = dev->priv; + + /* enter loop that stays in interrupt code until nothing to service */ + do { + serviced = 0; + + my_uic0msr = mfdcr (UIC0MSR); + my_uic1msr = mfdcr (uic1msr); +#if defined(CONFIG_440GX) + my_uic2msr = mfdcr (uic2msr); +#endif + if (!(my_uic0msr & (UIC_MRE | UIC_MTE)) + && !(my_uic1msr & (UIC_ETH0 | UIC_ETH1 | UIC_MS | UIC_MTDE | UIC_MRDE))) { + /* not for us */ + return (rc); + } +#if defined (CONFIG_440GX) + if (!(my_uic0msr & (UIC_MRE | UIC_MTE)) + && !(my_uic2msr & (UIC_ETH2 | UIC_ETH3))) { + /* not for us */ + return (rc); + } +#endif + /* get and clear controller status interrupts */ + /* look at Mal and EMAC interrupts */ + if ((my_uic0msr & (UIC_MRE | UIC_MTE)) + || (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 (dev, mal_isr, my_uic0msr, + MAL_UIC_DEF, MAL_UIC_ERR); + serviced = 1; + rc = 0; + } + } + + /* port by port dispatch of emac interrupts */ + if (hw_p->devnum == 0) { + if (UIC_ETH0 & my_uic1msr) { /* look for EMAC errors */ + emac_isr = in32 (EMAC_ISR + hw_p->hw_addr); + if ((hw_p->emac_ier & emac_isr) != 0) { + emac_err (dev, emac_isr); + serviced = 1; + rc = 0; + } + } + if ((hw_p->emac_ier & emac_isr) + || (my_uic1msr & (UIC_MS | UIC_MTDE | UIC_MRDE))) { + mtdcr (UIC0SR, UIC_MRE | UIC_MTE); /* Clear */ + mtdcr (uic1sr, UIC_ETH0 | UIC_MS | UIC_MTDE | UIC_MRDE); /* Clear */ + return (rc); /* we had errors so get out */ + } + } + +#if !defined(CONFIG_440SP) + if (hw_p->devnum == 1) { + if (UIC_ETH1 & my_uic1msr) { /* look for EMAC errors */ + emac_isr = in32 (EMAC_ISR + hw_p->hw_addr); + if ((hw_p->emac_ier & emac_isr) != 0) { + emac_err (dev, emac_isr); + serviced = 1; + rc = 0; + } + } + if ((hw_p->emac_ier & emac_isr) + || (my_uic1msr & (UIC_MS | UIC_MTDE | UIC_MRDE))) { + mtdcr (UIC0SR, UIC_MRE | UIC_MTE); /* Clear */ + mtdcr (uic1sr, UIC_ETH1 | UIC_MS | UIC_MTDE | UIC_MRDE); /* Clear */ + return (rc); /* we had errors so get out */ + } + } +#if defined (CONFIG_440GX) + if (hw_p->devnum == 2) { + if (UIC_ETH2 & my_uic2msr) { /* look for EMAC errors */ + emac_isr = in32 (EMAC_ISR + hw_p->hw_addr); + if ((hw_p->emac_ier & emac_isr) != 0) { + emac_err (dev, emac_isr); + serviced = 1; + rc = 0; + } + } + if ((hw_p->emac_ier & emac_isr) + || (my_uic1msr & (UIC_MS | UIC_MTDE | UIC_MRDE))) { + mtdcr (UIC0SR, UIC_MRE | UIC_MTE); /* Clear */ + mtdcr (uic1sr, UIC_MS | UIC_MTDE | UIC_MRDE); /* Clear */ + mtdcr (uic2sr, UIC_ETH2); + return (rc); /* we had errors so get out */ + } + } + + if (hw_p->devnum == 3) { + if (UIC_ETH3 & my_uic2msr) { /* look for EMAC errors */ + emac_isr = in32 (EMAC_ISR + hw_p->hw_addr); + if ((hw_p->emac_ier & emac_isr) != 0) { + emac_err (dev, emac_isr); + serviced = 1; + rc = 0; + } + } + if ((hw_p->emac_ier & emac_isr) + || (my_uic1msr & (UIC_MS | UIC_MTDE | UIC_MRDE))) { + mtdcr (UIC0SR, UIC_MRE | UIC_MTE); /* Clear */ + mtdcr (uic1sr, UIC_MS | UIC_MTDE | UIC_MRDE); /* Clear */ + mtdcr (uic2sr, UIC_ETH3); + return (rc); /* we had errors so get out */ + } + } +#endif /* CONFIG_440GX */ +#endif /* !CONFIG_440SP */ + + /* handle MAX TX EOB interrupt from a tx */ + if (my_uic0msr & UIC_MTE) { + mal_rx_eob = mfdcr (maltxeobisr); + mtdcr (maltxeobisr, mal_rx_eob); + mtdcr (UIC0SR, UIC_MTE); + } + /* 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 >> hw_p->devnum)) != 0) { /* call emac routine for channel x */ + /* clear EOB + mtdcr(malrxeobisr, mal_rx_eob); */ + enet_rcv (dev, emac_isr); + /* indicate that we serviced an interrupt */ + serviced = 1; + rc = 0; + } + } + + mtdcr (UIC0SR, UIC_MRE); /* Clear */ + mtdcr (uic1sr, UIC_MS | UIC_MTDE | UIC_MRDE); /* Clear */ + switch (hw_p->devnum) { + case 0: + mtdcr (uic1sr, UIC_ETH0); + break; + case 1: + mtdcr (uic1sr, UIC_ETH1); + break; +#if defined (CONFIG_440GX) + case 2: + mtdcr (uic2sr, UIC_ETH2); + break; + case 3: + mtdcr (uic2sr, UIC_ETH3); + break; +#endif /* CONFIG_440GX */ + default: + break; + } + } while (serviced); + + return (rc); +} + +#else /* CONFIG_440 */ + +int enetInt (struct eth_device *dev) +{ + 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; + + EMAC_4XX_HW_PST hw_p; + + /* + * Because the mal is generic, we need to get the current + * eth device + */ +#if defined(CONFIG_NET_MULTI) + dev = eth_get_dev(); +#else + dev = emac0_dev; +#endif + + hw_p = dev->priv; + + /* 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 (dev, mal_isr, my_uicmsr, MAL_UIC_DEF, MAL_UIC_ERR); + serviced = 1; + rc = 0; + } + } + + /* port by port dispatch of emac interrupts */ + + if ((SEL_UIC_DEF(hw_p->devnum) & my_uicmsr) != 0) { /* look for EMAC errors */ + emac_isr = in32 (EMAC_ISR + hw_p->hw_addr); + if ((hw_p->emac_ier & emac_isr) != 0) { + emac_err (dev, emac_isr); + serviced = 1; + rc = 0; + } + } + if (((hw_p->emac_ier & emac_isr) != 0) || ((MAL_UIC_ERR & my_uicmsr) != 0)) { + mtdcr (uicsr, MAL_UIC_DEF | SEL_UIC_DEF(hw_p->devnum)); /* Clear */ + return (rc); /* we had errors so get out */ + } + + /* handle MAX TX EOB interrupt from a tx */ + if (my_uicmsr & UIC_MAL_TXEOB) { + mal_rx_eob = mfdcr (maltxeobisr); + mtdcr (maltxeobisr, mal_rx_eob); + mtdcr (uicsr, UIC_MAL_TXEOB); + } + /* handle MAL RX EOB interupt from a receive */ + /* check for EOB on valid channels */ + if (my_uicmsr & UIC_MAL_RXEOB) + { + mal_rx_eob = mfdcr (malrxeobisr); + if ((mal_rx_eob & (0x80000000 >> hw_p->devnum)) != 0) { /* call emac routine for channel x */ + /* clear EOB + mtdcr(malrxeobisr, mal_rx_eob); */ + enet_rcv (dev, emac_isr); + /* indicate that we serviced an interrupt */ + serviced = 1; + rc = 0; + } + } + mtdcr (uicsr, MAL_UIC_DEF|EMAC_UIC_DEF|EMAC_UIC_DEF1); /* Clear */ + } + while (serviced); + + return (rc); +} + +#endif /* CONFIG_440 */ + +/*-----------------------------------------------------------------------------+ + * MAL Error Routine + *-----------------------------------------------------------------------------*/ +static void mal_err (struct eth_device *dev, unsigned long isr, + unsigned long uic, unsigned long maldef, + unsigned long mal_errr) +{ + EMAC_4XX_HW_PST hw_p = dev->priv; + + mtdcr (malesr, isr); /* clear interrupt */ + + /* clear DE interrupt */ + mtdcr (maltxdeir, 0xC0000000); + mtdcr (malrxdeir, 0x80000000); + +#ifdef INFO_4XX_ENET + printf ("\nMAL error occured.... ISR = %lx UIC = = %lx MAL_DEF = %lx MAL_ERR= %lx \n", isr, uic, maldef, mal_errr); +#endif + + eth_init (hw_p->bis); /* start again... */ +} + +/*-----------------------------------------------------------------------------+ + * EMAC Error Routine + *-----------------------------------------------------------------------------*/ +static void emac_err (struct eth_device *dev, unsigned long isr) +{ + EMAC_4XX_HW_PST hw_p = dev->priv; + + printf ("EMAC%d error occured.... ISR = %lx\n", hw_p->devnum, isr); + out32 (EMAC_ISR + hw_p->hw_addr, isr); +} + +/*-----------------------------------------------------------------------------+ + * enet_rcv() handles the ethernet receive data + *-----------------------------------------------------------------------------*/ +static void enet_rcv (struct eth_device *dev, unsigned long malisr) +{ + struct enet_frame *ef_ptr; + unsigned long data_len; + unsigned long rx_eob_isr; + EMAC_4XX_HW_PST hw_p = dev->priv; + + int handled = 0; + int i; + int loop_count = 0; + + rx_eob_isr = mfdcr (malrxeobisr); + if ((0x80000000 >> hw_p->devnum) & rx_eob_isr) { + /* clear EOB */ + mtdcr (malrxeobisr, rx_eob_isr); + + /* EMAC RX done */ + while (1) { /* do all */ + i = hw_p->rx_slot; + + if ((MAL_RX_CTRL_EMPTY & hw_p->rx[i].ctrl) + || (loop_count >= NUM_RX_BUFF)) + break; + loop_count++; + hw_p->rx_slot++; + if (NUM_RX_BUFF == hw_p->rx_slot) + hw_p->rx_slot = 0; + handled++; + data_len = (unsigned long) hw_p->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 & hw_p->rx[i].ctrl) { /* Check Errors */ + data_len = 0; + hw_p->stats.rx_err_log[hw_p-> + rx_err_index] + = hw_p->rx[i].ctrl; + hw_p->rx_err_index++; + if (hw_p->rx_err_index == + MAX_ERR_LOG) + hw_p->rx_err_index = + 0; + } /* emac_erros */ + } /* data_len < max mtu */ + } /* if data_len */ + if (!data_len) { /* no data */ + hw_p->rx[i].ctrl |= MAL_RX_CTRL_EMPTY; /* Free Recv Buffer */ + + hw_p->stats.data_len_err++; /* Error at Rx */ + } + + /* !data_len */ + /* AS.HARNOIS */ + /* Check if user has already eaten buffer */ + /* if not => ERROR */ + else if (hw_p->rx_ready[hw_p->rx_i_index] != -1) { + if (hw_p->is_receiving) + printf ("ERROR : Receive buffers are full!\n"); + break; + } else { + hw_p->stats.rx_frames++; + hw_p->stats.rx += data_len; + ef_ptr = (struct enet_frame *) hw_p->rx[i]. + data_ptr; +#ifdef INFO_4XX_ENET + hw_p->stats.pkts_rx++; +#endif + /* AS.HARNOIS + * use ring buffer + */ + hw_p->rx_ready[hw_p->rx_i_index] = i; + hw_p->rx_i_index++; + if (NUM_RX_BUFF == hw_p->rx_i_index) + hw_p->rx_i_index = 0; + + /* 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 */ +} + + +static int ppc_4xx_eth_rx (struct eth_device *dev) +{ + int length; + int user_index; + unsigned long msr; + EMAC_4XX_HW_PST hw_p = dev->priv; + + hw_p->is_receiving = 1; /* tell driver */ + + for (;;) { + /* AS.HARNOIS + * use ring buffer and + * get index from rx buffer desciptor queue + */ + user_index = hw_p->rx_ready[hw_p->rx_u_index]; + if (user_index == -1) { + length = -1; + break; /* nothing received - leave for() loop */ + } + + msr = mfmsr (); + mtmsr (msr & ~(MSR_EE)); + + length = hw_p->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 */ + hw_p->rx[user_index].ctrl |= MAL_RX_CTRL_EMPTY; + /* Free rx buffer descriptor queue */ + hw_p->rx_ready[hw_p->rx_u_index] = -1; + hw_p->rx_u_index++; + if (NUM_RX_BUFF == hw_p->rx_u_index) + hw_p->rx_u_index = 0; + +#ifdef INFO_4XX_ENET + hw_p->stats.pkts_handled++; +#endif + + mtmsr (msr); /* Enable IRQ's */ + } + + hw_p->is_receiving = 0; /* tell driver */ + + return length; +} + +int ppc_4xx_eth_initialize (bd_t * bis) +{ + static int virgin = 0; + struct eth_device *dev; + int eth_num = 0; + EMAC_4XX_HW_PST hw = NULL; + +#if defined(CONFIG_440GX) + unsigned long pfc1; + + mfsdr (sdr_pfc1, pfc1); + pfc1 &= ~(0x01e00000); + pfc1 |= 0x01200000; + mtsdr (sdr_pfc1, pfc1); +#endif + /* set phy num and mode */ + bis->bi_phynum[0] = CONFIG_PHY_ADDR; +#if defined(CONFIG_PHY1_ADDR) + bis->bi_phynum[1] = CONFIG_PHY1_ADDR; +#endif +#if defined(CONFIG_440GX) + bis->bi_phynum[2] = CONFIG_PHY2_ADDR; + bis->bi_phynum[3] = CONFIG_PHY3_ADDR; + bis->bi_phymode[0] = 0; + bis->bi_phymode[1] = 0; + bis->bi_phymode[2] = 2; + bis->bi_phymode[3] = 2; + +#if defined (CONFIG_440GX) + ppc_4xx_eth_setup_bridge(0, bis); +#endif +#endif + + for (eth_num = 0; eth_num < LAST_EMAC_NUM; eth_num++) { + + /* See if we can actually bring up the interface, otherwise, skip it */ + switch (eth_num) { + default: /* fall through */ + case 0: + if (memcmp (bis->bi_enetaddr, "\0\0\0\0\0\0", 6) == 0) { + bis->bi_phymode[eth_num] = BI_PHYMODE_NONE; + continue; + } + break; +#ifdef CONFIG_HAS_ETH1 + case 1: + if (memcmp (bis->bi_enet1addr, "\0\0\0\0\0\0", 6) == 0) { + bis->bi_phymode[eth_num] = BI_PHYMODE_NONE; + continue; + } + break; +#endif +#ifdef CONFIG_HAS_ETH2 + case 2: + if (memcmp (bis->bi_enet2addr, "\0\0\0\0\0\0", 6) == 0) { + bis->bi_phymode[eth_num] = BI_PHYMODE_NONE; + continue; + } + break; +#endif +#ifdef CONFIG_HAS_ETH3 + case 3: + if (memcmp (bis->bi_enet3addr, "\0\0\0\0\0\0", 6) == 0) { + bis->bi_phymode[eth_num] = BI_PHYMODE_NONE; + continue; + } + break; +#endif + } + + /* Allocate device structure */ + dev = (struct eth_device *) malloc (sizeof (*dev)); + if (dev == NULL) { + printf ("ppc_4xx_eth_initialize: " + "Cannot allocate eth_device %d\n", eth_num); + return (-1); + } + memset(dev, 0, sizeof(*dev)); + + /* Allocate our private use data */ + hw = (EMAC_4XX_HW_PST) malloc (sizeof (*hw)); + if (hw == NULL) { + printf ("ppc_4xx_eth_initialize: " + "Cannot allocate private hw data for eth_device %d", + eth_num); + free (dev); + return (-1); + } + memset(hw, 0, sizeof(*hw)); + + switch (eth_num) { + default: /* fall through */ + case 0: + hw->hw_addr = 0; + memcpy (dev->enetaddr, bis->bi_enetaddr, 6); + break; +#ifdef CONFIG_HAS_ETH1 + case 1: + hw->hw_addr = 0x100; + memcpy (dev->enetaddr, bis->bi_enet1addr, 6); + break; +#endif +#ifdef CONFIG_HAS_ETH2 + case 2: + hw->hw_addr = 0x400; + memcpy (dev->enetaddr, bis->bi_enet2addr, 6); + break; +#endif +#ifdef CONFIG_HAS_ETH3 + case 3: + hw->hw_addr = 0x600; + memcpy (dev->enetaddr, bis->bi_enet3addr, 6); + break; +#endif + } + + hw->devnum = eth_num; + hw->print_speed = 1; + + sprintf (dev->name, "ppc_4xx_eth%d", eth_num); + dev->priv = (void *) hw; + dev->init = ppc_4xx_eth_init; + dev->halt = ppc_4xx_eth_halt; + dev->send = ppc_4xx_eth_send; + dev->recv = ppc_4xx_eth_rx; + + if (0 == virgin) { + /* 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); + + /* install MAL interrupt handler */ + irq_install_handler (VECNUM_MS, + (interrupt_handler_t *) enetInt, + dev); + irq_install_handler (VECNUM_MTE, + (interrupt_handler_t *) enetInt, + dev); + irq_install_handler (VECNUM_MRE, + (interrupt_handler_t *) enetInt, + dev); + irq_install_handler (VECNUM_TXDE, + (interrupt_handler_t *) enetInt, + dev); + irq_install_handler (VECNUM_RXDE, + (interrupt_handler_t *) enetInt, + dev); + virgin = 1; + } + +#if defined(CONFIG_NET_MULTI) + eth_register (dev); +#else + emac0_dev = dev; +#endif +#if defined(CONFIG_MII) || (CONFIG_COMMANDS & CFG_CMD_MII) + miiphy_register (dev->name, + emac4xx_miiphy_read, emac4xx_miiphy_write); +#endif + + } /* end for each supported device */ + return (1); +} + + +#if !defined(CONFIG_NET_MULTI) +void eth_halt (void) { + if (emac0_dev) { + ppc_4xx_eth_halt(emac0_dev); + free(emac0_dev); + emac0_dev = NULL; + } +} + +int eth_init (bd_t *bis) +{ + ppc_4xx_eth_initialize(bis); + if (emac0_dev) { + return ppc_4xx_eth_init(emac0_dev, bis); + } else { + printf("ERROR: ethaddr not set!\n"); + return -1; + } +} + +int eth_send(volatile void *packet, int length) +{ + return (ppc_4xx_eth_send(emac0_dev, packet, length)); +} + +int eth_rx(void) +{ + return (ppc_4xx_eth_rx(emac0_dev)); +} + +int emac4xx_miiphy_initialize (bd_t * bis) +{ +#if defined(CONFIG_MII) || (CONFIG_COMMANDS & CFG_CMD_MII) + miiphy_register ("ppc_4xx_eth0", + emac4xx_miiphy_read, emac4xx_miiphy_write); +#endif + + return 0; +} +#endif /* !defined(CONFIG_NET_MULTI) */ + +#endif /* #if (CONFIG_COMMANDS & CFG_CMD_NET) */ diff --git a/cpu/ppc4xx/Makefile b/cpu/ppc4xx/Makefile new file mode 100755 index 0000000..c563457 --- /dev/null +++ b/cpu/ppc4xx/Makefile @@ -0,0 +1,50 @@ +# +# (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 $(TOPDIR)/config.mk + +LIB = lib$(CPU).a + +START = start.o resetvec.o kgdb.o +AOBJS = dcr.o +COBJS = 405gp_pci.o 4xx_enet.o \ + bedbug_405.o commproc.o \ + cpu.o cpu_init.o i2c.o interrupts.o \ + miiphy.o sdram.o serial.o \ + spd_sdram.o speed.o traps.o usb_ohci.o usbdev.o + +OBJS = $(AOBJS) $(COBJS) + +all: .depend $(START) $(LIB) + +$(LIB): $(OBJS) + $(AR) crv $@ $(OBJS) + +######################################################################### + +.depend: Makefile $(START:.o=.S) $(AOBJS:.o=.S) $(COBJS:.o=.c) + $(CC) -M $(CFLAGS) $(START:.o=.S) $(AOBJS:.o=.S) $(COBJS:.o=.c) > $@ + +sinclude .depend + +######################################################################### diff --git a/cpu/ppc4xx/bedbug_405.c b/cpu/ppc4xx/bedbug_405.c new file mode 100755 index 0000000..a3c2119 --- /dev/null +++ b/cpu/ppc4xx/bedbug_405.c @@ -0,0 +1,308 @@ +/* + * Bedbug Functions specific to the PPC405 chip + */ + +#include <common.h> +#include <command.h> +#include <linux/ctype.h> +#include <bedbug/type.h> +#include <bedbug/bedbug.h> +#include <bedbug/regs.h> +#include <bedbug/ppc.h> + +#if (CONFIG_COMMANDS & CFG_CMD_BEDBUG) && defined(CONFIG_4xx) + +#define MAX_BREAK_POINTS 4 + +extern CPU_DEBUG_CTX bug_ctx; + +void bedbug405_init __P ((void)); +void bedbug405_do_break __P ((cmd_tbl_t *, int, int, char *[])); +void bedbug405_break_isr __P ((struct pt_regs *)); +int bedbug405_find_empty __P ((void)); +int bedbug405_set __P ((int, unsigned long)); +int bedbug405_clear __P ((int)); + + +/* ====================================================================== + * Initialize the global bug_ctx structure for the AMCC PPC405. Clear all + * of the breakpoints. + * ====================================================================== */ + +void bedbug405_init (void) +{ + int i; + + /* -------------------------------------------------- */ + + bug_ctx.hw_debug_enabled = 0; + bug_ctx.stopped = 0; + bug_ctx.current_bp = 0; + bug_ctx.regs = NULL; + + bug_ctx.do_break = bedbug405_do_break; + bug_ctx.break_isr = bedbug405_break_isr; + bug_ctx.find_empty = bedbug405_find_empty; + bug_ctx.set = bedbug405_set; + bug_ctx.clear = bedbug405_clear; + + for (i = 1; i <= MAX_BREAK_POINTS; ++i) + (*bug_ctx.clear) (i); + + puts ("BEDBUG:ready\n"); + return; +} /* bedbug_init_breakpoints */ + + + +/* ====================================================================== + * Set/clear/show one of the hardware breakpoints for the 405. The "off" + * string will disable a specific breakpoint. The "show" string will + * display the current breakpoints. Otherwise an address will set a + * breakpoint at that address. Setting a breakpoint uses the CPU-specific + * set routine which will assign a breakpoint number. + * ====================================================================== */ + +void bedbug405_do_break (cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) +{ + long addr = 0; /* Address to break at */ + int which_bp; /* Breakpoint number */ + + /* -------------------------------------------------- */ + + if (argc < 2) { + printf ("Usage:\n%s\n", cmdtp->usage); + return; + } + + /* Turn off a breakpoint */ + + if (strcmp (argv[1], "off") == 0) { + if (bug_ctx.hw_debug_enabled == 0) { + printf ("No breakpoints enabled\n"); + return; + } + + which_bp = simple_strtoul (argv[2], NULL, 10); + + if (bug_ctx.clear) + (*bug_ctx.clear) (which_bp); + + printf ("Breakpoint %d removed\n", which_bp); + return; + } + + /* Show a list of breakpoints */ + + if (strcmp (argv[1], "show") == 0) { + for (which_bp = 1; which_bp <= MAX_BREAK_POINTS; ++which_bp) { + + switch (which_bp) { + case 1: + addr = GET_IAC1 (); + break; + case 2: + addr = GET_IAC2 (); + break; + case 3: + addr = GET_IAC3 (); + break; + case 4: + addr = GET_IAC4 (); + break; + } + + printf ("Breakpoint [%d]: ", which_bp); + if (addr == 0) + printf ("NOT SET\n"); + else + disppc ((unsigned char *) addr, 0, 1, bedbug_puts, + F_RADHEX); + } + return; + } + + /* Set a breakpoint at the address */ + + if (!isdigit (argv[1][0])) { + printf ("Usage:\n%s\n", cmdtp->usage); + return; + } + + addr = simple_strtoul (argv[1], NULL, 16) & 0xfffffffc; + + if ((bug_ctx.set) && (which_bp = (*bug_ctx.set) (0, addr)) > 0) { + printf ("Breakpoint [%d]: ", which_bp); + disppc ((unsigned char *) addr, 0, 1, bedbug_puts, F_RADHEX); + } + + return; +} /* bedbug405_do_break */ + + + +/* ====================================================================== + * Handle a breakpoint. First determine which breakpoint was hit by + * looking at the DeBug Status Register (DBSR), clear the breakpoint + * and enter a mini main loop. Stay in the loop until the stopped flag + * in the debug context is cleared. + * ====================================================================== */ + +void bedbug405_break_isr (struct pt_regs *regs) +{ + unsigned long dbsr_val; /* Value of the DBSR */ + unsigned long addr = 0; /* Address stopped at */ + + /* -------------------------------------------------- */ + + dbsr_val = GET_DBSR (); + + if (dbsr_val & DBSR_IA1) { + bug_ctx.current_bp = 1; + addr = GET_IAC1 (); + SET_DBSR (DBSR_IA1); /* Write a 1 to clear */ + } else if (dbsr_val & DBSR_IA2) { + bug_ctx.current_bp = 2; + addr = GET_IAC2 (); + SET_DBSR (DBSR_IA2); /* Write a 1 to clear */ + } else if (dbsr_val & DBSR_IA3) { + bug_ctx.current_bp = 3; + addr = GET_IAC3 (); + SET_DBSR (DBSR_IA3); /* Write a 1 to clear */ + } else if (dbsr_val & DBSR_IA4) { + bug_ctx.current_bp = 4; + addr = GET_IAC4 (); + SET_DBSR (DBSR_IA4); /* Write a 1 to clear */ + } + + bedbug_main_loop (addr, regs); + return; +} /* bedbug405_break_isr */ + + + +/* ====================================================================== + * Look through all of the hardware breakpoints available to see if one + * is unused. + * ====================================================================== */ + +int bedbug405_find_empty (void) +{ + /* -------------------------------------------------- */ + + if (GET_IAC1 () == 0) + return 1; + + if (GET_IAC2 () == 0) + return 2; + + if (GET_IAC3 () == 0) + return 3; + + if (GET_IAC4 () == 0) + return 4; + + return 0; +} /* bedbug405_find_empty */ + + + +/* ====================================================================== + * Set a breakpoint. If 'which_bp' is zero then find an unused breakpoint + * number, otherwise reassign the given breakpoint. If hardware debugging + * is not enabled, then turn it on via the MSR and DBCR0. Set the break + * address in the appropriate IACx register and enable proper address + * beakpoint in DBCR0. + * ====================================================================== */ + +int bedbug405_set (int which_bp, unsigned long addr) +{ + /* -------------------------------------------------- */ + + /* Only look if which_bp == 0, else use which_bp */ + if ((bug_ctx.find_empty) && (!which_bp) && + (which_bp = (*bug_ctx.find_empty) ()) == 0) { + printf ("All breakpoints in use\n"); + return 0; + } + + if (which_bp < 1 || which_bp > MAX_BREAK_POINTS) { + printf ("Invalid break point # %d\n", which_bp); + return 0; + } + + if (!bug_ctx.hw_debug_enabled) { + SET_MSR (GET_MSR () | 0x200); /* set MSR[ DE ] */ + SET_DBCR0 (GET_DBCR0 () | DBCR0_IDM); + bug_ctx.hw_debug_enabled = 1; + } + + switch (which_bp) { + case 1: + SET_IAC1 (addr); + SET_DBCR0 (GET_DBCR0 () | DBCR0_IA1); + break; + + case 2: + SET_IAC2 (addr); + SET_DBCR0 (GET_DBCR0 () | DBCR0_IA2); + break; + + case 3: + SET_IAC3 (addr); + SET_DBCR0 (GET_DBCR0 () | DBCR0_IA3); + break; + + case 4: + SET_IAC4 (addr); + SET_DBCR0 (GET_DBCR0 () | DBCR0_IA4); + break; + } + + return which_bp; +} /* bedbug405_set */ + + + +/* ====================================================================== + * Disable a specific breakoint by setting the appropriate IACx register + * to zero and claring the instruction address breakpoint in DBCR0. + * ====================================================================== */ + +int bedbug405_clear (int which_bp) +{ + /* -------------------------------------------------- */ + + if (which_bp < 1 || which_bp > MAX_BREAK_POINTS) { + printf ("Invalid break point # (%d)\n", which_bp); + return -1; + } + + switch (which_bp) { + case 1: + SET_IAC1 (0); + SET_DBCR0 (GET_DBCR0 () & ~DBCR0_IA1); + break; + + case 2: + SET_IAC2 (0); + SET_DBCR0 (GET_DBCR0 () & ~DBCR0_IA2); + break; + + case 3: + SET_IAC3 (0); + SET_DBCR0 (GET_DBCR0 () & ~DBCR0_IA3); + break; + + case 4: + SET_IAC4 (0); + SET_DBCR0 (GET_DBCR0 () & ~DBCR0_IA4); + break; + } + + return 0; +} /* bedbug405_clear */ + + +/* ====================================================================== */ +#endif diff --git a/cpu/ppc4xx/commproc.c b/cpu/ppc4xx/commproc.c new file mode 100755 index 0000000..68aab5b --- /dev/null +++ b/cpu/ppc4xx/commproc.c @@ -0,0 +1,69 @@ +/* + * (C) Copyright 2000-2004 + * 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 + * + * + * Atapted for ppc4XX by Denis Peter + */ + +#include <common.h> +#include <commproc.h> + + +#if defined(CONFIG_POST) || defined(CONFIG_LOGBUFFER) + +void post_word_store (ulong a) +{ + volatile void *save_addr = (volatile void *)(CFG_OCM_DATA_ADDR + CFG_POST_WORD_ADDR); + *(volatile ulong *) save_addr = a; +} + +ulong post_word_load (void) +{ + volatile void *save_addr = (volatile void *)(CFG_OCM_DATA_ADDR + CFG_POST_WORD_ADDR); + return *(volatile ulong *) save_addr; +} + +#endif /* CONFIG_POST || CONFIG_LOGBUFFER*/ + +#ifdef CONFIG_BOOTCOUNT_LIMIT + +void bootcount_store (ulong a) +{ + volatile ulong *save_addr = + (volatile ulong *)(CFG_OCM_DATA_ADDR + CFG_BOOTCOUNT_ADDR); + + save_addr[0] = a; + save_addr[1] = BOOTCOUNT_MAGIC; +} + +ulong bootcount_load (void) +{ + volatile ulong *save_addr = + (volatile ulong *)(CFG_OCM_DATA_ADDR + CFG_BOOTCOUNT_ADDR); + + if (save_addr[1] != BOOTCOUNT_MAGIC) + return 0; + else + return save_addr[0]; +} + +#endif /* CONFIG_BOOTCOUNT_LIMIT */ diff --git a/cpu/ppc4xx/config.mk b/cpu/ppc4xx/config.mk new file mode 100755 index 0000000..119e061 --- /dev/null +++ b/cpu/ppc4xx/config.mk @@ -0,0 +1,26 @@ +# +# (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 +# + +PLATFORM_RELFLAGS += -fPIC -ffixed-r14 -meabi -fno-strict-aliasing + +PLATFORM_CPPFLAGS += -DCONFIG_4xx -ffixed-r2 -ffixed-r29 -mstring -Wa,-m405 -mcpu=405 -msoft-float diff --git a/cpu/ppc4xx/cpu.c b/cpu/ppc4xx/cpu.c new file mode 100755 index 0000000..a26533c --- /dev/null +++ b/cpu/ppc4xx/cpu.c @@ -0,0 +1,371 @@ +/* + * (C) Copyright 2000-2003 + * 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 + */ + +/* + * 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) +#define FREQ_EBC (sys_info.freqEPB) +#else +#define FREQ_EBC (sys_info.freqPLB / sys_info.pllExtBusDiv) +#endif + +#if defined(CONFIG_405GP) || defined(CONFIG_440EP) || defined(CONFIG_440GR) + +#define PCI_ASYNC + +int pci_async_enabled(void) +{ +#if defined(CONFIG_405GP) + return (mfdcr(strap) & PSR_PCI_ASYNC_EN); +#endif + +#if defined(CONFIG_440EP) || defined(CONFIG_440GR) + unsigned long val; + + mfsdr(sdr_sdstp1, val); + return (val & SDR0_SDSTP1_PAME_MASK); +#endif +} +#endif + +#if defined(CONFIG_PCI) && !defined(CONFIG_IOP480) && !defined(CONFIG_405) +int pci_arbiter_enabled(void) +{ +#if defined(CONFIG_405GP) + return (mfdcr(strap) & PSR_PCI_ARBIT_EN); +#endif + +#if defined(CONFIG_405EP) + return (mfdcr(cpc0_pci) & CPC0_PCI_ARBIT_EN); +#endif + +#if defined(CONFIG_440GP) + return (mfdcr(cpc0_strp1) & CPC0_STRP1_PAE_MASK); +#endif + +#if defined(CONFIG_440GX) || defined(CONFIG_440EP) || defined(CONFIG_440GR) || defined(CONFIG_440SP) + unsigned long val; + + mfsdr(sdr_sdstp1, val); + return (val & SDR0_SDSTP1_PAE_MASK); +#endif +} +#endif + +#if defined(CONFIG_405EP)|| defined(CONFIG_440EP) || defined(CONFIG_440GR) || \ + defined(CONFIG_440GX) || defined(CONFIG_440SP) + +#define I2C_BOOTROM + +int i2c_bootrom_enabled(void) +{ +#if defined(CONFIG_405EP) + return (mfdcr(cpc0_boot) & CPC0_BOOT_SEP); +#endif + +#if defined(CONFIG_440GX) || defined(CONFIG_440EP) || defined(CONFIG_440GR) || defined(CONFIG_440SP) + unsigned long val; + + mfsdr(sdr_sdcs, val); + return (val & SDR0_SDCS_SDD); +#endif +} +#endif + + +#if defined(CONFIG_440) +static int do_chip_reset(unsigned long sys0, unsigned long sys1); +#endif + + +int checkcpu (void) +{ +#if !defined(CONFIG_405) /* not used on Xilinx 405 FPGA implementations */ + DECLARE_GLOBAL_DATA_PTR; + uint pvr = get_pvr(); + ulong clock = gd->cpu_clk; + char buf[32]; + +#if !defined(CONFIG_IOP480) + sys_info_t sys_info; + + puts ("CPU: "); + + get_sys_info(&sys_info); + + puts("AMCC PowerPC 4"); + +#if defined(CONFIG_405GP) || defined(CONFIG_405CR) || defined(CONFIG_405EP) + puts("05"); +#endif +#if defined(CONFIG_440) + puts("40"); +#endif + + switch (pvr) { + case PVR_405GP_RB: + puts("GP Rev. B"); + break; + + case PVR_405GP_RC: + puts("GP Rev. C"); + break; + + case PVR_405GP_RD: + puts("GP Rev. D"); + break; + +#ifdef CONFIG_405GP + case PVR_405GP_RE: /* 405GP rev E and 405CR rev C have same PVR */ + puts("GP Rev. E"); + break; +#endif + + case PVR_405CR_RA: + puts("CR Rev. A"); + break; + + case PVR_405CR_RB: + puts("CR Rev. B"); + break; + +#ifdef CONFIG_405CR + case PVR_405CR_RC: /* 405GP rev E and 405CR rev C have same PVR */ + puts("CR Rev. C"); + break; +#endif + + case PVR_405GPR_RB: + puts("GPr Rev. B"); + break; + + case PVR_405EP_RB: + puts("EP Rev. B"); + break; + +#if defined(CONFIG_440) + case PVR_440GP_RB: + puts("GP Rev. 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: + puts("GP Rev. C"); + break; + + case PVR_440GX_RA: + puts("GX Rev. A"); + break; + + case PVR_440GX_RB: + puts("GX Rev. B"); + break; + + case PVR_440GX_RC: + puts("GX Rev. C"); + break; + + case PVR_440GX_RF: + puts("GX Rev. F"); + break; + + case PVR_440EP_RA: + puts("EP Rev. A"); + break; + +#ifdef CONFIG_440EP + case PVR_440EP_RB: /* 440EP rev B and 440GR rev A have same PVR */ + puts("EP Rev. B"); + break; +#endif /* CONFIG_440EP */ + +#ifdef CONFIG_440GR + case PVR_440GR_RA: /* 440EP rev B and 440GR rev A have same PVR */ + puts("GR Rev. A"); + break; +#endif /* CONFIG_440GR */ +#endif /* CONFIG_440 */ + + case PVR_440SP_RA: + puts("SP Rev. A"); + break; + + case PVR_440SP_RB: + puts("SP Rev. B"); + break; + + default: + printf (" UNKNOWN (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, + FREQ_EBC / 1000000); + +#if defined(I2C_BOOTROM) + printf (" I2C boot EEPROM %sabled\n", i2c_bootrom_enabled() ? "en" : "dis"); +#endif + +#if defined(CONFIG_PCI) + printf (" Internal PCI arbiter %sabled", pci_arbiter_enabled() ? "en" : "dis"); +#endif + +#if defined(PCI_ASYNC) + if (pci_async_enabled()) { + printf (", PCI async ext clock used"); + } else { + printf (", PCI sync clock at %lu MHz", + sys_info.freqPLB / sys_info.pllPciDiv / 1000000); + } +#endif + +#if defined(CONFIG_PCI) + putc('\n'); +#endif + +#if defined(CONFIG_405EP) + printf (" 16 kB I-Cache 16 kB D-Cache"); +#elif defined(CONFIG_440) + printf (" 32 kB I-Cache 32 kB D-Cache"); +#else + printf (" 16 kB I-Cache %d kB D-Cache", + ((pvr | 0x00000001) == PVR_405GPR_RB) ? 16 : 8); +#endif +#endif /* !defined(CONFIG_IOP480) */ + +#if defined(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 + +#endif /* !defined(CONFIG_405) */ + + putc ('\n'); + + return 0; +} + + +/* ------------------------------------------------------------------------- */ + +int do_reset (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ +#if defined(CONFIG_YOSEMITE) || defined(CONFIG_YELLOWSTONE) + /*give reset to BCSR*/ + *(unsigned char*)(CFG_BCSR_BASE | 0x06) = 0x09; + +#else + + /* + * 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 + +#endif/* defined(CONFIG_YOSEMITE) || defined(CONFIG_YELLOWSTONE)*/ + 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_IOP480) + sys_info_t sys_info; + + get_sys_info(&sys_info); + return (sys_info.freqProcessor); +#else + return (66000000); +#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/cpu_init.c b/cpu/ppc4xx/cpu_init.c new file mode 100755 index 0000000..79cfba3 --- /dev/null +++ b/cpu/ppc4xx/cpu_init.c @@ -0,0 +1,268 @@ +/* + * (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 <watchdog.h> +#include <ppc4xx_enet.h> +#include <asm/processor.h> +#include <ppc4xx.h> + + +#define mtebc(reg, data) mtdcr(ebccfga,reg);mtdcr(ebccfgd,data) + +#ifdef CFG_INIT_DCACHE_CS +# if (CFG_INIT_DCACHE_CS == 0) +# define PBxAP pb0ap +# define PBxCR pb0cr +# if (defined(CFG_EBC_PB0AP) && defined(CFG_EBC_PB0CR)) +# define PBxAP_VAL CFG_EBC_PB0AP +# define PBxCR_VAL CFG_EBC_PB0CR +# endif +# endif +# if (CFG_INIT_DCACHE_CS == 1) +# define PBxAP pb1ap +# define PBxCR pb1cr +# if (defined(CFG_EBC_PB1AP) && defined(CFG_EBC_PB1CR)) +# define PBxAP_VAL CFG_EBC_PB1AP +# define PBxCR_VAL CFG_EBC_PB1CR +# endif +# endif +# if (CFG_INIT_DCACHE_CS == 2) +# define PBxAP pb2ap +# define PBxCR pb2cr +# if (defined(CFG_EBC_PB2AP) && defined(CFG_EBC_PB2CR)) +# define PBxAP_VAL CFG_EBC_PB2AP +# define PBxCR_VAL CFG_EBC_PB2CR +# endif +# endif +# if (CFG_INIT_DCACHE_CS == 3) +# define PBxAP pb3ap +# define PBxCR pb3cr +# if (defined(CFG_EBC_PB3AP) && defined(CFG_EBC_PB3CR)) +# define PBxAP_VAL CFG_EBC_PB3AP +# define PBxCR_VAL CFG_EBC_PB3CR +# endif +# endif +# if (CFG_INIT_DCACHE_CS == 4) +# define PBxAP pb4ap +# define PBxCR pb4cr +# if (defined(CFG_EBC_PB4AP) && defined(CFG_EBC_PB4CR)) +# define PBxAP_VAL CFG_EBC_PB4AP +# define PBxCR_VAL CFG_EBC_PB4CR +# endif +# endif +# if (CFG_INIT_DCACHE_CS == 5) +# define PBxAP pb5ap +# define PBxCR pb5cr +# if (defined(CFG_EBC_PB5AP) && defined(CFG_EBC_PB5CR)) +# define PBxAP_VAL CFG_EBC_PB5AP +# define PBxCR_VAL CFG_EBC_PB5CR +# endif +# endif +# if (CFG_INIT_DCACHE_CS == 6) +# define PBxAP pb6ap +# define PBxCR pb6cr +# if (defined(CFG_EBC_PB6AP) && defined(CFG_EBC_PB6CR)) +# define PBxAP_VAL CFG_EBC_PB6AP +# define PBxCR_VAL CFG_EBC_PB6CR +# endif +# endif +# if (CFG_INIT_DCACHE_CS == 7) +# define PBxAP pb7ap +# define PBxCR pb7cr +# if (defined(CFG_EBC_PB7AP) && defined(CFG_EBC_PB7CR)) +# define PBxAP_VAL CFG_EBC_PB7AP +# define PBxCR_VAL CFG_EBC_PB7CR +# endif +# endif +#endif /* CFG_INIT_DCACHE_CS */ + + +/* + * Breath some life into the CPU... + * + * Set up the memory map, + * initialize a bunch of registers + */ +void +cpu_init_f (void) +{ +#if defined(CONFIG_405EP) + /* + * GPIO0 setup (select GPIO or alternate function) + */ + 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); + out32(GPIO0_TCR, CFG_GPIO0_TCR); /* enable output driver for outputs */ + + /* + * Set EMAC noise filter bits + */ + mtdcr(cpc0_epctl, CPC0_EPRCSR_E0NFE | CPC0_EPRCSR_E1NFE); +#endif /* CONFIG_405EP */ + + /* + * External Bus Controller (EBC) Setup + */ +#if (defined(CFG_EBC_PB0AP) && defined(CFG_EBC_PB0CR)) + /* + * 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"); + + 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(CONFIG_WATCHDOG) + unsigned long val; + + val = mfspr(tcr); +#if defined(CONFIG_440EP) || defined(CONFIG_440GR) + val |= 0xb8000000; /* generate system reset after 1.34 seconds */ +#else + val |= 0xf0000000; /* generate system reset after 2.684 seconds */ +#endif + mtspr(tcr, val); + + val = mfspr(tsr); + val |= 0x80000000; /* enable watchdog timer */ + mtspr(tsr, val); + + reset_4xx_watchdog(); +#endif /* CONFIG_WATCHDOG */ +} + +/* + * initialize higher level parts of CPU like time base and timers + */ +int cpu_init_r (void) +{ +#if defined(CONFIG_405GP) || defined(CONFIG_405EP) + DECLARE_GLOBAL_DATA_PTR; + + bd_t *bd = gd->bd; + unsigned long reg; +#if defined(CONFIG_405GP) + uint pvr = get_pvr(); +#endif + +#ifdef CFG_INIT_DCACHE_CS + /* + * Flush and invalidate dcache, then disable CS for temporary stack. + * Afterwards, this CS can be used for other purposes + */ + dcache_disable(); /* flush and invalidate dcache */ + mtebc(PBxAP, 0); + mtebc(PBxCR, 0); /* disable CS for temporary stack */ + +#if (defined(PBxAP_VAL) && defined(PBxCR_VAL)) + /* + * Write new value into CS register + */ + mtebc(PBxAP, PBxAP_VAL); + mtebc(PBxCR, PBxCR_VAL); +#endif +#endif /* CFG_INIT_DCACHE_CS */ + + /* + * 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); +} diff --git a/cpu/ppc4xx/dcr.S b/cpu/ppc4xx/dcr.S new file mode 100755 index 0000000..7102364 --- /dev/null +++ b/cpu/ppc4xx/dcr.S @@ -0,0 +1,198 @@ +/* + * (C) Copyright 2001 + * Erik Theisen, Wave 7 Optics, etheisen@mindspring.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 <config.h> + +#if defined(CONFIG_4xx) && defined(CFG_CMD_SETGETDCR) + +#include <ppc4xx.h> + +#define _LINUX_CONFIG_H 1 /* avoid reading Linux autoconf.h file */ + +#include <ppc_asm.tmpl> +#include <ppc_defs.h> + +#include <asm/cache.h> +#include <asm/mmu.h> + +#define _ASMLANGUAGE + +/***************************************************************************** + * + * XXX - DANGER + * These routines make use of self modifying code. DO NOT CALL THEM + * UNTIL THEY ARE RELOCATED TO RAM. Additionally, I do not + * recommend them for use in anything other than an interactive + * debugging environment. This is mainly due to performance reasons. + * + ****************************************************************************/ + +/* + * static void _create_MFDCR(unsigned short dcrn) + * + * Builds a 'mfdcr' instruction for get_dcr + * function. + */ + .section ".text" + .align 2 + .type _create_MFDCR,@function +_create_MFDCR: + /* + * Build up a 'mfdcr' instruction formatted as follows: + * + * OPCD | RT | DCRF | XO | CR | + * ---------------|--------------|--------------|----| + * 0 5 | 6 10 | 11 20 | 21 30 | 31 | + * | | DCRN | | | + * 31 | %r3 | (5..9|0..4) | 323 | 0 | + * + * Where: + * OPCD = opcode - 31 + * RT = destination register - %r3 return register + * DCRF = DCRN # with upper and lower halves swapped + * XO = extended opcode - 323 + * CR = CR[CR0] NOT undefined - 0 + */ + rlwinm r0, r3, 27, 27, 31 /* OPCD = 31 */ + rlwinm r3, r3, 5, 22, 26 + or r3, r3, r0 + slwi r3, r3, 10 + oris r3, r3, 0x3e30 /* RT = %r3 */ + ori r3, r3, 323 /* XO = 323 */ + slwi r3, r3, 1 /* CR = 0 */ + + mflr r4 + stw r3, 0(r4) /* Store instr in get_dcr() */ + dcbst r0, r4 /* Make sure val is written out */ + sync /* Wait for write to complete */ + icbi r0, r4 /* Make sure old instr is dumped */ + isync /* Wait for icbi to complete */ + + blr +.Lfe1: .size _create_MFDCR,.Lfe1-_create_MFDCR +/* end _create_MFDCR() */ + +/* + * static void _create_MTDCR(unsigned short dcrn, unsigned long value) + * + * Builds a 'mtdcr' instruction for set_dcr + * function. + */ + .section ".text" + .align 2 + .type _create_MTDCR,@function +_create_MTDCR: + /* + * Build up a 'mtdcr' instruction formatted as follows: + * + * OPCD | RS | DCRF | XO | CR | + * ---------------|--------------|--------------|----| + * 0 5 | 6 10 | 11 20 | 21 30 | 31 | + * | | DCRN | | | + * 31 | %r3 | (5..9|0..4) | 451 | 0 | + * + * Where: + * OPCD = opcode - 31 + * RS = source register - %r4 + * DCRF = dest. DCRN # with upper and lower halves swapped + * XO = extended opcode - 451 + * CR = CR[CR0] NOT undefined - 0 + */ + rlwinm r0, r3, 27, 27, 31 /* OPCD = 31 */ + rlwinm r3, r3, 5, 22, 26 + or r3, r3, r0 + slwi r3, r3, 10 + oris r3, r3, 0x3e40 /* RS = %r4 */ + ori r3, r3, 451 /* XO = 451 */ + slwi r3, r3, 1 /* CR = 0 */ + + mflr r5 + stw r3, 0(r5) /* Store instr in set_dcr() */ + dcbst r0, r5 /* Make sure val is written out */ + sync /* Wait for write to complete */ + icbi r0, r5 /* Make sure old instr is dumped */ + isync /* Wait for icbi to complete */ + + blr +.Lfe2: .size _create_MTDCR,.Lfe2-_create_MTDCR +/* end _create_MTDCR() */ + + +/* + * unsigned long get_dcr(unsigned short dcrn) + * + * Return a given DCR's value. + */ + /* */ + /* XXX - This is self modifying code, hence */ + /* it is in the data section. */ + /* */ + .section ".data" + .align 2 + .globl get_dcr + .type get_dcr,@function +get_dcr: + mflr r0 /* Get link register */ + stwu r1, -32(r1) /* Save back chain and move SP */ + stw r0, +36(r1) /* Save link register */ + + bl _create_MFDCR /* Build following instruction */ + /* XXX - we build this instuction up on the fly. */ + .long 0 /* Get DCR's value */ + + lwz r0, +36(r1) /* Get saved link register */ + mtlr r0 /* Restore link register */ + addi r1, r1, +32 /* Remove frame from stack */ + blr /* Return to calling function */ +.Lfe3: .size get_dcr,.Lfe3-get_dcr +/* end get_dcr() */ + + +/* + * unsigned void set_dcr(unsigned short dcrn, unsigned long value) + * + * Return a given DCR's value. + */ + /* + * XXX - This is self modifying code, hence + * it is in the data section. + */ + .section ".data" + .align 2 + .globl set_dcr + .type set_dcr,@function +set_dcr: + mflr r0 /* Get link register */ + stwu r1, -32(r1) /* Save back chain and move SP */ + stw r0, +36(r1) /* Save link register */ + + bl _create_MTDCR /* Build following instruction */ + /* XXX - we build this instuction up on the fly. */ + .long 0 /* Set DCR's value */ + + lwz r0, +36(r1) /* Get saved link register */ + mtlr r0 /* Restore link register */ + addi r1, r1, +32 /* Remove frame from stack */ + blr /* Return to calling function */ +.Lfe4: .size set_dcr,.Lfe4-set_dcr +/* end set_dcr() */ +#endif /* CONFIG_4xx & CFG_CMD_SETGETDCR */ diff --git a/cpu/ppc4xx/i2c.c b/cpu/ppc4xx/i2c.c new file mode 100755 index 0000000..be94b57 --- /dev/null +++ b/cpu/ppc4xx/i2c.c @@ -0,0 +1,445 @@ +/*****************************************************************************/ +/* 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; + +#ifdef CFG_I2C_INIT_BOARD + /* call board specific i2c bus reset routine before accessing the */ + /* environment, which might be in a chip on that bus. For details */ + /* about this problem see doc/I2C_Edge_Conditions. */ + i2c_init_board(); +#endif + + /* Handle possible failed I2C state */ + /* FIXME: put this into i2c_init_board()? */ + _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; + DECLARE_GLOBAL_DATA_PTR; + + 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) { + if (gd->have_console) + 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); +} + +/*----------------------------------------------------------------------- + * Read a register + */ +uchar i2c_reg_read(uchar i2c_addr, uchar reg) +{ + uchar buf; + + i2c_read(i2c_addr, reg, 1, &buf, 1); + + return(buf); +} + +/*----------------------------------------------------------------------- + * Write a register + */ +void i2c_reg_write(uchar i2c_addr, uchar reg, uchar val) +{ + i2c_write(i2c_addr, reg, 1, &val, 1); +} +#endif /* CONFIG_HARD_I2C */ diff --git a/cpu/ppc4xx/interrupts.c b/cpu/ppc4xx/interrupts.c new file mode 100755 index 0000000..1d8dc7c --- /dev/null +++ b/cpu/ppc4xx/interrupts.c @@ -0,0 +1,571 @@ +/* + * (C) Copyright 2000-2002 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * (C) Copyright 2002 (440 port) + * Scott McNutt, Artesyn Communication Producs, smcnutt@artsyncp.com + * + * (C) Copyright 2003 (440GX port) + * Travis B. Sawyer, Sandburst Corporation, tsawyer@sandburst.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 <watchdog.h> +#include <command.h> +#include <asm/processor.h> +#include <ppc4xx.h> +#include <ppc_asm.tmpl> +#include <commproc.h> +#include "vecnum.h" + +/****************************************************************************/ + +/* + * CPM interrupt vector functions. + */ +struct irq_action { + interrupt_handler_t *handler; + void *arg; + int count; +}; + +static struct irq_action irq_vecs[32]; + +#if defined(CONFIG_440) +static struct irq_action irq_vecs1[32]; /* For UIC1 */ + +void uic1_interrupt( void * parms); /* UIC1 handler */ + +#if defined(CONFIG_440GX) +static struct irq_action irq_vecs2[32]; /* For UIC2 */ + +void uic0_interrupt( void * parms); /* UIC0 handler */ +void uic2_interrupt( void * parms); /* UIC2 handler */ +#endif /* CONFIG_440GX */ + +#endif /* CONFIG_440 */ + +/****************************************************************************/ +#if defined(CONFIG_440) + +/* SPRN changed in 440 */ +static __inline__ void set_evpr(unsigned long val) +{ + asm volatile("mtspr 0x03f,%0" : : "r" (val)); +} + +#else /* !defined(CONFIG_440) */ + +static __inline__ void set_pit(unsigned long val) +{ + asm volatile("mtpit %0" : : "r" (val)); +} + + +static __inline__ void set_tcr(unsigned long val) +{ + asm volatile("mttcr %0" : : "r" (val)); +} + + +static __inline__ void set_evpr(unsigned long val) +{ + asm volatile("mtevpr %0" : : "r" (val)); +} +#endif /* defined(CONFIG_440 */ + +/****************************************************************************/ + +int interrupt_init_cpu (unsigned *decrementer_count) +{ + DECLARE_GLOBAL_DATA_PTR; + + int vec; + unsigned long val; + + /* decrementer is automatically reloaded */ + *decrementer_count = 0; + + /* + * Mark all irqs as free + */ + for (vec=0; vec<32; vec++) { + irq_vecs[vec].handler = NULL; + irq_vecs[vec].arg = NULL; + irq_vecs[vec].count = 0; +#if defined(CONFIG_440) + irq_vecs1[vec].handler = NULL; + irq_vecs1[vec].arg = NULL; + irq_vecs1[vec].count = 0; +#if defined(CONFIG_440GX) + irq_vecs2[vec].handler = NULL; + irq_vecs2[vec].arg = NULL; + irq_vecs2[vec].count = 0; +#endif /* CONFIG_440GX */ +#endif + } + +#ifdef CONFIG_4xx + /* + * Init PIT + */ +#if defined(CONFIG_440) + val = mfspr( tcr ); + val &= (~0x04400000); /* clear DIS & ARE */ + mtspr( tcr, val ); + mtspr( dec, 0 ); /* Prevent exception after TSR clear*/ + mtspr( decar, 0 ); /* clear reload */ + mtspr( tsr, 0x08000000 ); /* clear DEC status */ + val = gd->bd->bi_intfreq/1000; /* 1 msec */ + mtspr( decar, val ); /* Set auto-reload value */ + mtspr( dec, val ); /* Set inital val */ +#else + set_pit(gd->bd->bi_intfreq / 1000); +#endif +#endif /* CONFIG_4xx */ + +#ifdef CONFIG_ADCIOP + /* + * Init PIT + */ + set_pit(66000); +#endif + + /* + * Enable PIT + */ + val = mfspr(tcr); + val |= 0x04400000; + mtspr(tcr, val); + + /* + * Set EVPR to 0 + */ + set_evpr(0x00000000); + +#if defined(CONFIG_440) +#if !defined(CONFIG_440GX) + /* Install the UIC1 handlers */ + irq_install_handler(VECNUM_UIC1NC, uic1_interrupt, 0); + irq_install_handler(VECNUM_UIC1C, uic1_interrupt, 0); +#endif +#endif + +#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); + + /* Enable UIC interrupts via UIC Base Enable Register */ + mtdcr(uicb0sr, UICB0_ALL); + mtdcr(uicb0er, 0x54000000); + /* None are critical */ + mtdcr(uicb0cr, 0); +#endif + + return (0); +} + +/****************************************************************************/ + +/* + * Handle external interrupts + */ +#if defined(CONFIG_440GX) +void external_interrupt(struct pt_regs *regs) +{ + ulong uic_msr; + + /* + * Read masked interrupt status register to determine interrupt source + */ + /* 440 GX uses base uic register */ + uic_msr = mfdcr(uicb0msr); + + if ( (UICB0_UIC0CI & uic_msr) || (UICB0_UIC0NCI & uic_msr) ) + uic0_interrupt(0); + + if ( (UICB0_UIC1CI & uic_msr) || (UICB0_UIC1NCI & uic_msr) ) + uic1_interrupt(0); + + if ( (UICB0_UIC2CI & uic_msr) || (UICB0_UIC2NCI & uic_msr) ) + uic2_interrupt(0); + + mtdcr(uicb0sr, uic_msr); + + return; + +} /* external_interrupt CONFIG_440GX */ + +#else + +void external_interrupt(struct pt_regs *regs) +{ + ulong uic_msr; + ulong msr_shift; + int vec; + + /* + * Read masked interrupt status register to determine interrupt source + */ + uic_msr = mfdcr(uicmsr); + msr_shift = uic_msr; + vec = 0; + + while (msr_shift != 0) { + if (msr_shift & 0x80000000) { + /* + * Increment irq counter (for debug purpose only) + */ + irq_vecs[vec].count++; + + if (irq_vecs[vec].handler != NULL) { + /* call isr */ + (*irq_vecs[vec].handler)(irq_vecs[vec].arg); + } else { + mtdcr(uicer, mfdcr(uicer) & ~(0x80000000 >> vec)); + printf ("Masking bogus interrupt vector 0x%x\n", vec); + } + + /* + * After servicing the interrupt, we have to remove the status indicator. + */ + mtdcr(uicsr, (0x80000000 >> vec)); + } + + /* + * Shift msr to next position and increment vector + */ + msr_shift <<= 1; + vec++; + } +} +#endif + +#if defined(CONFIG_440GX) +/* Handler for UIC0 interrupt */ +void uic0_interrupt( void * parms) +{ + ulong uic_msr; + ulong msr_shift; + int vec; + + /* + * Read masked interrupt status register to determine interrupt source + */ + uic_msr = mfdcr(uicmsr); + msr_shift = uic_msr; + vec = 0; + + while (msr_shift != 0) { + if (msr_shift & 0x80000000) { + /* + * Increment irq counter (for debug purpose only) + */ + irq_vecs[vec].count++; + + if (irq_vecs[vec].handler != NULL) { + /* call isr */ + (*irq_vecs[vec].handler)(irq_vecs[vec].arg); + } else { + mtdcr(uicer, mfdcr(uicer) & ~(0x80000000 >> vec)); + printf ("Masking bogus interrupt vector (uic0) 0x%x\n", vec); + } + + /* + * After servicing the interrupt, we have to remove the status indicator. + */ + mtdcr(uicsr, (0x80000000 >> vec)); + } + + /* + * Shift msr to next position and increment vector + */ + msr_shift <<= 1; + vec++; + } +} + +#endif /* CONFIG_440GX */ + +#if defined(CONFIG_440) +/* Handler for UIC1 interrupt */ +void uic1_interrupt( void * parms) +{ + ulong uic1_msr; + ulong msr_shift; + int vec; + + /* + * Read masked interrupt status register to determine interrupt source + */ + uic1_msr = mfdcr(uic1msr); + msr_shift = uic1_msr; + vec = 0; + + while (msr_shift != 0) { + if (msr_shift & 0x80000000) { + /* + * Increment irq counter (for debug purpose only) + */ + irq_vecs1[vec].count++; + + if (irq_vecs1[vec].handler != NULL) { + /* call isr */ + (*irq_vecs1[vec].handler)(irq_vecs1[vec].arg); + } else { + mtdcr(uic1er, mfdcr(uic1er) & ~(0x80000000 >> vec)); + printf ("Masking bogus interrupt vector (uic1) 0x%x\n", vec); + } + + /* + * After servicing the interrupt, we have to remove the status indicator. + */ + mtdcr(uic1sr, (0x80000000 >> vec)); + } + + /* + * Shift msr to next position and increment vector + */ + msr_shift <<= 1; + vec++; + } +} +#endif /* defined(CONFIG_440) */ + +#if defined(CONFIG_440GX) +/* Handler for UIC1 interrupt */ +void uic2_interrupt( void * parms) +{ + ulong uic2_msr; + ulong msr_shift; + int vec; + + /* + * Read masked interrupt status register to determine interrupt source + */ + uic2_msr = mfdcr(uic2msr); + msr_shift = uic2_msr; + vec = 0; + + while (msr_shift != 0) { + if (msr_shift & 0x80000000) { + /* + * Increment irq counter (for debug purpose only) + */ + irq_vecs2[vec].count++; + + if (irq_vecs2[vec].handler != NULL) { + /* call isr */ + (*irq_vecs2[vec].handler)(irq_vecs2[vec].arg); + } else { + mtdcr(uic2er, mfdcr(uic2er) & ~(0x80000000 >> vec)); + printf ("Masking bogus interrupt vector (uic1) 0x%x\n", vec); + } + + /* + * After servicing the interrupt, we have to remove the status indicator. + */ + mtdcr(uic2sr, (0x80000000 >> vec)); + } + + /* + * Shift msr to next position and increment vector + */ + msr_shift <<= 1; + vec++; + } +} +#endif /* defined(CONFIG_440GX) */ + +/****************************************************************************/ + +/* + * Install and free a interrupt handler. + */ + +void irq_install_handler (int vec, interrupt_handler_t * handler, void *arg) +{ + struct irq_action *irqa = irq_vecs; + int i = vec; + +#if defined(CONFIG_440) +#if defined(CONFIG_440GX) + if ((vec > 31) && (vec < 64)) { + i = vec - 32; + irqa = irq_vecs1; + } else if (vec > 63) { + i = vec - 64; + irqa = irq_vecs2; + } +#else /* CONFIG_440GX */ + if (vec > 31) { + i = vec - 32; + irqa = irq_vecs1; + } +#endif /* CONFIG_440GX */ +#endif /* CONFIG_440 */ + + /* + * print warning when replacing with a different irq vector + */ + if ((irqa[i].handler != NULL) && (irqa[i].handler != handler)) { + printf ("Interrupt vector %d: handler 0x%x replacing 0x%x\n", + vec, (uint) handler, (uint) irqa[i].handler); + } + irqa[i].handler = handler; + irqa[i].arg = arg; + +#if defined(CONFIG_440) +#if defined(CONFIG_440GX) + if ((vec > 31) && (vec < 64)) + mtdcr (uic1er, mfdcr (uic1er) | (0x80000000 >> i)); + else if (vec > 63) + mtdcr (uic2er, mfdcr (uic2er) | (0x80000000 >> i)); + else +#endif /* CONFIG_440GX */ + if (vec > 31) + mtdcr (uic1er, mfdcr (uic1er) | (0x80000000 >> i)); + else +#endif + mtdcr (uicer, mfdcr (uicer) | (0x80000000 >> i)); +#if 0 + printf ("Install interrupt for vector %d ==> %p\n", vec, handler); +#endif +} + +void irq_free_handler (int vec) +{ + struct irq_action *irqa = irq_vecs; + int i = vec; + +#if defined(CONFIG_440) +#if defined(CONFIG_440GX) + if ((vec > 31) && (vec < 64)) { + irqa = irq_vecs1; + i = vec - 32; + } else if (vec > 63) { + irqa = irq_vecs2; + i = vec - 64; + } +#endif /* CONFIG_440GX */ + if (vec > 31) { + irqa = irq_vecs1; + i = vec - 32; + } +#endif + +#if 0 + printf ("Free interrupt for vector %d ==> %p\n", + vec, irq_vecs[vec].handler); +#endif + +#if defined(CONFIG_440) +#if defined(CONFIG_440GX) + if ((vec > 31) && (vec < 64)) + mtdcr (uic1er, mfdcr (uic1er) & ~(0x80000000 >> i)); + else if (vec > 63) + mtdcr (uic2er, mfdcr (uic2er) & ~(0x80000000 >> i)); + else +#endif /* CONFIG_440GX */ + if (vec > 31) + mtdcr (uic1er, mfdcr (uic1er) & ~(0x80000000 >> i)); + else +#endif + mtdcr (uicer, mfdcr (uicer) & ~(0x80000000 >> i)); + + irqa[i].handler = NULL; + irqa[i].arg = NULL; +} + +/****************************************************************************/ + +void timer_interrupt_cpu (struct pt_regs *regs) +{ + /* nothing to do here */ + return; +} + +/****************************************************************************/ + +#if (CONFIG_COMMANDS & CFG_CMD_IRQ) + +/******************************************************************************* + * + * irqinfo - print information about PCI devices + * + */ +int +do_irqinfo(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + int vec; + + printf ("\nInterrupt-Information:\n"); +#if defined(CONFIG_440) + printf ("\nUIC 0\n"); +#endif + printf ("Nr Routine Arg Count\n"); + + for (vec=0; vec<32; vec++) { + if (irq_vecs[vec].handler != NULL) { + printf ("%02d %08lx %08lx %d\n", + vec, + (ulong)irq_vecs[vec].handler, + (ulong)irq_vecs[vec].arg, + irq_vecs[vec].count); + } + } + +#if defined(CONFIG_440) + printf ("\nUIC 1\n"); + printf ("Nr Routine Arg Count\n"); + + for (vec=0; vec<32; vec++) { + if (irq_vecs1[vec].handler != NULL) + printf ("%02d %08lx %08lx %d\n", + vec+31, (ulong)irq_vecs1[vec].handler, + (ulong)irq_vecs1[vec].arg, irq_vecs1[vec].count); + } + printf("\n"); +#endif + +#if defined(CONFIG_440GX) + printf ("\nUIC 2\n"); + printf ("Nr Routine Arg Count\n"); + + for (vec=0; vec<32; vec++) { + if (irq_vecs2[vec].handler != NULL) + printf ("%02d %08lx %08lx %d\n", + vec+63, (ulong)irq_vecs2[vec].handler, + (ulong)irq_vecs2[vec].arg, irq_vecs2[vec].count); + } + printf("\n"); +#endif + + return 0; +} +#endif /* CONFIG_COMMANDS & CFG_CMD_IRQ */ diff --git a/cpu/ppc4xx/kgdb.S b/cpu/ppc4xx/kgdb.S new file mode 100755 index 0000000..be28340 --- /dev/null +++ b/cpu/ppc4xx/kgdb.S @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2000 Murray Jensen <Murray.Jensen@cmst.csiro.au> + * + * 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 <config.h> +#include <command.h> +#include <ppc4xx.h> +#include <version.h> + +#define CONFIG_405GP 1 /* needed for Linux kernel header files */ +#define _LINUX_CONFIG_H 1 /* avoid reading Linux autoconf.h file */ + +#include <ppc_asm.tmpl> +#include <ppc_defs.h> + +#include <asm/cache.h> +#include <asm/mmu.h> + +#if (CONFIG_COMMANDS & CFG_CMD_KGDB) + /* + * cache flushing routines for kgdb + */ + + .globl kgdb_flush_cache_all +kgdb_flush_cache_all: + /* icache */ + iccci r0,r0 /* iccci invalidates the entire I cache */ + /* dcache */ + addi r6,0,0x0000 /* clear GPR 6 */ + addi r7,r0, 128 /* do loop for # of dcache lines */ + /* NOTE: dccci invalidates both */ + mtctr r7 /* ways in the D cache */ +..dcloop: + dccci 0,r6 /* invalidate line */ + addi r6,r6, 32 /* bump to next line */ + bdnz ..dcloop + blr + + .globl kgdb_flush_cache_range +kgdb_flush_cache_range: + li r5,CFG_CACHELINE_SIZE-1 + andc r3,r3,r5 + subf r4,r3,r4 + add r4,r4,r5 + srwi. r4,r4,CFG_CACHELINE_SHIFT + beqlr + mtctr r4 + mr r6,r3 +1: dcbst 0,r3 + addi r3,r3,CFG_CACHELINE_SIZE + bdnz 1b + sync /* wait for dcbst's to get to ram */ + mtctr r4 +2: icbi 0,r6 + addi r6,r6,CFG_CACHELINE_SIZE + bdnz 2b + SYNC + blr + +#endif /* CFG_CMD_KGDB */ diff --git a/cpu/ppc4xx/miiphy.c b/cpu/ppc4xx/miiphy.c new file mode 100755 index 0000000..f26f2a2 --- /dev/null +++ b/cpu/ppc4xx/miiphy.c @@ -0,0 +1,260 @@ +/*-----------------------------------------------------------------------------+ + | + | 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: miiphy.c + | + | Function: This module has utilities for accessing the MII PHY through + | the EMAC3 macro. + | + | Author: Mark Wisner + | + | Change Activity- + | + | Date Description of Change BY + | --------- --------------------- --- + | 05-May-99 Created MKW + | 01-Jul-99 Changed clock setting of sta_reg from 66Mhz to 50Mhz to + | better match OPB speed. Also modified delay times. JWB + | 29-Jul-99 Added Full duplex support MKW + | 24-Aug-99 Removed printf from dp83843_duplex() JWB + | 19-Jul-00 Ported to esd cpci405 sr + | 23-Dec-03 Ported from miiphy.c to 440GX Travis Sawyer TBS + | <travis.sawyer@sandburst.com> + | + +-----------------------------------------------------------------------------*/ + +#include <common.h> +#include <asm/processor.h> +#include <ppc_asm.tmpl> +#include <commproc.h> +#include <ppc4xx_enet.h> +#include <405_mal.h> +#include <miiphy.h> + + +/***********************************************************/ +/* Dump out to the screen PHY regs */ +/***********************************************************/ + +void miiphy_dump (char *devname, unsigned char addr) +{ + unsigned long i; + unsigned short data; + + + for (i = 0; i < 0x1A; i++) { + if (miiphy_read (devname, addr, i, &data)) { + printf ("read error for reg %lx\n", i); + return; + } + printf ("Phy reg %lx ==> %4x\n", i, data); + + /* jump to the next set of regs */ + if (i == 0x07) + i = 0x0f; + + } /* end for loop */ +} /* end dump */ + + +/***********************************************************/ +/* (Re)start autonegotiation */ +/***********************************************************/ +int phy_setup_aneg (char *devname, unsigned char addr) +{ + unsigned short ctl, adv; + + /* Setup standard advertise */ + miiphy_read (devname, addr, PHY_ANAR, &adv); + adv |= (PHY_ANLPAR_ACK | PHY_ANLPAR_RF | PHY_ANLPAR_T4 | + PHY_ANLPAR_TXFD | PHY_ANLPAR_TX | PHY_ANLPAR_10FD | + PHY_ANLPAR_10); + miiphy_write (devname, addr, PHY_ANAR, adv); + + /* Start/Restart aneg */ + miiphy_read (devname, addr, PHY_BMCR, &ctl); + ctl |= (PHY_BMCR_AUTON | PHY_BMCR_RST_NEG); + miiphy_write (devname, addr, PHY_BMCR, ctl); + + return 0; +} + + +/***********************************************************/ +/* read a phy reg and return the value with a rc */ +/***********************************************************/ +unsigned int miiphy_getemac_offset (void) +{ +#if (defined(CONFIG_440) && !defined(CONFIG_440SP)) && defined(CONFIG_NET_MULTI) + unsigned long zmii; + unsigned long eoffset; + + /* Need to find out which mdi port we're using */ + zmii = in32 (ZMII_FER); + + if (zmii & (ZMII_FER_MDI << ZMII_FER_V (0))) { + /* using port 0 */ + eoffset = 0; + } else if (zmii & (ZMII_FER_MDI << ZMII_FER_V (1))) { + /* using port 1 */ + eoffset = 0x100; + } else if (zmii & (ZMII_FER_MDI << ZMII_FER_V (2))) { + /* using port 2 */ + eoffset = 0x400; + } else if (zmii & (ZMII_FER_MDI << ZMII_FER_V (3))) { + /* using port 3 */ + eoffset = 0x600; + } else { + /* None of the mdi ports are enabled! */ + /* enable port 0 */ + zmii |= ZMII_FER_MDI << ZMII_FER_V (0); + out32 (ZMII_FER, zmii); + eoffset = 0; + /* need to soft reset port 0 */ + zmii = in32 (EMAC_M0); + zmii |= EMAC_M0_SRST; + out32 (EMAC_M0, zmii); + } + + return (eoffset); +#else + return 0; +#endif +} + + +int emac4xx_miiphy_read (char *devname, unsigned char addr, + unsigned char reg, unsigned short *value) +{ + unsigned long sta_reg; /* STA scratch area */ + unsigned long i; + unsigned long emac_reg; + + + emac_reg = miiphy_getemac_offset (); + /* see if it is ready for 1000 nsec */ + i = 0; + + /* see if it is ready for sec */ + while ((in32 (EMAC_STACR + emac_reg) & EMAC_STACR_OC) == 0) { + udelay (7); + if (i > 5) { +#if 0 + printf ("read err 1\n"); +#endif + return -1; + } + i++; + } + sta_reg = reg; /* reg address */ + /* set clock (50Mhz) and read flags */ +#if defined(CONFIG_440GX) + sta_reg |= EMAC_STACR_READ; +#else + sta_reg = (sta_reg | EMAC_STACR_READ) & ~EMAC_STACR_CLK_100MHZ; +#endif + +#if defined(CONFIG_PHY_CLK_FREQ) && !defined(CONFIG_440GX) + sta_reg = sta_reg | CONFIG_PHY_CLK_FREQ; +#endif + sta_reg = sta_reg | (addr << 5); /* Phy address */ + + out32 (EMAC_STACR + emac_reg, sta_reg); +#if 0 /* test-only */ + printf ("a2: write: EMAC_STACR=0x%0x\n", sta_reg); /* test-only */ +#endif + + sta_reg = in32 (EMAC_STACR + emac_reg); + i = 0; + while ((sta_reg & EMAC_STACR_OC) == 0) { + udelay (7); + if (i > 5) { + return -1; + } + i++; + sta_reg = in32 (EMAC_STACR + emac_reg); + } + if ((sta_reg & EMAC_STACR_PHYE) != 0) { + return -1; + } + + *value = *(short *) (&sta_reg); + return 0; + + +} /* phy_read */ + + +/***********************************************************/ +/* write a phy reg and return the value with a rc */ +/***********************************************************/ + +int emac4xx_miiphy_write (char *devname, unsigned char addr, + unsigned char reg, unsigned short value) +{ + unsigned long sta_reg; /* STA scratch area */ + unsigned long i; + unsigned long emac_reg; + + emac_reg = miiphy_getemac_offset (); + /* see if it is ready for 1000 nsec */ + i = 0; + + while ((in32 (EMAC_STACR + emac_reg) & EMAC_STACR_OC) == 0) { + if (i > 5) + return -1; + udelay (7); + i++; + } + sta_reg = 0; + sta_reg = reg; /* reg address */ + /* set clock (50Mhz) and read flags */ +#if defined(CONFIG_440GX) + sta_reg |= EMAC_STACR_WRITE; +#else + sta_reg = (sta_reg | EMAC_STACR_WRITE) & ~EMAC_STACR_CLK_100MHZ; +#endif + +#if defined(CONFIG_PHY_CLK_FREQ) && !defined(CONFIG_440GX) + sta_reg = sta_reg | CONFIG_PHY_CLK_FREQ; /* Set clock frequency (PLB freq. dependend) */ +#endif + sta_reg = sta_reg | ((unsigned long) addr << 5); /* Phy address */ + memcpy (&sta_reg, &value, 2); /* put in data */ + + out32 (EMAC_STACR + emac_reg, sta_reg); + + /* wait for completion */ + i = 0; + sta_reg = in32 (EMAC_STACR + emac_reg); + while ((sta_reg & EMAC_STACR_OC) == 0) { + udelay (7); + if (i > 5) + return -1; + i++; + sta_reg = in32 (EMAC_STACR + emac_reg); + } + + if ((sta_reg & EMAC_STACR_PHYE) != 0) + return -1; + return 0; + +} /* phy_write */ diff --git a/cpu/ppc4xx/resetvec.S b/cpu/ppc4xx/resetvec.S new file mode 100755 index 0000000..b3308bd --- /dev/null +++ b/cpu/ppc4xx/resetvec.S @@ -0,0 +1,12 @@ +/* Copyright MontaVista Software Incorporated, 2000 */ +#include <config.h> + .section .resetvec,"ax" +#if defined(CONFIG_440) + b _start_440 +#else +#if defined(CONFIG_BOOT_PCI) && defined(CONFIG_MIP405) + b _start_pci +#else + b _start +#endif +#endif diff --git a/cpu/ppc4xx/sdram.c b/cpu/ppc4xx/sdram.c new file mode 100755 index 0000000..e9548cd --- /dev/null +++ b/cpu/ppc4xx/sdram.c @@ -0,0 +1,178 @@ +/* + * (C) Copyright 2005 + * Stefan Roese, DENX Software Engineering, sr@denx.de. + * + * (C) Copyright 2002-2004 + * 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> + + +#ifdef CONFIG_SDRAM_BANK0 + + +#define mtsdram0(reg, data) mtdcr(memcfga,reg);mtdcr(memcfgd,data) + + +struct sdram_conf_s { + unsigned long size; + unsigned long reg; +}; + +typedef struct sdram_conf_s sdram_conf_t; + +#ifndef CFG_SDRAM_TABLE +sdram_conf_t mb0cf[] = { + {(128 << 20), 0x000A4001}, /* (0-128MB) Address Mode 3, 13x10(4) */ + {(64 << 20), 0x00084001}, /* (0-64MB) Address Mode 3, 13x9(4) */ + {(32 << 20), 0x00062001}, /* (0-32MB) Address Mode 2, 12x9(4) */ + {(16 << 20), 0x00046001}, /* (0-16MB) Address Mode 4, 12x8(4) */ + {(4 << 20), 0x00008001}, /* (0-4MB) Address Mode 5, 11x8(2) */ +}; +#else +sdram_conf_t mb0cf[] = CFG_SDRAM_TABLE; +#endif + +#define N_MB0CF (sizeof(mb0cf) / sizeof(mb0cf[0])) + + +#ifndef CONFIG_440 + +/* + * Autodetect onboard SDRAM on 405 platforms + */ +void sdram_init(void) +{ + ulong sdtr1; + ulong rtr; + int i; + + /* + * Support for 100MHz and 133MHz SDRAM + */ + if (get_bus_freq(0) > 100000000) { + /* + * 133 MHz SDRAM + */ + sdtr1 = 0x01074015; + rtr = 0x07f00000; + } else { + /* + * default: 100 MHz SDRAM + */ + sdtr1 = 0x0086400d; + rtr = 0x05f00000; + } + + for (i=0; i<N_MB0CF; i++) { + /* + * Disable memory controller. + */ + mtsdram0(mem_mcopt1, 0x00000000); + + /* + * Set MB0CF for bank 0. + */ + mtsdram0(mem_mb0cf, mb0cf[i].reg); + mtsdram0(mem_sdtr1, sdtr1); + mtsdram0(mem_rtr, rtr); + + 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); + + udelay(10000); + + if (get_ram_size(0, mb0cf[i].size) == mb0cf[i].size) { + /* + * OK, size detected -> all done + */ + return; + } + } +} + +#else /* CONFIG_440 */ + +/* + * Autodetect onboard DDR SDRAM on 440 platforms + * + * NOTE: Some of the hardcoded values are hardware dependant, + * so this should be extended for other future boards + * using this routine! + */ +long int initdram(int board_type) +{ + int i; + + for (i=0; i<N_MB0CF; i++) { + /* + * Disable memory controller. + */ + mtsdram(mem_cfg0, 0x00000000); + + /* + * Setup some default + */ + mtsdram(mem_uabba, 0x00000000); /* ubba=0 (default) */ + mtsdram(mem_slio, 0x00000000); /* rdre=0 wrre=0 rarw=0 */ + mtsdram(mem_devopt, 0x00000000); /* dll=0 ds=0 (normal) */ + mtsdram(mem_wddctr, 0x00000000); /* wrcp=0 dcd=0 */ + mtsdram(mem_clktr, 0x40000000); /* clkp=1 (90 deg wr) dcdt=0 */ + + /* + * Following for CAS Latency = 2.5 @ 133 MHz PLB + */ + mtsdram(mem_b0cr, mb0cf[i].reg); + mtsdram(mem_tr0, 0x41094012); + mtsdram(mem_tr1, 0x80800800); /* SS=T2 SL=STAGE 3 CD=1 CT=0x00*/ + mtsdram(mem_rtr, 0x7e000000); /* Interval 15.20µs @ 133MHz PLB*/ + mtsdram(mem_cfg1, 0x00000000); /* Self-refresh exit, disable PM*/ + udelay(400); /* Delay 200 usecs (min) */ + + /* + * Enable the controller, then wait for DCEN to complete + */ + mtsdram(mem_cfg0, 0x86000000); /* DCEN=1, PMUD=1, 64-bit */ + udelay(10000); + + if (get_ram_size(0, mb0cf[i].size) == mb0cf[i].size) { + /* + * OK, size detected -> all done + */ + return mb0cf[i].size; + } + } + + return 0; /* nothing found ! */ +} + +#endif /* CONFIG_440 */ + +#endif /* CONFIG_SDRAM_BANK0 */ diff --git a/cpu/ppc4xx/serial.c b/cpu/ppc4xx/serial.c new file mode 100755 index 0000000..e7f6bcb --- /dev/null +++ b/cpu/ppc4xx/serial.c @@ -0,0 +1,1064 @@ +/* + * (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 + */ +/*------------------------------------------------------------------------------+ */ +/* + * 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 + */ +/*------------------------------------------------------------------------------- */ +/* + * Travis Sawyer 15 September 2004 + * Added CONFIG_SERIAL_MULTI support + */ +#include <common.h> +#include <commproc.h> +#include <asm/processor.h> +#include <watchdog.h> +#include "vecnum.h" + +#ifdef CONFIG_SERIAL_MULTI +#include <serial.h> +#endif + +#ifdef CONFIG_SERIAL_SOFTWARE_FIFO +#include <malloc.h> +#endif + +/*****************************************************************************/ +#ifdef CONFIG_IOP480 + +#define SPU_BASE 0x40000000 + +#define spu_LineStat_rc 0x00 /* Line Status Register (Read/Clear) */ +#define spu_LineStat_w 0x04 /* Line Status Register (Set) */ +#define spu_Handshk_rc 0x08 /* Handshake Status Register (Read/Clear) */ +#define spu_Handshk_w 0x0c /* Handshake Status Register (Set) */ +#define spu_BRateDivh 0x10 /* Baud rate divisor high */ +#define spu_BRateDivl 0x14 /* Baud rate divisor low */ +#define spu_CtlReg 0x18 /* Control Register */ +#define spu_RxCmd 0x1c /* Rx Command Register */ +#define spu_TxCmd 0x20 /* Tx Command Register */ +#define spu_RxBuff 0x24 /* Rx data buffer */ +#define spu_TxBuff 0x24 /* Tx data buffer */ + +/*-----------------------------------------------------------------------------+ + | Line Status Register. + +-----------------------------------------------------------------------------*/ +#define asyncLSRport1 0x40000000 +#define asyncLSRport1set 0x40000004 +#define asyncLSRDataReady 0x80 +#define asyncLSRFramingError 0x40 +#define asyncLSROverrunError 0x20 +#define asyncLSRParityError 0x10 +#define asyncLSRBreakInterrupt 0x08 +#define asyncLSRTxHoldEmpty 0x04 +#define asyncLSRTxShiftEmpty 0x02 + +/*-----------------------------------------------------------------------------+ + | Handshake Status Register. + +-----------------------------------------------------------------------------*/ +#define asyncHSRport1 0x40000008 +#define asyncHSRport1set 0x4000000c +#define asyncHSRDsr 0x80 +#define asyncLSRCts 0x40 + +/*-----------------------------------------------------------------------------+ + | Control Register. + +-----------------------------------------------------------------------------*/ +#define asyncCRport1 0x40000018 +#define asyncCRNormal 0x00 +#define asyncCRLoopback 0x40 +#define asyncCRAutoEcho 0x80 +#define asyncCRDtr 0x20 +#define asyncCRRts 0x10 +#define asyncCRWordLength7 0x00 +#define asyncCRWordLength8 0x08 +#define asyncCRParityDisable 0x00 +#define asyncCRParityEnable 0x04 +#define asyncCREvenParity 0x00 +#define asyncCROddParity 0x02 +#define asyncCRStopBitsOne 0x00 +#define asyncCRStopBitsTwo 0x01 +#define asyncCRDisableDtrRts 0x00 + +/*-----------------------------------------------------------------------------+ + | Receiver Command Register. + +-----------------------------------------------------------------------------*/ +#define asyncRCRport1 0x4000001c +#define asyncRCRDisable 0x00 +#define asyncRCREnable 0x80 +#define asyncRCRIntDisable 0x00 +#define asyncRCRIntEnabled 0x20 +#define asyncRCRDMACh2 0x40 +#define asyncRCRDMACh3 0x60 +#define asyncRCRErrorInt 0x10 +#define asyncRCRPauseEnable 0x08 + +/*-----------------------------------------------------------------------------+ + | Transmitter Command Register. + +-----------------------------------------------------------------------------*/ +#define asyncTCRport1 0x40000020 +#define asyncTCRDisable 0x00 +#define asyncTCREnable 0x80 +#define asyncTCRIntDisable 0x00 +#define asyncTCRIntEnabled 0x20 +#define asyncTCRDMACh2 0x40 +#define asyncTCRDMACh3 0x60 +#define asyncTCRTxEmpty 0x10 +#define asyncTCRErrorInt 0x08 +#define asyncTCRStopPause 0x04 +#define asyncTCRBreakGen 0x02 + +/*-----------------------------------------------------------------------------+ + | Miscellanies defines. + +-----------------------------------------------------------------------------*/ +#define asyncTxBufferport1 0x40000024 +#define asyncRxBufferport1 0x40000024 +#define asyncDLABLsbport1 0x40000014 +#define asyncDLABMsbport1 0x40000010 +#define asyncXOFFchar 0x13 +#define asyncXONchar 0x11 + +/* + * Minimal serial functions needed to use one of the SMC ports + * as serial console interface. + */ + +int serial_init (void) +{ + DECLARE_GLOBAL_DATA_PTR; + + volatile char val; + unsigned short br_reg; + + br_reg = ((((CONFIG_CPUCLOCK * 1000000) / 16) / gd->baudrate) - 1); + + /* + * Init onboard UART + */ + out8 (SPU_BASE + spu_LineStat_rc, 0x78); /* Clear all bits in Line Status Reg */ + out8 (SPU_BASE + spu_BRateDivl, (br_reg & 0x00ff)); /* Set baud rate divisor... */ + out8 (SPU_BASE + spu_BRateDivh, ((br_reg & 0xff00) >> 8)); /* ... */ + out8 (SPU_BASE + spu_CtlReg, 0x08); /* Set 8 bits, no parity and 1 stop bit */ + out8 (SPU_BASE + spu_RxCmd, 0xb0); /* Enable Rx */ + out8 (SPU_BASE + spu_TxCmd, 0x9c); /* Enable Tx */ + out8 (SPU_BASE + spu_Handshk_rc, 0xff); /* Clear Handshake */ + val = in8 (SPU_BASE + spu_RxBuff); /* Dummy read, to clear receiver */ + + return (0); +} + +void serial_setbrg (void) +{ + DECLARE_GLOBAL_DATA_PTR; + + unsigned short br_reg; + + br_reg = ((((CONFIG_CPUCLOCK * 1000000) / 16) / gd->baudrate) - 1); + + out8 (SPU_BASE + spu_BRateDivl, (br_reg & 0x00ff)); /* Set baud rate divisor... */ + out8 (SPU_BASE + spu_BRateDivh, ((br_reg & 0xff00) >> 8)); /* ... */ +} + +void serial_putc (const char c) +{ + if (c == '\n') + serial_putc ('\r'); + + /* load status from handshake register */ + if (in8 (SPU_BASE + spu_Handshk_rc) != 00) + out8 (SPU_BASE + spu_Handshk_rc, 0xff); /* Clear Handshake */ + + out8 (SPU_BASE + spu_TxBuff, c); /* Put char */ + + while ((in8 (SPU_BASE + spu_LineStat_rc) & 04) != 04) { + if (in8 (SPU_BASE + spu_Handshk_rc) != 00) + out8 (SPU_BASE + spu_Handshk_rc, 0xff); /* Clear Handshake */ + } +} + +void serial_puts (const char *s) +{ + while (*s) { + serial_putc (*s++); + } +} + +int serial_getc () +{ + unsigned char status = 0; + + while (1) { + status = in8 (asyncLSRport1); + if ((status & asyncLSRDataReady) != 0x0) { + break; + } + if ((status & ( asyncLSRFramingError | + asyncLSROverrunError | + asyncLSRParityError | + asyncLSRBreakInterrupt )) != 0) { + (void) out8 (asyncLSRport1, + asyncLSRFramingError | + asyncLSROverrunError | + asyncLSRParityError | + asyncLSRBreakInterrupt ); + } + } + return (0x000000ff & (int) in8 (asyncRxBufferport1)); +} + +int serial_tstc () +{ + unsigned char status; + + status = in8 (asyncLSRport1); + if ((status & asyncLSRDataReady) != 0x0) { + return (1); + } + if ((status & ( asyncLSRFramingError | + asyncLSROverrunError | + asyncLSRParityError | + asyncLSRBreakInterrupt )) != 0) { + (void) out8 (asyncLSRport1, + asyncLSRFramingError | + asyncLSROverrunError | + asyncLSRParityError | + asyncLSRBreakInterrupt); + } + return 0; +} + +#endif /* CONFIG_IOP480 */ + +/*****************************************************************************/ +#if defined(CONFIG_405GP) || defined(CONFIG_405CR) || defined(CONFIG_440) || defined(CONFIG_405EP) + +#if defined(CONFIG_440) +#if defined(CONFIG_440EP) || defined(CONFIG_440GR) +#define UART0_BASE CFG_PERIPHERAL_BASE + 0x00000300 +#define UART1_BASE CFG_PERIPHERAL_BASE + 0x00000400 +#else +#define UART0_BASE CFG_PERIPHERAL_BASE + 0x00000200 +#define UART1_BASE CFG_PERIPHERAL_BASE + 0x00000300 +#endif + +#if defined(CONFIG_440SP) +#define UART2_BASE CFG_PERIPHERAL_BASE + 0x00000600 +#endif + +#if defined(CONFIG_440GX) || defined(CONFIG_440EP) || defined(CONFIG_440GR) || defined(CONFIG_440SP) +#define CR0_MASK 0xdfffffff +#define CR0_EXTCLK_ENA 0x00800000 +#define CR0_UDIV_POS 0 +#else +#define CR0_MASK 0x3fff0000 +#define CR0_EXTCLK_ENA 0x00600000 +#define CR0_UDIV_POS 16 +#endif /* CONFIG_440GX */ +#elif defined(CONFIG_405EP) +#define UART0_BASE 0xef600300 +#define UART1_BASE 0xef600400 +#define UCR0_MASK 0x0000007f +#define UCR1_MASK 0x00007f00 +#define UCR0_UDIV_POS 0 +#define UCR1_UDIV_POS 8 +#define UDIV_MAX 127 +#else /* CONFIG_405GP || CONFIG_405CR */ +#define UART0_BASE 0xef600300 +#define UART1_BASE 0xef600400 +#define CR0_MASK 0x00001fff +#define CR0_EXTCLK_ENA 0x000000c0 +#define CR0_UDIV_POS 1 +#define UDIV_MAX 32 +#endif + +/* using serial port 0 or 1 as U-Boot console ? */ +#if defined(CONFIG_UART1_CONSOLE) +#define ACTING_UART0_BASE UART1_BASE +#define ACTING_UART1_BASE UART0_BASE +#if defined(CONFIG_440GX) || defined(CONFIG_440EP) || defined(CONFIG_440GR) || defined(CONFIG_440SP) +#define UART0_SDR sdr_uart1 +#define UART1_SDR sdr_uart0 +#endif /* CONFIG_440GX */ +#else +#define ACTING_UART0_BASE UART0_BASE +#define ACTING_UART1_BASE UART1_BASE +#if defined(CONFIG_440GX) || defined(CONFIG_440EP) || defined(CONFIG_440GR) || defined(CONFIG_440SP) +#define UART0_SDR sdr_uart0 +#define UART1_SDR sdr_uart1 +#endif /* CONFIG_440GX */ +#endif + +#if defined(CONFIG_405EP) && defined(CFG_EXT_SERIAL_CLOCK) +#error "External serial clock not supported on AMCC PPC405EP!" +#endif + +#define UART_RBR 0x00 +#define UART_THR 0x00 +#define UART_IER 0x01 +#define UART_IIR 0x02 +#define UART_FCR 0x02 +#define UART_LCR 0x03 +#define UART_MCR 0x04 +#define UART_LSR 0x05 +#define UART_MSR 0x06 +#define UART_SCR 0x07 +#define UART_DLL 0x00 +#define UART_DLM 0x01 + +/*-----------------------------------------------------------------------------+ + | Line Status Register. + +-----------------------------------------------------------------------------*/ +/*#define asyncLSRport1 ACTING_UART0_BASE+0x05 */ +#define asyncLSRDataReady1 0x01 +#define asyncLSROverrunError1 0x02 +#define asyncLSRParityError1 0x04 +#define asyncLSRFramingError1 0x08 +#define asyncLSRBreakInterrupt1 0x10 +#define asyncLSRTxHoldEmpty1 0x20 +#define asyncLSRTxShiftEmpty1 0x40 +#define asyncLSRRxFifoError1 0x80 + +/*-----------------------------------------------------------------------------+ + | Miscellanies defines. + +-----------------------------------------------------------------------------*/ +/*#define asyncTxBufferport1 ACTING_UART0_BASE+0x00 */ +/*#define asyncRxBufferport1 ACTING_UART0_BASE+0x00 */ + +#ifdef CONFIG_SERIAL_SOFTWARE_FIFO +/*-----------------------------------------------------------------------------+ + | Fifo + +-----------------------------------------------------------------------------*/ +typedef struct { + char *rx_buffer; + ulong rx_put; + ulong rx_get; +} serial_buffer_t; + +volatile static serial_buffer_t buf_info; +#endif + +#if defined(CONFIG_440) && !defined(CFG_EXT_SERIAL_CLOCK) +static void serial_divs (int baudrate, unsigned long *pudiv, + unsigned short *pbdiv ) +{ + sys_info_t sysinfo; + unsigned long div; /* total divisor udiv * bdiv */ + unsigned long umin; /* minimum udiv */ + unsigned short diff; /* smallest diff */ + unsigned long udiv; /* best udiv */ + + unsigned short idiff; /* current diff */ + unsigned short ibdiv; /* current bdiv */ + unsigned long i; + unsigned long est; /* current estimate */ + + get_sys_info( &sysinfo ); + + udiv = 32; /* Assume lowest possible serial clk */ + div = sysinfo.freqPLB/(16*baudrate); /* total divisor */ + umin = sysinfo.pllOpbDiv<<1; /* 2 x OPB divisor */ + diff = 32; /* highest possible */ + + /* i is the test udiv value -- start with the largest + * possible (32) to minimize serial clock and constrain + * search to umin. + */ + for( i = 32; i > umin; i-- ){ + ibdiv = div/i; + est = i * ibdiv; + idiff = (est > div) ? (est-div) : (div-est); + if( idiff == 0 ){ + udiv = i; + break; /* can't do better */ + } + else if( idiff < diff ){ + udiv = i; /* best so far */ + diff = idiff; /* update lowest diff*/ + } + } + + *pudiv = udiv; + *pbdiv = div/udiv; + +} +#endif /* defined(CONFIG_440) && !defined(CFG_EXT_SERIAL_CLK */ + +/* + * Minimal serial functions needed to use one of the SMC ports + * as serial console interface. + */ + +#if defined(CONFIG_440) +#if defined(CONFIG_SERIAL_MULTI) +int serial_init_dev (unsigned long dev_base) +#else +int serial_init(void) +#endif +{ + DECLARE_GLOBAL_DATA_PTR; + + unsigned long reg; + unsigned long udiv; + unsigned short bdiv; + volatile char val; +#ifdef CFG_EXT_SERIAL_CLOCK + unsigned long tmp; +#endif + +#if defined(CONFIG_440GX) || defined(CONFIG_440SP) +#if defined(CONFIG_SERIAL_MULTI) + if (UART0_BASE == dev_base) { + mfsdr(UART0_SDR,reg); + reg &= ~CR0_MASK; + } else { + mfsdr(UART1_SDR,reg); + reg &= ~CR0_MASK; + } +#else + mfsdr(UART0_SDR,reg); + reg &= ~CR0_MASK; +#endif +#else + reg = mfdcr(cntrl0) & ~CR0_MASK; +#endif /* CONFIG_440GX */ +#ifdef CFG_EXT_SERIAL_CLOCK + reg |= CR0_EXTCLK_ENA; + udiv = 1; + tmp = gd->baudrate * 16; + bdiv = (CFG_EXT_SERIAL_CLOCK + tmp / 2) / tmp; +#else + /* For 440, the cpu clock is on divider chain A, UART on divider + * chain B ... so cpu clock is irrelevant. Get the "optimized" + * values that are subject to the 1/2 opb clock constraint + */ + serial_divs (gd->baudrate, &udiv, &bdiv); +#endif + +#if defined(CONFIG_440GX) || defined(CONFIG_440EP) || defined(CONFIG_440GR) || defined(CONFIG_440SP) + reg |= udiv << CR0_UDIV_POS; /* set the UART divisor */ +#if defined(CONFIG_SERIAL_MULTI) + if (UART0_BASE == dev_base) { + mtsdr (UART0_SDR,reg); + } else { + mtsdr (UART1_SDR,reg); + } +#else + mtsdr (UART0_SDR,reg); +#endif +#else + reg |= (udiv - 1) << CR0_UDIV_POS; /* set the UART divisor */ + mtdcr (cntrl0, reg); +#endif + +#if defined(CONFIG_SERIAL_MULTI) + out8 (dev_base + UART_LCR, 0x80); /* set DLAB bit */ + out8 (dev_base + UART_DLL, bdiv); /* set baudrate divisor */ + out8 (dev_base + UART_DLM, bdiv >> 8);/* set baudrate divisor */ + out8 (dev_base + UART_LCR, 0x03); /* clear DLAB; set 8 bits, no parity */ + out8 (dev_base + UART_FCR, 0x00); /* disable FIFO */ + out8 (dev_base + UART_MCR, 0x00); /* no modem control DTR RTS */ + val = in8 (dev_base + UART_LSR); /* clear line status */ + val = in8 (dev_base + UART_RBR); /* read receive buffer */ + out8 (dev_base + UART_SCR, 0x00); /* set scratchpad */ + out8 (dev_base + UART_IER, 0x00); /* set interrupt enable reg */ +#else + out8 (ACTING_UART0_BASE + UART_LCR, 0x80); /* set DLAB bit */ + out8 (ACTING_UART0_BASE + UART_DLL, bdiv); /* set baudrate divisor */ + out8 (ACTING_UART0_BASE + UART_DLM, bdiv >> 8);/* set baudrate divisor */ + out8 (ACTING_UART0_BASE + UART_LCR, 0x03); /* clear DLAB; set 8 bits, no parity */ + out8 (ACTING_UART0_BASE + UART_FCR, 0x00); /* disable FIFO */ + out8 (ACTING_UART0_BASE + UART_MCR, 0x00); /* no modem control DTR RTS */ + val = in8 (ACTING_UART0_BASE + UART_LSR); /* clear line status */ + val = in8 (ACTING_UART0_BASE + UART_RBR); /* read receive buffer */ + out8 (ACTING_UART0_BASE + UART_SCR, 0x00); /* set scratchpad */ + out8 (ACTING_UART0_BASE + UART_IER, 0x00); /* set interrupt enable reg */ +#endif + return (0); +} + +#else /* !defined(CONFIG_440) */ + +#if defined(CONFIG_SERIAL_MULTI) +int serial_init_dev (unsigned long dev_base) +#else +int serial_init (void) +#endif +{ + DECLARE_GLOBAL_DATA_PTR; + + unsigned long reg; + unsigned long tmp; + unsigned long clk; + unsigned long udiv; + unsigned short bdiv; + volatile char val; + +#ifdef CONFIG_405EP + reg = mfdcr(cpc0_ucr) & ~(UCR0_MASK | UCR1_MASK); + clk = gd->cpu_clk; + tmp = CFG_BASE_BAUD * 16; + udiv = (clk + tmp / 2) / tmp; + if (udiv > UDIV_MAX) /* max. n bits for udiv */ + udiv = UDIV_MAX; + reg |= (udiv) << UCR0_UDIV_POS; /* set the UART divisor */ + reg |= (udiv) << UCR1_UDIV_POS; /* set the UART divisor */ + mtdcr (cpc0_ucr, reg); +#else /* CONFIG_405EP */ + reg = mfdcr(cntrl0) & ~CR0_MASK; +#ifdef CFG_EXT_SERIAL_CLOCK + clk = CFG_EXT_SERIAL_CLOCK; + udiv = 1; + reg |= CR0_EXTCLK_ENA; +#else + clk = gd->cpu_clk; +#ifdef CFG_405_UART_ERRATA_59 + udiv = 31; /* Errata 59: stuck at 31 */ +#else + tmp = CFG_BASE_BAUD * 16; + udiv = (clk + tmp / 2) / tmp; + if (udiv > UDIV_MAX) /* max. n bits for udiv */ + udiv = UDIV_MAX; +#endif +#endif + reg |= (udiv - 1) << CR0_UDIV_POS; /* set the UART divisor */ + mtdcr (cntrl0, reg); +#endif /* CONFIG_405EP */ + + tmp = gd->baudrate * udiv * 16; + bdiv = (clk + tmp / 2) / tmp; + +#if defined(CONFIG_SERIAL_MULTI) + out8 (dev_base + UART_LCR, 0x80); /* set DLAB bit */ + out8 (dev_base + UART_DLL, bdiv); /* set baudrate divisor */ + out8 (dev_base + UART_DLM, bdiv >> 8);/* set baudrate divisor */ + out8 (dev_base + UART_LCR, 0x03); /* clear DLAB; set 8 bits, no parity */ + out8 (dev_base + UART_FCR, 0x00); /* disable FIFO */ + out8 (dev_base + UART_MCR, 0x00); /* no modem control DTR RTS */ + val = in8 (dev_base + UART_LSR); /* clear line status */ + val = in8 (dev_base + UART_RBR); /* read receive buffer */ + out8 (dev_base + UART_SCR, 0x00); /* set scratchpad */ + out8 (dev_base + UART_IER, 0x00); /* set interrupt enable reg */ +#else + out8 (ACTING_UART0_BASE + UART_LCR, 0x80); /* set DLAB bit */ + out8 (ACTING_UART0_BASE + UART_DLL, bdiv); /* set baudrate divisor */ + out8 (ACTING_UART0_BASE + UART_DLM, bdiv >> 8);/* set baudrate divisor */ + out8 (ACTING_UART0_BASE + UART_LCR, 0x03); /* clear DLAB; set 8 bits, no parity */ + out8 (ACTING_UART0_BASE + UART_FCR, 0x00); /* disable FIFO */ + out8 (ACTING_UART0_BASE + UART_MCR, 0x00); /* no modem control DTR RTS */ + val = in8 (ACTING_UART0_BASE + UART_LSR); /* clear line status */ + val = in8 (ACTING_UART0_BASE + UART_RBR); /* read receive buffer */ + out8 (ACTING_UART0_BASE + UART_SCR, 0x00); /* set scratchpad */ + out8 (ACTING_UART0_BASE + UART_IER, 0x00); /* set interrupt enable reg */ +#endif + return (0); +} + +#endif /* if defined(CONFIG_440) */ + +#if defined(CONFIG_SERIAL_MULTI) +void serial_setbrg_dev (unsigned long dev_base) +#else +void serial_setbrg (void) +#endif +{ + DECLARE_GLOBAL_DATA_PTR; + + unsigned long tmp; + unsigned long clk; + unsigned long udiv; + unsigned short bdiv; + +#ifdef CFG_EXT_SERIAL_CLOCK + clk = CFG_EXT_SERIAL_CLOCK; +#else + clk = gd->cpu_clk; +#endif + +#ifdef CONFIG_405EP + udiv = ((mfdcr (cpc0_ucr) & UCR0_MASK) >> UCR0_UDIV_POS); +#else + udiv = ((mfdcr (cntrl0) & 0x3e) >> 1) + 1; +#endif /* CONFIG_405EP */ + tmp = gd->baudrate * udiv * 16; + bdiv = (clk + tmp / 2) / tmp; + +#if defined(CONFIG_SERIAL_MULTI) + out8 (dev_base + UART_LCR, 0x80); /* set DLAB bit */ + out8 (dev_base + UART_DLL, bdiv); /* set baudrate divisor */ + out8 (dev_base + UART_DLM, bdiv >> 8);/* set baudrate divisor */ + out8 (dev_base + UART_LCR, 0x03); /* clear DLAB; set 8 bits, no parity */ +#else + out8 (ACTING_UART0_BASE + UART_LCR, 0x80); /* set DLAB bit */ + out8 (ACTING_UART0_BASE + UART_DLL, bdiv); /* set baudrate divisor */ + out8 (ACTING_UART0_BASE + UART_DLM, bdiv >> 8);/* set baudrate divisor */ + out8 (ACTING_UART0_BASE + UART_LCR, 0x03); /* clear DLAB; set 8 bits, no parity */ +#endif +} + +#if defined(CONFIG_SERIAL_MULTI) +void serial_putc_dev (unsigned long dev_base, const char c) +#else +void serial_putc (const char c) +#endif +{ + int i; + + if (c == '\n') +#if defined(CONFIG_SERIAL_MULTI) + serial_putc_dev (dev_base, '\r'); +#else + serial_putc ('\r'); +#endif + + /* check THRE bit, wait for transmiter available */ + for (i = 1; i < 3500; i++) { +#if defined(CONFIG_SERIAL_MULTI) + if ((in8 (dev_base + UART_LSR) & 0x20) == 0x20) +#else + if ((in8 (ACTING_UART0_BASE + UART_LSR) & 0x20) == 0x20) +#endif + break; + udelay (100); + } +#if defined(CONFIG_SERIAL_MULTI) + out8 (dev_base + UART_THR, c); /* put character out */ +#else + out8 (ACTING_UART0_BASE + UART_THR, c); /* put character out */ +#endif +} + +#if defined(CONFIG_SERIAL_MULTI) +void serial_puts_dev (unsigned long dev_base, const char *s) +#else +void serial_puts (const char *s) +#endif +{ + while (*s) { +#if defined(CONFIG_SERIAL_MULTI) + serial_putc_dev (dev_base, *s++); +#else + serial_putc (*s++); +#endif + } +} + +#if defined(CONFIG_SERIAL_MULTI) +int serial_getc_dev (unsigned long dev_base) +#else +int serial_getc (void) +#endif +{ + unsigned char status = 0; + + while (1) { +#if defined(CONFIG_HW_WATCHDOG) + WATCHDOG_RESET (); /* Reset HW Watchdog, if needed */ +#endif /* CONFIG_HW_WATCHDOG */ +#if defined(CONFIG_SERIAL_MULTI) + status = in8 (dev_base + UART_LSR); +#else + status = in8 (ACTING_UART0_BASE + UART_LSR); +#endif + if ((status & asyncLSRDataReady1) != 0x0) { + break; + } + if ((status & ( asyncLSRFramingError1 | + asyncLSROverrunError1 | + asyncLSRParityError1 | + asyncLSRBreakInterrupt1 )) != 0) { +#if defined(CONFIG_SERIAL_MULTI) + out8 (dev_base + UART_LSR, +#else + out8 (ACTING_UART0_BASE + UART_LSR, +#endif + asyncLSRFramingError1 | + asyncLSROverrunError1 | + asyncLSRParityError1 | + asyncLSRBreakInterrupt1); + } + } +#if defined(CONFIG_SERIAL_MULTI) + return (0x000000ff & (int) in8 (dev_base)); +#else + return (0x000000ff & (int) in8 (ACTING_UART0_BASE)); +#endif +} + +#if defined(CONFIG_SERIAL_MULTI) +int serial_tstc_dev (unsigned long dev_base) +#else +int serial_tstc (void) +#endif +{ + unsigned char status; + +#if defined(CONFIG_SERIAL_MULTI) + status = in8 (dev_base + UART_LSR); +#else + status = in8 (ACTING_UART0_BASE + UART_LSR); +#endif + if ((status & asyncLSRDataReady1) != 0x0) { + return (1); + } + if ((status & ( asyncLSRFramingError1 | + asyncLSROverrunError1 | + asyncLSRParityError1 | + asyncLSRBreakInterrupt1 )) != 0) { +#if defined(CONFIG_SERIAL_MULTI) + out8 (dev_base + UART_LSR, +#else + out8 (ACTING_UART0_BASE + UART_LSR, +#endif + asyncLSRFramingError1 | + asyncLSROverrunError1 | + asyncLSRParityError1 | + asyncLSRBreakInterrupt1); + } + return 0; +} + +#ifdef CONFIG_SERIAL_SOFTWARE_FIFO + +void serial_isr (void *arg) +{ + int space; + int c; + const int rx_get = buf_info.rx_get; + int rx_put = buf_info.rx_put; + + if (rx_get <= rx_put) { + space = CONFIG_SERIAL_SOFTWARE_FIFO - (rx_put - rx_get); + } else { + space = rx_get - rx_put; + } + while (serial_tstc_dev (ACTING_UART0_BASE)) { + c = serial_getc_dev (ACTING_UART0_BASE); + if (space) { + buf_info.rx_buffer[rx_put++] = c; + space--; + } + if (rx_put == CONFIG_SERIAL_SOFTWARE_FIFO) + rx_put = 0; + if (space < CONFIG_SERIAL_SOFTWARE_FIFO / 4) { + /* Stop flow by setting RTS inactive */ + out8 (ACTING_UART0_BASE + UART_MCR, + in8 (ACTING_UART0_BASE + UART_MCR) & (0xFF ^ 0x02)); + } + } + buf_info.rx_put = rx_put; +} + +void serial_buffered_init (void) +{ + serial_puts ("Switching to interrupt driven serial input mode.\n"); + buf_info.rx_buffer = malloc (CONFIG_SERIAL_SOFTWARE_FIFO); + buf_info.rx_put = 0; + buf_info.rx_get = 0; + + if (in8 (ACTING_UART0_BASE + UART_MSR) & 0x10) { + serial_puts ("Check CTS signal present on serial port: OK.\n"); + } else { + serial_puts ("WARNING: CTS signal not present on serial port.\n"); + } + + irq_install_handler ( VECNUM_U0 /*UART0 */ /*int vec */ , + serial_isr /*interrupt_handler_t *handler */ , + (void *) &buf_info /*void *arg */ ); + + /* Enable "RX Data Available" Interrupt on UART */ + /* out8(ACTING_UART0_BASE + UART_IER, in8(ACTING_UART0_BASE + UART_IER) |0x01); */ + out8 (ACTING_UART0_BASE + UART_IER, 0x01); + /* Set DTR active */ + out8 (ACTING_UART0_BASE + UART_MCR, in8 (ACTING_UART0_BASE + UART_MCR) | 0x01); + /* Start flow by setting RTS active */ + out8 (ACTING_UART0_BASE + UART_MCR, in8 (ACTING_UART0_BASE + UART_MCR) | 0x02); + /* Setup UART FIFO: RX trigger level: 4 byte, Enable FIFO */ + out8 (ACTING_UART0_BASE + UART_FCR, (1 << 6) | 1); +} + +void serial_buffered_putc (const char c) +{ + /* Wait for CTS */ +#if defined(CONFIG_HW_WATCHDOG) + while (!(in8 (ACTING_UART0_BASE + UART_MSR) & 0x10)) + WATCHDOG_RESET (); +#else + while (!(in8 (ACTING_UART0_BASE + UART_MSR) & 0x10)); +#endif + serial_putc (c); +} + +void serial_buffered_puts (const char *s) +{ + serial_puts (s); +} + +int serial_buffered_getc (void) +{ + int space; + int c; + int rx_get = buf_info.rx_get; + int rx_put; + +#if defined(CONFIG_HW_WATCHDOG) + while (rx_get == buf_info.rx_put) + WATCHDOG_RESET (); +#else + while (rx_get == buf_info.rx_put); +#endif + c = buf_info.rx_buffer[rx_get++]; + if (rx_get == CONFIG_SERIAL_SOFTWARE_FIFO) + rx_get = 0; + buf_info.rx_get = rx_get; + + rx_put = buf_info.rx_put; + if (rx_get <= rx_put) { + space = CONFIG_SERIAL_SOFTWARE_FIFO - (rx_put - rx_get); + } else { + space = rx_get - rx_put; + } + if (space > CONFIG_SERIAL_SOFTWARE_FIFO / 2) { + /* Start flow by setting RTS active */ + out8 (ACTING_UART0_BASE + UART_MCR, in8 (ACTING_UART0_BASE + UART_MCR) | 0x02); + } + + return c; +} + +int serial_buffered_tstc (void) +{ + return (buf_info.rx_get != buf_info.rx_put) ? 1 : 0; +} + +#endif /* CONFIG_SERIAL_SOFTWARE_FIFO */ + +#if (CONFIG_COMMANDS & CFG_CMD_KGDB) +/* + AS HARNOIS : according to CONFIG_KGDB_SER_INDEX kgdb uses serial port + number 0 or number 1 + - if CONFIG_KGDB_SER_INDEX = 1 => serial port number 0 : + configuration has been already done + - if CONFIG_KGDB_SER_INDEX = 2 => serial port number 1 : + configure port 1 for serial I/O with rate = CONFIG_KGDB_BAUDRATE +*/ +#if (CONFIG_KGDB_SER_INDEX & 2) +void kgdb_serial_init (void) +{ + DECLARE_GLOBAL_DATA_PTR; + + volatile char val; + unsigned short br_reg; + + get_clocks (); + br_reg = (((((gd->cpu_clk / 16) / 18) * 10) / CONFIG_KGDB_BAUDRATE) + + 5) / 10; + /* + * Init onboard 16550 UART + */ + out8 (ACTING_UART1_BASE + UART_LCR, 0x80); /* set DLAB bit */ + out8 (ACTING_UART1_BASE + UART_DLL, (br_reg & 0x00ff)); /* set divisor for 9600 baud */ + out8 (ACTING_UART1_BASE + UART_DLM, ((br_reg & 0xff00) >> 8)); /* set divisor for 9600 baud */ + out8 (ACTING_UART1_BASE + UART_LCR, 0x03); /* line control 8 bits no parity */ + out8 (ACTING_UART1_BASE + UART_FCR, 0x00); /* disable FIFO */ + out8 (ACTING_UART1_BASE + UART_MCR, 0x00); /* no modem control DTR RTS */ + val = in8 (ACTING_UART1_BASE + UART_LSR); /* clear line status */ + val = in8 (ACTING_UART1_BASE + UART_RBR); /* read receive buffer */ + out8 (ACTING_UART1_BASE + UART_SCR, 0x00); /* set scratchpad */ + out8 (ACTING_UART1_BASE + UART_IER, 0x00); /* set interrupt enable reg */ +} + +void putDebugChar (const char c) +{ + if (c == '\n') + serial_putc ('\r'); + + out8 (ACTING_UART1_BASE + UART_THR, c); /* put character out */ + + /* check THRE bit, wait for transfer done */ + while ((in8 (ACTING_UART1_BASE + UART_LSR) & 0x20) != 0x20); +} + +void putDebugStr (const char *s) +{ + while (*s) { + serial_putc (*s++); + } +} + +int getDebugChar (void) +{ + unsigned char status = 0; + + while (1) { + status = in8 (ACTING_UART1_BASE + UART_LSR); + if ((status & asyncLSRDataReady1) != 0x0) { + break; + } + if ((status & ( asyncLSRFramingError1 | + asyncLSROverrunError1 | + asyncLSRParityError1 | + asyncLSRBreakInterrupt1 )) != 0) { + out8 (ACTING_UART1_BASE + UART_LSR, + asyncLSRFramingError1 | + asyncLSROverrunError1 | + asyncLSRParityError1 | + asyncLSRBreakInterrupt1); + } + } + return (0x000000ff & (int) in8 (ACTING_UART1_BASE)); +} + +void kgdb_interruptible (int yes) +{ + return; +} + +#else /* ! (CONFIG_KGDB_SER_INDEX & 2) */ + +void kgdb_serial_init (void) +{ + serial_printf ("[on serial] "); +} + +void putDebugChar (int c) +{ + serial_putc (c); +} + +void putDebugStr (const char *str) +{ + serial_puts (str); +} + +int getDebugChar (void) +{ + return serial_getc (); +} + +void kgdb_interruptible (int yes) +{ + return; +} +#endif /* (CONFIG_KGDB_SER_INDEX & 2) */ +#endif /* CFG_CMD_KGDB */ + + +#if defined(CONFIG_SERIAL_MULTI) +int serial0_init(void) +{ + return (serial_init_dev(UART0_BASE)); +} + +int serial1_init(void) +{ + return (serial_init_dev(UART1_BASE)); +} +void serial0_setbrg (void) +{ + serial_setbrg_dev(UART0_BASE); +} +void serial1_setbrg (void) +{ + serial_setbrg_dev(UART1_BASE); +} + +void serial0_putc(const char c) +{ + serial_putc_dev(UART0_BASE,c); +} + +void serial1_putc(const char c) +{ + serial_putc_dev(UART1_BASE, c); +} +void serial0_puts(const char *s) +{ + serial_puts_dev(UART0_BASE, s); +} + +void serial1_puts(const char *s) +{ + serial_puts_dev(UART1_BASE, s); +} + +int serial0_getc(void) +{ + return(serial_getc_dev(UART0_BASE)); +} + +int serial1_getc(void) +{ + return(serial_getc_dev(UART1_BASE)); +} +int serial0_tstc(void) +{ + return (serial_tstc_dev(UART0_BASE)); +} + +int serial1_tstc(void) +{ + return (serial_tstc_dev(UART1_BASE)); +} + +struct serial_device serial0_device = +{ + "serial0", + "UART0", + serial0_init, + serial0_setbrg, + serial0_getc, + serial0_tstc, + serial0_putc, + serial0_puts, +}; + +struct serial_device serial1_device = +{ + "serial1", + "UART1", + serial1_init, + serial1_setbrg, + serial1_getc, + serial1_tstc, + serial1_putc, + serial1_puts, +}; +#endif /* CONFIG_SERIAL_MULTI */ + +#endif /* CONFIG_405GP || CONFIG_405CR */ diff --git a/cpu/ppc4xx/spd_sdram.c b/cpu/ppc4xx/spd_sdram.c new file mode 100755 index 0000000..ebd5f39 --- /dev/null +++ b/cpu/ppc4xx/spd_sdram.c @@ -0,0 +1,1831 @@ +/* + * (C) Copyright 2001 + * Bill Hunter, Wave 7 Optics, williamhunter@attbi.com + * + * Based on code by: + * + * Kenneth Johansson ,Ericsson AB. + * kenneth.johansson@etx.ericsson.se + * + * hacked up by bill hunter. fixed so we could run before + * serial_init and console_init. previous version avoided this by + * running out of cache memory during serial/console init, then running + * this code later. + * + * (C) Copyright 2002 + * Jun Gu, Artesyn Technology, jung@artesyncp.com + * Support for AMCC 440 based on OpenBIOS draminit.c from IBM. + * + * (C) Copyright 2005 + * Stefan Roese, DENX Software Engineering, sr@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 <asm/processor.h> +#include <i2c.h> +#include <ppc4xx.h> + +#ifdef CONFIG_SPD_EEPROM + +/* + * Set default values + */ +#ifndef CFG_I2C_SPEED +#define CFG_I2C_SPEED 50000 +#endif + +#ifndef CFG_I2C_SLAVE +#define CFG_I2C_SLAVE 0xFE +#endif + +#define ONE_BILLION 1000000000 + +#ifndef CONFIG_440 /* for 405 WALNUT/SYCAMORE/BUBINGA boards */ + +#define SDRAM0_CFG_DCE 0x80000000 +#define SDRAM0_CFG_SRE 0x40000000 +#define SDRAM0_CFG_PME 0x20000000 +#define SDRAM0_CFG_MEMCHK 0x10000000 +#define SDRAM0_CFG_REGEN 0x08000000 +#define SDRAM0_CFG_ECCDD 0x00400000 +#define SDRAM0_CFG_EMDULR 0x00200000 +#define SDRAM0_CFG_DRW_SHIFT (31-6) +#define SDRAM0_CFG_BRPF_SHIFT (31-8) + +#define SDRAM0_TR_CASL_SHIFT (31-8) +#define SDRAM0_TR_PTA_SHIFT (31-13) +#define SDRAM0_TR_CTP_SHIFT (31-15) +#define SDRAM0_TR_LDF_SHIFT (31-17) +#define SDRAM0_TR_RFTA_SHIFT (31-29) +#define SDRAM0_TR_RCD_SHIFT (31-31) + +#define SDRAM0_RTR_SHIFT (31-15) +#define SDRAM0_ECCCFG_SHIFT (31-11) + +/* SDRAM0_CFG enable macro */ +#define SDRAM0_CFG_BRPF(x) ( ( x & 0x3)<< SDRAM0_CFG_BRPF_SHIFT ) + +#define SDRAM0_BXCR_SZ_MASK 0x000e0000 +#define SDRAM0_BXCR_AM_MASK 0x0000e000 + +#define SDRAM0_BXCR_SZ_SHIFT (31-14) +#define SDRAM0_BXCR_AM_SHIFT (31-18) + +#define SDRAM0_BXCR_SZ(x) ( (( x << SDRAM0_BXCR_SZ_SHIFT) & SDRAM0_BXCR_SZ_MASK) ) +#define SDRAM0_BXCR_AM(x) ( (( x << SDRAM0_BXCR_AM_SHIFT) & SDRAM0_BXCR_AM_MASK) ) + +#ifdef CONFIG_SPDDRAM_SILENT +# define SPD_ERR(x) do { return 0; } while (0) +#else +# define SPD_ERR(x) do { printf(x); return(0); } while (0) +#endif + +#define sdram_HZ_to_ns(hertz) (1000000000/(hertz)) + +/* function prototypes */ +int spd_read(uint addr); + + +/* + * This function is reading data from the DIMM module EEPROM over the SPD bus + * and uses that to program the sdram controller. + * + * This works on boards that has the same schematics that the AMCC walnut has. + * + * Input: null for default I2C spd functions or a pointer to a custom function + * returning spd_data. + */ + +long int spd_sdram(int(read_spd)(uint addr)) +{ + int tmp,row,col; + int total_size,bank_size,bank_code; + int ecc_on; + int mode; + int bank_cnt; + + int sdram0_pmit=0x07c00000; +#ifndef CONFIG_405EP /* not on PPC405EP */ + int sdram0_besr0=-1; + int sdram0_besr1=-1; + int sdram0_eccesr=-1; +#endif + int sdram0_ecccfg; + + int sdram0_rtr=0; + int sdram0_tr=0; + + int sdram0_b0cr; + int sdram0_b1cr; + int sdram0_b2cr; + int sdram0_b3cr; + + int sdram0_cfg=0; + + int t_rp; + int t_rcd; + int t_ras; + int t_rc; + int min_cas; + + PPC405_SYS_INFO sys_info; + unsigned long bus_period_x_10; + + /* + * get the board info + */ + get_sys_info(&sys_info); + bus_period_x_10 = ONE_BILLION / (sys_info.freqPLB / 10); + + if (read_spd == 0){ + read_spd=spd_read; + /* + * Make sure I2C controller is initialized + * before continuing. + */ + i2c_init(CFG_I2C_SPEED, CFG_I2C_SLAVE); + } + + /* Make shure we are using SDRAM */ + if (read_spd(2) != 0x04) { + SPD_ERR("SDRAM - non SDRAM memory module found\n"); + } + + /* ------------------------------------------------------------------ + * configure memory timing register + * + * data from DIMM: + * 27 IN Row Precharge Time ( t RP) + * 29 MIN RAS to CAS Delay ( t RCD) + * 127 Component and Clock Detail ,clk0-clk3, junction temp, CAS + * -------------------------------------------------------------------*/ + + /* + * first figure out which cas latency mode to use + * use the min supported mode + */ + + tmp = read_spd(127) & 0x6; + if (tmp == 0x02) { /* only cas = 2 supported */ + min_cas = 2; +/* t_ck = read_spd(9); */ +/* t_ac = read_spd(10); */ + } else if (tmp == 0x04) { /* only cas = 3 supported */ + min_cas = 3; +/* t_ck = read_spd(9); */ +/* t_ac = read_spd(10); */ + } else if (tmp == 0x06) { /* 2,3 supported, so use 2 */ + min_cas = 2; +/* t_ck = read_spd(23); */ +/* t_ac = read_spd(24); */ + } else { + SPD_ERR("SDRAM - unsupported CAS latency \n"); + } + + /* get some timing values, t_rp,t_rcd,t_ras,t_rc + */ + t_rp = read_spd(27); + t_rcd = read_spd(29); + t_ras = read_spd(30); + t_rc = t_ras + t_rp; + + /* The following timing calcs subtract 1 before deviding. + * this has effect of using ceiling instead of floor rounding, + * and also subtracting 1 to convert number to reg value + */ + /* set up CASL */ + sdram0_tr = (min_cas - 1) << SDRAM0_TR_CASL_SHIFT; + /* set up PTA */ + sdram0_tr |= ((((t_rp - 1) * 10)/bus_period_x_10) & 0x3) << SDRAM0_TR_PTA_SHIFT; + /* set up CTP */ + tmp = (((t_rc - t_rcd - t_rp -1) * 10) / bus_period_x_10) & 0x3; + if (tmp < 1) + tmp = 1; + sdram0_tr |= tmp << SDRAM0_TR_CTP_SHIFT; + /* set LDF = 2 cycles, reg value = 1 */ + sdram0_tr |= 1 << SDRAM0_TR_LDF_SHIFT; + /* set RFTA = t_rfc/bus_period, use t_rfc = t_rc */ + tmp = (((t_rc - 1) * 10) / bus_period_x_10) - 3; + if (tmp < 0) + tmp = 0; + if (tmp > 6) + tmp = 6; + sdram0_tr |= tmp << SDRAM0_TR_RFTA_SHIFT; + /* set RCD = t_rcd/bus_period*/ + sdram0_tr |= ((((t_rcd - 1) * 10) / bus_period_x_10) &0x3) << SDRAM0_TR_RCD_SHIFT ; + + + /*------------------------------------------------------------------ + * configure RTR register + * -------------------------------------------------------------------*/ + row = read_spd(3); + col = read_spd(4); + tmp = read_spd(12) & 0x7f ; /* refresh type less self refresh bit */ + switch (tmp) { + case 0x00: + tmp = 15625; + break; + case 0x01: + tmp = 15625 / 4; + break; + case 0x02: + tmp = 15625 / 2; + break; + case 0x03: + tmp = 15625 * 2; + break; + case 0x04: + tmp = 15625 * 4; + break; + case 0x05: + tmp = 15625 * 8; + break; + default: + SPD_ERR("SDRAM - Bad refresh period \n"); + } + /* convert from nsec to bus cycles */ + tmp = (tmp * 10) / bus_period_x_10; + sdram0_rtr = (tmp & 0x3ff8) << SDRAM0_RTR_SHIFT; + + /*------------------------------------------------------------------ + * determine the number of banks used + * -------------------------------------------------------------------*/ + /* byte 7:6 is module data width */ + if (read_spd(7) != 0) + SPD_ERR("SDRAM - unsupported module width\n"); + tmp = read_spd(6); + if (tmp < 32) + SPD_ERR("SDRAM - unsupported module width\n"); + else if (tmp < 64) + bank_cnt = 1; /* one bank per sdram side */ + else if (tmp < 73) + bank_cnt = 2; /* need two banks per side */ + else if (tmp < 161) + bank_cnt = 4; /* need four banks per side */ + else + SPD_ERR("SDRAM - unsupported module width\n"); + + /* byte 5 is the module row count (refered to as dimm "sides") */ + tmp = read_spd(5); + if (tmp == 1) + ; + else if (tmp==2) + bank_cnt *= 2; + else if (tmp==4) + bank_cnt *= 4; + else + bank_cnt = 8; /* 8 is an error code */ + + if (bank_cnt > 4) /* we only have 4 banks to work with */ + SPD_ERR("SDRAM - unsupported module rows for this width\n"); + + /* now check for ECC ability of module. We only support ECC + * on 32 bit wide devices with 8 bit ECC. + */ + if ((read_spd(11)==2) && (read_spd(6)==40) && (read_spd(14)==8)) { + sdram0_ecccfg = 0xf << SDRAM0_ECCCFG_SHIFT; + ecc_on = 1; + } else { + sdram0_ecccfg = 0; + ecc_on = 0; + } + + /*------------------------------------------------------------------ + * calculate total size + * -------------------------------------------------------------------*/ + /* calculate total size and do sanity check */ + tmp = read_spd(31); + total_size = 1 << 22; /* total_size = 4MB */ + /* now multiply 4M by the smallest device row density */ + /* note that we don't support asymetric rows */ + while (((tmp & 0x0001) == 0) && (tmp != 0)) { + total_size = total_size << 1; + tmp = tmp >> 1; + } + total_size *= read_spd(5); /* mult by module rows (dimm sides) */ + + /*------------------------------------------------------------------ + * map rows * cols * banks to a mode + * -------------------------------------------------------------------*/ + + switch (row) { + case 11: + switch (col) { + case 8: + mode=4; /* mode 5 */ + break; + case 9: + case 10: + mode=0; /* mode 1 */ + break; + default: + SPD_ERR("SDRAM - unsupported mode\n"); + } + break; + case 12: + switch (col) { + case 8: + mode=3; /* mode 4 */ + break; + case 9: + case 10: + mode=1; /* mode 2 */ + break; + default: + SPD_ERR("SDRAM - unsupported mode\n"); + } + break; + case 13: + switch (col) { + case 8: + mode=5; /* mode 6 */ + break; + case 9: + case 10: + if (read_spd(17) == 2) + mode = 6; /* mode 7 */ + else + mode = 2; /* mode 3 */ + break; + case 11: + mode = 2; /* mode 3 */ + break; + default: + SPD_ERR("SDRAM - unsupported mode\n"); + } + break; + default: + SPD_ERR("SDRAM - unsupported mode\n"); + } + + /*------------------------------------------------------------------ + * using the calculated values, compute the bank + * config register values. + * -------------------------------------------------------------------*/ + sdram0_b1cr = 0; + sdram0_b2cr = 0; + sdram0_b3cr = 0; + + /* compute the size of each bank */ + bank_size = total_size / bank_cnt; + /* convert bank size to bank size code for ppc4xx + by takeing log2(bank_size) - 22 */ + tmp = bank_size; /* start with tmp = bank_size */ + bank_code = 0; /* and bank_code = 0 */ + while (tmp > 1) { /* this takes log2 of tmp */ + bank_code++; /* and stores result in bank_code */ + tmp = tmp >> 1; + } /* bank_code is now log2(bank_size) */ + bank_code -= 22; /* subtract 22 to get the code */ + + tmp = SDRAM0_BXCR_SZ(bank_code) | SDRAM0_BXCR_AM(mode) | 1; + sdram0_b0cr = (bank_size * 0) | tmp; +#ifndef CONFIG_405EP /* not on PPC405EP */ + if (bank_cnt > 1) + sdram0_b2cr = (bank_size * 1) | tmp; + if (bank_cnt > 2) + sdram0_b1cr = (bank_size * 2) | tmp; + if (bank_cnt > 3) + sdram0_b3cr = (bank_size * 3) | tmp; +#else + /* PPC405EP chip only supports two SDRAM banks */ + if (bank_cnt > 1) + sdram0_b1cr = (bank_size * 1) | tmp; + if (bank_cnt > 2) + total_size = 2 * bank_size; +#endif + + /* + * enable sdram controller DCE=1 + * enable burst read prefetch to 32 bytes BRPF=2 + * leave other functions off + */ + + /*------------------------------------------------------------------ + * now that we've done our calculations, we are ready to + * program all the registers. + * -------------------------------------------------------------------*/ + +#define mtsdram0(reg, data) mtdcr(memcfga,reg);mtdcr(memcfgd,data) + /* disable memcontroller so updates work */ + mtsdram0( mem_mcopt1, 0 ); + +#ifndef CONFIG_405EP /* not on PPC405EP */ + mtsdram0( mem_besra , sdram0_besr0 ); + mtsdram0( mem_besrb , sdram0_besr1 ); + mtsdram0( mem_ecccf , sdram0_ecccfg ); + mtsdram0( mem_eccerr, sdram0_eccesr ); +#endif + mtsdram0( mem_rtr , sdram0_rtr ); + mtsdram0( mem_pmit , sdram0_pmit ); + mtsdram0( mem_mb0cf , sdram0_b0cr ); + mtsdram0( mem_mb1cf , sdram0_b1cr ); +#ifndef CONFIG_405EP /* not on PPC405EP */ + mtsdram0( mem_mb2cf , sdram0_b2cr ); + mtsdram0( mem_mb3cf , sdram0_b3cr ); +#endif + mtsdram0( mem_sdtr1 , sdram0_tr ); + + /* SDRAM have a power on delay, 500 micro should do */ + udelay(500); + sdram0_cfg = SDRAM0_CFG_DCE | SDRAM0_CFG_BRPF(1) | SDRAM0_CFG_ECCDD | SDRAM0_CFG_EMDULR; + if (ecc_on) + sdram0_cfg |= SDRAM0_CFG_MEMCHK; + mtsdram0(mem_mcopt1, sdram0_cfg); + + return (total_size); +} + +int spd_read(uint addr) +{ + uchar data[2]; + + if (i2c_read(SPD_EEPROM_ADDRESS, addr, 1, data, 1) == 0) + return (int)data[0]; + else + return 0; +} + +#else /* CONFIG_440 */ + +/*----------------------------------------------------------------------------- + | Memory Controller Options 0 + +-----------------------------------------------------------------------------*/ +#define SDRAM_CFG0_DCEN 0x80000000 /* SDRAM Controller Enable */ +#define SDRAM_CFG0_MCHK_MASK 0x30000000 /* Memory data errchecking mask */ +#define SDRAM_CFG0_MCHK_NON 0x00000000 /* No ECC generation */ +#define SDRAM_CFG0_MCHK_GEN 0x20000000 /* ECC generation */ +#define SDRAM_CFG0_MCHK_CHK 0x30000000 /* ECC generation and checking */ +#define SDRAM_CFG0_RDEN 0x08000000 /* Registered DIMM enable */ +#define SDRAM_CFG0_PMUD 0x04000000 /* Page management unit */ +#define SDRAM_CFG0_DMWD_MASK 0x02000000 /* DRAM width mask */ +#define SDRAM_CFG0_DMWD_32 0x00000000 /* 32 bits */ +#define SDRAM_CFG0_DMWD_64 0x02000000 /* 64 bits */ +#define SDRAM_CFG0_UIOS_MASK 0x00C00000 /* Unused IO State */ +#define SDRAM_CFG0_PDP 0x00200000 /* Page deallocation policy */ + +/*----------------------------------------------------------------------------- + | Memory Controller Options 1 + +-----------------------------------------------------------------------------*/ +#define SDRAM_CFG1_SRE 0x80000000 /* Self-Refresh Entry */ +#define SDRAM_CFG1_PMEN 0x40000000 /* Power Management Enable */ + +/*-----------------------------------------------------------------------------+ + | SDRAM DEVPOT Options + +-----------------------------------------------------------------------------*/ +#define SDRAM_DEVOPT_DLL 0x80000000 +#define SDRAM_DEVOPT_DS 0x40000000 + +/*-----------------------------------------------------------------------------+ + | SDRAM MCSTS Options + +-----------------------------------------------------------------------------*/ +#define SDRAM_MCSTS_MRSC 0x80000000 +#define SDRAM_MCSTS_SRMS 0x40000000 +#define SDRAM_MCSTS_CIS 0x20000000 + +/*----------------------------------------------------------------------------- + | SDRAM Refresh Timer Register + +-----------------------------------------------------------------------------*/ +#define SDRAM_RTR_RINT_MASK 0xFFFF0000 +#define SDRAM_RTR_RINT_ENCODE(n) (((n) << 16) & SDRAM_RTR_RINT_MASK) +#define sdram_HZ_to_ns(hertz) (1000000000/(hertz)) + +/*-----------------------------------------------------------------------------+ + | SDRAM UABus Base Address Reg + +-----------------------------------------------------------------------------*/ +#define SDRAM_UABBA_UBBA_MASK 0x0000000F + +/*-----------------------------------------------------------------------------+ + | Memory Bank 0-7 configuration + +-----------------------------------------------------------------------------*/ +#define SDRAM_BXCR_SDBA_MASK 0xff800000 /* Base address */ +#define SDRAM_BXCR_SDSZ_MASK 0x000e0000 /* Size */ +#define SDRAM_BXCR_SDSZ_8 0x00020000 /* 8M */ +#define SDRAM_BXCR_SDSZ_16 0x00040000 /* 16M */ +#define SDRAM_BXCR_SDSZ_32 0x00060000 /* 32M */ +#define SDRAM_BXCR_SDSZ_64 0x00080000 /* 64M */ +#define SDRAM_BXCR_SDSZ_128 0x000a0000 /* 128M */ +#define SDRAM_BXCR_SDSZ_256 0x000c0000 /* 256M */ +#define SDRAM_BXCR_SDSZ_512 0x000e0000 /* 512M */ +#define SDRAM_BXCR_SDAM_MASK 0x0000e000 /* Addressing mode */ +#define SDRAM_BXCR_SDAM_1 0x00000000 /* Mode 1 */ +#define SDRAM_BXCR_SDAM_2 0x00002000 /* Mode 2 */ +#define SDRAM_BXCR_SDAM_3 0x00004000 /* Mode 3 */ +#define SDRAM_BXCR_SDAM_4 0x00006000 /* Mode 4 */ +#define SDRAM_BXCR_SDBE 0x00000001 /* Memory Bank Enable */ + +/*-----------------------------------------------------------------------------+ + | SDRAM TR0 Options + +-----------------------------------------------------------------------------*/ +#define SDRAM_TR0_SDWR_MASK 0x80000000 +#define SDRAM_TR0_SDWR_2_CLK 0x00000000 +#define SDRAM_TR0_SDWR_3_CLK 0x80000000 +#define SDRAM_TR0_SDWD_MASK 0x40000000 +#define SDRAM_TR0_SDWD_0_CLK 0x00000000 +#define SDRAM_TR0_SDWD_1_CLK 0x40000000 +#define SDRAM_TR0_SDCL_MASK 0x01800000 +#define SDRAM_TR0_SDCL_2_0_CLK 0x00800000 +#define SDRAM_TR0_SDCL_2_5_CLK 0x01000000 +#define SDRAM_TR0_SDCL_3_0_CLK 0x01800000 +#define SDRAM_TR0_SDPA_MASK 0x000C0000 +#define SDRAM_TR0_SDPA_2_CLK 0x00040000 +#define SDRAM_TR0_SDPA_3_CLK 0x00080000 +#define SDRAM_TR0_SDPA_4_CLK 0x000C0000 +#define SDRAM_TR0_SDCP_MASK 0x00030000 +#define SDRAM_TR0_SDCP_2_CLK 0x00000000 +#define SDRAM_TR0_SDCP_3_CLK 0x00010000 +#define SDRAM_TR0_SDCP_4_CLK 0x00020000 +#define SDRAM_TR0_SDCP_5_CLK 0x00030000 +#define SDRAM_TR0_SDLD_MASK 0x0000C000 +#define SDRAM_TR0_SDLD_1_CLK 0x00000000 +#define SDRAM_TR0_SDLD_2_CLK 0x00004000 +#define SDRAM_TR0_SDRA_MASK 0x0000001C +#define SDRAM_TR0_SDRA_6_CLK 0x00000000 +#define SDRAM_TR0_SDRA_7_CLK 0x00000004 +#define SDRAM_TR0_SDRA_8_CLK 0x00000008 +#define SDRAM_TR0_SDRA_9_CLK 0x0000000C +#define SDRAM_TR0_SDRA_10_CLK 0x00000010 +#define SDRAM_TR0_SDRA_11_CLK 0x00000014 +#define SDRAM_TR0_SDRA_12_CLK 0x00000018 +#define SDRAM_TR0_SDRA_13_CLK 0x0000001C +#define SDRAM_TR0_SDRD_MASK 0x00000003 +#define SDRAM_TR0_SDRD_2_CLK 0x00000001 +#define SDRAM_TR0_SDRD_3_CLK 0x00000002 +#define SDRAM_TR0_SDRD_4_CLK 0x00000003 + +/*-----------------------------------------------------------------------------+ + | SDRAM TR1 Options + +-----------------------------------------------------------------------------*/ +#define SDRAM_TR1_RDSS_MASK 0xC0000000 +#define SDRAM_TR1_RDSS_TR0 0x00000000 +#define SDRAM_TR1_RDSS_TR1 0x40000000 +#define SDRAM_TR1_RDSS_TR2 0x80000000 +#define SDRAM_TR1_RDSS_TR3 0xC0000000 +#define SDRAM_TR1_RDSL_MASK 0x00C00000 +#define SDRAM_TR1_RDSL_STAGE1 0x00000000 +#define SDRAM_TR1_RDSL_STAGE2 0x00400000 +#define SDRAM_TR1_RDSL_STAGE3 0x00800000 +#define SDRAM_TR1_RDCD_MASK 0x00000800 +#define SDRAM_TR1_RDCD_RCD_0_0 0x00000000 +#define SDRAM_TR1_RDCD_RCD_1_2 0x00000800 +#define SDRAM_TR1_RDCT_MASK 0x000001FF +#define SDRAM_TR1_RDCT_ENCODE(x) (((x) << 0) & SDRAM_TR1_RDCT_MASK) +#define SDRAM_TR1_RDCT_DECODE(x) (((x) & SDRAM_TR1_RDCT_MASK) >> 0) +#define SDRAM_TR1_RDCT_MIN 0x00000000 +#define SDRAM_TR1_RDCT_MAX 0x000001FF + +/*-----------------------------------------------------------------------------+ + | SDRAM WDDCTR Options + +-----------------------------------------------------------------------------*/ +#define SDRAM_WDDCTR_WRCP_MASK 0xC0000000 +#define SDRAM_WDDCTR_WRCP_0DEG 0x00000000 +#define SDRAM_WDDCTR_WRCP_90DEG 0x40000000 +#define SDRAM_WDDCTR_WRCP_180DEG 0x80000000 +#define SDRAM_WDDCTR_DCD_MASK 0x000001FF + +/*-----------------------------------------------------------------------------+ + | SDRAM CLKTR Options + +-----------------------------------------------------------------------------*/ +#define SDRAM_CLKTR_CLKP_MASK 0xC0000000 +#define SDRAM_CLKTR_CLKP_0DEG 0x00000000 +#define SDRAM_CLKTR_CLKP_90DEG 0x40000000 +#define SDRAM_CLKTR_CLKP_180DEG 0x80000000 +#define SDRAM_CLKTR_DCDT_MASK 0x000001FF + +/*-----------------------------------------------------------------------------+ + | SDRAM DLYCAL Options + +-----------------------------------------------------------------------------*/ +#define SDRAM_DLYCAL_DLCV_MASK 0x000003FC +#define SDRAM_DLYCAL_DLCV_ENCODE(x) (((x)<<2) & SDRAM_DLYCAL_DLCV_MASK) +#define SDRAM_DLYCAL_DLCV_DECODE(x) (((x) & SDRAM_DLYCAL_DLCV_MASK)>>2) + +/*-----------------------------------------------------------------------------+ + | General Definition + +-----------------------------------------------------------------------------*/ +#define DEFAULT_SPD_ADDR1 0x53 +#define DEFAULT_SPD_ADDR2 0x52 +#define MAXBANKS 4 /* at most 4 dimm banks */ +#define MAX_SPD_BYTES 256 +#define NUMHALFCYCLES 4 +#define NUMMEMTESTS 8 +#define NUMMEMWORDS 8 +#define MAXBXCR 4 +#define TRUE 1 +#define FALSE 0 + +const unsigned long test[NUMMEMTESTS][NUMMEMWORDS] = { + {0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF}, + {0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000}, + {0xAAAAAAAA, 0xAAAAAAAA, 0x55555555, 0x55555555, 0xAAAAAAAA, 0xAAAAAAAA, + 0x55555555, 0x55555555}, + {0x55555555, 0x55555555, 0xAAAAAAAA, 0xAAAAAAAA, 0x55555555, 0x55555555, + 0xAAAAAAAA, 0xAAAAAAAA}, + {0xA5A5A5A5, 0xA5A5A5A5, 0x5A5A5A5A, 0x5A5A5A5A, 0xA5A5A5A5, 0xA5A5A5A5, + 0x5A5A5A5A, 0x5A5A5A5A}, + {0x5A5A5A5A, 0x5A5A5A5A, 0xA5A5A5A5, 0xA5A5A5A5, 0x5A5A5A5A, 0x5A5A5A5A, + 0xA5A5A5A5, 0xA5A5A5A5}, + {0xAA55AA55, 0xAA55AA55, 0x55AA55AA, 0x55AA55AA, 0xAA55AA55, 0xAA55AA55, + 0x55AA55AA, 0x55AA55AA}, + {0x55AA55AA, 0x55AA55AA, 0xAA55AA55, 0xAA55AA55, 0x55AA55AA, 0x55AA55AA, + 0xAA55AA55, 0xAA55AA55} +}; + +/* bank_parms is used to sort the bank sizes by descending order */ +struct bank_param { + unsigned long cr; + unsigned long bank_size_bytes; +}; + +typedef struct bank_param BANKPARMS; + +#ifdef CFG_SIMULATE_SPD_EEPROM +extern unsigned char cfg_simulate_spd_eeprom[128]; +#endif + +unsigned char spd_read(uchar chip, uint addr); + +void get_spd_info(unsigned long* dimm_populated, + unsigned char* iic0_dimm_addr, + unsigned long num_dimm_banks); + +void check_mem_type +(unsigned long* dimm_populated, + unsigned char* iic0_dimm_addr, + unsigned long num_dimm_banks); + +void check_volt_type +(unsigned long* dimm_populated, + unsigned char* iic0_dimm_addr, + unsigned long num_dimm_banks); + +void program_cfg0(unsigned long* dimm_populated, + unsigned char* iic0_dimm_addr, + unsigned long num_dimm_banks); + +void program_cfg1(unsigned long* dimm_populated, + unsigned char* iic0_dimm_addr, + unsigned long num_dimm_banks); + +void program_rtr (unsigned long* dimm_populated, + unsigned char* iic0_dimm_addr, + unsigned long num_dimm_banks); + +void program_tr0 (unsigned long* dimm_populated, + unsigned char* iic0_dimm_addr, + unsigned long num_dimm_banks); + +void program_tr1 (void); + +void program_ecc (unsigned long num_bytes); + +unsigned +long program_bxcr(unsigned long* dimm_populated, + unsigned char* iic0_dimm_addr, + unsigned long num_dimm_banks); + +/* + * This function is reading data from the DIMM module EEPROM over the SPD bus + * and uses that to program the sdram controller. + * + * This works on boards that has the same schematics that the AMCC walnut has. + * + * BUG: Don't handle ECC memory + * BUG: A few values in the TR register is currently hardcoded + */ + +long int spd_sdram(void) { + unsigned char iic0_dimm_addr[] = SPD_EEPROM_ADDRESS; + unsigned long dimm_populated[sizeof(iic0_dimm_addr)]; + unsigned long total_size; + unsigned long cfg0; + unsigned long mcsts; + unsigned long num_dimm_banks; /* on board dimm banks */ + + num_dimm_banks = sizeof(iic0_dimm_addr); + + /* + * Make sure I2C controller is initialized + * before continuing. + */ + i2c_init(CFG_I2C_SPEED, CFG_I2C_SLAVE); + + /* + * Read the SPD information using I2C interface. Check to see if the + * DIMM slots are populated. + */ + get_spd_info(dimm_populated, iic0_dimm_addr, num_dimm_banks); + + /* + * Check the memory type for the dimms plugged. + */ + check_mem_type(dimm_populated, iic0_dimm_addr, num_dimm_banks); + + /* + * Check the voltage type for the dimms plugged. + */ + check_volt_type(dimm_populated, iic0_dimm_addr, num_dimm_banks); + +#if defined(CONFIG_440GX) + /* + * Soft-reset SDRAM controller. + */ + mtsdr(sdr_srst, SDR0_SRST_DMC); + mtsdr(sdr_srst, 0x00000000); +#endif + + /* + * program 440GP SDRAM controller options (SDRAM0_CFG0) + */ + program_cfg0(dimm_populated, iic0_dimm_addr, num_dimm_banks); + + /* + * program 440GP SDRAM controller options (SDRAM0_CFG1) + */ + program_cfg1(dimm_populated, iic0_dimm_addr, num_dimm_banks); + + /* + * program SDRAM refresh register (SDRAM0_RTR) + */ + program_rtr(dimm_populated, iic0_dimm_addr, num_dimm_banks); + + /* + * program SDRAM Timing Register 0 (SDRAM0_TR0) + */ + program_tr0(dimm_populated, iic0_dimm_addr, num_dimm_banks); + + /* + * program the BxCR registers to find out total sdram installed + */ + total_size = program_bxcr(dimm_populated, iic0_dimm_addr, + num_dimm_banks); + + /* + * program SDRAM Clock Timing Register (SDRAM0_CLKTR) + */ + mtsdram(mem_clktr, 0x40000000); + + /* + * delay to ensure 200 usec has elapsed + */ + udelay(400); + + /* + * enable the memory controller + */ + mfsdram(mem_cfg0, cfg0); + mtsdram(mem_cfg0, cfg0 | SDRAM_CFG0_DCEN); + + /* + * wait for SDRAM_CFG0_DC_EN to complete + */ + while (1) { + mfsdram(mem_mcsts, mcsts); + if ((mcsts & SDRAM_MCSTS_MRSC) != 0) { + break; + } + } + + /* + * program SDRAM Timing Register 1, adding some delays + */ + program_tr1(); + + /* + * if ECC is enabled, initialize parity bits + */ + + return total_size; +} + +unsigned char spd_read(uchar chip, uint addr) +{ + unsigned char data[2]; + +#ifdef CFG_SIMULATE_SPD_EEPROM + if (chip == CFG_SIMULATE_SPD_EEPROM) { + /* + * Onboard spd eeprom requested -> simulate values + */ + return cfg_simulate_spd_eeprom[addr]; + } +#endif /* CFG_SIMULATE_SPD_EEPROM */ + + if (i2c_probe(chip) == 0) { + if (i2c_read(chip, addr, 1, data, 1) == 0) { + return data[0]; + } + } + + return 0; +} + +void get_spd_info(unsigned long* dimm_populated, + unsigned char* iic0_dimm_addr, + unsigned long num_dimm_banks) +{ + unsigned long dimm_num; + unsigned long dimm_found; + unsigned char num_of_bytes; + unsigned char total_size; + + dimm_found = FALSE; + for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) { + num_of_bytes = 0; + total_size = 0; + + num_of_bytes = spd_read(iic0_dimm_addr[dimm_num], 0); + total_size = spd_read(iic0_dimm_addr[dimm_num], 1); + + if ((num_of_bytes != 0) && (total_size != 0)) { + dimm_populated[dimm_num] = TRUE; + dimm_found = TRUE; +#if 0 + printf("DIMM slot %lu: populated\n", dimm_num); +#endif + } else { + dimm_populated[dimm_num] = FALSE; +#if 0 + printf("DIMM slot %lu: Not populated\n", dimm_num); +#endif + } + } + + if (dimm_found == FALSE) { + printf("ERROR - No memory installed. Install a DDR-SDRAM DIMM.\n\n"); + hang(); + } +} + +void check_mem_type(unsigned long* dimm_populated, + unsigned char* iic0_dimm_addr, + unsigned long num_dimm_banks) +{ + unsigned long dimm_num; + unsigned char dimm_type; + + for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) { + if (dimm_populated[dimm_num] == TRUE) { + dimm_type = spd_read(iic0_dimm_addr[dimm_num], 2); + switch (dimm_type) { + case 7: +#if 0 + printf("DIMM slot %lu: DDR SDRAM detected\n", dimm_num); +#endif + break; + default: + printf("ERROR: Unsupported DIMM detected in slot %lu.\n", + dimm_num); + printf("Only DDR SDRAM DIMMs are supported.\n"); + printf("Replace the DIMM module with a supported DIMM.\n\n"); + hang(); + break; + } + } + } +} + + +void check_volt_type(unsigned long* dimm_populated, + unsigned char* iic0_dimm_addr, + unsigned long num_dimm_banks) +{ + unsigned long dimm_num; + unsigned long voltage_type; + + for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) { + if (dimm_populated[dimm_num] == TRUE) { + voltage_type = spd_read(iic0_dimm_addr[dimm_num], 8); + if (voltage_type != 0x04) { + printf("ERROR: DIMM %lu with unsupported voltage level.\n", + dimm_num); + hang(); + } else { +#if 0 + printf("DIMM %lu voltage level supported.\n", dimm_num); +#endif + } + break; + } + } +} + +void program_cfg0(unsigned long* dimm_populated, + unsigned char* iic0_dimm_addr, + unsigned long num_dimm_banks) +{ + unsigned long dimm_num; + unsigned long cfg0; + unsigned long ecc_enabled; + unsigned char ecc; + unsigned char attributes; + unsigned long data_width; + unsigned long dimm_32bit; + unsigned long dimm_64bit; + + /* + * get Memory Controller Options 0 data + */ + mfsdram(mem_cfg0, cfg0); + + /* + * clear bits + */ + cfg0 &= ~(SDRAM_CFG0_DCEN | SDRAM_CFG0_MCHK_MASK | + SDRAM_CFG0_RDEN | SDRAM_CFG0_PMUD | + SDRAM_CFG0_DMWD_MASK | + SDRAM_CFG0_UIOS_MASK | SDRAM_CFG0_PDP); + + + /* + * FIXME: assume the DDR SDRAMs in both banks are the same + */ + ecc_enabled = TRUE; + for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) { + if (dimm_populated[dimm_num] == TRUE) { + ecc = spd_read(iic0_dimm_addr[dimm_num], 11); + if (ecc != 0x02) { + ecc_enabled = FALSE; + } + + /* + * program Registered DIMM Enable + */ + attributes = spd_read(iic0_dimm_addr[dimm_num], 21); + if ((attributes & 0x02) != 0x00) { + cfg0 |= SDRAM_CFG0_RDEN; + } + + /* + * program DDR SDRAM Data Width + */ + data_width = + (unsigned long)spd_read(iic0_dimm_addr[dimm_num],6) + + (((unsigned long)spd_read(iic0_dimm_addr[dimm_num],7)) << 8); + if (data_width == 64 || data_width == 72) { + dimm_64bit = TRUE; + cfg0 |= SDRAM_CFG0_DMWD_64; + } else if (data_width == 32 || data_width == 40) { + dimm_32bit = TRUE; + cfg0 |= SDRAM_CFG0_DMWD_32; + } else { + printf("WARNING: DIMM with datawidth of %lu bits.\n", + data_width); + printf("Only DIMMs with 32 or 64 bit datawidths supported.\n"); + hang(); + } + break; + } + } + + /* + * program Memory Data Error Checking + */ + if (ecc_enabled == TRUE) { + cfg0 |= SDRAM_CFG0_MCHK_GEN; + } else { + cfg0 |= SDRAM_CFG0_MCHK_NON; + } + + /* + * program Page Management Unit + */ + cfg0 |= SDRAM_CFG0_PMUD; + + /* + * program Memory Controller Options 0 + * Note: DCEN must be enabled after all DDR SDRAM controller + * configuration registers get initialized. + */ + mtsdram(mem_cfg0, cfg0); +} + +void program_cfg1(unsigned long* dimm_populated, + unsigned char* iic0_dimm_addr, + unsigned long num_dimm_banks) +{ + unsigned long cfg1; + mfsdram(mem_cfg1, cfg1); + + /* + * Self-refresh exit, disable PM + */ + cfg1 &= ~(SDRAM_CFG1_SRE | SDRAM_CFG1_PMEN); + + /* + * program Memory Controller Options 1 + */ + mtsdram(mem_cfg1, cfg1); +} + +void program_rtr (unsigned long* dimm_populated, + unsigned char* iic0_dimm_addr, + unsigned long num_dimm_banks) +{ + unsigned long dimm_num; + unsigned long bus_period_x_10; + unsigned long refresh_rate = 0; + unsigned char refresh_rate_type; + unsigned long refresh_interval; + unsigned long sdram_rtr; + PPC440_SYS_INFO sys_info; + + /* + * get the board info + */ + get_sys_info(&sys_info); + bus_period_x_10 = ONE_BILLION / (sys_info.freqPLB / 10); + + + for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) { + if (dimm_populated[dimm_num] == TRUE) { + refresh_rate_type = 0x7F & spd_read(iic0_dimm_addr[dimm_num], 12); + switch (refresh_rate_type) { + case 0x00: + refresh_rate = 15625; + break; + case 0x01: + refresh_rate = 15625/4; + break; + case 0x02: + refresh_rate = 15625/2; + break; + case 0x03: + refresh_rate = 15626*2; + break; + case 0x04: + refresh_rate = 15625*4; + break; + case 0x05: + refresh_rate = 15625*8; + break; + default: + printf("ERROR: DIMM %lu, unsupported refresh rate/type.\n", + dimm_num); + printf("Replace the DIMM module with a supported DIMM.\n"); + break; + } + + break; + } + } + + refresh_interval = refresh_rate * 10 / bus_period_x_10; + sdram_rtr = (refresh_interval & 0x3ff8) << 16; + + /* + * program Refresh Timer Register (SDRAM0_RTR) + */ + mtsdram(mem_rtr, sdram_rtr); +} + +void program_tr0 (unsigned long* dimm_populated, + unsigned char* iic0_dimm_addr, + unsigned long num_dimm_banks) +{ + unsigned long dimm_num; + unsigned long tr0; + unsigned char wcsbc; + unsigned char t_rp_ns; + unsigned char t_rcd_ns; + unsigned char t_ras_ns; + unsigned long t_rp_clk; + unsigned long t_ras_rcd_clk; + unsigned long t_rcd_clk; + unsigned long t_rfc_clk; + unsigned long plb_check; + unsigned char cas_bit; + unsigned long cas_index; + unsigned char cas_2_0_available; + unsigned char cas_2_5_available; + unsigned char cas_3_0_available; + unsigned long cycle_time_ns_x_10[3]; + unsigned long tcyc_3_0_ns_x_10; + unsigned long tcyc_2_5_ns_x_10; + unsigned long tcyc_2_0_ns_x_10; + unsigned long tcyc_reg; + unsigned long bus_period_x_10; + PPC440_SYS_INFO sys_info; + unsigned long residue; + + /* + * get the board info + */ + get_sys_info(&sys_info); + bus_period_x_10 = ONE_BILLION / (sys_info.freqPLB / 10); + + /* + * get SDRAM Timing Register 0 (SDRAM_TR0) and clear bits + */ + mfsdram(mem_tr0, tr0); + tr0 &= ~(SDRAM_TR0_SDWR_MASK | SDRAM_TR0_SDWD_MASK | + SDRAM_TR0_SDCL_MASK | SDRAM_TR0_SDPA_MASK | + SDRAM_TR0_SDCP_MASK | SDRAM_TR0_SDLD_MASK | + SDRAM_TR0_SDRA_MASK | SDRAM_TR0_SDRD_MASK); + + /* + * initialization + */ + wcsbc = 0; + t_rp_ns = 0; + t_rcd_ns = 0; + t_ras_ns = 0; + cas_2_0_available = TRUE; + cas_2_5_available = TRUE; + cas_3_0_available = TRUE; + tcyc_2_0_ns_x_10 = 0; + tcyc_2_5_ns_x_10 = 0; + tcyc_3_0_ns_x_10 = 0; + + for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) { + if (dimm_populated[dimm_num] == TRUE) { + wcsbc = spd_read(iic0_dimm_addr[dimm_num], 15); + t_rp_ns = spd_read(iic0_dimm_addr[dimm_num], 27) >> 2; + t_rcd_ns = spd_read(iic0_dimm_addr[dimm_num], 29) >> 2; + t_ras_ns = spd_read(iic0_dimm_addr[dimm_num], 30); + cas_bit = spd_read(iic0_dimm_addr[dimm_num], 18); + + for (cas_index = 0; cas_index < 3; cas_index++) { + switch (cas_index) { + case 0: + tcyc_reg = spd_read(iic0_dimm_addr[dimm_num], 9); + break; + case 1: + tcyc_reg = spd_read(iic0_dimm_addr[dimm_num], 23); + break; + default: + tcyc_reg = spd_read(iic0_dimm_addr[dimm_num], 25); + break; + } + + if ((tcyc_reg & 0x0F) >= 10) { + printf("ERROR: Tcyc incorrect for DIMM in slot %lu\n", + dimm_num); + hang(); + } + + cycle_time_ns_x_10[cas_index] = + (((tcyc_reg & 0xF0) >> 4) * 10) + (tcyc_reg & 0x0F); + } + + cas_index = 0; + + if ((cas_bit & 0x80) != 0) { + cas_index += 3; + } else if ((cas_bit & 0x40) != 0) { + cas_index += 2; + } else if ((cas_bit & 0x20) != 0) { + cas_index += 1; + } + + if (((cas_bit & 0x10) != 0) && (cas_index < 3)) { + tcyc_3_0_ns_x_10 = cycle_time_ns_x_10[cas_index]; + cas_index++; + } else { + if (cas_index != 0) { + cas_index++; + } + cas_3_0_available = FALSE; + } + + if (((cas_bit & 0x08) != 0) || (cas_index < 3)) { + tcyc_2_5_ns_x_10 = cycle_time_ns_x_10[cas_index]; + cas_index++; + } else { + if (cas_index != 0) { + cas_index++; + } + cas_2_5_available = FALSE; + } + + if (((cas_bit & 0x04) != 0) || (cas_index < 3)) { + tcyc_2_0_ns_x_10 = cycle_time_ns_x_10[cas_index]; + cas_index++; + } else { + if (cas_index != 0) { + cas_index++; + } + cas_2_0_available = FALSE; + } + + break; + } + } + + /* + * Program SD_WR and SD_WCSBC fields + */ + tr0 |= SDRAM_TR0_SDWR_2_CLK; /* Write Recovery: 2 CLK */ + switch (wcsbc) { + case 0: + tr0 |= SDRAM_TR0_SDWD_0_CLK; + break; + default: + tr0 |= SDRAM_TR0_SDWD_1_CLK; + break; + } + + /* + * Program SD_CASL field + */ + if ((cas_2_0_available == TRUE) && + (bus_period_x_10 >= tcyc_2_0_ns_x_10)) { + tr0 |= SDRAM_TR0_SDCL_2_0_CLK; + } else if ((cas_2_5_available == TRUE) && + (bus_period_x_10 >= tcyc_2_5_ns_x_10)) { + tr0 |= SDRAM_TR0_SDCL_2_5_CLK; + } else if ((cas_3_0_available == TRUE) && + (bus_period_x_10 >= tcyc_3_0_ns_x_10)) { + tr0 |= SDRAM_TR0_SDCL_3_0_CLK; + } else { + printf("ERROR: No supported CAS latency with the installed DIMMs.\n"); + printf("Only CAS latencies of 2.0, 2.5, and 3.0 are supported.\n"); + printf("Make sure the PLB speed is within the supported range.\n"); + hang(); + } + + /* + * Calculate Trp in clock cycles and round up if necessary + * Program SD_PTA field + */ + t_rp_clk = sys_info.freqPLB * t_rp_ns / ONE_BILLION; + plb_check = ONE_BILLION * t_rp_clk / t_rp_ns; + if (sys_info.freqPLB != plb_check) { + t_rp_clk++; + } + switch ((unsigned long)t_rp_clk) { + case 0: + case 1: + case 2: + tr0 |= SDRAM_TR0_SDPA_2_CLK; + break; + case 3: + tr0 |= SDRAM_TR0_SDPA_3_CLK; + break; + default: + tr0 |= SDRAM_TR0_SDPA_4_CLK; + break; + } + + /* + * Program SD_CTP field + */ + t_ras_rcd_clk = sys_info.freqPLB * (t_ras_ns - t_rcd_ns) / ONE_BILLION; + plb_check = ONE_BILLION * t_ras_rcd_clk / (t_ras_ns - t_rcd_ns); + if (sys_info.freqPLB != plb_check) { + t_ras_rcd_clk++; + } + switch (t_ras_rcd_clk) { + case 0: + case 1: + case 2: + tr0 |= SDRAM_TR0_SDCP_2_CLK; + break; + case 3: + tr0 |= SDRAM_TR0_SDCP_3_CLK; + break; + case 4: + tr0 |= SDRAM_TR0_SDCP_4_CLK; + break; + default: + tr0 |= SDRAM_TR0_SDCP_5_CLK; + break; + } + + /* + * Program SD_LDF field + */ + tr0 |= SDRAM_TR0_SDLD_2_CLK; + + /* + * Program SD_RFTA field + * FIXME tRFC hardcoded as 75 nanoseconds + */ + t_rfc_clk = sys_info.freqPLB / (ONE_BILLION / 75); + residue = sys_info.freqPLB % (ONE_BILLION / 75); + if (residue >= (ONE_BILLION / 150)) { + t_rfc_clk++; + } + switch (t_rfc_clk) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + tr0 |= SDRAM_TR0_SDRA_6_CLK; + break; + case 7: + tr0 |= SDRAM_TR0_SDRA_7_CLK; + break; + case 8: + tr0 |= SDRAM_TR0_SDRA_8_CLK; + break; + case 9: + tr0 |= SDRAM_TR0_SDRA_9_CLK; + break; + case 10: + tr0 |= SDRAM_TR0_SDRA_10_CLK; + break; + case 11: + tr0 |= SDRAM_TR0_SDRA_11_CLK; + break; + case 12: + tr0 |= SDRAM_TR0_SDRA_12_CLK; + break; + default: + tr0 |= SDRAM_TR0_SDRA_13_CLK; + break; + } + + /* + * Program SD_RCD field + */ + t_rcd_clk = sys_info.freqPLB * t_rcd_ns / ONE_BILLION; + plb_check = ONE_BILLION * t_rcd_clk / t_rcd_ns; + if (sys_info.freqPLB != plb_check) { + t_rcd_clk++; + } + switch (t_rcd_clk) { + case 0: + case 1: + case 2: + tr0 |= SDRAM_TR0_SDRD_2_CLK; + break; + case 3: + tr0 |= SDRAM_TR0_SDRD_3_CLK; + break; + default: + tr0 |= SDRAM_TR0_SDRD_4_CLK; + break; + } + +#if 0 + printf("tr0: %x\n", tr0); +#endif + mtsdram(mem_tr0, tr0); +} + +void program_tr1 (void) +{ + unsigned long tr0; + unsigned long tr1; + unsigned long cfg0; + unsigned long ecc_temp; + unsigned long dlycal; + unsigned long dly_val; + unsigned long i, j, k; + unsigned long bxcr_num; + unsigned long max_pass_length; + unsigned long current_pass_length; + unsigned long current_fail_length; + unsigned long current_start; + unsigned long rdclt; + unsigned long rdclt_offset; + long max_start; + long max_end; + long rdclt_average; + unsigned char window_found; + unsigned char fail_found; + unsigned char pass_found; + unsigned long * membase; + PPC440_SYS_INFO sys_info; + + /* + * get the board info + */ + get_sys_info(&sys_info); + + /* + * get SDRAM Timing Register 0 (SDRAM_TR0) and clear bits + */ + mfsdram(mem_tr1, tr1); + tr1 &= ~(SDRAM_TR1_RDSS_MASK | SDRAM_TR1_RDSL_MASK | + SDRAM_TR1_RDCD_MASK | SDRAM_TR1_RDCT_MASK); + + mfsdram(mem_tr0, tr0); + if (((tr0 & SDRAM_TR0_SDCL_MASK) == SDRAM_TR0_SDCL_2_5_CLK) && + (sys_info.freqPLB > 100000000)) { + tr1 |= SDRAM_TR1_RDSS_TR2; + tr1 |= SDRAM_TR1_RDSL_STAGE3; + tr1 |= SDRAM_TR1_RDCD_RCD_1_2; + } else { + tr1 |= SDRAM_TR1_RDSS_TR1; + tr1 |= SDRAM_TR1_RDSL_STAGE2; + tr1 |= SDRAM_TR1_RDCD_RCD_0_0; + } + + /* + * save CFG0 ECC setting to a temporary variable and turn ECC off + */ + mfsdram(mem_cfg0, cfg0); + ecc_temp = cfg0 & SDRAM_CFG0_MCHK_MASK; + mtsdram(mem_cfg0, (cfg0 & ~SDRAM_CFG0_MCHK_MASK) | SDRAM_CFG0_MCHK_NON); + + /* + * get the delay line calibration register value + */ + mfsdram(mem_dlycal, dlycal); + dly_val = SDRAM_DLYCAL_DLCV_DECODE(dlycal) << 2; + + max_pass_length = 0; + max_start = 0; + max_end = 0; + current_pass_length = 0; + current_fail_length = 0; + current_start = 0; + rdclt_offset = 0; + window_found = FALSE; + fail_found = FALSE; + pass_found = FALSE; +#ifdef DEBUG + printf("Starting memory test "); +#endif + for (k = 0; k < NUMHALFCYCLES; k++) { + for (rdclt = 0; rdclt < dly_val; rdclt++) { + /* + * Set the timing reg for the test. + */ + mtsdram(mem_tr1, (tr1 | SDRAM_TR1_RDCT_ENCODE(rdclt))); + + for (bxcr_num = 0; bxcr_num < MAXBXCR; bxcr_num++) { + mtdcr(memcfga, mem_b0cr + (bxcr_num<<2)); + if ((mfdcr(memcfgd) & SDRAM_BXCR_SDBE) == SDRAM_BXCR_SDBE) { + /* Bank is enabled */ + membase = (unsigned long*) + (mfdcr(memcfgd) & SDRAM_BXCR_SDBA_MASK); + + /* + * Run the short memory test + */ + for (i = 0; i < NUMMEMTESTS; i++) { + for (j = 0; j < NUMMEMWORDS; j++) { + membase[j] = test[i][j]; + ppcDcbf((unsigned long)&(membase[j])); + } + + for (j = 0; j < NUMMEMWORDS; j++) { + if (membase[j] != test[i][j]) { + ppcDcbf((unsigned long)&(membase[j])); + break; + } + ppcDcbf((unsigned long)&(membase[j])); + } + + if (j < NUMMEMWORDS) { + break; + } + } + + /* + * see if the rdclt value passed + */ + if (i < NUMMEMTESTS) { + break; + } + } + } + + if (bxcr_num == MAXBXCR) { + if (fail_found == TRUE) { + pass_found = TRUE; + if (current_pass_length == 0) { + current_start = rdclt_offset + rdclt; + } + + current_fail_length = 0; + current_pass_length++; + + if (current_pass_length > max_pass_length) { + max_pass_length = current_pass_length; + max_start = current_start; + max_end = rdclt_offset + rdclt; + } + } + } else { + current_pass_length = 0; + current_fail_length++; + + if (current_fail_length >= (dly_val>>2)) { + if (fail_found == FALSE) { + fail_found = TRUE; + } else if (pass_found == TRUE) { + window_found = TRUE; + break; + } + } + } + } +#ifdef DEBUG + printf("."); +#endif + if (window_found == TRUE) { + break; + } + + tr1 = tr1 ^ SDRAM_TR1_RDCD_MASK; + rdclt_offset += dly_val; + } +#ifdef DEBUG + printf("\n"); +#endif + + /* + * make sure we find the window + */ + if (window_found == FALSE) { + printf("ERROR: Cannot determine a common read delay.\n"); + hang(); + } + + /* + * restore the orignal ECC setting + */ + mtsdram(mem_cfg0, (cfg0 & ~SDRAM_CFG0_MCHK_MASK) | ecc_temp); + + /* + * set the SDRAM TR1 RDCD value + */ + tr1 &= ~SDRAM_TR1_RDCD_MASK; + if ((tr0 & SDRAM_TR0_SDCL_MASK) == SDRAM_TR0_SDCL_2_5_CLK) { + tr1 |= SDRAM_TR1_RDCD_RCD_1_2; + } else { + tr1 |= SDRAM_TR1_RDCD_RCD_0_0; + } + + /* + * set the SDRAM TR1 RDCLT value + */ + tr1 &= ~SDRAM_TR1_RDCT_MASK; + while (max_end >= (dly_val << 1)) { + max_end -= (dly_val << 1); + max_start -= (dly_val << 1); + } + + rdclt_average = ((max_start + max_end) >> 1); + if (rdclt_average >= 0x60) + while (1) + ; + + if (rdclt_average < 0) { + rdclt_average = 0; + } + + if (rdclt_average >= dly_val) { + rdclt_average -= dly_val; + tr1 = tr1 ^ SDRAM_TR1_RDCD_MASK; + } + tr1 |= SDRAM_TR1_RDCT_ENCODE(rdclt_average); + +#if 0 + printf("tr1: %x\n", tr1); +#endif + /* + * program SDRAM Timing Register 1 TR1 + */ + mtsdram(mem_tr1, tr1); +} + +unsigned long program_bxcr(unsigned long* dimm_populated, + unsigned char* iic0_dimm_addr, + unsigned long num_dimm_banks) +{ + unsigned long dimm_num; + unsigned long bank_base_addr; + unsigned long cr; + unsigned long i; + unsigned long j; + unsigned long temp; + unsigned char num_row_addr; + unsigned char num_col_addr; + unsigned char num_banks; + unsigned char bank_size_id; + unsigned long ctrl_bank_num[MAXBANKS]; + unsigned long bx_cr_num; + unsigned long largest_size_index; + unsigned long largest_size; + unsigned long current_size_index; + BANKPARMS bank_parms[MAXBXCR]; + unsigned long sorted_bank_num[MAXBXCR]; /* DDR Controller bank number table (sorted by size) */ + unsigned long sorted_bank_size[MAXBXCR]; /* DDR Controller bank size table (sorted by size)*/ + + /* + * Set the BxCR regs. First, wipe out the bank config registers. + */ + for (bx_cr_num = 0; bx_cr_num < MAXBXCR; bx_cr_num++) { + mtdcr(memcfga, mem_b0cr + (bx_cr_num << 2)); + mtdcr(memcfgd, 0x00000000); + bank_parms[bx_cr_num].bank_size_bytes = 0; + } + +#ifdef CONFIG_BAMBOO + /* + * This next section is hardware dependent and must be programmed + * to match the hardware. For bammboo, the following holds... + * 1. SDRAM0_B0CR: Bank 0 of dimm 0 ctrl_bank_num : 0 + * 2. SDRAM0_B1CR: Bank 0 of dimm 1 ctrl_bank_num : 1 + * 3. SDRAM0_B2CR: Bank 1 of dimm 1 ctrl_bank_num : 1 + * 4. SDRAM0_B3CR: Bank 0 of dimm 2 ctrl_bank_num : 3 + * ctrl_bank_num corresponds to the first usable DDR controller bank number by DIMM + */ + ctrl_bank_num[0] = 0; + ctrl_bank_num[1] = 1; + ctrl_bank_num[2] = 3; +#else + ctrl_bank_num[0] = 0; + ctrl_bank_num[1] = 1; + ctrl_bank_num[2] = 2; + ctrl_bank_num[3] = 3; +#endif + + /* + * reset the bank_base address + */ + bank_base_addr = CFG_SDRAM_BASE; + + for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) { + if (dimm_populated[dimm_num] == TRUE) { + num_row_addr = spd_read(iic0_dimm_addr[dimm_num], 3); + num_col_addr = spd_read(iic0_dimm_addr[dimm_num], 4); + num_banks = spd_read(iic0_dimm_addr[dimm_num], 5); + bank_size_id = spd_read(iic0_dimm_addr[dimm_num], 31); + + /* + * Set the SDRAM0_BxCR regs + */ + cr = 0; + switch (bank_size_id) { + case 0x02: + cr |= SDRAM_BXCR_SDSZ_8; + break; + case 0x04: + cr |= SDRAM_BXCR_SDSZ_16; + break; + case 0x08: + cr |= SDRAM_BXCR_SDSZ_32; + break; + case 0x10: + cr |= SDRAM_BXCR_SDSZ_64; + break; + case 0x20: + cr |= SDRAM_BXCR_SDSZ_128; + break; + case 0x40: + cr |= SDRAM_BXCR_SDSZ_256; + break; + case 0x80: + cr |= SDRAM_BXCR_SDSZ_512; + break; + default: + printf("DDR-SDRAM: DIMM %lu BxCR configuration.\n", + dimm_num); + printf("ERROR: Unsupported value for the banksize: %d.\n", + bank_size_id); + printf("Replace the DIMM module with a supported DIMM.\n\n"); + hang(); + } + + switch (num_col_addr) { + case 0x08: + cr |= SDRAM_BXCR_SDAM_1; + break; + case 0x09: + cr |= SDRAM_BXCR_SDAM_2; + break; + case 0x0A: + cr |= SDRAM_BXCR_SDAM_3; + break; + case 0x0B: + cr |= SDRAM_BXCR_SDAM_4; + break; + default: + printf("DDR-SDRAM: DIMM %lu BxCR configuration.\n", + dimm_num); + printf("ERROR: Unsupported value for number of " + "column addresses: %d.\n", num_col_addr); + printf("Replace the DIMM module with a supported DIMM.\n\n"); + hang(); + } + + /* + * enable the bank + */ + cr |= SDRAM_BXCR_SDBE; + + for (i = 0; i < num_banks; i++) { + bank_parms[ctrl_bank_num[dimm_num]+i].bank_size_bytes = + (4 * 1024 * 1024) * bank_size_id; + bank_parms[ctrl_bank_num[dimm_num]+i].cr = cr; + } + } + } + + /* Initialize sort tables */ + for (i = 0; i < MAXBXCR; i++) { + sorted_bank_num[i] = i; + sorted_bank_size[i] = bank_parms[i].bank_size_bytes; + } + + for (i = 0; i < MAXBXCR-1; i++) { + largest_size = sorted_bank_size[i]; + largest_size_index = 255; + + /* Find the largest remaining value */ + for (j = i + 1; j < MAXBXCR; j++) { + if (sorted_bank_size[j] > largest_size) { + /* Save largest remaining value and its index */ + largest_size = sorted_bank_size[j]; + largest_size_index = j; + } + } + + if (largest_size_index != 255) { + /* Swap the current and largest values */ + current_size_index = sorted_bank_num[largest_size_index]; + sorted_bank_size[largest_size_index] = sorted_bank_size[i]; + sorted_bank_size[i] = largest_size; + sorted_bank_num[largest_size_index] = sorted_bank_num[i]; + sorted_bank_num[i] = current_size_index; + } + } + + /* Set the SDRAM0_BxCR regs thanks to sort tables */ + for (bx_cr_num = 0, bank_base_addr = 0; bx_cr_num < MAXBXCR; bx_cr_num++) { + if (bank_parms[sorted_bank_num[bx_cr_num]].bank_size_bytes) { + mtdcr(memcfga, mem_b0cr + (sorted_bank_num[bx_cr_num] << 2)); + temp = mfdcr(memcfgd) & ~(SDRAM_BXCR_SDBA_MASK | SDRAM_BXCR_SDSZ_MASK | + SDRAM_BXCR_SDAM_MASK | SDRAM_BXCR_SDBE); + temp = temp | (bank_base_addr & SDRAM_BXCR_SDBA_MASK) | + bank_parms[sorted_bank_num[bx_cr_num]].cr; + mtdcr(memcfgd, temp); + bank_base_addr += bank_parms[sorted_bank_num[bx_cr_num]].bank_size_bytes; + } + } + + return(bank_base_addr); +} + +void program_ecc (unsigned long num_bytes) +{ + unsigned long bank_base_addr; + unsigned long current_address; + unsigned long end_address; + unsigned long address_increment; + unsigned long cfg0; + + /* + * get Memory Controller Options 0 data + */ + mfsdram(mem_cfg0, cfg0); + + /* + * reset the bank_base address + */ + bank_base_addr = CFG_SDRAM_BASE; + + if ((cfg0 & SDRAM_CFG0_MCHK_MASK) != SDRAM_CFG0_MCHK_NON) { + mtsdram(mem_cfg0, (cfg0 & ~SDRAM_CFG0_MCHK_MASK) | + SDRAM_CFG0_MCHK_GEN); + + if ((cfg0 & SDRAM_CFG0_DMWD_MASK) == SDRAM_CFG0_DMWD_32) { + address_increment = 4; + } else { + address_increment = 8; + } + + current_address = (unsigned long)(bank_base_addr); + end_address = (unsigned long)(bank_base_addr) + num_bytes; + + while (current_address < end_address) { + *((unsigned long*)current_address) = 0x00000000; + current_address += address_increment; + } + + mtsdram(mem_cfg0, (cfg0 & ~SDRAM_CFG0_MCHK_MASK) | + SDRAM_CFG0_MCHK_CHK); + } +} + +#endif /* CONFIG_440 */ + +#endif /* CONFIG_SPD_EEPROM */ diff --git a/cpu/ppc4xx/speed.c b/cpu/ppc4xx/speed.c new file mode 100755 index 0000000..553c491 --- /dev/null +++ b/cpu/ppc4xx/speed.c @@ -0,0 +1,568 @@ +/* + * (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_RB & 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->freqVCOHz = (1000000000000LL * (unsigned long long)m) / + (unsigned long long)sysClkPeriodPs; + sysInfo->freqProcessor = sysInfo->freqVCOHz / sysInfo->pllFwdDiv; + sysInfo->freqPLB = sysInfo->freqVCOHz / (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->freqVCOHz = ( 1000000000000LL * + (unsigned long long)sysInfo->pllFwdDiv * + (unsigned long long)sysInfo->pllFbkDiv * + (unsigned long long)sysInfo->pllPlbDiv + ) / (unsigned long long)sysClkPeriodPs; + sysInfo->freqPLB = (ONE_BILLION / ((sysClkPeriodPs * 10) / + sysInfo->pllFbkDiv)) * 10000; + sysInfo->freqProcessor = sysInfo->freqPLB * sysInfo->pllPlbDiv; + } + } +} + + +/******************************************** + * 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) + +#if defined(CONFIG_440EP) || defined(CONFIG_440GR) +void get_sys_info (sys_info_t *sysInfo) +{ + unsigned long temp; + unsigned long reg; + unsigned long lfdiv; + unsigned long m; + unsigned long prbdv0; + /* + WARNING: ASSUMES the following: + ENG=1 + PRADV0=1 + PRBDV0=1 + */ + + /* Decode CPR0_PLLD0 for divisors */ + mfclk(clk_plld, reg); + temp = (reg & PLLD_FWDVA_MASK) >> 16; + sysInfo->pllFwdDivA = temp ? temp : 16; + temp = (reg & PLLD_FWDVB_MASK) >> 8; + sysInfo->pllFwdDivB = temp ? temp: 8 ; + temp = (reg & PLLD_FBDV_MASK) >> 24; + sysInfo->pllFbkDiv = temp ? temp : 32; + lfdiv = reg & PLLD_LFBDV_MASK; + + mfclk(clk_opbd, reg); + temp = (reg & OPBDDV_MASK) >> 24; + sysInfo->pllOpbDiv = temp ? temp : 4; + + mfclk(clk_perd, reg); + temp = (reg & PERDV_MASK) >> 24; + sysInfo->pllExtBusDiv = temp ? temp : 8; + + mfclk(clk_primbd, reg); + temp = (reg & PRBDV_MASK) >> 24; + prbdv0 = temp ? temp : 8; + + mfclk(clk_spcid, reg); + temp = (reg & SPCID_MASK) >> 24; + sysInfo->pllPciDiv = temp ? temp : 4; + + /* Calculate 'M' based on feedback source */ + mfsdr(sdr_sdstp0, reg); + temp = (reg & PLLSYS0_SEL_MASK) >> 27; + if (temp == 0) { /* PLL output */ + /* Figure which pll to use */ + mfclk(clk_pllc, reg); + temp = (reg & PLLC_SRC_MASK) >> 29; + if (!temp) /* PLLOUTA */ + m = sysInfo->pllFbkDiv * lfdiv * sysInfo->pllFwdDivA; + else /* PLLOUTB */ + m = sysInfo->pllFbkDiv * lfdiv * sysInfo->pllFwdDivB; + } + else if (temp == 1) /* CPU output */ + m = sysInfo->pllFbkDiv * sysInfo->pllFwdDivA; + else /* PerClk */ + m = sysInfo->pllExtBusDiv * sysInfo->pllOpbDiv * sysInfo->pllFwdDivB; + + /* 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/prbdv0; + sysInfo->freqOPB = sysInfo->freqPLB/sysInfo->pllOpbDiv; + sysInfo->freqEPB = sysInfo->freqPLB/sysInfo->pllExtBusDiv; + sysInfo->freqPCI = sysInfo->freqPLB/sysInfo->pllPciDiv; + + /* Figure which timer source to use */ + if (mfspr(ccr1) & 0x0080) { /* External Clock, assume same as SYS_CLK */ + temp = sysInfo->freqProcessor / 2; /* Max extern clock speed */ + if (CONFIG_SYS_CLK_FREQ > temp) + sysInfo->freqTmrClk = temp; + else + sysInfo->freqTmrClk = CONFIG_SYS_CLK_FREQ; + } + else /* Internal clock */ + sysInfo->freqTmrClk = sysInfo->freqProcessor; +} +/******************************************** + * get_PCI_freq + * return PCI bus freq in Hz + *********************************************/ +ulong get_PCI_freq (void) +{ + sys_info_t sys_info; + get_sys_info (&sys_info); + return sys_info.freqPCI; +} + +#elif !defined(CONFIG_440GX) && !defined(CONFIG_440SP) +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; + +} +#else +void get_sys_info (sys_info_t * sysInfo) +{ + unsigned long strp0; + unsigned long strp1; + unsigned long temp; + unsigned long temp1; + unsigned long lfdiv; + unsigned long m; + unsigned long prbdv0; + + /* Extract configured divisors */ + mfsdr( sdr_sdstp0,strp0 ); + mfsdr( sdr_sdstp1,strp1 ); + + temp = ((strp0 & PLLSYS0_FWD_DIV_A_MASK) >> 8); + sysInfo->pllFwdDivA = temp ? temp : 16 ; + temp = ((strp0 & PLLSYS0_FWD_DIV_B_MASK) >> 5); + sysInfo->pllFwdDivB = temp ? temp: 8 ; + temp = (strp0 & PLLSYS0_FB_DIV_MASK) >> 12; + sysInfo->pllFbkDiv = temp ? temp : 32; + temp = (strp0 & PLLSYS0_OPB_DIV_MASK); + sysInfo->pllOpbDiv = temp ? temp : 4; + temp = (strp1 & PLLSYS1_PERCLK_DIV_MASK) >> 24; + sysInfo->pllExtBusDiv = temp ? temp : 4; + prbdv0 = (strp0 >> 2) & 0x7; + + /* Calculate 'M' based on feedback source */ + temp = (strp0 & PLLSYS0_SEL_MASK) >> 27; + temp1 = (strp1 & PLLSYS1_LF_DIV_MASK) >> 26; + lfdiv = temp1 ? temp1 : 64; + if (temp == 0) { /* PLL output */ + /* Figure which pll to use */ + temp = (strp0 & PLLSYS0_SRC_MASK) >> 30; + if (!temp) + m = sysInfo->pllFbkDiv * lfdiv * sysInfo->pllFwdDivA; + else + m = sysInfo->pllFbkDiv * lfdiv * sysInfo->pllFwdDivB; + } + else if (temp == 1) /* CPU output */ + m = sysInfo->pllFbkDiv * sysInfo->pllFwdDivA; + else /* PerClk */ + m = sysInfo->pllExtBusDiv * sysInfo->pllOpbDiv * sysInfo->pllFwdDivB; + + /* 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/prbdv0; + sysInfo->freqOPB = sysInfo->freqPLB/sysInfo->pllOpbDiv; + sysInfo->freqEPB = sysInfo->freqOPB/sysInfo->pllExtBusDiv; + +} +#endif + +ulong get_OPB_freq (void) +{ + + sys_info_t sys_info; + get_sys_info (&sys_info); + return sys_info.freqOPB; +} + +#elif defined(CONFIG_XILINX_ML300) +extern void get_sys_info (sys_info_t * sysInfo); +extern ulong get_PCI_freq (void); + +#elif defined(CONFIG_AP1000) +void get_sys_info (sys_info_t * sysInfo) { + sysInfo->freqProcessor = 240 * 1000 * 1000; + sysInfo->freqPLB = 80 * 1000 * 1000; + sysInfo->freqPCI = 33 * 1000 * 1000; +} + +#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; + +} + +#elif defined(CONFIG_405EP) +void get_sys_info (PPC405_SYS_INFO * sysInfo) +{ + unsigned long pllmr0; + unsigned long pllmr1; + unsigned long sysClkPeriodPs = ONE_BILLION / (CONFIG_SYS_CLK_FREQ / 1000); + unsigned long m; + unsigned long pllmr0_ccdv; + + /* + * Read PLL Mode registers + */ + pllmr0 = mfdcr (cpc0_pllmr0); + pllmr1 = mfdcr (cpc0_pllmr1); + + /* + * Determine forward divider A + */ + sysInfo->pllFwdDiv = 8 - ((pllmr1 & PLLMR1_FWDVA_MASK) >> 16); + + /* + * Determine forward divider B (should be equal to A) + */ + sysInfo->pllFwdDivB = 8 - ((pllmr1 & PLLMR1_FWDVB_MASK) >> 12); + + /* + * Determine FBK_DIV. + */ + sysInfo->pllFbkDiv = ((pllmr1 & PLLMR1_FBMUL_MASK) >> 20); + if (sysInfo->pllFbkDiv == 0) { + sysInfo->pllFbkDiv = 16; + } + + /* + * Determine PLB_DIV. + */ + sysInfo->pllPlbDiv = ((pllmr0 & PLLMR0_CPU_TO_PLB_MASK) >> 16) + 1; + + /* + * Determine PCI_DIV. + */ + sysInfo->pllPciDiv = (pllmr0 & PLLMR0_PCI_TO_PLB_MASK) + 1; + + /* + * Determine EXTBUS_DIV. + */ + sysInfo->pllExtBusDiv = ((pllmr0 & PLLMR0_EXB_TO_PLB_MASK) >> 8) + 2; + + /* + * Determine OPB_DIV. + */ + sysInfo->pllOpbDiv = ((pllmr0 & PLLMR0_OPB_TO_PLB_MASK) >> 12) + 1; + + /* + * Determine the M factor + */ + m = sysInfo->pllFbkDiv * sysInfo->pllFwdDivB; + + /* + * Determine VCO clock frequency + */ + sysInfo->freqVCOHz = (1000000000000LL * (unsigned long long)m) / + (unsigned long long)sysClkPeriodPs; + + /* + * Determine CPU clock frequency + */ + pllmr0_ccdv = ((pllmr0 & PLLMR0_CPU_DIV_MASK) >> 20) + 1; + if (pllmr1 & PLLMR1_SSCS_MASK) { + /* + * This is true if FWDVA == FWDVB: + * sysInfo->freqProcessor = (CONFIG_SYS_CLK_FREQ * sysInfo->pllFbkDiv) + * / pllmr0_ccdv; + */ + sysInfo->freqProcessor = (CONFIG_SYS_CLK_FREQ * sysInfo->pllFbkDiv * sysInfo->pllFwdDivB) + / sysInfo->pllFwdDiv / pllmr0_ccdv; + } else { + sysInfo->freqProcessor = CONFIG_SYS_CLK_FREQ / pllmr0_ccdv; + } + + /* + * Determine PLB clock frequency + */ + sysInfo->freqPLB = sysInfo->freqProcessor / sysInfo->pllPlbDiv; +} + + +/******************************************** + * 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; +} + +#endif + +int get_clocks (void) +{ +#if defined(CONFIG_405GP) || defined(CONFIG_405CR) || defined(CONFIG_440) || defined(CONFIG_405) || defined(CONFIG_405EP) + 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) || defined(CONFIG_405EP) + 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/start.S b/cpu/ppc4xx/start.S new file mode 100755 index 0000000..48b430d --- /dev/null +++ b/cpu/ppc4xx/start.S @@ -0,0 +1,1687 @@ +/* + * Copyright (C) 1998 Dan Malek <dmalek@jlc.net> + * Copyright (C) 1999 Magnus Damm <kieraypc01.p.y.kie.era.ericsson.se> + * Copyright (C) 2000,2001,2002 Wolfgang Denk <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 + */ +/*------------------------------------------------------------------------------+ */ +/* */ +/* 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 */ +/*------------------------------------------------------------------------------- */ + +/* U-Boot - Startup Code for AMCC 4xx PowerPC based Embedded Boards + * + * + * The processor starts at 0xfffffffc and the code is executed + * from flash/rom. + * in memory, but as long we don't jump around before relocating. + * board_init lies at a quite high address and when the cpu has + * jumped there, everything is ok. + * This works because the cpu gives the FLASH (CS0) the whole + * address space at startup, and board_init lies as a echo of + * the flash somewhere up there in the memorymap. + * + * board_init will change CS0 to be positioned at the correct + * address and (s)dram will be positioned at address 0 + */ +#include <config.h> +#include <mpc8xx.h> +#include <ppc4xx.h> +#include <version.h> + +#define _LINUX_CONFIG_H 1 /* avoid reading Linux autoconf.h file */ + +#include <ppc_asm.tmpl> +#include <ppc_defs.h> + +#include <asm/cache.h> +#include <asm/mmu.h> + +#ifndef CONFIG_IDENT_STRING +#define CONFIG_IDENT_STRING "" +#endif + +#ifdef CFG_INIT_DCACHE_CS +# if (CFG_INIT_DCACHE_CS == 0) +# define PBxAP pb0ap +# define PBxCR pb0cr +# endif +# if (CFG_INIT_DCACHE_CS == 1) +# define PBxAP pb1ap +# define PBxCR pb1cr +# endif +# if (CFG_INIT_DCACHE_CS == 2) +# define PBxAP pb2ap +# define PBxCR pb2cr +# endif +# if (CFG_INIT_DCACHE_CS == 3) +# define PBxAP pb3ap +# define PBxCR pb3cr +# endif +# if (CFG_INIT_DCACHE_CS == 4) +# define PBxAP pb4ap +# define PBxCR pb4cr +# endif +# if (CFG_INIT_DCACHE_CS == 5) +# define PBxAP pb5ap +# define PBxCR pb5cr +# endif +# if (CFG_INIT_DCACHE_CS == 6) +# define PBxAP pb6ap +# define PBxCR pb6cr +# endif +# if (CFG_INIT_DCACHE_CS == 7) +# define PBxAP pb7ap +# define PBxCR pb7cr +# endif +#endif /* CFG_INIT_DCACHE_CS */ + +/* We don't want the MMU yet. +*/ +#undef MSR_KERNEL +#define MSR_KERNEL ( MSR_ME ) /* Machine Check */ + + + .extern ext_bus_cntlr_init + .extern sdram_init + +/* + * Set up GOT: Global Offset Table + * + * Use r14 to access the GOT + */ + START_GOT + GOT_ENTRY(_GOT2_TABLE_) + GOT_ENTRY(_FIXUP_TABLE_) + + GOT_ENTRY(_start) + GOT_ENTRY(_start_of_vectors) + GOT_ENTRY(_end_of_vectors) + GOT_ENTRY(transfer_to_handler) + + GOT_ENTRY(__init_end) + GOT_ENTRY(_end) + GOT_ENTRY(__bss_start) + END_GOT + +/* + * 440 Startup -- on reset only the top 4k of the effective + * address space is mapped in by an entry in the instruction + * and data shadow TLB. The .bootpg section is located in the + * top 4k & does only what's necessary to map in the the rest + * of the boot rom. Once the boot rom is mapped in we can + * proceed with normal startup. + * + * NOTE: CS0 only covers the top 2MB of the effective address + * space after reset. + */ + +#if defined(CONFIG_440) + .section .bootpg,"ax" + .globl _start_440 + +/**************************************************************************/ +_start_440: + /*----------------------------------------------------------------*/ + /* Clear and set up some registers. */ + /*----------------------------------------------------------------*/ + iccci r0,r0 /* NOTE: operands not used for 440 */ + dccci r0,r0 /* NOTE: operands not used for 440 */ + sync + li r0,0 + mtspr srr0,r0 + mtspr srr1,r0 + mtspr csrr0,r0 + mtspr csrr1,r0 +#if defined(CONFIG_440GX) || defined(CONFIG_440SP) /* NOTE: 440GX adds machine check status regs */ + mtspr mcsrr0,r0 + mtspr mcsrr1,r0 + mfspr r1, mcsr + mtspr mcsr,r1 +#endif + /*----------------------------------------------------------------*/ + /* Initialize debug */ + /*----------------------------------------------------------------*/ + mtspr dbcr0,r0 + mtspr dbcr1,r0 + mtspr dbcr2,r0 + mtspr iac1,r0 + mtspr iac2,r0 + mtspr iac3,r0 + mtspr dac1,r0 + mtspr dac2,r0 + mtspr dvc1,r0 + mtspr dvc2,r0 + + mfspr r1,dbsr + mtspr dbsr,r1 /* Clear all valid bits */ + + /*----------------------------------------------------------------*/ + /* CCR0 init */ + /*----------------------------------------------------------------*/ + /* Disable store gathering & broadcast, guarantee inst/data + * cache block touch, force load/store alignment + * (see errata 1.12: 440_33) + */ + lis r1,0x0030 /* store gathering & broadcast disable */ + ori r1,r1,0x6000 /* cache touch */ + mtspr ccr0,r1 + + /*----------------------------------------------------------------*/ + /* Setup interrupt vectors */ + /*----------------------------------------------------------------*/ + mtspr ivpr,r0 /* Vectors start at 0x0000_0000 */ + li r1,0x0100 + mtspr ivor0,r1 /* Critical input */ + li r1,0x0200 + mtspr ivor1,r1 /* Machine check */ + li r1,0x0300 + mtspr ivor2,r1 /* Data storage */ + li r1,0x0400 + mtspr ivor3,r1 /* Instruction storage */ + li r1,0x0500 + mtspr ivor4,r1 /* External interrupt */ + li r1,0x0600 + mtspr ivor5,r1 /* Alignment */ + li r1,0x0700 + mtspr ivor6,r1 /* Program check */ + li r1,0x0800 + mtspr ivor7,r1 /* Floating point unavailable */ + li r1,0x0c00 + mtspr ivor8,r1 /* System call */ + li r1,0x1000 + mtspr ivor10,r1 /* Decrementer (PIT for 440) */ + li r1,0x1400 + mtspr ivor13,r1 /* Data TLB error */ + li r1,0x1300 + mtspr ivor14,r1 /* Instr TLB error */ + li r1,0x2000 + mtspr ivor15,r1 /* Debug */ + + /*----------------------------------------------------------------*/ + /* Configure cache regions */ + /*----------------------------------------------------------------*/ + mtspr inv0,r0 + mtspr inv1,r0 + mtspr inv2,r0 + mtspr inv3,r0 + mtspr dnv0,r0 + mtspr dnv1,r0 + mtspr dnv2,r0 + mtspr dnv3,r0 + mtspr itv0,r0 + mtspr itv1,r0 + mtspr itv2,r0 + mtspr itv3,r0 + mtspr dtv0,r0 + mtspr dtv1,r0 + mtspr dtv2,r0 + mtspr dtv3,r0 + + /*----------------------------------------------------------------*/ + /* Cache victim limits */ + /*----------------------------------------------------------------*/ + /* floors 0, ceiling max to use the entire cache -- nothing locked + */ + lis r1,0x0001 + ori r1,r1,0xf800 + mtspr ivlim,r1 + mtspr dvlim,r1 + + /*----------------------------------------------------------------*/ + /* Clear all TLB entries -- TID = 0, TS = 0 */ + /*----------------------------------------------------------------*/ + mtspr mmucr,r0 + li r1,0x003f /* 64 TLB entries */ + mtctr r1 +0: tlbwe r0,r1,0x0000 /* Invalidate all entries (V=0)*/ + subi r1,r1,0x0001 + bdnz 0b + + /*----------------------------------------------------------------*/ + /* TLB entry setup -- step thru tlbtab */ + /*----------------------------------------------------------------*/ + bl tlbtab /* Get tlbtab pointer */ + mr r5,r0 + li r1,0x003f /* 64 TLB entries max */ + mtctr r1 + li r4,0 /* TLB # */ + + addi r5,r5,-4 +1: lwzu r0,4(r5) + cmpwi r0,0 + beq 2f /* 0 marks end */ + lwzu r1,4(r5) + lwzu r2,4(r5) + tlbwe r0,r4,0 /* TLB Word 0 */ + tlbwe r1,r4,1 /* TLB Word 1 */ + tlbwe r2,r4,2 /* TLB Word 2 */ + addi r4,r4,1 /* Next TLB */ + bdnz 1b + + /*----------------------------------------------------------------*/ + /* Continue from 'normal' start */ + /*----------------------------------------------------------------*/ +2: bl 3f + b _start + +3: li r0,0 + mtspr srr1,r0 /* Keep things disabled for now */ + mflr r1 + mtspr srr0,r1 + rfi +#endif /* CONFIG_440 */ + +/* + * r3 - 1st arg to board_init(): IMMP pointer + * r4 - 2nd arg to board_init(): boot flag + */ + .text + .long 0x27051956 /* U-Boot Magic Number */ + .globl version_string +version_string: + .ascii U_BOOT_VERSION + .ascii " (", __DATE__, " - ", __TIME__, ")" + .ascii CONFIG_IDENT_STRING, "\0" + +/* + * Maybe this should be moved somewhere else because the current + * location (0x100) is where the CriticalInput Execption should be. + */ + . = EXC_OFF_SYS_RESET + .globl _start +_start: + +/*****************************************************************************/ +#if defined(CONFIG_440) + + /*----------------------------------------------------------------*/ + /* Clear and set up some registers. */ + /*----------------------------------------------------------------*/ + li r0,0x0000 + lis r1,0xffff + mtspr dec,r0 /* prevent dec exceptions */ + mtspr tbl,r0 /* prevent fit & wdt exceptions */ + mtspr tbu,r0 + mtspr tsr,r1 /* clear all timer exception status */ + mtspr tcr,r0 /* disable all */ + mtspr esr,r0 /* clear exception syndrome register */ + mtxer r0 /* clear integer exception register */ +#if !defined(CONFIG_440GX) + lis r1,0x0002 /* set CE bit (Critical Exceptions) */ + ori r1,r1,0x1000 /* set ME bit (Machine Exceptions) */ + mtmsr r1 /* change MSR */ +#elif !defined(CONFIG_440EP) && !defined(CONFIG_440GR) + bl __440gx_msr_set + b __440gx_msr_continue + +__440gx_msr_set: + lis r1, 0x0002 /* set CE bit (Critical Exceptions) */ + ori r1,r1,0x1000 /* set ME bit (Machine Exceptions) */ + mtspr srr1,r1 + mflr r1 + mtspr srr0,r1 + rfi +__440gx_msr_continue: +#endif + + /*----------------------------------------------------------------*/ + /* Debug setup -- some (not very good) ice's need an event*/ + /* to establish control :-( Define CFG_INIT_DBCR to the dbsr */ + /* value you need in this case 0x8cff 0000 should do the trick */ + /*----------------------------------------------------------------*/ +#if defined(CFG_INIT_DBCR) + lis r1,0xffff + ori r1,r1,0xffff + mtspr dbsr,r1 /* Clear all status bits */ + lis r0,CFG_INIT_DBCR@h + ori r0,r0,CFG_INIT_DBCR@l + mtspr dbcr0,r0 + isync +#endif + + /*----------------------------------------------------------------*/ + /* Setup the internal SRAM */ + /*----------------------------------------------------------------*/ + li r0,0 +#if defined(CONFIG_440EP) || defined(CONFIG_440GR) + /* Clear Dcache to use as RAM */ + addis r3,r0,CFG_INIT_RAM_ADDR@h + ori r3,r3,CFG_INIT_RAM_ADDR@l + addis r4,r0,CFG_INIT_RAM_END@h + ori r4,r4,CFG_INIT_RAM_END@l + rlwinm. r5,r4,0,27,31 + rlwinm r5,r4,27,5,31 + beq ..d_ran + addi r5,r5,0x0001 +..d_ran: + mtctr r5 +..d_ag: + dcbz r0,r3 + addi r3,r3,32 + bdnz ..d_ag +#else +#if defined (CONFIG_440GX) || defined(CONFIG_440SP) + mtdcr l2_cache_cfg,r0 /* Ensure L2 Cache is off */ +#endif + mtdcr isram0_sb1cr,r0 /* Disable bank 1 */ + + li r2,0x7fff + ori r2,r2,0xffff + mfdcr r1,isram0_dpc + and r1,r1,r2 /* Disable parity check */ + mtdcr isram0_dpc,r1 + mfdcr r1,isram0_pmeg + andis. r1,r1,r2 /* Disable pwr mgmt */ + mtdcr isram0_pmeg,r1 + + lis r1,0x8000 /* BAS = 8000_0000 */ +#if defined(CONFIG_440GX) || defined(CONFIG_440SP) + ori r1,r1,0x0980 /* first 64k */ + mtdcr isram0_sb0cr,r1 + lis r1,0x8001 + ori r1,r1,0x0980 /* second 64k */ + mtdcr isram0_sb1cr,r1 + lis r1, 0x8002 + ori r1,r1, 0x0980 /* third 64k */ + mtdcr isram0_sb2cr,r1 + lis r1, 0x8003 + ori r1,r1, 0x0980 /* fourth 64k */ + mtdcr isram0_sb3cr,r1 +#else + ori r1,r1,0x0380 /* 8k rw */ + mtdcr isram0_sb0cr,r1 +#endif +#endif + + /*----------------------------------------------------------------*/ + /* Setup the stack in internal SRAM */ + /*----------------------------------------------------------------*/ + lis r1,CFG_INIT_RAM_ADDR@h + ori r1,r1,CFG_INIT_SP_OFFSET@l + li r0,0 + stwu r0,-4(r1) + stwu r0,-4(r1) /* Terminate call chain */ + + stwu r1,-8(r1) /* Save back chain and move SP */ + lis r0,RESET_VECTOR@h /* Address of reset vector */ + ori r0,r0, RESET_VECTOR@l + stwu r1,-8(r1) /* Save back chain and move SP */ + stw r0,+12(r1) /* Save return addr (underflow vect) */ + + GET_GOT + + bl cpu_init_f /* run low-level CPU init code (from Flash) */ + bl board_init_f + +#endif /* CONFIG_440 */ + +/*****************************************************************************/ +#ifdef CONFIG_IOP480 + /*----------------------------------------------------------------------- */ + /* Set up some machine state registers. */ + /*----------------------------------------------------------------------- */ + addi r0,r0,0x0000 /* initialize r0 to zero */ + mtspr esr,r0 /* clear Exception Syndrome Reg */ + mttcr r0 /* timer control register */ + mtexier r0 /* disable all interrupts */ + addi r4,r0,0x1000 /* set ME bit (Machine Exceptions) */ + oris r4,r4,0x2 /* set CE bit (Critical Exceptions) */ + mtmsr r4 /* change MSR */ + addis r4,r0,0xFFFF /* set r4 to 0xFFFFFFFF (status in the */ + ori r4,r4,0xFFFF /* dbsr is cleared by setting bits to 1) */ + mtdbsr r4 /* clear/reset the dbsr */ + mtexisr r4 /* clear all pending interrupts */ + addis r4,r0,0x8000 + mtexier r4 /* enable critical exceptions */ + addis r4,r0,0x0000 /* assume 403GCX - enable core clk */ + ori r4,r4,0x4020 /* dbling (no harm done on GA and GC */ + mtiocr r4 /* since bit not used) & DRC to latch */ + /* data bus on rising edge of CAS */ + /*----------------------------------------------------------------------- */ + /* Clear XER. */ + /*----------------------------------------------------------------------- */ + mtxer r0 + /*----------------------------------------------------------------------- */ + /* Invalidate i-cache and d-cache TAG arrays. */ + /*----------------------------------------------------------------------- */ + addi r3,0,1024 /* 1/4 of I-cache size, half of D-cache */ + addi r4,0,1024 /* 1/4 of I-cache */ +..cloop: + iccci 0,r3 + iccci r4,r3 + dccci 0,r3 + addic. r3,r3,-16 /* move back one cache line */ + bne ..cloop /* loop back to do rest until r3 = 0 */ + + /* */ + /* initialize IOP480 so it can read 1 MB code area for SRAM spaces */ + /* this requires enabling MA[17..0], by default only MA[12..0] are enabled. */ + /* */ + + /* first copy IOP480 register base address into r3 */ + addis r3,0,0x5000 /* IOP480 register base address hi */ +/* ori r3,r3,0x0000 / IOP480 register base address lo */ + +#ifdef CONFIG_ADCIOP + /* use r4 as the working variable */ + /* turn on CS3 (LOCCTL.7) */ + lwz r4,0x84(r3) /* LOCTL is at offset 0x84 */ + andi. r4,r4,0xff7f /* make bit 7 = 0 -- CS3 mode */ + stw r4,0x84(r3) /* LOCTL is at offset 0x84 */ +#endif + +#ifdef CONFIG_DASA_SIM + /* use r4 as the working variable */ + /* turn on MA17 (LOCCTL.7) */ + lwz r4,0x84(r3) /* LOCTL is at offset 0x84 */ + ori r4,r4,0x80 /* make bit 7 = 1 -- MA17 mode */ + stw r4,0x84(r3) /* LOCTL is at offset 0x84 */ +#endif + + /* turn on MA16..13 (LCS0BRD.12 = 0) */ + lwz r4,0x100(r3) /* LCS0BRD is at offset 0x100 */ + andi. r4,r4,0xefff /* make bit 12 = 0 */ + stw r4,0x100(r3) /* LCS0BRD is at offset 0x100 */ + + /* make sure above stores all comlete before going on */ + sync + + /* last thing, set local init status done bit (DEVINIT.31) */ + lwz r4,0x80(r3) /* DEVINIT is at offset 0x80 */ + oris r4,r4,0x8000 /* make bit 31 = 1 */ + stw r4,0x80(r3) /* DEVINIT is at offset 0x80 */ + + /* clear all pending interrupts and disable all interrupts */ + li r4,-1 /* set p1 to 0xffffffff */ + stw r4,0x1b0(r3) /* clear all pending interrupts */ + stw r4,0x1b8(r3) /* clear all pending interrupts */ + li r4,0 /* set r4 to 0 */ + stw r4,0x1b4(r3) /* disable all interrupts */ + stw r4,0x1bc(r3) /* disable all interrupts */ + + /* make sure above stores all comlete before going on */ + sync + + /*----------------------------------------------------------------------- */ + /* Enable two 128MB cachable regions. */ + /*----------------------------------------------------------------------- */ + addis r1,r0,0x8000 + addi r1,r1,0x0001 + mticcr r1 /* instruction cache */ + + addis r1,r0,0x0000 + addi r1,r1,0x0000 + mtdccr r1 /* data cache */ + + addis r1,r0,CFG_INIT_RAM_ADDR@h + ori r1,r1,CFG_INIT_SP_OFFSET /* set up the stack to SDRAM */ + li r0, 0 /* Make room for stack frame header and */ + stwu r0, -4(r1) /* clear final stack frame so that */ + stwu r0, -4(r1) /* stack backtraces terminate cleanly */ + + GET_GOT /* initialize GOT access */ + + bl board_init_f /* run first part of init code (from Flash) */ + +#endif /* CONFIG_IOP480 */ + +/*****************************************************************************/ +#if defined(CONFIG_405GP) || defined(CONFIG_405CR) || defined(CONFIG_405) || defined(CONFIG_405EP) + /*----------------------------------------------------------------------- */ + /* Clear and set up some registers. */ + /*----------------------------------------------------------------------- */ + addi r4,r0,0x0000 + mtspr sgr,r4 + mtspr dcwr,r4 + mtesr r4 /* clear Exception Syndrome Reg */ + mttcr r4 /* clear Timer Control Reg */ + mtxer r4 /* clear Fixed-Point Exception Reg */ + mtevpr r4 /* clear Exception Vector Prefix Reg */ + addi r4,r0,0x1000 /* set ME bit (Machine Exceptions) */ + oris r4,r4,0x0002 /* set CE bit (Critical Exceptions) */ + mtmsr r4 /* change MSR */ + addi r4,r0,(0xFFFF-0x10000) /* set r4 to 0xFFFFFFFF (status in the */ + /* dbsr is cleared by setting bits to 1) */ + mtdbsr r4 /* clear/reset the dbsr */ + + /*----------------------------------------------------------------------- */ + /* Invalidate I and D caches. Enable I cache for defined memory regions */ + /* to speed things up. Leave the D cache disabled for now. It will be */ + /* enabled/left disabled later based on user selected menu options. */ + /* Be aware that the I cache may be disabled later based on the menu */ + /* options as well. See miscLib/main.c. */ + /*----------------------------------------------------------------------- */ + bl invalidate_icache + bl invalidate_dcache + + /*----------------------------------------------------------------------- */ + /* Enable two 128MB cachable regions. */ + /*----------------------------------------------------------------------- */ + addis r4,r0,0x8000 + addi r4,r4,0x0001 + mticcr r4 /* instruction cache */ + isync + + addis r4,r0,0x0000 + addi r4,r4,0x0000 + mtdccr r4 /* data cache */ + +#if !(defined(CFG_EBC_PB0AP) && defined(CFG_EBC_PB0CR)) + /*----------------------------------------------------------------------- */ + /* Tune the speed and size for flash CS0 */ + /*----------------------------------------------------------------------- */ + bl ext_bus_cntlr_init +#endif + +#if defined(CONFIG_405EP) + /*----------------------------------------------------------------------- */ + /* DMA Status, clear to come up clean */ + /*----------------------------------------------------------------------- */ + addis r3,r0, 0xFFFF /* Clear all existing DMA status */ + ori r3,r3, 0xFFFF + mtdcr dmasr, r3 + + bl ppc405ep_init /* do ppc405ep specific init */ +#endif /* CONFIG_405EP */ + +#if defined(CFG_OCM_DATA_ADDR) && defined(CFG_OCM_DATA_SIZE) + /******************************************************************** + * Setup OCM - On Chip Memory + *******************************************************************/ + /* Setup OCM */ + lis r0, 0x7FFF + ori r0, r0, 0xFFFF + mfdcr r3, ocmiscntl /* get instr-side IRAM config */ + mfdcr r4, ocmdscntl /* get data-side IRAM config */ + and r3, r3, r0 /* disable data-side IRAM */ + and r4, r4, r0 /* disable data-side IRAM */ + mtdcr ocmiscntl, r3 /* set instr-side IRAM config */ + mtdcr ocmdscntl, r4 /* set data-side IRAM config */ + isync + + addis r3, 0, CFG_OCM_DATA_ADDR@h /* OCM location */ + mtdcr ocmdsarc, r3 + addis r4, 0, 0xC000 /* OCM data area enabled */ + mtdcr ocmdscntl, r4 + isync +#endif + + /*----------------------------------------------------------------------- */ + /* Setup temporary stack in DCACHE or OCM if needed for SDRAM SPD. */ + /*----------------------------------------------------------------------- */ +#ifdef CFG_INIT_DCACHE_CS + /*----------------------------------------------------------------------- */ + /* Memory Bank x (nothingness) initialization 1GB+64MEG */ + /* used as temporary stack pointer for stage0 */ + /*----------------------------------------------------------------------- */ + li r4,PBxAP + mtdcr ebccfga,r4 + lis r4,0x0380 + ori r4,r4,0x0480 + mtdcr ebccfgd,r4 + + addi r4,0,PBxCR + mtdcr ebccfga,r4 + lis r4,0x400D + ori r4,r4,0xa000 + mtdcr ebccfgd,r4 + + /* turn on data chache for this region */ + lis r4,0x0080 + mtdccr r4 + + /* set stack pointer and clear stack to known value */ + + lis r1,CFG_INIT_RAM_ADDR@h + ori r1,r1,CFG_INIT_SP_OFFSET@l + + li r4,2048 /* we store 2048 words to stack */ + mtctr r4 + + lis r2,CFG_INIT_RAM_ADDR@h /* we also clear data area */ + ori r2,r2,CFG_INIT_RAM_END@l /* so cant copy value from r1 */ + + lis r4,0xdead /* we store 0xdeaddead in the stack */ + ori r4,r4,0xdead + +..stackloop: + stwu r4,-4(r2) + bdnz ..stackloop + + li r0, 0 /* Make room for stack frame header and */ + stwu r0, -4(r1) /* clear final stack frame so that */ + stwu r0, -4(r1) /* stack backtraces terminate cleanly */ + /* + * Set up a dummy frame to store reset vector as return address. + * this causes stack underflow to reset board. + */ + stwu r1, -8(r1) /* Save back chain and move SP */ + addis r0, 0, RESET_VECTOR@h /* Address of reset vector */ + ori r0, r0, RESET_VECTOR@l + stwu r1, -8(r1) /* Save back chain and move SP */ + stw r0, +12(r1) /* Save return addr (underflow vect) */ + +#elif defined(CFG_TEMP_STACK_OCM) && \ + (defined(CFG_OCM_DATA_ADDR) && defined(CFG_OCM_DATA_SIZE)) + /* + * Stack in OCM. + */ + + /* Set up Stack at top of OCM */ + lis r1, (CFG_INIT_RAM_ADDR + CFG_INIT_SP_OFFSET)@h + ori r1, r1, (CFG_INIT_RAM_ADDR + CFG_INIT_SP_OFFSET)@l + + /* Set up a zeroized stack frame so that backtrace works right */ + li r0, 0 + stwu r0, -4(r1) + stwu r0, -4(r1) + + /* + * Set up a dummy frame to store reset vector as return address. + * this causes stack underflow to reset board. + */ + stwu r1, -8(r1) /* Save back chain and move SP */ + lis r0, RESET_VECTOR@h /* Address of reset vector */ + ori r0, r0, RESET_VECTOR@l + stwu r1, -8(r1) /* Save back chain and move SP */ + stw r0, +12(r1) /* Save return addr (underflow vect) */ +#endif /* CFG_INIT_DCACHE_CS */ + + /*----------------------------------------------------------------------- */ + /* Initialize SDRAM Controller */ + /*----------------------------------------------------------------------- */ + bl sdram_init + + /* + * Setup temporary stack pointer only for boards + * that do not use SDRAM SPD I2C stuff since it + * is already initialized to use DCACHE or OCM + * stacks. + */ +#if !(defined(CFG_INIT_DCACHE_CS) || defined(CFG_TEMP_STACK_OCM)) + lis r1, CFG_INIT_RAM_ADDR@h + ori r1,r1,CFG_INIT_SP_OFFSET /* set up the stack in SDRAM */ + + li r0, 0 /* Make room for stack frame header and */ + stwu r0, -4(r1) /* clear final stack frame so that */ + stwu r0, -4(r1) /* stack backtraces terminate cleanly */ + /* + * Set up a dummy frame to store reset vector as return address. + * this causes stack underflow to reset board. + */ + stwu r1, -8(r1) /* Save back chain and move SP */ + lis r0, RESET_VECTOR@h /* Address of reset vector */ + ori r0, r0, RESET_VECTOR@l + stwu r1, -8(r1) /* Save back chain and move SP */ + stw r0, +12(r1) /* Save return addr (underflow vect) */ +#endif /* !(CFG_INIT_DCACHE_CS || !CFG_TEM_STACK_OCM) */ + + GET_GOT /* initialize GOT access */ + + bl cpu_init_f /* run low-level CPU init code (from Flash) */ + + /* NEVER RETURNS! */ + bl board_init_f /* run first part of init code (from Flash) */ + +#endif /* CONFIG_405GP || CONFIG_405CR || CONFIG_405 || CONFIG_405EP */ + /*----------------------------------------------------------------------- */ + + +/*****************************************************************************/ + .globl _start_of_vectors +_start_of_vectors: + +#if 0 +/*TODO Fixup _start above so we can do this*/ +/* Critical input. */ + CRIT_EXCEPTION(0x100, CritcalInput, CritcalInputException) +#endif + +/* Machine check */ + CRIT_EXCEPTION(0x200, MachineCheck, MachineCheckException) + +/* Data Storage exception. */ + STD_EXCEPTION(0x300, DataStorage, UnknownException) + +/* Instruction Storage exception. */ + STD_EXCEPTION(0x400, InstStorage, UnknownException) + +/* External Interrupt exception. */ + STD_EXCEPTION(0x500, ExtInterrupt, external_interrupt) + +/* Alignment exception. */ + . = 0x600 +Alignment: + EXCEPTION_PROLOG + mfspr r4,DAR + stw r4,_DAR(r21) + mfspr r5,DSISR + stw r5,_DSISR(r21) + addi r3,r1,STACK_FRAME_OVERHEAD + li r20,MSR_KERNEL + rlwimi r20,r23,0,16,16 /* copy EE bit from saved MSR */ + lwz r6,GOT(transfer_to_handler) + mtlr r6 + blrl +.L_Alignment: + .long AlignmentException - _start + EXC_OFF_SYS_RESET + .long int_return - _start + EXC_OFF_SYS_RESET + +/* Program check exception */ + . = 0x700 +ProgramCheck: + EXCEPTION_PROLOG + addi r3,r1,STACK_FRAME_OVERHEAD + li r20,MSR_KERNEL + rlwimi r20,r23,0,16,16 /* copy EE bit from saved MSR */ + lwz r6,GOT(transfer_to_handler) + mtlr r6 + blrl +.L_ProgramCheck: + .long ProgramCheckException - _start + EXC_OFF_SYS_RESET + .long int_return - _start + EXC_OFF_SYS_RESET + + /* No FPU on MPC8xx. This exception is not supposed to happen. + */ + STD_EXCEPTION(0x800, FPUnavailable, UnknownException) + + /* I guess we could implement decrementer, and may have + * to someday for timekeeping. + */ + STD_EXCEPTION(0x900, Decrementer, timer_interrupt) + STD_EXCEPTION(0xa00, Trap_0a, UnknownException) + STD_EXCEPTION(0xb00, Trap_0b, UnknownException) + STD_EXCEPTION(0xc00, SystemCall, UnknownException) + STD_EXCEPTION(0xd00, SingleStep, UnknownException) + + STD_EXCEPTION(0xe00, Trap_0e, UnknownException) + STD_EXCEPTION(0xf00, Trap_0f, UnknownException) + + /* On the MPC8xx, this is a software emulation interrupt. It occurs + * for all unimplemented and illegal instructions. + */ + STD_EXCEPTION(0x1000, PIT, PITException) + + STD_EXCEPTION(0x1100, InstructionTLBMiss, UnknownException) + STD_EXCEPTION(0x1200, DataTLBMiss, UnknownException) + STD_EXCEPTION(0x1300, InstructionTLBError, UnknownException) + STD_EXCEPTION(0x1400, DataTLBError, UnknownException) + + STD_EXCEPTION(0x1500, Reserved5, UnknownException) + STD_EXCEPTION(0x1600, Reserved6, UnknownException) + STD_EXCEPTION(0x1700, Reserved7, UnknownException) + STD_EXCEPTION(0x1800, Reserved8, UnknownException) + STD_EXCEPTION(0x1900, Reserved9, UnknownException) + STD_EXCEPTION(0x1a00, ReservedA, UnknownException) + STD_EXCEPTION(0x1b00, ReservedB, UnknownException) + + STD_EXCEPTION(0x1c00, DataBreakpoint, UnknownException) + STD_EXCEPTION(0x1d00, InstructionBreakpoint, UnknownException) + STD_EXCEPTION(0x1e00, PeripheralBreakpoint, UnknownException) + STD_EXCEPTION(0x1f00, DevPortBreakpoint, UnknownException) + + CRIT_EXCEPTION(0x2000, DebugBreakpoint, DebugException ) + + .globl _end_of_vectors +_end_of_vectors: + + + . = 0x2100 + +/* + * This code finishes saving the registers to the exception frame + * and jumps to the appropriate handler for the exception. + * Register r21 is pointer into trap frame, r1 has new stack pointer. + */ + .globl transfer_to_handler +transfer_to_handler: + stw r22,_NIP(r21) + lis r22,MSR_POW@h + andc r23,r23,r22 + stw r23,_MSR(r21) + SAVE_GPR(7, r21) + SAVE_4GPRS(8, r21) + SAVE_8GPRS(12, r21) + SAVE_8GPRS(24, r21) +#if 0 + andi. r23,r23,MSR_PR + mfspr r23,SPRG3 /* if from user, fix up tss.regs */ + beq 2f + addi r24,r1,STACK_FRAME_OVERHEAD + stw r24,PT_REGS(r23) +2: addi r2,r23,-TSS /* set r2 to current */ + tovirt(r2,r2,r23) +#endif + mflr r23 + andi. r24,r23,0x3f00 /* get vector offset */ + stw r24,TRAP(r21) + li r22,0 + stw r22,RESULT(r21) + mtspr SPRG2,r22 /* r1 is now kernel sp */ +#if 0 + addi r24,r2,TASK_STRUCT_SIZE /* check for kernel stack overflow */ + cmplw 0,r1,r2 + cmplw 1,r1,r24 + crand 1,1,4 + bgt stack_ovf /* if r2 < r1 < r2+TASK_STRUCT_SIZE */ +#endif + lwz r24,0(r23) /* virtual address of handler */ + lwz r23,4(r23) /* where to go when done */ + mtspr SRR0,r24 + mtspr SRR1,r20 + mtlr r23 + SYNC + rfi /* jump to handler, enable MMU */ + +int_return: + mfmsr r28 /* Disable interrupts */ + li r4,0 + ori r4,r4,MSR_EE + andc r28,r28,r4 + SYNC /* Some chip revs need this... */ + mtmsr r28 + SYNC + lwz r2,_CTR(r1) + lwz r0,_LINK(r1) + mtctr r2 + mtlr r0 + lwz r2,_XER(r1) + lwz r0,_CCR(r1) + mtspr XER,r2 + mtcrf 0xFF,r0 + REST_10GPRS(3, r1) + REST_10GPRS(13, r1) + REST_8GPRS(23, r1) + REST_GPR(31, r1) + lwz r2,_NIP(r1) /* Restore environment */ + lwz r0,_MSR(r1) + mtspr SRR0,r2 + mtspr SRR1,r0 + lwz r0,GPR0(r1) + lwz r2,GPR2(r1) + lwz r1,GPR1(r1) + SYNC + rfi + +crit_return: + mfmsr r28 /* Disable interrupts */ + li r4,0 + ori r4,r4,MSR_EE + andc r28,r28,r4 + SYNC /* Some chip revs need this... */ + mtmsr r28 + SYNC + lwz r2,_CTR(r1) + lwz r0,_LINK(r1) + mtctr r2 + mtlr r0 + lwz r2,_XER(r1) + lwz r0,_CCR(r1) + mtspr XER,r2 + mtcrf 0xFF,r0 + REST_10GPRS(3, r1) + REST_10GPRS(13, r1) + REST_8GPRS(23, r1) + REST_GPR(31, r1) + lwz r2,_NIP(r1) /* Restore environment */ + lwz r0,_MSR(r1) + mtspr 990,r2 /* SRR2 */ + mtspr 991,r0 /* SRR3 */ + lwz r0,GPR0(r1) + lwz r2,GPR2(r1) + lwz r1,GPR1(r1) + SYNC + rfci + +/* Cache functions. +*/ +invalidate_icache: + iccci r0,r0 /* for 405, iccci invalidates the */ + blr /* entire I cache */ + +invalidate_dcache: + addi r6,0,0x0000 /* clear GPR 6 */ + /* Do loop for # of dcache congruence classes. */ + lis r7, (CFG_DCACHE_SIZE / CFG_CACHELINE_SIZE / 2)@ha /* TBS for large sized cache */ + ori r7, r7, (CFG_DCACHE_SIZE / CFG_CACHELINE_SIZE / 2)@l + /* NOTE: dccci invalidates both */ + mtctr r7 /* ways in the D cache */ +..dcloop: + dccci 0,r6 /* invalidate line */ + addi r6,r6, CFG_CACHELINE_SIZE /* bump to next line */ + bdnz ..dcloop + blr + +flush_dcache: + addis r9,r0,0x0002 /* set mask for EE and CE msr bits */ + ori r9,r9,0x8000 + mfmsr r12 /* save msr */ + andc r9,r12,r9 + mtmsr r9 /* disable EE and CE */ + addi r10,r0,0x0001 /* enable data cache for unused memory */ + mfdccr r9 /* region 0xF8000000-0xFFFFFFFF via */ + or r10,r10,r9 /* bit 31 in dccr */ + mtdccr r10 + + /* do loop for # of congruence classes. */ + lis r10,(CFG_DCACHE_SIZE / CFG_CACHELINE_SIZE / 2)@ha /* TBS: for large cache sizes */ + ori r10,r10,(CFG_DCACHE_SIZE / CFG_CACHELINE_SIZE / 2)@l + lis r11,(CFG_DCACHE_SIZE / 2)@ha /* D cache set size - 2 way sets */ + ori r11,r11,(CFG_DCACHE_SIZE / 2)@l /* D cache set size - 2 way sets */ + mtctr r10 + addi r10,r0,(0xE000-0x10000) /* start at 0xFFFFE000 */ + add r11,r10,r11 /* add to get to other side of cache line */ +..flush_dcache_loop: + lwz r3,0(r10) /* least recently used side */ + lwz r3,0(r11) /* the other side */ + dccci r0,r11 /* invalidate both sides */ + addi r10,r10,CFG_CACHELINE_SIZE /* bump to next line */ + addi r11,r11,CFG_CACHELINE_SIZE /* bump to next line */ + bdnz ..flush_dcache_loop + sync /* allow memory access to complete */ + mtdccr r9 /* restore dccr */ + mtmsr r12 /* restore msr */ + blr + + .globl icache_enable +icache_enable: + mflr r8 + bl invalidate_icache + mtlr r8 + isync + addis r3,r0, 0x8000 /* set bit 0 */ + mticcr r3 + blr + + .globl icache_disable +icache_disable: + addis r3,r0, 0x0000 /* clear bit 0 */ + mticcr r3 + isync + blr + + .globl icache_status +icache_status: + mficcr r3 + srwi r3, r3, 31 /* >>31 => select bit 0 */ + blr + + .globl dcache_enable +dcache_enable: + mflr r8 + bl invalidate_dcache + mtlr r8 + isync + addis r3,r0, 0x8000 /* set bit 0 */ + mtdccr r3 + blr + + .globl dcache_disable +dcache_disable: + mflr r8 + bl flush_dcache + mtlr r8 + addis r3,r0, 0x0000 /* clear bit 0 */ + mtdccr r3 + blr + + .globl dcache_status +dcache_status: + mfdccr r3 + srwi r3, r3, 31 /* >>31 => select bit 0 */ + blr + + .globl get_pvr +get_pvr: + mfspr r3, PVR + blr + +#if !defined(CONFIG_440) + .globl wr_pit +wr_pit: + mtspr pit, r3 + blr +#endif + + .globl wr_tcr +wr_tcr: + mtspr tcr, r3 + blr + +/*------------------------------------------------------------------------------- */ +/* Function: in8 */ +/* Description: Input 8 bits */ +/*------------------------------------------------------------------------------- */ + .globl in8 +in8: + lbz r3,0x0000(r3) + blr + +/*------------------------------------------------------------------------------- */ +/* Function: out8 */ +/* Description: Output 8 bits */ +/*------------------------------------------------------------------------------- */ + .globl out8 +out8: + stb r4,0x0000(r3) + blr + +/*------------------------------------------------------------------------------- */ +/* Function: out16 */ +/* Description: Output 16 bits */ +/*------------------------------------------------------------------------------- */ + .globl out16 +out16: + sth r4,0x0000(r3) + blr + +/*------------------------------------------------------------------------------- */ +/* Function: out16r */ +/* Description: Byte reverse and output 16 bits */ +/*------------------------------------------------------------------------------- */ + .globl out16r +out16r: + sthbrx r4,r0,r3 + blr + +/*------------------------------------------------------------------------------- */ +/* Function: out32 */ +/* Description: Output 32 bits */ +/*------------------------------------------------------------------------------- */ + .globl out32 +out32: + stw r4,0x0000(r3) + blr + +/*------------------------------------------------------------------------------- */ +/* Function: out32r */ +/* Description: Byte reverse and output 32 bits */ +/*------------------------------------------------------------------------------- */ + .globl out32r +out32r: + stwbrx r4,r0,r3 + blr + +/*------------------------------------------------------------------------------- */ +/* Function: in16 */ +/* Description: Input 16 bits */ +/*------------------------------------------------------------------------------- */ + .globl in16 +in16: + lhz r3,0x0000(r3) + blr + +/*------------------------------------------------------------------------------- */ +/* Function: in16r */ +/* Description: Input 16 bits and byte reverse */ +/*------------------------------------------------------------------------------- */ + .globl in16r +in16r: + lhbrx r3,r0,r3 + blr + +/*------------------------------------------------------------------------------- */ +/* Function: in32 */ +/* Description: Input 32 bits */ +/*------------------------------------------------------------------------------- */ + .globl in32 +in32: + lwz 3,0x0000(3) + blr + +/*------------------------------------------------------------------------------- */ +/* Function: in32r */ +/* Description: Input 32 bits and byte reverse */ +/*------------------------------------------------------------------------------- */ + .globl in32r +in32r: + lwbrx r3,r0,r3 + blr + +/*------------------------------------------------------------------------------- */ +/* Function: ppcDcbf */ +/* Description: Data Cache block flush */ +/* Input: r3 = effective address */ +/* Output: none. */ +/*------------------------------------------------------------------------------- */ + .globl ppcDcbf +ppcDcbf: + dcbf r0,r3 + blr + +/*------------------------------------------------------------------------------- */ +/* Function: ppcDcbi */ +/* Description: Data Cache block Invalidate */ +/* Input: r3 = effective address */ +/* Output: none. */ +/*------------------------------------------------------------------------------- */ + .globl ppcDcbi +ppcDcbi: + dcbi r0,r3 + blr + +/*------------------------------------------------------------------------------- */ +/* Function: ppcSync */ +/* Description: Processor Synchronize */ +/* Input: none. */ +/* Output: none. */ +/*------------------------------------------------------------------------------- */ + .globl ppcSync +ppcSync: + sync + blr + +/*------------------------------------------------------------------------------*/ + +/* + * void relocate_code (addr_sp, gd, addr_moni) + * + * This "function" does not return, instead it continues in RAM + * after relocating the monitor code. + * + * r3 = dest + * r4 = src + * r5 = length in bytes + * r6 = cachelinesize + */ + .globl relocate_code +relocate_code: +#if defined(CONFIG_440EP) || defined(CONFIG_440GR) + dccci 0,0 /* Invalidate data cache, now no longer our stack */ + sync + addi r1,r0,0x0000 /* TLB entry #0 */ + tlbre r0,r1,0x0002 /* Read contents */ + ori r0,r0,0x0c00 /* Or in the inhibit, write through bit */ + tlbwe r0,r1,0x0002 /* Save it out */ + isync +#endif + mr r1, r3 /* Set new stack pointer */ + mr r9, r4 /* Save copy of Init Data pointer */ + mr r10, r5 /* Save copy of Destination Address */ + + mr r3, r5 /* Destination Address */ + lis r4, CFG_MONITOR_BASE@h /* Source Address */ + ori r4, r4, CFG_MONITOR_BASE@l + lwz r5, GOT(__init_end) + sub r5, r5, r4 + li r6, CFG_CACHELINE_SIZE /* Cache Line Size */ + + /* + * Fix GOT pointer: + * + * New GOT-PTR = (old GOT-PTR - CFG_MONITOR_BASE) + Destination Address + * + * Offset: + */ + sub r15, r10, r4 + + /* First our own GOT */ + add r14, r14, r15 + /* the the one used by the C code */ + add r30, r30, r15 + + /* + * Now relocate code + */ + + cmplw cr1,r3,r4 + addi r0,r5,3 + srwi. r0,r0,2 + beq cr1,4f /* In place copy is not necessary */ + beq 7f /* Protect against 0 count */ + mtctr r0 + bge cr1,2f + + la r8,-4(r4) + la r7,-4(r3) +1: lwzu r0,4(r8) + stwu r0,4(r7) + bdnz 1b + b 4f + +2: slwi r0,r0,2 + add r8,r4,r0 + add r7,r3,r0 +3: lwzu r0,-4(r8) + stwu r0,-4(r7) + bdnz 3b + +/* + * Now flush the cache: note that we must start from a cache aligned + * address. Otherwise we might miss one cache line. + */ +4: cmpwi r6,0 + add r5,r3,r5 + beq 7f /* Always flush prefetch queue in any case */ + subi r0,r6,1 + andc r3,r3,r0 + mr r4,r3 +5: dcbst 0,r4 + add r4,r4,r6 + cmplw r4,r5 + blt 5b + sync /* Wait for all dcbst to complete on bus */ + mr r4,r3 +6: icbi 0,r4 + add r4,r4,r6 + cmplw r4,r5 + blt 6b +7: sync /* Wait for all icbi to complete on bus */ + isync + +/* + * We are done. Do not return, instead branch to second part of board + * initialization, now running from RAM. + */ + + addi r0, r10, in_ram - _start + EXC_OFF_SYS_RESET + mtlr r0 + blr /* NEVER RETURNS! */ + +in_ram: + + /* + * Relocation Function, r14 point to got2+0x8000 + * + * Adjust got2 pointers, no need to check for 0, this code + * already puts a few entries in the table. + */ + li r0,__got2_entries@sectoff@l + la r3,GOT(_GOT2_TABLE_) + lwz r11,GOT(_GOT2_TABLE_) + mtctr r0 + sub r11,r3,r11 + addi r3,r3,-4 +1: lwzu r0,4(r3) + add r0,r0,r11 + stw r0,0(r3) + bdnz 1b + + /* + * Now adjust the fixups and the pointers to the fixups + * in case we need to move ourselves again. + */ +2: li r0,__fixup_entries@sectoff@l + lwz r3,GOT(_FIXUP_TABLE_) + cmpwi r0,0 + mtctr r0 + addi r3,r3,-4 + beq 4f +3: lwzu r4,4(r3) + lwzux r0,r4,r11 + add r0,r0,r11 + stw r10,0(r3) + stw r0,0(r4) + bdnz 3b +4: +clear_bss: + /* + * Now clear BSS segment + */ + lwz r3,GOT(__bss_start) + lwz r4,GOT(_end) + + cmplw 0, r3, r4 + beq 6f + + li r0, 0 +5: + stw r0, 0(r3) + addi r3, r3, 4 + cmplw 0, r3, r4 + bne 5b +6: + + mr r3, r9 /* Init Data pointer */ + mr r4, r10 /* Destination Address */ + bl board_init_r + + /* + * Copy exception vector code to low memory + * + * r3: dest_addr + * r7: source address, r8: end address, r9: target address + */ + .globl trap_init +trap_init: + lwz r7, GOT(_start) + lwz r8, GOT(_end_of_vectors) + + li r9, 0x100 /* reset vector always at 0x100 */ + + cmplw 0, r7, r8 + bgelr /* return if r7>=r8 - just in case */ + + mflr r4 /* save link register */ +1: + lwz r0, 0(r7) + stw r0, 0(r9) + addi r7, r7, 4 + addi r9, r9, 4 + cmplw 0, r7, r8 + bne 1b + + /* + * relocate `hdlr' and `int_return' entries + */ + li r7, .L_MachineCheck - _start + EXC_OFF_SYS_RESET + li r8, Alignment - _start + EXC_OFF_SYS_RESET +2: + bl trap_reloc + addi r7, r7, 0x100 /* next exception vector */ + cmplw 0, r7, r8 + blt 2b + + li r7, .L_Alignment - _start + EXC_OFF_SYS_RESET + bl trap_reloc + + li r7, .L_ProgramCheck - _start + EXC_OFF_SYS_RESET + bl trap_reloc + + li r7, .L_FPUnavailable - _start + EXC_OFF_SYS_RESET + li r8, SystemCall - _start + EXC_OFF_SYS_RESET +3: + bl trap_reloc + addi r7, r7, 0x100 /* next exception vector */ + cmplw 0, r7, r8 + blt 3b + + li r7, .L_SingleStep - _start + EXC_OFF_SYS_RESET + li r8, _end_of_vectors - _start + EXC_OFF_SYS_RESET +4: + bl trap_reloc + addi r7, r7, 0x100 /* next exception vector */ + cmplw 0, r7, r8 + blt 4b + + mtlr r4 /* restore link register */ + blr + + /* + * Function: relocate entries for one exception vector + */ +trap_reloc: + lwz r0, 0(r7) /* hdlr ... */ + add r0, r0, r3 /* ... += dest_addr */ + stw r0, 0(r7) + + lwz r0, 4(r7) /* int_return ... */ + add r0, r0, r3 /* ... += dest_addr */ + stw r0, 4(r7) + + blr + + +/**************************************************************************/ +/* PPC405EP specific stuff */ +/**************************************************************************/ +#ifdef CONFIG_405EP +ppc405ep_init: + +#ifdef CONFIG_BUBINGA + /* + * Initialize EBC chip selects 1 & 4 and GPIO pins (for alternate + * function) to support FPGA and NVRAM accesses below. + */ + + lis r3,GPIO0_OSRH@h /* config GPIO output select */ + ori r3,r3,GPIO0_OSRH@l + lis r4,CFG_GPIO0_OSRH@h + ori r4,r4,CFG_GPIO0_OSRH@l + stw r4,0(r3) + lis r3,GPIO0_OSRL@h + ori r3,r3,GPIO0_OSRL@l + lis r4,CFG_GPIO0_OSRL@h + ori r4,r4,CFG_GPIO0_OSRL@l + stw r4,0(r3) + + lis r3,GPIO0_ISR1H@h /* config GPIO input select */ + ori r3,r3,GPIO0_ISR1H@l + lis r4,CFG_GPIO0_ISR1H@h + ori r4,r4,CFG_GPIO0_ISR1H@l + stw r4,0(r3) + lis r3,GPIO0_ISR1L@h + ori r3,r3,GPIO0_ISR1L@l + lis r4,CFG_GPIO0_ISR1L@h + ori r4,r4,CFG_GPIO0_ISR1L@l + stw r4,0(r3) + + lis r3,GPIO0_TSRH@h /* config GPIO three-state select */ + ori r3,r3,GPIO0_TSRH@l + lis r4,CFG_GPIO0_TSRH@h + ori r4,r4,CFG_GPIO0_TSRH@l + stw r4,0(r3) + lis r3,GPIO0_TSRL@h + ori r3,r3,GPIO0_TSRL@l + lis r4,CFG_GPIO0_TSRL@h + ori r4,r4,CFG_GPIO0_TSRL@l + stw r4,0(r3) + + lis r3,GPIO0_TCR@h /* config GPIO driver output enables */ + ori r3,r3,GPIO0_TCR@l + lis r4,CFG_GPIO0_TCR@h + ori r4,r4,CFG_GPIO0_TCR@l + stw r4,0(r3) + + li r3,pb1ap /* program EBC bank 1 for RTC access */ + mtdcr ebccfga,r3 + lis r3,CFG_EBC_PB1AP@h + ori r3,r3,CFG_EBC_PB1AP@l + mtdcr ebccfgd,r3 + li r3,pb1cr + mtdcr ebccfga,r3 + lis r3,CFG_EBC_PB1CR@h + ori r3,r3,CFG_EBC_PB1CR@l + mtdcr ebccfgd,r3 + + li r3,pb1ap /* program EBC bank 1 for RTC access */ + mtdcr ebccfga,r3 + lis r3,CFG_EBC_PB1AP@h + ori r3,r3,CFG_EBC_PB1AP@l + mtdcr ebccfgd,r3 + li r3,pb1cr + mtdcr ebccfga,r3 + lis r3,CFG_EBC_PB1CR@h + ori r3,r3,CFG_EBC_PB1CR@l + mtdcr ebccfgd,r3 + + li r3,pb4ap /* program EBC bank 4 for FPGA access */ + mtdcr ebccfga,r3 + lis r3,CFG_EBC_PB4AP@h + ori r3,r3,CFG_EBC_PB4AP@l + mtdcr ebccfgd,r3 + li r3,pb4cr + mtdcr ebccfga,r3 + lis r3,CFG_EBC_PB4CR@h + ori r3,r3,CFG_EBC_PB4CR@l + mtdcr ebccfgd,r3 +#endif + + addi r3,0,CPC0_PCI_HOST_CFG_EN +#ifdef CONFIG_BUBINGA + /* + !----------------------------------------------------------------------- + ! Check FPGA for PCI internal/external arbitration + ! If board is set to internal arbitration, update cpc0_pci + !----------------------------------------------------------------------- + */ + addis r5,r0,FPGA_REG1@h /* set offset for FPGA_REG1 */ + ori r5,r5,FPGA_REG1@l + lbz r5,0x0(r5) /* read to get PCI arb selection */ + andi. r6,r5,FPGA_REG1_PCI_INT_ARB /* using internal arbiter ?*/ + beq ..pci_cfg_set /* if not set, then bypass reg write*/ +#endif + ori r3,r3,CPC0_PCI_ARBIT_EN +..pci_cfg_set: + mtdcr CPC0_PCI, r3 /* Enable internal arbiter*/ + + /* + !----------------------------------------------------------------------- + ! Check to see if chip is in bypass mode. + ! If so, write stored CPC0_PLLMR0 and CPC0_PLLMR1 values and perform a + ! CPU reset Otherwise, skip this step and keep going. + ! Note: Running BIOS in bypass mode is not supported since PLB speed + ! will not be fast enough for the SDRAM (min 66MHz) + !----------------------------------------------------------------------- + */ + mfdcr r5, CPC0_PLLMR1 + rlwinm r4,r5,1,0x1 /* get system clock source (SSCS) */ + cmpi cr0,0,r4,0x1 + + beq pll_done /* if SSCS =b'1' then PLL has */ + /* already been set */ + /* and CPU has been reset */ + /* so skip to next section */ + +#ifdef CONFIG_BUBINGA + /* + !----------------------------------------------------------------------- + ! Read NVRAM to get value to write in PLLMR. + ! If value has not been correctly saved, write default value + ! Default config values (assuming on-board 33MHz SYS_CLK) are above. + ! See CPU_DEFAULT_200 and CPU_DEFAULT_266 above. + ! + ! WARNING: This code assumes the first three words in the nvram_t + ! structure in openbios.h. Changing the beginning of + ! the structure will break this code. + ! + !----------------------------------------------------------------------- + */ + addis r3,0,NVRAM_BASE@h + addi r3,r3,NVRAM_BASE@l + + lwz r4, 0(r3) + addis r5,0,NVRVFY1@h + addi r5,r5,NVRVFY1@l + cmp cr0,0,r4,r5 /* Compare 1st NVRAM Magic number*/ + bne ..no_pllset + addi r3,r3,4 + lwz r4, 0(r3) + addis r5,0,NVRVFY2@h + addi r5,r5,NVRVFY2@l + cmp cr0,0,r4,r5 /* Compare 2 NVRAM Magic number */ + bne ..no_pllset + addi r3,r3,8 /* Skip over conf_size */ + lwz r4, 4(r3) /* Load PLLMR1 value from NVRAM */ + lwz r3, 0(r3) /* Load PLLMR0 value from NVRAM */ + rlwinm r5,r4,1,0x1 /* get system clock source (SSCS) */ + cmpi cr0,0,r5,1 /* See if PLL is locked */ + beq pll_write +..no_pllset: +#endif /* CONFIG_BUBINGA */ + + addis r3,0,PLLMR0_DEFAULT@h /* PLLMR0 default value */ + ori r3,r3,PLLMR0_DEFAULT@l /* */ + addis r4,0,PLLMR1_DEFAULT@h /* PLLMR1 default value */ + ori r4,r4,PLLMR1_DEFAULT@l /* */ + + b pll_write /* Write the CPC0_PLLMR with new value */ + +pll_done: + /* + !----------------------------------------------------------------------- + ! Clear Soft Reset Register + ! This is needed to enable PCI if not booting from serial EPROM + !----------------------------------------------------------------------- + */ + addi r3, 0, 0x0 + mtdcr CPC0_SRR, r3 + + addis r3,0,0x0010 + mtctr r3 +pci_wait: + bdnz pci_wait + + blr /* return to main code */ + +/* +!----------------------------------------------------------------------------- +! Function: pll_write +! Description: Updates the value of the CPC0_PLLMR according to CMOS27E documentation +! That is: +! 1. Pll is first disabled (de-activated by putting in bypass mode) +! 2. PLL is reset +! 3. Clock dividers are set while PLL is held in reset and bypassed +! 4. PLL Reset is cleared +! 5. Wait 100us for PLL to lock +! 6. A core reset is performed +! Input: r3 = Value to write to CPC0_PLLMR0 +! Input: r4 = Value to write to CPC0_PLLMR1 +! Output r3 = none +!----------------------------------------------------------------------------- +*/ +pll_write: + mfdcr r5, CPC0_UCR + andis. r5,r5,0xFFFF + ori r5,r5,0x0101 /* Stop the UART clocks */ + mtdcr CPC0_UCR,r5 /* Before changing PLL */ + + mfdcr r5, CPC0_PLLMR1 + rlwinm r5,r5,0,0x7FFFFFFF /* Disable PLL */ + mtdcr CPC0_PLLMR1,r5 + oris r5,r5,0x4000 /* Set PLL Reset */ + mtdcr CPC0_PLLMR1,r5 + + mtdcr CPC0_PLLMR0,r3 /* Set clock dividers */ + rlwinm r5,r4,0,0x3FFFFFFF /* Reset & Bypass new PLL dividers */ + oris r5,r5,0x4000 /* Set PLL Reset */ + mtdcr CPC0_PLLMR1,r5 /* Set clock dividers */ + rlwinm r5,r5,0,0xBFFFFFFF /* Clear PLL Reset */ + mtdcr CPC0_PLLMR1,r5 + + /* + ! Wait min of 100us for PLL to lock. + ! See CMOS 27E databook for more info. + ! At 200MHz, that means waiting 20,000 instructions + */ + addi r3,0,20000 /* 2000 = 0x4e20 */ + mtctr r3 +pll_wait: + bdnz pll_wait + + oris r5,r5,0x8000 /* Enable PLL */ + mtdcr CPC0_PLLMR1,r5 /* Engage */ + + /* + * Reset CPU to guarantee timings are OK + * Not sure if this is needed... + */ + addis r3,0,0x1000 + mtspr dbcr0,r3 /* This will cause a CPU core reset, and */ + /* execution will continue from the poweron */ + /* vector of 0xfffffffc */ +#endif /* CONFIG_405EP */ diff --git a/cpu/ppc4xx/traps.c b/cpu/ppc4xx/traps.c new file mode 100755 index 0000000..6aecca2 --- /dev/null +++ b/cpu/ppc4xx/traps.c @@ -0,0 +1,290 @@ +/* + * linux/arch/ppc/kernel/traps.c + * + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Modified by Cort Dougan (cort@cs.nmt.edu) + * and Paul Mackerras (paulus@cs.anu.edu.au) + * + * (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 + */ + +/* + * This file handles the architecture-dependent parts of hardware exceptions + */ + +#include <common.h> +#include <command.h> +#include <asm/processor.h> + +#if (CONFIG_COMMANDS & CFG_CMD_KGDB) +int (*debugger_exception_handler)(struct pt_regs *) = 0; +#endif + +/* Returns 0 if exception not found and fixup otherwise. */ +extern unsigned long search_exception_table(unsigned long); + +/* THIS NEEDS CHANGING to use the board info structure. + */ +#define END_OF_MEM 0x00400000 + + +static __inline__ void set_tsr(unsigned long val) +{ +#if defined(CONFIG_440) + asm volatile("mtspr 0x150, %0" : : "r" (val)); +#else + asm volatile("mttsr %0" : : "r" (val)); +#endif +} + +static __inline__ unsigned long get_esr(void) +{ + unsigned long val; + +#if defined(CONFIG_440) + asm volatile("mfspr %0, 0x03e" : "=r" (val) :); +#else + asm volatile("mfesr %0" : "=r" (val) :); +#endif + return val; +} + +#define ESR_MCI 0x80000000 +#define ESR_PIL 0x08000000 +#define ESR_PPR 0x04000000 +#define ESR_PTR 0x02000000 +#define ESR_DST 0x00800000 +#define ESR_DIZ 0x00400000 +#define ESR_U0F 0x00008000 + +#if (CONFIG_COMMANDS & CFG_CMD_BEDBUG) +extern void do_bedbug_breakpoint(struct pt_regs *); +#endif + +/* + * Trap & Exception support + */ + +void +print_backtrace(unsigned long *sp) +{ + int cnt = 0; + unsigned long i; + + printf("Call backtrace: "); + while (sp) { + if ((uint)sp > END_OF_MEM) + break; + + i = sp[1]; + if (cnt++ % 7 == 0) + printf("\n"); + printf("%08lX ", i); + if (cnt > 32) break; + sp = (unsigned long *)*sp; + } + printf("\n"); +} + +void show_regs(struct pt_regs * regs) +{ + int i; + + printf("NIP: %08lX XER: %08lX LR: %08lX REGS: %p TRAP: %04lx DAR: %08lX\n", + regs->nip, regs->xer, regs->link, regs, regs->trap, regs->dar); + printf("MSR: %08lx EE: %01x PR: %01x FP: %01x ME: %01x IR/DR: %01x%01x\n", + regs->msr, regs->msr&MSR_EE ? 1 : 0, regs->msr&MSR_PR ? 1 : 0, + regs->msr & MSR_FP ? 1 : 0,regs->msr&MSR_ME ? 1 : 0, + regs->msr&MSR_IR ? 1 : 0, + regs->msr&MSR_DR ? 1 : 0); + + printf("\n"); + for (i = 0; i < 32; i++) { + if ((i % 8) == 0) + { + printf("GPR%02d: ", i); + } + + printf("%08lX ", regs->gpr[i]); + if ((i % 8) == 7) + { + printf("\n"); + } + } +} + + +void +_exception(int signr, struct pt_regs *regs) +{ + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("Exception in kernel pc %lx signal %d",regs->nip,signr); +} + +void +MachineCheckException(struct pt_regs *regs) +{ + unsigned long fixup; + + /* Probing PCI using config cycles cause this exception + * when a device is not present. Catch it and return to + * the PCI exception handler. + */ + if ((fixup = search_exception_table(regs->nip)) != 0) { + regs->nip = fixup; + return; + } + +#if (CONFIG_COMMANDS & CFG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + + printf("Machine check in kernel mode.\n"); + printf("Caused by (from msr): "); + printf("regs %p ",regs); + switch( regs->msr & 0x000F0000) { + case (0x80000000>>12): + printf("Machine check signal - probably due to mm fault\n" + "with mmu off\n"); + break; + case (0x80000000>>13): + printf("Transfer error ack signal\n"); + break; + case (0x80000000>>14): + printf("Data parity signal\n"); + break; + case (0x80000000>>15): + printf("Address parity signal\n"); + break; + default: + printf("Unknown values in msr\n"); + } + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("machine check"); +} + +void +AlignmentException(struct pt_regs *regs) +{ +#if (CONFIG_COMMANDS & CFG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("Alignment Exception"); +} + +void +ProgramCheckException(struct pt_regs *regs) +{ + long esr_val; + +#if (CONFIG_COMMANDS & CFG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + + show_regs(regs); + + esr_val = get_esr(); + if( esr_val & ESR_PIL ) + printf( "** Illegal Instruction **\n" ); + else if( esr_val & ESR_PPR ) + printf( "** Privileged Instruction **\n" ); + else if( esr_val & ESR_PTR ) + printf( "** Trap Instruction **\n" ); + + print_backtrace((unsigned long *)regs->gpr[1]); + panic("Program Check Exception"); +} + +void +PITException(struct pt_regs *regs) +{ + /* + * Reset PIT interrupt + */ + set_tsr(0x08000000); + + /* + * Call timer_interrupt routine in interrupts.c + */ + timer_interrupt(NULL); +} + + +void +UnknownException(struct pt_regs *regs) +{ +#if (CONFIG_COMMANDS & CFG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + + printf("Bad trap at PC: %lx, SR: %lx, vector=%lx\n", + regs->nip, regs->msr, regs->trap); + _exception(0, regs); +} + +void +DebugException(struct pt_regs *regs) +{ + printf("Debugger trap at @ %lx\n", regs->nip ); + show_regs(regs); +#if (CONFIG_COMMANDS & CFG_CMD_BEDBUG) + do_bedbug_breakpoint( regs ); +#endif +} + +/* Probe an address by reading. If not present, return -1, otherwise + * return 0. + */ +int +addr_probe(uint *addr) +{ +#if 0 + int retval; + + __asm__ __volatile__( \ + "1: lwz %0,0(%1)\n" \ + " eieio\n" \ + " li %0,0\n" \ + "2:\n" \ + ".section .fixup,\"ax\"\n" \ + "3: li %0,-1\n" \ + " b 2b\n" \ + ".section __ex_table,\"a\"\n" \ + " .align 2\n" \ + " .long 1b,3b\n" \ + ".text" \ + : "=r" (retval) : "r"(addr)); + + return (retval); +#endif + return 0; +} diff --git a/cpu/ppc4xx/usb_ohci.c b/cpu/ppc4xx/usb_ohci.c new file mode 100755 index 0000000..bb57658 --- /dev/null +++ b/cpu/ppc4xx/usb_ohci.c @@ -0,0 +1,1642 @@ +/* + * URB OHCI HCD (Host Controller Driver) for USB on the PPC440EP. + * + * (C) Copyright 2003-2004 + * Gary Jennejohn, DENX Software Engineering <gj@denx.de> + * + * (C) Copyright 2004 + * Pierre Aubert, Staubli Faverges <p.aubert@staubli.com> + * + * Note: Much of this code has been derived from Linux 2.4 + * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at> + * (C) Copyright 2000-2002 David Brownell + * + * 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 + * + */ +/* + * IMPORTANT NOTES + * 1 - this driver is intended for use with USB Mass Storage Devices + * (BBB) ONLY. There is NO support for Interrupt or Isochronous pipes! + */ + +#include <common.h> + +#ifdef CONFIG_USB_OHCI + +#include <malloc.h> +#include <usb.h> +#include "usb_ohci.h" + +#include "usbdev.h" + +#define OHCI_USE_NPS /* force NoPowerSwitching mode */ +#undef OHCI_VERBOSE_DEBUG /* not always helpful */ +#undef DEBUG +#undef SHOW_INFO +#undef OHCI_FILL_TRACE + +/* For initializing controller (mask in an HCFS mode too) */ +#define OHCI_CONTROL_INIT \ + (OHCI_CTRL_CBSR & 0x3) | OHCI_CTRL_IE | OHCI_CTRL_PLE + +#define readl(a) (*((vu_long *)(a))) +#define writel(a, b) (*((vu_long *)(b)) = ((vu_long)a)) + +#define min_t(type,x,y) ({ type __x = (x); type __y = (y); __x < __y ? __x: __y; }) + +#ifdef DEBUG +#define dbg(format, arg...) printf("DEBUG: " format "\n", ## arg) +#else +#define dbg(format, arg...) do {} while(0) +#endif /* DEBUG */ +#define err(format, arg...) printf("ERROR: " format "\n", ## arg) +#ifdef SHOW_INFO +#define info(format, arg...) printf("INFO: " format "\n", ## arg) +#else +#define info(format, arg...) do {} while(0) +#endif + +#define m16_swap(x) swap_16(x) +#define m32_swap(x) swap_32(x) + +#ifdef CONFIG_440EP +#define ohci_cpu_to_le16(x) (x) +#define ohci_cpu_to_le32(x) (x) +#else +#define ohci_cpu_to_le16(x) swap_16(x) +#define ohci_cpu_to_le32(x) swap_32(x) +#endif + +/* global ohci_t */ +static ohci_t gohci; +/* this must be aligned to a 256 byte boundary */ +struct ohci_hcca ghcca[1]; +/* a pointer to the aligned storage */ +struct ohci_hcca *phcca; +/* this allocates EDs for all possible endpoints */ +struct ohci_device ohci_dev; +/* urb_priv */ +urb_priv_t urb_priv; +/* RHSC flag */ +int got_rhsc; +/* device which was disconnected */ +struct usb_device *devgone; +/* flag guarding URB transation */ +int urb_finished = 0; + +/*-------------------------------------------------------------------------*/ + +/* AMD-756 (D2 rev) reports corrupt register contents in some cases. + * The erratum (#4) description is incorrect. AMD's workaround waits + * till some bits (mostly reserved) are clear; ok for all revs. + */ +#define OHCI_QUIRK_AMD756 0xabcd +#define read_roothub(hc, register, mask) ({ \ + u32 temp = readl (&hc->regs->roothub.register); \ + if (hc->flags & OHCI_QUIRK_AMD756) \ + while (temp & mask) \ + temp = readl (&hc->regs->roothub.register); \ + temp; }) + +static u32 roothub_a (struct ohci *hc) + { return read_roothub (hc, a, 0xfc0fe000); } +static inline u32 roothub_b (struct ohci *hc) + { return readl (&hc->regs->roothub.b); } +static inline u32 roothub_status (struct ohci *hc) + { return readl (&hc->regs->roothub.status); } +static u32 roothub_portstatus (struct ohci *hc, int i) + { return read_roothub (hc, portstatus [i], 0xffe0fce0); } + + +/* forward declaration */ +static int hc_interrupt (void); +static void +td_submit_job (struct usb_device * dev, unsigned long pipe, void * buffer, + int transfer_len, struct devrequest * setup, urb_priv_t * urb, int interval); + +/*-------------------------------------------------------------------------* + * URB support functions + *-------------------------------------------------------------------------*/ + +/* free HCD-private data associated with this URB */ + +static void urb_free_priv (urb_priv_t * urb) +{ + int i; + int last; + struct td * td; + + last = urb->length - 1; + if (last >= 0) { + for (i = 0; i <= last; i++) { + td = urb->td[i]; + if (td) { + td->usb_dev = NULL; + urb->td[i] = NULL; + } + } + } +} + +/*-------------------------------------------------------------------------*/ + +#ifdef DEBUG +static int sohci_get_current_frame_number (struct usb_device * dev); + +/* debug| print the main components of an URB + * small: 0) header + data packets 1) just header */ + +static void pkt_print (struct usb_device * dev, unsigned long pipe, void * buffer, + int transfer_len, struct devrequest * setup, char * str, int small) +{ + urb_priv_t * purb = &urb_priv; + + dbg("%s URB:[%4x] dev:%2d,ep:%2d-%c,type:%s,len:%d/%d stat:%#lx", + str, + sohci_get_current_frame_number (dev), + usb_pipedevice (pipe), + usb_pipeendpoint (pipe), + usb_pipeout (pipe)? 'O': 'I', + usb_pipetype (pipe) < 2? (usb_pipeint (pipe)? "INTR": "ISOC"): + (usb_pipecontrol (pipe)? "CTRL": "BULK"), + purb->actual_length, + transfer_len, dev->status); +#ifdef OHCI_VERBOSE_DEBUG + if (!small) { + int i, len; + + if (usb_pipecontrol (pipe)) { + printf (__FILE__ ": cmd(8):"); + for (i = 0; i < 8 ; i++) + printf (" %02x", ((__u8 *) setup) [i]); + printf ("\n"); + } + if (transfer_len > 0 && buffer) { + printf (__FILE__ ": data(%d/%d):", + purb->actual_length, + transfer_len); + len = usb_pipeout (pipe)? + transfer_len: purb->actual_length; + for (i = 0; i < 16 && i < len; i++) + printf (" %02x", ((__u8 *) buffer) [i]); + printf ("%s\n", i < len? "...": ""); + } + } +#endif +} + +/* just for debugging; prints non-empty branches of the int ed tree inclusive iso eds*/ +void ep_print_int_eds (ohci_t *ohci, char * str) { + int i, j; + __u32 * ed_p; + for (i= 0; i < 32; i++) { + j = 5; + ed_p = &(ohci->hcca->int_table [i]); + if (*ed_p == 0) + continue; + printf (__FILE__ ": %s branch int %2d(%2x):", str, i, i); + while (*ed_p != 0 && j--) { + ed_t *ed = (ed_t *)ohci_cpu_to_le32(ed_p); + printf (" ed: %4x;", ed->hwINFO); + ed_p = &ed->hwNextED; + } + printf ("\n"); + } +} + +static void ohci_dump_intr_mask (char *label, __u32 mask) +{ + dbg ("%s: 0x%08x%s%s%s%s%s%s%s%s%s", + label, + mask, + (mask & OHCI_INTR_MIE) ? " MIE" : "", + (mask & OHCI_INTR_OC) ? " OC" : "", + (mask & OHCI_INTR_RHSC) ? " RHSC" : "", + (mask & OHCI_INTR_FNO) ? " FNO" : "", + (mask & OHCI_INTR_UE) ? " UE" : "", + (mask & OHCI_INTR_RD) ? " RD" : "", + (mask & OHCI_INTR_SF) ? " SF" : "", + (mask & OHCI_INTR_WDH) ? " WDH" : "", + (mask & OHCI_INTR_SO) ? " SO" : "" + ); +} + +static void maybe_print_eds (char *label, __u32 value) +{ + ed_t *edp = (ed_t *)value; + + if (value) { + dbg ("%s %08x", label, value); + dbg ("%08x", edp->hwINFO); + dbg ("%08x", edp->hwTailP); + dbg ("%08x", edp->hwHeadP); + dbg ("%08x", edp->hwNextED); + } +} + +static char * hcfs2string (int state) +{ + switch (state) { + case OHCI_USB_RESET: return "reset"; + case OHCI_USB_RESUME: return "resume"; + case OHCI_USB_OPER: return "operational"; + case OHCI_USB_SUSPEND: return "suspend"; + } + return "?"; +} + +/* dump control and status registers */ +static void ohci_dump_status (ohci_t *controller) +{ + struct ohci_regs *regs = controller->regs; + __u32 temp; + + temp = readl (®s->revision) & 0xff; + if (temp != 0x10) + dbg ("spec %d.%d", (temp >> 4), (temp & 0x0f)); + + temp = readl (®s->control); + dbg ("control: 0x%08x%s%s%s HCFS=%s%s%s%s%s CBSR=%d", temp, + (temp & OHCI_CTRL_RWE) ? " RWE" : "", + (temp & OHCI_CTRL_RWC) ? " RWC" : "", + (temp & OHCI_CTRL_IR) ? " IR" : "", + hcfs2string (temp & OHCI_CTRL_HCFS), + (temp & OHCI_CTRL_BLE) ? " BLE" : "", + (temp & OHCI_CTRL_CLE) ? " CLE" : "", + (temp & OHCI_CTRL_IE) ? " IE" : "", + (temp & OHCI_CTRL_PLE) ? " PLE" : "", + temp & OHCI_CTRL_CBSR + ); + + temp = readl (®s->cmdstatus); + dbg ("cmdstatus: 0x%08x SOC=%d%s%s%s%s", temp, + (temp & OHCI_SOC) >> 16, + (temp & OHCI_OCR) ? " OCR" : "", + (temp & OHCI_BLF) ? " BLF" : "", + (temp & OHCI_CLF) ? " CLF" : "", + (temp & OHCI_HCR) ? " HCR" : "" + ); + + ohci_dump_intr_mask ("intrstatus", readl (®s->intrstatus)); + ohci_dump_intr_mask ("intrenable", readl (®s->intrenable)); + + maybe_print_eds ("ed_periodcurrent", readl (®s->ed_periodcurrent)); + + maybe_print_eds ("ed_controlhead", readl (®s->ed_controlhead)); + maybe_print_eds ("ed_controlcurrent", readl (®s->ed_controlcurrent)); + + maybe_print_eds ("ed_bulkhead", readl (®s->ed_bulkhead)); + maybe_print_eds ("ed_bulkcurrent", readl (®s->ed_bulkcurrent)); + + maybe_print_eds ("donehead", readl (®s->donehead)); +} + +static void ohci_dump_roothub (ohci_t *controller, int verbose) +{ + __u32 temp, ndp, i; + + temp = roothub_a (controller); + ndp = (temp & RH_A_NDP); + + if (verbose) { + dbg ("roothub.a: %08x POTPGT=%d%s%s%s%s%s NDP=%d", temp, + ((temp & RH_A_POTPGT) >> 24) & 0xff, + (temp & RH_A_NOCP) ? " NOCP" : "", + (temp & RH_A_OCPM) ? " OCPM" : "", + (temp & RH_A_DT) ? " DT" : "", + (temp & RH_A_NPS) ? " NPS" : "", + (temp & RH_A_PSM) ? " PSM" : "", + ndp + ); + temp = roothub_b (controller); + dbg ("roothub.b: %08x PPCM=%04x DR=%04x", + temp, + (temp & RH_B_PPCM) >> 16, + (temp & RH_B_DR) + ); + temp = roothub_status (controller); + dbg ("roothub.status: %08x%s%s%s%s%s%s", + temp, + (temp & RH_HS_CRWE) ? " CRWE" : "", + (temp & RH_HS_OCIC) ? " OCIC" : "", + (temp & RH_HS_LPSC) ? " LPSC" : "", + (temp & RH_HS_DRWE) ? " DRWE" : "", + (temp & RH_HS_OCI) ? " OCI" : "", + (temp & RH_HS_LPS) ? " LPS" : "" + ); + } + + for (i = 0; i < ndp; i++) { + temp = roothub_portstatus (controller, i); + dbg ("roothub.portstatus [%d] = 0x%08x%s%s%s%s%s%s%s%s%s%s%s%s", + i, + temp, + (temp & RH_PS_PRSC) ? " PRSC" : "", + (temp & RH_PS_OCIC) ? " OCIC" : "", + (temp & RH_PS_PSSC) ? " PSSC" : "", + (temp & RH_PS_PESC) ? " PESC" : "", + (temp & RH_PS_CSC) ? " CSC" : "", + + (temp & RH_PS_LSDA) ? " LSDA" : "", + (temp & RH_PS_PPS) ? " PPS" : "", + (temp & RH_PS_PRS) ? " PRS" : "", + (temp & RH_PS_POCI) ? " POCI" : "", + (temp & RH_PS_PSS) ? " PSS" : "", + + (temp & RH_PS_PES) ? " PES" : "", + (temp & RH_PS_CCS) ? " CCS" : "" + ); + } +} + +static void ohci_dump (ohci_t *controller, int verbose) +{ + dbg ("OHCI controller usb-%s state", controller->slot_name); + + /* dumps some of the state we know about */ + ohci_dump_status (controller); + if (verbose) + ep_print_int_eds (controller, "hcca"); + dbg ("hcca frame #%04x", controller->hcca->frame_no); + ohci_dump_roothub (controller, 1); +} + + +#endif /* DEBUG */ + +/*-------------------------------------------------------------------------* + * Interface functions (URB) + *-------------------------------------------------------------------------*/ + +/* get a transfer request */ + +int sohci_submit_job(struct usb_device *dev, unsigned long pipe, void *buffer, + int transfer_len, struct devrequest *setup, int interval) +{ + ohci_t *ohci; + ed_t * ed; + urb_priv_t *purb_priv; + int i, size = 0; + + ohci = &gohci; + + /* when controller's hung, permit only roothub cleanup attempts + * such as powering down ports */ + if (ohci->disabled) { + err("sohci_submit_job: EPIPE"); + return -1; + } + + /* if we have an unfinished URB from previous transaction let's + * fail and scream as quickly as possible so as not to corrupt + * further communication */ + if (!urb_finished) { + err("sohci_submit_job: URB NOT FINISHED"); + return -1; + } + /* we're about to begin a new transaction here so mark the URB unfinished */ + urb_finished = 0; + + /* every endpoint has a ed, locate and fill it */ + if (!(ed = ep_add_ed (dev, pipe))) { + err("sohci_submit_job: ENOMEM"); + return -1; + } + + /* for the private part of the URB we need the number of TDs (size) */ + switch (usb_pipetype (pipe)) { + case PIPE_BULK: /* one TD for every 4096 Byte */ + size = (transfer_len - 1) / 4096 + 1; + break; + case PIPE_CONTROL: /* 1 TD for setup, 1 for ACK and 1 for every 4096 B */ + size = (transfer_len == 0)? 2: + (transfer_len - 1) / 4096 + 3; + break; + } + + if (size >= (N_URB_TD - 1)) { + err("need %d TDs, only have %d", size, N_URB_TD); + return -1; + } + purb_priv = &urb_priv; + purb_priv->pipe = pipe; + + /* fill the private part of the URB */ + purb_priv->length = size; + purb_priv->ed = ed; + purb_priv->actual_length = 0; + + /* allocate the TDs */ + /* note that td[0] was allocated in ep_add_ed */ + for (i = 0; i < size; i++) { + purb_priv->td[i] = td_alloc (dev); + if (!purb_priv->td[i]) { + purb_priv->length = i; + urb_free_priv (purb_priv); + err("sohci_submit_job: ENOMEM"); + return -1; + } + } + + if (ed->state == ED_NEW || (ed->state & ED_DEL)) { + urb_free_priv (purb_priv); + err("sohci_submit_job: EINVAL"); + return -1; + } + + /* link the ed into a chain if is not already */ + if (ed->state != ED_OPER) + ep_link (ohci, ed); + + /* fill the TDs and link it to the ed */ + td_submit_job(dev, pipe, buffer, transfer_len, setup, purb_priv, interval); + + return 0; +} + +/*-------------------------------------------------------------------------*/ + +#ifdef DEBUG +/* tell us the current USB frame number */ + +static int sohci_get_current_frame_number (struct usb_device *usb_dev) +{ + ohci_t *ohci = &gohci; + + return ohci_cpu_to_le16 (ohci->hcca->frame_no); +} +#endif + +/*-------------------------------------------------------------------------* + * ED handling functions + *-------------------------------------------------------------------------*/ + +/* link an ed into one of the HC chains */ + +static int ep_link (ohci_t *ohci, ed_t *edi) +{ + volatile ed_t *ed = edi; + + ed->state = ED_OPER; + + switch (ed->type) { + case PIPE_CONTROL: + ed->hwNextED = 0; + if (ohci->ed_controltail == NULL) { + writel (ed, &ohci->regs->ed_controlhead); + } else { + ohci->ed_controltail->hwNextED = ohci_cpu_to_le32 ((unsigned long)ed); + } + ed->ed_prev = ohci->ed_controltail; + if (!ohci->ed_controltail && !ohci->ed_rm_list[0] && + !ohci->ed_rm_list[1] && !ohci->sleeping) { + ohci->hc_control |= OHCI_CTRL_CLE; + writel (ohci->hc_control, &ohci->regs->control); + } + ohci->ed_controltail = edi; + break; + + case PIPE_BULK: + ed->hwNextED = 0; + if (ohci->ed_bulktail == NULL) { + writel (ed, &ohci->regs->ed_bulkhead); + } else { + ohci->ed_bulktail->hwNextED = ohci_cpu_to_le32 ((unsigned long)ed); + } + ed->ed_prev = ohci->ed_bulktail; + if (!ohci->ed_bulktail && !ohci->ed_rm_list[0] && + !ohci->ed_rm_list[1] && !ohci->sleeping) { + ohci->hc_control |= OHCI_CTRL_BLE; + writel (ohci->hc_control, &ohci->regs->control); + } + ohci->ed_bulktail = edi; + break; + } + return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* unlink an ed from one of the HC chains. + * just the link to the ed is unlinked. + * the link from the ed still points to another operational ed or 0 + * so the HC can eventually finish the processing of the unlinked ed */ + +static int ep_unlink (ohci_t *ohci, ed_t *edi) +{ + volatile ed_t *ed = edi; + + ed->hwINFO |= ohci_cpu_to_le32 (OHCI_ED_SKIP); + + switch (ed->type) { + case PIPE_CONTROL: + if (ed->ed_prev == NULL) { + if (!ed->hwNextED) { + ohci->hc_control &= ~OHCI_CTRL_CLE; + writel (ohci->hc_control, &ohci->regs->control); + } + writel (ohci_cpu_to_le32 (*((__u32 *)&ed->hwNextED)), &ohci->regs->ed_controlhead); + } else { + ed->ed_prev->hwNextED = ed->hwNextED; + } + if (ohci->ed_controltail == ed) { + ohci->ed_controltail = ed->ed_prev; + } else { + ((ed_t *)ohci_cpu_to_le32 (*((__u32 *)&ed->hwNextED)))->ed_prev = ed->ed_prev; + } + break; + + case PIPE_BULK: + if (ed->ed_prev == NULL) { + if (!ed->hwNextED) { + ohci->hc_control &= ~OHCI_CTRL_BLE; + writel (ohci->hc_control, &ohci->regs->control); + } + writel (ohci_cpu_to_le32 (*((__u32 *)&ed->hwNextED)), &ohci->regs->ed_bulkhead); + } else { + ed->ed_prev->hwNextED = ed->hwNextED; + } + if (ohci->ed_bulktail == ed) { + ohci->ed_bulktail = ed->ed_prev; + } else { + ((ed_t *)ohci_cpu_to_le32 (*((__u32 *)&ed->hwNextED)))->ed_prev = ed->ed_prev; + } + break; + } + ed->state = ED_UNLINK; + return 0; +} + + +/*-------------------------------------------------------------------------*/ + +/* add/reinit an endpoint; this should be done once at the usb_set_configuration command, + * but the USB stack is a little bit stateless so we do it at every transaction + * if the state of the ed is ED_NEW then a dummy td is added and the state is changed to ED_UNLINK + * in all other cases the state is left unchanged + * the ed info fields are setted anyway even though most of them should not change */ + +static ed_t * ep_add_ed (struct usb_device *usb_dev, unsigned long pipe) +{ + td_t *td; + ed_t *ed_ret; + volatile ed_t *ed; + + ed = ed_ret = &ohci_dev.ed[(usb_pipeendpoint (pipe) << 1) | + (usb_pipecontrol (pipe)? 0: usb_pipeout (pipe))]; + + if ((ed->state & ED_DEL) || (ed->state & ED_URB_DEL)) { + err("ep_add_ed: pending delete"); + /* pending delete request */ + return NULL; + } + + if (ed->state == ED_NEW) { + ed->hwINFO = ohci_cpu_to_le32 (OHCI_ED_SKIP); /* skip ed */ + /* dummy td; end of td list for ed */ + td = td_alloc (usb_dev); + ed->hwTailP = ohci_cpu_to_le32 ((unsigned long)td); + ed->hwHeadP = ed->hwTailP; + ed->state = ED_UNLINK; + ed->type = usb_pipetype (pipe); + ohci_dev.ed_cnt++; + } + + ed->hwINFO = ohci_cpu_to_le32 (usb_pipedevice (pipe) + | usb_pipeendpoint (pipe) << 7 + | (usb_pipeisoc (pipe)? 0x8000: 0) + | (usb_pipecontrol (pipe)? 0: (usb_pipeout (pipe)? 0x800: 0x1000)) + | usb_pipeslow (pipe) << 13 + | usb_maxpacket (usb_dev, pipe) << 16); + + return ed_ret; +} + +/*-------------------------------------------------------------------------* + * TD handling functions + *-------------------------------------------------------------------------*/ + +/* enqueue next TD for this URB (OHCI spec 5.2.8.2) */ + +static void td_fill (ohci_t *ohci, unsigned int info, + void *data, int len, + struct usb_device *dev, int index, urb_priv_t *urb_priv) +{ + volatile td_t *td, *td_pt; +#ifdef OHCI_FILL_TRACE + int i; +#endif + + if (index > urb_priv->length) { + err("index > length"); + return; + } + /* use this td as the next dummy */ + td_pt = urb_priv->td [index]; + td_pt->hwNextTD = 0; + + /* fill the old dummy TD */ + td = urb_priv->td [index] = (td_t *)(ohci_cpu_to_le32 (urb_priv->ed->hwTailP) & ~0xf); + + td->ed = urb_priv->ed; + td->next_dl_td = NULL; + td->index = index; + td->data = (__u32)data; +#ifdef OHCI_FILL_TRACE + if ((usb_pipetype(urb_priv->pipe) == PIPE_BULK) && usb_pipeout(urb_priv->pipe)) { + for (i = 0; i < len; i++) + printf("td->data[%d] %#2x ",i, ((unsigned char *)td->data)[i]); + printf("\n"); + } +#endif + if (!len) + data = 0; + + td->hwINFO = ohci_cpu_to_le32 (info); + td->hwCBP = ohci_cpu_to_le32 ((unsigned long)data); + if (data) + td->hwBE = ohci_cpu_to_le32 ((unsigned long)(data + len - 1)); + else + td->hwBE = 0; + td->hwNextTD = ohci_cpu_to_le32 ((unsigned long)td_pt); + + /* append to queue */ + td->ed->hwTailP = td->hwNextTD; +} + +/*-------------------------------------------------------------------------*/ + +/* prepare all TDs of a transfer */ +static void td_submit_job (struct usb_device *dev, unsigned long pipe, void *buffer, + int transfer_len, struct devrequest *setup, urb_priv_t *urb, int interval) +{ + ohci_t *ohci = &gohci; + int data_len = transfer_len; + void *data; + int cnt = 0; + __u32 info = 0; + unsigned int toggle = 0; + + /* OHCI handles the DATA-toggles itself, we just use the USB-toggle bits for reseting */ + if(usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe))) { + toggle = TD_T_TOGGLE; + } else { + toggle = TD_T_DATA0; + usb_settoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe), 1); + } + urb->td_cnt = 0; + if (data_len) + data = buffer; + else + data = 0; + + switch (usb_pipetype (pipe)) { + case PIPE_BULK: + info = usb_pipeout (pipe)? + TD_CC | TD_DP_OUT : TD_CC | TD_DP_IN ; + while(data_len > 4096) { + td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle), data, 4096, dev, cnt, urb); + data += 4096; data_len -= 4096; cnt++; + } + info = usb_pipeout (pipe)? + TD_CC | TD_DP_OUT : TD_CC | TD_R | TD_DP_IN ; + td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle), data, data_len, dev, cnt, urb); + cnt++; + + if (!ohci->sleeping) + writel (OHCI_BLF, &ohci->regs->cmdstatus); /* start bulk list */ + break; + + case PIPE_CONTROL: + info = TD_CC | TD_DP_SETUP | TD_T_DATA0; + td_fill (ohci, info, setup, 8, dev, cnt++, urb); + if (data_len > 0) { + info = usb_pipeout (pipe)? + TD_CC | TD_R | TD_DP_OUT | TD_T_DATA1 : TD_CC | TD_R | TD_DP_IN | TD_T_DATA1; + /* NOTE: mishandles transfers >8K, some >4K */ + td_fill (ohci, info, data, data_len, dev, cnt++, urb); + } + info = usb_pipeout (pipe)? + TD_CC | TD_DP_IN | TD_T_DATA1: TD_CC | TD_DP_OUT | TD_T_DATA1; + td_fill (ohci, info, data, 0, dev, cnt++, urb); + if (!ohci->sleeping) + writel (OHCI_CLF, &ohci->regs->cmdstatus); /* start Control list */ + break; + } + if (urb->length != cnt) + dbg("TD LENGTH %d != CNT %d", urb->length, cnt); +} + +/*-------------------------------------------------------------------------* + * Done List handling functions + *-------------------------------------------------------------------------*/ + + +/* calculate the transfer length and update the urb */ + +static void dl_transfer_length(td_t * td) +{ + __u32 tdINFO, tdBE, tdCBP; + urb_priv_t *lurb_priv = &urb_priv; + + tdINFO = ohci_cpu_to_le32 (td->hwINFO); + tdBE = ohci_cpu_to_le32 (td->hwBE); + tdCBP = ohci_cpu_to_le32 (td->hwCBP); + + + if (!(usb_pipetype (lurb_priv->pipe) == PIPE_CONTROL && + ((td->index == 0) || (td->index == lurb_priv->length - 1)))) { + if (tdBE != 0) { + if (td->hwCBP == 0) + lurb_priv->actual_length += tdBE - td->data + 1; + else + lurb_priv->actual_length += tdCBP - td->data; + } + } +} + +/*-------------------------------------------------------------------------*/ + +/* replies to the request have to be on a FIFO basis so + * we reverse the reversed done-list */ + +static td_t * dl_reverse_done_list (ohci_t *ohci) +{ + __u32 td_list_hc; + td_t *td_rev = NULL; + td_t *td_list = NULL; + urb_priv_t *lurb_priv = NULL; + + td_list_hc = ohci_cpu_to_le32 (ohci->hcca->done_head) & 0xfffffff0; + ohci->hcca->done_head = 0; + + while (td_list_hc) { + td_list = (td_t *)td_list_hc; + + if (TD_CC_GET (ohci_cpu_to_le32 (td_list->hwINFO))) { + lurb_priv = &urb_priv; + dbg(" USB-error/status: %x : %p", + TD_CC_GET (ohci_cpu_to_le32 (td_list->hwINFO)), td_list); + if (td_list->ed->hwHeadP & ohci_cpu_to_le32 (0x1)) { + if (lurb_priv && ((td_list->index + 1) < lurb_priv->length)) { + td_list->ed->hwHeadP = + (lurb_priv->td[lurb_priv->length - 1]->hwNextTD & ohci_cpu_to_le32 (0xfffffff0)) | + (td_list->ed->hwHeadP & ohci_cpu_to_le32 (0x2)); + lurb_priv->td_cnt += lurb_priv->length - td_list->index - 1; + } else + td_list->ed->hwHeadP &= ohci_cpu_to_le32 (0xfffffff2); + } +#ifdef CONFIG_MPC5200 + td_list->hwNextTD = 0; +#endif + } + + td_list->next_dl_td = td_rev; + td_rev = td_list; + td_list_hc = ohci_cpu_to_le32 (td_list->hwNextTD) & 0xfffffff0; + } + return td_list; +} + +/*-------------------------------------------------------------------------*/ + +/* td done list */ +static int dl_done_list (ohci_t *ohci, td_t *td_list) +{ + td_t *td_list_next = NULL; + ed_t *ed; + int cc = 0; + int stat = 0; + /* urb_t *urb; */ + urb_priv_t *lurb_priv; + __u32 tdINFO, edHeadP, edTailP; + + while (td_list) { + td_list_next = td_list->next_dl_td; + + lurb_priv = &urb_priv; + tdINFO = ohci_cpu_to_le32 (td_list->hwINFO); + + ed = td_list->ed; + + dl_transfer_length(td_list); + + /* error code of transfer */ + cc = TD_CC_GET (tdINFO); + if (++(lurb_priv->td_cnt) == lurb_priv->length) { + if ((ed->state & (ED_OPER | ED_UNLINK)) + && (lurb_priv->state != URB_DEL)) { + dbg("ConditionCode %#x", cc); + stat = cc_to_error[cc]; + urb_finished = 1; + } + } + + if (ed->state != ED_NEW) { + edHeadP = ohci_cpu_to_le32 (ed->hwHeadP) & 0xfffffff0; + edTailP = ohci_cpu_to_le32 (ed->hwTailP); + + /* unlink eds if they are not busy */ + if ((edHeadP == edTailP) && (ed->state == ED_OPER)) + ep_unlink (ohci, ed); + } + + td_list = td_list_next; + } + return stat; +} + +/*-------------------------------------------------------------------------* + * Virtual Root Hub + *-------------------------------------------------------------------------*/ + +/* Device descriptor */ +static __u8 root_hub_dev_des[] = +{ + 0x12, /* __u8 bLength; */ + 0x01, /* __u8 bDescriptorType; Device */ + 0x10, /* __u16 bcdUSB; v1.1 */ + 0x01, + 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */ + 0x00, /* __u8 bDeviceSubClass; */ + 0x00, /* __u8 bDeviceProtocol; */ + 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */ + 0x00, /* __u16 idVendor; */ + 0x00, + 0x00, /* __u16 idProduct; */ + 0x00, + 0x00, /* __u16 bcdDevice; */ + 0x00, + 0x00, /* __u8 iManufacturer; */ + 0x01, /* __u8 iProduct; */ + 0x00, /* __u8 iSerialNumber; */ + 0x01 /* __u8 bNumConfigurations; */ +}; + + +/* Configuration descriptor */ +static __u8 root_hub_config_des[] = +{ + 0x09, /* __u8 bLength; */ + 0x02, /* __u8 bDescriptorType; Configuration */ + 0x19, /* __u16 wTotalLength; */ + 0x00, + 0x01, /* __u8 bNumInterfaces; */ + 0x01, /* __u8 bConfigurationValue; */ + 0x00, /* __u8 iConfiguration; */ + 0x40, /* __u8 bmAttributes; + Bit 7: Bus-powered, 6: Self-powered, 5 Remote-wakwup, 4..0: resvd */ + 0x00, /* __u8 MaxPower; */ + + /* interface */ + 0x09, /* __u8 if_bLength; */ + 0x04, /* __u8 if_bDescriptorType; Interface */ + 0x00, /* __u8 if_bInterfaceNumber; */ + 0x00, /* __u8 if_bAlternateSetting; */ + 0x01, /* __u8 if_bNumEndpoints; */ + 0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */ + 0x00, /* __u8 if_bInterfaceSubClass; */ + 0x00, /* __u8 if_bInterfaceProtocol; */ + 0x00, /* __u8 if_iInterface; */ + + /* endpoint */ + 0x07, /* __u8 ep_bLength; */ + 0x05, /* __u8 ep_bDescriptorType; Endpoint */ + 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */ + 0x03, /* __u8 ep_bmAttributes; Interrupt */ + 0x02, /* __u16 ep_wMaxPacketSize; ((MAX_ROOT_PORTS + 1) / 8 */ + 0x00, + 0xff /* __u8 ep_bInterval; 255 ms */ +}; + +static unsigned char root_hub_str_index0[] = +{ + 0x04, /* __u8 bLength; */ + 0x03, /* __u8 bDescriptorType; String-descriptor */ + 0x09, /* __u8 lang ID */ + 0x04, /* __u8 lang ID */ +}; + +static unsigned char root_hub_str_index1[] = +{ + 28, /* __u8 bLength; */ + 0x03, /* __u8 bDescriptorType; String-descriptor */ + 'O', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'H', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'C', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'I', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + ' ', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'R', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'o', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'o', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 't', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + ' ', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'H', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'u', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'b', /* __u8 Unicode */ + 0, /* __u8 Unicode */ +}; + +/* Hub class-specific descriptor is constructed dynamically */ + + +/*-------------------------------------------------------------------------*/ + +#define OK(x) len = (x); break +#ifdef DEBUG +#define WR_RH_STAT(x) {info("WR:status %#8x", (x));writel((x), &gohci.regs->roothub.status);} +#define WR_RH_PORTSTAT(x) {info("WR:portstatus[%d] %#8x", wIndex-1, (x));writel((x), &gohci.regs->roothub.portstatus[wIndex-1]);} +#else +#define WR_RH_STAT(x) writel((x), &gohci.regs->roothub.status) +#define WR_RH_PORTSTAT(x) writel((x), &gohci.regs->roothub.portstatus[wIndex-1]) +#endif +#define RD_RH_STAT roothub_status(&gohci) +#define RD_RH_PORTSTAT roothub_portstatus(&gohci,wIndex-1) + +/* request to virtual root hub */ + +int rh_check_port_status(ohci_t *controller) +{ + __u32 temp, ndp, i; + int res; + + res = -1; + temp = roothub_a (controller); + ndp = (temp & RH_A_NDP); + for (i = 0; i < ndp; i++) { + temp = roothub_portstatus (controller, i); + /* check for a device disconnect */ + if (((temp & (RH_PS_PESC | RH_PS_CSC)) == + (RH_PS_PESC | RH_PS_CSC)) && + ((temp & RH_PS_CCS) == 0)) { + res = i; + break; + } + } + return res; +} + +static int ohci_submit_rh_msg(struct usb_device *dev, unsigned long pipe, + void *buffer, int transfer_len, struct devrequest *cmd) +{ + void * data = buffer; + int leni = transfer_len; + int len = 0; + int stat = 0; + __u32 datab[4]; + __u8 *data_buf = (__u8 *)datab; + __u16 bmRType_bReq; + __u16 wValue; + __u16 wIndex; + __u16 wLength; + +#ifdef DEBUG +urb_priv.actual_length = 0; +pkt_print(dev, pipe, buffer, transfer_len, cmd, "SUB(rh)", usb_pipein(pipe)); +#endif + if ((pipe & PIPE_INTERRUPT) == PIPE_INTERRUPT) { + info("Root-Hub submit IRQ: NOT implemented"); + return 0; + } + + bmRType_bReq = cmd->requesttype | (cmd->request << 8); + wValue = m16_swap (cmd->value); + wIndex = m16_swap (cmd->index); + wLength = m16_swap (cmd->length); + + info("Root-Hub: adr: %2x cmd(%1x): %08x %04x %04x %04x", + dev->devnum, 8, bmRType_bReq, wValue, wIndex, wLength); + + switch (bmRType_bReq) { + /* Request Destination: + without flags: Device, + RH_INTERFACE: interface, + RH_ENDPOINT: endpoint, + RH_CLASS means HUB here, + RH_OTHER | RH_CLASS almost ever means HUB_PORT here + */ + + case RH_GET_STATUS: + *(__u16 *) data_buf = m16_swap (1); OK (2); + case RH_GET_STATUS | RH_INTERFACE: + *(__u16 *) data_buf = m16_swap (0); OK (2); + case RH_GET_STATUS | RH_ENDPOINT: + *(__u16 *) data_buf = m16_swap (0); OK (2); + case RH_GET_STATUS | RH_CLASS: + *(__u32 *) data_buf = m32_swap ( + RD_RH_STAT & ~(RH_HS_CRWE | RH_HS_DRWE)); + OK (4); + case RH_GET_STATUS | RH_OTHER | RH_CLASS: + *(__u32 *) data_buf = m32_swap (RD_RH_PORTSTAT); OK (4); + + case RH_CLEAR_FEATURE | RH_ENDPOINT: + switch (wValue) { + case (RH_ENDPOINT_STALL): OK (0); + } + break; + + case RH_CLEAR_FEATURE | RH_CLASS: + switch (wValue) { + case RH_C_HUB_LOCAL_POWER: + OK(0); + case (RH_C_HUB_OVER_CURRENT): + WR_RH_STAT(RH_HS_OCIC); OK (0); + } + break; + + case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS: + switch (wValue) { + case (RH_PORT_ENABLE): + WR_RH_PORTSTAT (RH_PS_CCS ); OK (0); + case (RH_PORT_SUSPEND): + WR_RH_PORTSTAT (RH_PS_POCI); OK (0); + case (RH_PORT_POWER): + WR_RH_PORTSTAT (RH_PS_LSDA); OK (0); + case (RH_C_PORT_CONNECTION): + WR_RH_PORTSTAT (RH_PS_CSC ); OK (0); + case (RH_C_PORT_ENABLE): + WR_RH_PORTSTAT (RH_PS_PESC); OK (0); + case (RH_C_PORT_SUSPEND): + WR_RH_PORTSTAT (RH_PS_PSSC); OK (0); + case (RH_C_PORT_OVER_CURRENT): + WR_RH_PORTSTAT (RH_PS_OCIC); OK (0); + case (RH_C_PORT_RESET): + WR_RH_PORTSTAT (RH_PS_PRSC); OK (0); + } + break; + + case RH_SET_FEATURE | RH_OTHER | RH_CLASS: + switch (wValue) { + case (RH_PORT_SUSPEND): + WR_RH_PORTSTAT (RH_PS_PSS ); OK (0); + case (RH_PORT_RESET): /* BUG IN HUP CODE *********/ + if (RD_RH_PORTSTAT & RH_PS_CCS) + WR_RH_PORTSTAT (RH_PS_PRS); + OK (0); + case (RH_PORT_POWER): + WR_RH_PORTSTAT (RH_PS_PPS ); OK (0); + case (RH_PORT_ENABLE): /* BUG IN HUP CODE *********/ + if (RD_RH_PORTSTAT & RH_PS_CCS) + WR_RH_PORTSTAT (RH_PS_PES ); + OK (0); + } + break; + + case RH_SET_ADDRESS: gohci.rh.devnum = wValue; OK(0); + + case RH_GET_DESCRIPTOR: + switch ((wValue & 0xff00) >> 8) { + case (0x01): /* device descriptor */ + len = min_t(unsigned int, + leni, + min_t(unsigned int, + sizeof (root_hub_dev_des), + wLength)); + data_buf = root_hub_dev_des; OK(len); + case (0x02): /* configuration descriptor */ + len = min_t(unsigned int, + leni, + min_t(unsigned int, + sizeof (root_hub_config_des), + wLength)); + data_buf = root_hub_config_des; OK(len); + case (0x03): /* string descriptors */ + if(wValue==0x0300) { + len = min_t(unsigned int, + leni, + min_t(unsigned int, + sizeof (root_hub_str_index0), + wLength)); + data_buf = root_hub_str_index0; + OK(len); + } + if(wValue==0x0301) { + len = min_t(unsigned int, + leni, + min_t(unsigned int, + sizeof (root_hub_str_index1), + wLength)); + data_buf = root_hub_str_index1; + OK(len); + } + default: + stat = USB_ST_STALLED; + } + break; + + case RH_GET_DESCRIPTOR | RH_CLASS: + { + __u32 temp = roothub_a (&gohci); + + data_buf [0] = 9; /* min length; */ + data_buf [1] = 0x29; + data_buf [2] = temp & RH_A_NDP; + data_buf [3] = 0; + if (temp & RH_A_PSM) /* per-port power switching? */ + data_buf [3] |= 0x1; + if (temp & RH_A_NOCP) /* no overcurrent reporting? */ + data_buf [3] |= 0x10; + else if (temp & RH_A_OCPM) /* per-port overcurrent reporting? */ + data_buf [3] |= 0x8; + + /* corresponds to data_buf[4-7] */ + datab [1] = 0; + data_buf [5] = (temp & RH_A_POTPGT) >> 24; + temp = roothub_b (&gohci); + data_buf [7] = temp & RH_B_DR; + if (data_buf [2] < 7) { + data_buf [8] = 0xff; + } else { + data_buf [0] += 2; + data_buf [8] = (temp & RH_B_DR) >> 8; + data_buf [10] = data_buf [9] = 0xff; + } + + len = min_t(unsigned int, leni, + min_t(unsigned int, data_buf [0], wLength)); + OK (len); + } + + case RH_GET_CONFIGURATION: *(__u8 *) data_buf = 0x01; OK (1); + + case RH_SET_CONFIGURATION: WR_RH_STAT (0x10000); OK (0); + + default: + dbg ("unsupported root hub command"); + stat = USB_ST_STALLED; + } + +#ifdef DEBUG + ohci_dump_roothub (&gohci, 1); +#endif + + len = min_t(int, len, leni); + if (data != data_buf) + memcpy (data, data_buf, len); + dev->act_len = len; + dev->status = stat; + +#ifdef DEBUG + if (transfer_len) + urb_priv.actual_length = transfer_len; + pkt_print(dev, pipe, buffer, transfer_len, cmd, "RET(rh)", 0/*usb_pipein(pipe)*/); +#endif + + return stat; +} + +/*-------------------------------------------------------------------------*/ + +/* common code for handling submit messages - used for all but root hub */ +/* accesses. */ +int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int transfer_len, struct devrequest *setup, int interval) +{ + int stat = 0; + int maxsize = usb_maxpacket(dev, pipe); + int timeout; + + /* device pulled? Shortcut the action. */ + if (devgone == dev) { + dev->status = USB_ST_CRC_ERR; + return 0; + } + +#ifdef DEBUG + urb_priv.actual_length = 0; + pkt_print(dev, pipe, buffer, transfer_len, setup, "SUB", usb_pipein(pipe)); +#endif + if (!maxsize) { + err("submit_common_message: pipesize for pipe %lx is zero", + pipe); + return -1; + } + + if (sohci_submit_job(dev, pipe, buffer, transfer_len, setup, interval) < 0) { + err("sohci_submit_job failed"); + return -1; + } + + /* allow more time for a BULK device to react - some are slow */ +#define BULK_TO 5000 /* timeout in milliseconds */ + if (usb_pipetype (pipe) == PIPE_BULK) + timeout = BULK_TO; + else + timeout = 100; + + /* wait for it to complete */ + for (;;) { + /* check whether the controller is done */ + stat = hc_interrupt(); + if (stat < 0) { + stat = USB_ST_CRC_ERR; + break; + } + + /* NOTE: since we are not interrupt driven in U-Boot and always + * handle only one URB at a time, we cannot assume the + * transaction finished on the first successful return from + * hc_interrupt().. unless the flag for current URB is set, + * meaning that all TD's to/from device got actually + * transferred and processed. If the current URB is not + * finished we need to re-iterate this loop so as + * hc_interrupt() gets called again as there needs to be some + * more TD's to process still */ + if ((stat >= 0) && (stat != 0xff) && (urb_finished)) { + /* 0xff is returned for an SF-interrupt */ + break; + } + + if (--timeout) { + wait_ms(1); + if (!urb_finished) + dbg("\%"); + + } else { + err("CTL:TIMEOUT "); + dbg("submit_common_msg: TO status %x\n", stat); + stat = USB_ST_CRC_ERR; + urb_finished = 1; + break; + } + } +#if 0 + /* we got an Root Hub Status Change interrupt */ + if (got_rhsc) { +#ifdef DEBUG + ohci_dump_roothub (&gohci, 1); +#endif + got_rhsc = 0; + /* abuse timeout */ + timeout = rh_check_port_status(&gohci); + if (timeout >= 0) { +#if 0 /* this does nothing useful, but leave it here in case that changes */ + /* the called routine adds 1 to the passed value */ + usb_hub_port_connect_change(gohci.rh.dev, timeout - 1); +#endif + /* + * XXX + * This is potentially dangerous because it assumes + * that only one device is ever plugged in! + */ + devgone = dev; + } + } +#endif + + dev->status = stat; + dev->act_len = transfer_len; + +#ifdef DEBUG + pkt_print(dev, pipe, buffer, transfer_len, setup, "RET(ctlr)", usb_pipein(pipe)); +#endif + + /* free TDs in urb_priv */ + urb_free_priv (&urb_priv); + return 0; +} + +/* submit routines called from usb.c */ +int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int transfer_len) +{ + info("submit_bulk_msg"); + return submit_common_msg(dev, pipe, buffer, transfer_len, NULL, 0); +} + +int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int transfer_len, struct devrequest *setup) +{ + int maxsize = usb_maxpacket(dev, pipe); + + info("submit_control_msg"); +#ifdef DEBUG + urb_priv.actual_length = 0; + pkt_print(dev, pipe, buffer, transfer_len, setup, "SUB", usb_pipein(pipe)); +#endif + if (!maxsize) { + err("submit_control_message: pipesize for pipe %lx is zero", + pipe); + return -1; + } + if (((pipe >> 8) & 0x7f) == gohci.rh.devnum) { + gohci.rh.dev = dev; + /* root hub - redirect */ + return ohci_submit_rh_msg(dev, pipe, buffer, transfer_len, + setup); + } + + return submit_common_msg(dev, pipe, buffer, transfer_len, setup, 0); +} + +int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int transfer_len, int interval) +{ + info("submit_int_msg"); + return -1; +} + +/*-------------------------------------------------------------------------* + * HC functions + *-------------------------------------------------------------------------*/ + +/* reset the HC and BUS */ + +static int hc_reset (ohci_t *ohci) +{ + int timeout = 30; + int smm_timeout = 50; /* 0,5 sec */ + + if (readl (&ohci->regs->control) & OHCI_CTRL_IR) { /* SMM owns the HC */ + writel (OHCI_OCR, &ohci->regs->cmdstatus); /* request ownership */ + info("USB HC TakeOver from SMM"); + while (readl (&ohci->regs->control) & OHCI_CTRL_IR) { + wait_ms (10); + if (--smm_timeout == 0) { + err("USB HC TakeOver failed!"); + return -1; + } + } + } + + /* Disable HC interrupts */ + writel (OHCI_INTR_MIE, &ohci->regs->intrdisable); + + dbg("USB HC reset_hc usb-%s: ctrl = 0x%X ;", + ohci->slot_name, + readl (&ohci->regs->control)); + + /* Reset USB (needed by some controllers) */ + ohci->hc_control = 0; + writel (ohci->hc_control, &ohci->regs->control); + + /* HC Reset requires max 10 us delay */ + writel (OHCI_HCR, &ohci->regs->cmdstatus); + while ((readl (&ohci->regs->cmdstatus) & OHCI_HCR) != 0) { + if (--timeout == 0) { + err("USB HC reset timed out!"); + return -1; + } + udelay (1); + } + return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* Start an OHCI controller, set the BUS operational + * enable interrupts + * connect the virtual root hub */ + +static int hc_start (ohci_t * ohci) +{ + __u32 mask; + unsigned int fminterval; + + ohci->disabled = 1; + + /* Tell the controller where the control and bulk lists are + * The lists are empty now. */ + + writel (0, &ohci->regs->ed_controlhead); + writel (0, &ohci->regs->ed_bulkhead); + + writel ((__u32)ohci->hcca, &ohci->regs->hcca); /* a reset clears this */ + + fminterval = 0x2edf; + writel ((fminterval * 9) / 10, &ohci->regs->periodicstart); + fminterval |= ((((fminterval - 210) * 6) / 7) << 16); + writel (fminterval, &ohci->regs->fminterval); + writel (0x628, &ohci->regs->lsthresh); + + /* start controller operations */ + ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER; + ohci->disabled = 0; + writel (ohci->hc_control, &ohci->regs->control); + + /* disable all interrupts */ + mask = (OHCI_INTR_SO | OHCI_INTR_WDH | OHCI_INTR_SF | OHCI_INTR_RD | + OHCI_INTR_UE | OHCI_INTR_FNO | OHCI_INTR_RHSC | + OHCI_INTR_OC | OHCI_INTR_MIE); + writel (mask, &ohci->regs->intrdisable); + /* clear all interrupts */ + mask &= ~OHCI_INTR_MIE; + writel (mask, &ohci->regs->intrstatus); + /* Choose the interrupts we care about now - but w/o MIE */ + mask = OHCI_INTR_RHSC | OHCI_INTR_UE | OHCI_INTR_WDH | OHCI_INTR_SO; + writel (mask, &ohci->regs->intrenable); + +#ifdef OHCI_USE_NPS + /* required for AMD-756 and some Mac platforms */ + writel ((roothub_a (ohci) | RH_A_NPS) & ~RH_A_PSM, + &ohci->regs->roothub.a); + writel (RH_HS_LPSC, &ohci->regs->roothub.status); +#endif /* OHCI_USE_NPS */ + +#define mdelay(n) ({unsigned long msec=(n); while (msec--) udelay(1000);}) + /* POTPGT delay is bits 24-31, in 2 ms units. */ + mdelay ((roothub_a (ohci) >> 23) & 0x1fe); + + /* connect the virtual root hub */ + ohci->rh.devnum = 0; + + return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* an interrupt happens */ + +static int +hc_interrupt (void) +{ + ohci_t *ohci = &gohci; + struct ohci_regs *regs = ohci->regs; + int ints; + int stat = -1; + + if ((ohci->hcca->done_head != 0) && + !(ohci_cpu_to_le32(ohci->hcca->done_head) & 0x01)) { + + ints = OHCI_INTR_WDH; + + } else if ((ints = readl (®s->intrstatus)) == ~(u32)0) { + ohci->disabled++; + err ("%s device removed!", ohci->slot_name); + return -1; + + } else if ((ints &= readl (®s->intrenable)) == 0) { + dbg("hc_interrupt: returning..\n"); + return 0xff; + } + + /* dbg("Interrupt: %x frame: %x", ints, le16_to_cpu (ohci->hcca->frame_no)); */ + + if (ints & OHCI_INTR_RHSC) { + got_rhsc = 1; + stat = 0xff; + } + + if (ints & OHCI_INTR_UE) { + ohci->disabled++; + err ("OHCI Unrecoverable Error, controller usb-%s disabled", + ohci->slot_name); + /* e.g. due to PCI Master/Target Abort */ + +#ifdef DEBUG + ohci_dump (ohci, 1); +#endif + /* FIXME: be optimistic, hope that bug won't repeat often. */ + /* Make some non-interrupt context restart the controller. */ + /* Count and limit the retries though; either hardware or */ + /* software errors can go forever... */ + hc_reset (ohci); + return -1; + } + + if (ints & OHCI_INTR_WDH) { + writel (OHCI_INTR_WDH, ®s->intrdisable); + stat = dl_done_list (&gohci, dl_reverse_done_list (&gohci)); + writel (OHCI_INTR_WDH, ®s->intrenable); + } + + if (ints & OHCI_INTR_SO) { + dbg("USB Schedule overrun\n"); + writel (OHCI_INTR_SO, ®s->intrenable); + stat = -1; + } + + /* FIXME: this assumes SOF (1/ms) interrupts don't get lost... */ + if (ints & OHCI_INTR_SF) { + unsigned int frame = ohci_cpu_to_le16 (ohci->hcca->frame_no) & 1; + wait_ms(1); + writel (OHCI_INTR_SF, ®s->intrdisable); + if (ohci->ed_rm_list[frame] != NULL) + writel (OHCI_INTR_SF, ®s->intrenable); + stat = 0xff; + } + + writel (ints, ®s->intrstatus); + return stat; +} + +/*-------------------------------------------------------------------------*/ + +/*-------------------------------------------------------------------------*/ + +/* De-allocate all resources.. */ + +static void hc_release_ohci (ohci_t *ohci) +{ + dbg ("USB HC release ohci usb-%s", ohci->slot_name); + + if (!ohci->disabled) + hc_reset (ohci); +} + +/*-------------------------------------------------------------------------*/ + +/* + * low level initalisation routine, called from usb.c + */ +static char ohci_inited = 0; + +int usb_lowlevel_init(void) +{ + memset (&gohci, 0, sizeof (ohci_t)); + memset (&urb_priv, 0, sizeof (urb_priv_t)); + + /* align the storage */ + if ((__u32)&ghcca[0] & 0xff) { + err("HCCA not aligned!!"); + return -1; + } + phcca = &ghcca[0]; + info("aligned ghcca %p", phcca); + memset(&ohci_dev, 0, sizeof(struct ohci_device)); + if ((__u32)&ohci_dev.ed[0] & 0x7) { + err("EDs not aligned!!"); + return -1; + } + memset(gtd, 0, sizeof(td_t) * (NUM_TD + 1)); + if ((__u32)gtd & 0x7) { + err("TDs not aligned!!"); + return -1; + } + ptd = gtd; + gohci.hcca = phcca; + memset (phcca, 0, sizeof (struct ohci_hcca)); + + gohci.disabled = 1; + gohci.sleeping = 0; + gohci.irq = -1; + gohci.regs = (struct ohci_regs *)(CFG_PERIPHERAL_BASE | 0x1000); + + gohci.flags = 0; + gohci.slot_name = "ppc440"; + + if (hc_reset (&gohci) < 0) { + hc_release_ohci (&gohci); + return -1; + } + + if (hc_start (&gohci) < 0) { + err ("can't start usb-%s", gohci.slot_name); + hc_release_ohci (&gohci); + return -1; + } + +#ifdef DEBUG + ohci_dump (&gohci, 1); +#endif + ohci_inited = 1; + urb_finished = 1; + + /* init the device driver */ + usb_dev_init(); + + return 0; +} + +int usb_lowlevel_stop(void) +{ + /* this gets called really early - before the controller has */ + /* even been initialized! */ + if (!ohci_inited) + return 0; + /* TODO release any interrupts, etc. */ + /* call hc_release_ohci() here ? */ + hc_reset (&gohci); + return 0; +} + +#endif /* CONFIG_USB_OHCI */ diff --git a/cpu/ppc4xx/usb_ohci.h b/cpu/ppc4xx/usb_ohci.h new file mode 100755 index 0000000..706e05e --- /dev/null +++ b/cpu/ppc4xx/usb_ohci.h @@ -0,0 +1,410 @@ +/* + * URB OHCI HCD (Host Controller Driver) for USB. + * + * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at> + * (C) Copyright 2000-2001 David Brownell <dbrownell@users.sourceforge.net> + * + * usb-ohci.h + */ + +static int cc_to_error[16] = { + +/* mapping of the OHCI CC status to error codes */ + /* No Error */ 0, + /* CRC Error */ USB_ST_CRC_ERR, + /* Bit Stuff */ USB_ST_BIT_ERR, + /* Data Togg */ USB_ST_CRC_ERR, + /* Stall */ USB_ST_STALLED, + /* DevNotResp */ -1, + /* PIDCheck */ USB_ST_BIT_ERR, + /* UnExpPID */ USB_ST_BIT_ERR, + /* DataOver */ USB_ST_BUF_ERR, + /* DataUnder */ USB_ST_BUF_ERR, + /* reservd */ -1, + /* reservd */ -1, + /* BufferOver */ USB_ST_BUF_ERR, + /* BuffUnder */ USB_ST_BUF_ERR, + /* Not Access */ -1, + /* Not Access */ -1 +}; + +/* ED States */ + +#define ED_NEW 0x00 +#define ED_UNLINK 0x01 +#define ED_OPER 0x02 +#define ED_DEL 0x04 +#define ED_URB_DEL 0x08 + +/* usb_ohci_ed */ +struct ed { + __u32 hwINFO; + __u32 hwTailP; + __u32 hwHeadP; + __u32 hwNextED; + + struct ed *ed_prev; + __u8 int_period; + __u8 int_branch; + __u8 int_load; + __u8 int_interval; + __u8 state; + __u8 type; + __u16 last_iso; + struct ed *ed_rm_list; + + struct usb_device *usb_dev; + __u32 unused[3]; +} __attribute((aligned(16))); +typedef struct ed ed_t; + +/* TD info field */ +#define TD_CC 0xf0000000 +#define TD_CC_GET(td_p) ((td_p >>28) & 0x0f) +#define TD_CC_SET(td_p, cc) (td_p) = ((td_p) & 0x0fffffff) | (((cc) & 0x0f) << 28) +#define TD_EC 0x0C000000 +#define TD_T 0x03000000 +#define TD_T_DATA0 0x02000000 +#define TD_T_DATA1 0x03000000 +#define TD_T_TOGGLE 0x00000000 +#define TD_R 0x00040000 +#define TD_DI 0x00E00000 +#define TD_DI_SET(X) (((X) & 0x07)<< 21) +#define TD_DP 0x00180000 +#define TD_DP_SETUP 0x00000000 +#define TD_DP_IN 0x00100000 +#define TD_DP_OUT 0x00080000 + +#define TD_ISO 0x00010000 +#define TD_DEL 0x00020000 + +/* CC Codes */ +#define TD_CC_NOERROR 0x00 +#define TD_CC_CRC 0x01 +#define TD_CC_BITSTUFFING 0x02 +#define TD_CC_DATATOGGLEM 0x03 +#define TD_CC_STALL 0x04 +#define TD_DEVNOTRESP 0x05 +#define TD_PIDCHECKFAIL 0x06 +#define TD_UNEXPECTEDPID 0x07 +#define TD_DATAOVERRUN 0x08 +#define TD_DATAUNDERRUN 0x09 +#define TD_BUFFEROVERRUN 0x0C +#define TD_BUFFERUNDERRUN 0x0D +#define TD_NOTACCESSED 0x0F + +#define MAXPSW 1 + +struct td { + __u32 hwINFO; + __u32 hwCBP; /* Current Buffer Pointer */ + __u32 hwNextTD; /* Next TD Pointer */ + __u32 hwBE; /* Memory Buffer End Pointer */ + + __u16 hwPSW[MAXPSW]; + __u8 unused; + __u8 index; + struct ed *ed; + struct td *next_dl_td; + struct usb_device *usb_dev; + int transfer_len; + __u32 data; + + __u32 unused2[2]; +} __attribute((aligned(32))); +typedef struct td td_t; + +#define OHCI_ED_SKIP (1 << 14) + +/* + * The HCCA (Host Controller Communications Area) is a 256 byte + * structure defined in the OHCI spec. that the host controller is + * told the base address of. It must be 256-byte aligned. + */ + +#define NUM_INTS 32 /* part of the OHCI standard */ +struct ohci_hcca { + __u32 int_table[NUM_INTS]; /* Interrupt ED table */ +#if defined(CONFIG_MPC5200) + __u16 pad1; /* set to 0 on each frame_no change */ + __u16 frame_no; /* current frame number */ +#else + __u16 frame_no; /* current frame number */ + __u16 pad1; /* set to 0 on each frame_no change */ +#endif + __u32 done_head; /* info returned for an interrupt */ + u8 reserved_for_hc[116]; +} __attribute((aligned(256))); + +/* + * Maximum number of root hub ports. + */ +#define MAX_ROOT_PORTS 15 /* maximum OHCI root hub ports */ + +/* + * This is the structure of the OHCI controller's memory mapped I/O + * region. This is Memory Mapped I/O. You must use the readl() and + * writel() macros defined in asm/io.h to access these!! + */ +struct ohci_regs { + /* control and status registers */ + __u32 revision; + __u32 control; + __u32 cmdstatus; + __u32 intrstatus; + __u32 intrenable; + __u32 intrdisable; + /* memory pointers */ + __u32 hcca; + __u32 ed_periodcurrent; + __u32 ed_controlhead; + __u32 ed_controlcurrent; + __u32 ed_bulkhead; + __u32 ed_bulkcurrent; + __u32 donehead; + /* frame counters */ + __u32 fminterval; + __u32 fmremaining; + __u32 fmnumber; + __u32 periodicstart; + __u32 lsthresh; + /* Root hub ports */ + struct ohci_roothub_regs { + __u32 a; + __u32 b; + __u32 status; + __u32 portstatus[MAX_ROOT_PORTS]; + } roothub; +} __attribute((aligned(32))); + +/* OHCI CONTROL AND STATUS REGISTER MASKS */ + +/* + * HcControl (control) register masks + */ +#define OHCI_CTRL_CBSR (3 << 0) /* control/bulk service ratio */ +#define OHCI_CTRL_PLE (1 << 2) /* periodic list enable */ +#define OHCI_CTRL_IE (1 << 3) /* isochronous enable */ +#define OHCI_CTRL_CLE (1 << 4) /* control list enable */ +#define OHCI_CTRL_BLE (1 << 5) /* bulk list enable */ +#define OHCI_CTRL_HCFS (3 << 6) /* host controller functional state */ +#define OHCI_CTRL_IR (1 << 8) /* interrupt routing */ +#define OHCI_CTRL_RWC (1 << 9) /* remote wakeup connected */ +#define OHCI_CTRL_RWE (1 << 10) /* remote wakeup enable */ + +/* pre-shifted values for HCFS */ +# define OHCI_USB_RESET (0 << 6) +# define OHCI_USB_RESUME (1 << 6) +# define OHCI_USB_OPER (2 << 6) +# define OHCI_USB_SUSPEND (3 << 6) + +/* + * HcCommandStatus (cmdstatus) register masks + */ +#define OHCI_HCR (1 << 0) /* host controller reset */ +#define OHCI_CLF (1 << 1) /* control list filled */ +#define OHCI_BLF (1 << 2) /* bulk list filled */ +#define OHCI_OCR (1 << 3) /* ownership change request */ +#define OHCI_SOC (3 << 16) /* scheduling overrun count */ + +/* + * masks used with interrupt registers: + * HcInterruptStatus (intrstatus) + * HcInterruptEnable (intrenable) + * HcInterruptDisable (intrdisable) + */ +#define OHCI_INTR_SO (1 << 0) /* scheduling overrun */ +#define OHCI_INTR_WDH (1 << 1) /* writeback of done_head */ +#define OHCI_INTR_SF (1 << 2) /* start frame */ +#define OHCI_INTR_RD (1 << 3) /* resume detect */ +#define OHCI_INTR_UE (1 << 4) /* unrecoverable error */ +#define OHCI_INTR_FNO (1 << 5) /* frame number overflow */ +#define OHCI_INTR_RHSC (1 << 6) /* root hub status change */ +#define OHCI_INTR_OC (1 << 30) /* ownership change */ +#define OHCI_INTR_MIE (1 << 31) /* master interrupt enable */ + +/* Virtual Root HUB */ +struct virt_root_hub { + int devnum; /* Address of Root Hub endpoint */ + void *dev; /* was urb */ + void *int_addr; + int send; + int interval; +}; + +/* USB HUB CONSTANTS (not OHCI-specific; see hub.h) */ + +/* destination of request */ +#define RH_INTERFACE 0x01 +#define RH_ENDPOINT 0x02 +#define RH_OTHER 0x03 + +#define RH_CLASS 0x20 +#define RH_VENDOR 0x40 + +/* Requests: bRequest << 8 | bmRequestType */ +#define RH_GET_STATUS 0x0080 +#define RH_CLEAR_FEATURE 0x0100 +#define RH_SET_FEATURE 0x0300 +#define RH_SET_ADDRESS 0x0500 +#define RH_GET_DESCRIPTOR 0x0680 +#define RH_SET_DESCRIPTOR 0x0700 +#define RH_GET_CONFIGURATION 0x0880 +#define RH_SET_CONFIGURATION 0x0900 +#define RH_GET_STATE 0x0280 +#define RH_GET_INTERFACE 0x0A80 +#define RH_SET_INTERFACE 0x0B00 +#define RH_SYNC_FRAME 0x0C80 +/* Our Vendor Specific Request */ +#define RH_SET_EP 0x2000 + +/* Hub port features */ +#define RH_PORT_CONNECTION 0x00 +#define RH_PORT_ENABLE 0x01 +#define RH_PORT_SUSPEND 0x02 +#define RH_PORT_OVER_CURRENT 0x03 +#define RH_PORT_RESET 0x04 +#define RH_PORT_POWER 0x08 +#define RH_PORT_LOW_SPEED 0x09 + +#define RH_C_PORT_CONNECTION 0x10 +#define RH_C_PORT_ENABLE 0x11 +#define RH_C_PORT_SUSPEND 0x12 +#define RH_C_PORT_OVER_CURRENT 0x13 +#define RH_C_PORT_RESET 0x14 + +/* Hub features */ +#define RH_C_HUB_LOCAL_POWER 0x00 +#define RH_C_HUB_OVER_CURRENT 0x01 + +#define RH_DEVICE_REMOTE_WAKEUP 0x00 +#define RH_ENDPOINT_STALL 0x01 + +#define RH_ACK 0x01 +#define RH_REQ_ERR -1 +#define RH_NACK 0x00 + +/* OHCI ROOT HUB REGISTER MASKS */ + +/* roothub.portstatus [i] bits */ +#define RH_PS_CCS 0x00000001 /* current connect status */ +#define RH_PS_PES 0x00000002 /* port enable status */ +#define RH_PS_PSS 0x00000004 /* port suspend status */ +#define RH_PS_POCI 0x00000008 /* port over current indicator */ +#define RH_PS_PRS 0x00000010 /* port reset status */ +#define RH_PS_PPS 0x00000100 /* port power status */ +#define RH_PS_LSDA 0x00000200 /* low speed device attached */ +#define RH_PS_CSC 0x00010000 /* connect status change */ +#define RH_PS_PESC 0x00020000 /* port enable status change */ +#define RH_PS_PSSC 0x00040000 /* port suspend status change */ +#define RH_PS_OCIC 0x00080000 /* over current indicator change */ +#define RH_PS_PRSC 0x00100000 /* port reset status change */ + +/* roothub.status bits */ +#define RH_HS_LPS 0x00000001 /* local power status */ +#define RH_HS_OCI 0x00000002 /* over current indicator */ +#define RH_HS_DRWE 0x00008000 /* device remote wakeup enable */ +#define RH_HS_LPSC 0x00010000 /* local power status change */ +#define RH_HS_OCIC 0x00020000 /* over current indicator change */ +#define RH_HS_CRWE 0x80000000 /* clear remote wakeup enable */ + +/* roothub.b masks */ +#define RH_B_DR 0x0000ffff /* device removable flags */ +#define RH_B_PPCM 0xffff0000 /* port power control mask */ + +/* roothub.a masks */ +#define RH_A_NDP (0xff << 0) /* number of downstream ports */ +#define RH_A_PSM (1 << 8) /* power switching mode */ +#define RH_A_NPS (1 << 9) /* no power switching */ +#define RH_A_DT (1 << 10) /* device type (mbz) */ +#define RH_A_OCPM (1 << 11) /* over current protection mode */ +#define RH_A_NOCP (1 << 12) /* no over current protection */ +#define RH_A_POTPGT (0xff << 24) /* power on to power good time */ + +/* urb */ +#define N_URB_TD 48 +typedef struct { + ed_t *ed; + __u16 length; /* number of tds associated with this request */ + __u16 td_cnt; /* number of tds already serviced */ + int state; + unsigned long pipe; + int actual_length; + td_t *td[N_URB_TD]; /* list pointer to all corresponding TDs associated with this request */ +} urb_priv_t; +#define URB_DEL 1 + +/* + * This is the full ohci controller description + * + * Note how the "proper" USB information is just + * a subset of what the full implementation needs. (Linus) + */ + +typedef struct ohci { + struct ohci_hcca *hcca; /* hcca */ + /*dma_addr_t hcca_dma; */ + + int irq; + int disabled; /* e.g. got a UE, we're hung */ + int sleeping; + unsigned long flags; /* for HC bugs */ + + struct ohci_regs *regs; /* OHCI controller's memory */ + + ed_t *ed_rm_list[2]; /* lists of all endpoints to be removed */ + ed_t *ed_bulktail; /* last endpoint of bulk list */ + ed_t *ed_controltail; /* last endpoint of control list */ + int intrstatus; + __u32 hc_control; /* copy of the hc control reg */ + struct usb_device *dev[32]; + struct virt_root_hub rh; + + const char *slot_name; +} ohci_t; + +#define NUM_EDS 8 /* num of preallocated endpoint descriptors */ + +struct ohci_device { + ed_t ed[NUM_EDS]; + int ed_cnt; +}; + +/* hcd */ +/* endpoint */ +static int ep_link(ohci_t * ohci, ed_t * ed); +static int ep_unlink(ohci_t * ohci, ed_t * ed); +static ed_t *ep_add_ed(struct usb_device *usb_dev, unsigned long pipe); + +/*-------------------------------------------------------------------------*/ + +/* we need more TDs than EDs */ +#define NUM_TD 64 + +/* +1 so we can align the storage */ +td_t gtd[NUM_TD + 1]; +/* pointers to aligned storage */ +td_t *ptd; + +/* TDs ... */ +static inline struct td *td_alloc(struct usb_device *usb_dev) +{ + int i; + struct td *td; + + td = NULL; + for (i = 0; i < NUM_TD; i++) { + if (ptd[i].usb_dev == NULL) { + td = &ptd[i]; + td->usb_dev = usb_dev; + break; + } + } + + return td; +} + +static inline void ed_free(struct ed *ed) +{ + ed->usb_dev = NULL; +} diff --git a/cpu/ppc4xx/usbdev.c b/cpu/ppc4xx/usbdev.c new file mode 100755 index 0000000..8262c54 --- /dev/null +++ b/cpu/ppc4xx/usbdev.c @@ -0,0 +1,214 @@ +/*USB 1.1,2.0 device*/ + +#include <common.h> +#include <asm/processor.h> + +#ifdef CONFIG_440EP + +#include <usb.h> +#include "usbdev.h" +#include "vecnum.h" + +#define USB_DT_DEVICE 0x01 +#define USB_DT_CONFIG 0x02 +#define USB_DT_STRING 0x03 +#define USB_DT_INTERFACE 0x04 +#define USB_DT_ENDPOINT 0x05 + +int set_value = -1; + +void process_endpoints(unsigned short usb2d0_intrin) +{ + /*will hold the packet received */ + struct usb_device_descriptor usb_device_packet; + struct usb_config_descriptor usb_config_packet; + struct usb_string_descriptor usb_string_packet; + struct devrequest setup_packet; + unsigned int *setup_packet_pt; + unsigned char *packet_pt = NULL; + int temp, temp1; + + int i; + + /*printf("{USB device} - endpoint 0x%X \n", usb2d0_intrin); */ + + /*set usb address, seems to not work unless it is done in the next + interrupt, so that is why it is done this way */ + if (set_value != -1) + *(unsigned char *)USB2D0_FADDR_8 = (unsigned char)set_value; + + /*endpoint 1 */ + if (usb2d0_intrin & 0x01) { + setup_packet_pt = (unsigned int *)&setup_packet; + + /*copy packet */ + setup_packet_pt[0] = *(unsigned int *)USB2D0_FIFO_0; + setup_packet_pt[1] = *(unsigned int *)USB2D0_FIFO_0; + temp = *(unsigned int *)USB2D0_FIFO_0; + temp1 = *(unsigned int *)USB2D0_FIFO_0; + + /*do some swapping */ + setup_packet.value = swap_16(setup_packet.value); + setup_packet.index = swap_16(setup_packet.index); + setup_packet.length = swap_16(setup_packet.length); + + /*clear rx packet */ + *(unsigned short *)USB2D0_INCSR0_8 = 0x48; + + /*printf("0x%X 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X\n", setup_packet.requesttype, + setup_packet.request, setup_packet.value, + setup_packet.index, setup_packet.length, temp, temp1 ); */ + + switch (setup_packet.request) { + case USB_REQ_GET_DESCRIPTOR: + + switch (setup_packet.value >> 8) { + case USB_DT_DEVICE: + /*create packet */ + usb_device_packet.bLength = 18; + usb_device_packet.bDescriptorType = + USB_DT_DEVICE; +#ifdef USB_2_0_DEVICE + usb_device_packet.bcdUSB = swap_16(0x200); +#else + usb_device_packet.bcdUSB = swap_16(0x110); +#endif + usb_device_packet.bDeviceClass = 0xff; + usb_device_packet.bDeviceSubClass = 0; + usb_device_packet.bDeviceProtocol = 0; + usb_device_packet.bMaxPacketSize0 = 32; + usb_device_packet.idVendor = swap_16(1); + usb_device_packet.idProduct = swap_16(2); + usb_device_packet.bcdDevice = swap_16(0x300); + usb_device_packet.iManufacturer = 1; + usb_device_packet.iProduct = 1; + usb_device_packet.iSerialNumber = 1; + usb_device_packet.bNumConfigurations = 1; + + /*put packet in fifo */ + packet_pt = (unsigned char *)&usb_device_packet; + break; + + case USB_DT_CONFIG: + /*create packet */ + usb_config_packet.bLength = 9; + usb_config_packet.bDescriptorType = + USB_DT_CONFIG; + usb_config_packet.wTotalLength = swap_16(25); + usb_config_packet.bNumInterfaces = 1; + usb_config_packet.bConfigurationValue = 1; + usb_config_packet.iConfiguration = 0; + usb_config_packet.bmAttributes = 0x40; + usb_config_packet.MaxPower = 0; + + /*put packet in fifo */ + packet_pt = (unsigned char *)&usb_config_packet; + break; + + case USB_DT_STRING: + /*create packet */ + usb_string_packet.bLength = 2; + usb_string_packet.bDescriptorType = + USB_DT_STRING; + usb_string_packet.wData[0] = 0x0094; + + /*put packet in fifo */ + packet_pt = (unsigned char *)&usb_string_packet; + break; + } + + /*put packet in fifo */ + for (i = 0; i < (setup_packet.length); i++) { + *(unsigned char *)USB2D0_FIFO_0 = packet_pt[i]; + } + + /*give tx command */ + *(unsigned short *)USB2D0_INCSR0_8 = 0x0a; + + break; + + case USB_REQ_SET_ADDRESS: + + /*copy usb address */ + set_value = setup_packet.value; + + break; + } + + } +} + +void process_other(unsigned char usb2d0_intrusb) +{ + + /*check for sof */ + if (usb2d0_intrusb & 0x08) { + /*printf("{USB device} - sof detected\n"); */ + } + + /*check for reset */ + if (usb2d0_intrusb & 0x04) { + /*printf("{USB device} - reset detected\n"); */ + + /*copy usb address of zero, need to do this when usb reset */ + set_value = 0; + } + + if (usb2d0_intrusb & 0x02) { + /*printf("{USB device} - resume detected\n"); */ + } + + if (usb2d0_intrusb & 0x01) { + /*printf("{USB device} - suspend detected\n"); */ + } +} + +int usbInt(void) +{ + /*Must read these 2 registers and use values to clear interrupts. If you + do not read them then the interrupt will not be cleared. If you do not + use the variable the optimizer will not do a read. */ + volatile unsigned short usb2d0_intrin = + *(unsigned short *)USB2D0_INTRIN_16; + volatile unsigned char usb2d0_intrusb = + *(unsigned char *)USB2D0_INTRUSB_8; + + /*check if there was an endpoint interrupt */ + if (usb2d0_intrin != 0) { + process_endpoints(usb2d0_intrin); + } + + /*check for other interrupts */ + if (usb2d0_intrusb != 0) { + process_other(usb2d0_intrusb); + } + + return 0; +} + +void usb_dev_init() +{ +#ifdef USB_2_0_DEVICE + printf("USB 2.0 Device init\n"); + /*select 2.0 device */ + mtsdr(sdr_usb0, 0x0); /* 2.0 */ + + /*usb dev init */ + *(unsigned char *)USB2D0_POWER_8 = 0xa1; /* 2.0 */ +#else + printf("USB 1.1 Device init\n"); + /*select 1.1 device */ + mtsdr(sdr_usb0, 0x2); /* 1.1 */ + + /*usb dev init */ + *(unsigned char *)USB2D0_POWER_8 = 0xc0; /* 1.1 */ +#endif + + /*enable interrupts */ + *(unsigned char *)USB2D0_INTRUSBE_8 = 0x0f; + + irq_install_handler(VECNUM_USBDEV, (interrupt_handler_t *) usbInt, + NULL); +} + +#endif /*CONFIG_440EP */ diff --git a/cpu/ppc4xx/usbdev.h b/cpu/ppc4xx/usbdev.h new file mode 100755 index 0000000..3446d98 --- /dev/null +++ b/cpu/ppc4xx/usbdev.h @@ -0,0 +1,31 @@ +#include <config.h> + +/*Common Registers*/ +#define USB2D0_INTRIN_16 (CFG_USB_DEVICE | 0x100) +#define USB2D0_POWER_8 (CFG_USB_DEVICE | 0x102) +#define USB2D0_FADDR_8 (CFG_USB_DEVICE | 0x103) +#define USB2D0_INTRINE_16 (CFG_USB_DEVICE | 0x104) +#define USB2D0_INTROUT_16 (CFG_USB_DEVICE | 0x106) +#define USB2D0_INTRUSBE_8 (CFG_USB_DEVICE | 0x108) +#define USB2D0_INTRUSB_8 (CFG_USB_DEVICE | 0x109) +#define USB2D0_INTROUTE_16 (CFG_USB_DEVICE | 0x10a) +#define USB2D0_TSTMODE_8 (CFG_USB_DEVICE | 0x10c) +#define USB2D0_INDEX_8 (CFG_USB_DEVICE | 0x10d) +#define USB2D0_FRAME_16 (CFG_USB_DEVICE | 0x10e) + +/*Indexed Registers*/ +#define USB2D0_INCSR0_8 (CFG_USB_DEVICE | 0x110) +#define USB2D0_INCSR_16 (CFG_USB_DEVICE | 0x110) +#define USB2D0_INMAXP_16 (CFG_USB_DEVICE | 0x112) +#define USB2D0_OUTCSR_16 (CFG_USB_DEVICE | 0x114) +#define USB2D0_OUTMAXP_16 (CFG_USB_DEVICE | 0x116) +#define USB2D0_OUTCOUNT0_8 (CFG_USB_DEVICE | 0x11a) +#define USB2D0_OUTCOUNT_16 (CFG_USB_DEVICE | 0x11a) + +/*FIFOs*/ +#define USB2D0_FIFO_0 (CFG_USB_DEVICE | 0x120) +#define USB2D0_FIFO_1 (CFG_USB_DEVICE | 0x124) +#define USB2D0_FIFO_2 (CFG_USB_DEVICE | 0x128) +#define USB2D0_FIFO_3 (CFG_USB_DEVICE | 0x12c) + +void usb_dev_init(void); diff --git a/cpu/ppc4xx/vecnum.h b/cpu/ppc4xx/vecnum.h new file mode 100755 index 0000000..cbfe41d --- /dev/null +++ b/cpu/ppc4xx/vecnum.h @@ -0,0 +1,129 @@ +/* +* 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_440SP) + +/* 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_UIC1NC 30 /* UIC1 non-critical interrupt */ +#define VECNUM_UIC1C 31 /* UIC1 critical interrupt */ + +/* UIC 1 */ +#define VECNUM_EIR0 (32 + 0) /* External interrupt 0 */ +#define VECNUM_MS (32 + 1) /* MAL SERR */ +#define VECNUM_TXDE (32 + 2) /* MAL TXDE */ +#define VECNUM_RXDE (32 + 3) /* MAL RXDE */ +#define VECNUM_MTE (32 + 6) /* MAL Tx EOB */ +#define VECNUM_MRE (32 + 7) /* MAL Rx EOB */ +#define VECNUM_CT0 (32 + 12) /* GPT compare timer 0 */ +#define VECNUM_CT1 (32 + 13) /* GPT compare timer 1 */ +#define VECNUM_CT2 (32 + 14) /* GPT compare timer 2 */ +#define VECNUM_CT3 (32 + 15) /* GPT compare timer 3 */ +#define VECNUM_CT4 (32 + 16) /* GPT compare timer 4 */ +#define VECNUM_ETH0 (32 + 28) /* Ethernet interrupt status */ +#define VECNUM_EWU0 (32 + 29) /* Emac wakeup */ + +#elif 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_USBDEV (32 + 23) /* USB 1.1/USB 2.0 Device */ +#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_ */ |