diff options
author | Wolfgang Denk <wd@denx.de> | 2007-09-08 20:52:57 +0200 |
---|---|---|
committer | Wolfgang Denk <wd@denx.de> | 2007-09-08 20:52:57 +0200 |
commit | 87eb200ea87571f00473dc5a73fadbb5aa6dd309 (patch) | |
tree | ad48edbe0f860c7061597aa59320540881add629 /cpu/ppc4xx | |
parent | fd63d832cd929f8e8d8fcac9b3e55b1091588a43 (diff) | |
parent | 6efc1fc0b63e55f94c5bc61d8dd23c918e3bc778 (diff) | |
download | u-boot-imx-87eb200ea87571f00473dc5a73fadbb5aa6dd309.zip u-boot-imx-87eb200ea87571f00473dc5a73fadbb5aa6dd309.tar.gz u-boot-imx-87eb200ea87571f00473dc5a73fadbb5aa6dd309.tar.bz2 |
Merge with /home/raj/git/u-boot#440SPe_PCIe_fixes
Diffstat (limited to 'cpu/ppc4xx')
-rw-r--r-- | cpu/ppc4xx/405gp_pci.c | 17 | ||||
-rw-r--r-- | cpu/ppc4xx/440spe_pcie.c | 162 | ||||
-rw-r--r-- | cpu/ppc4xx/440spe_pcie.h | 16 | ||||
-rw-r--r-- | cpu/ppc4xx/traps.c | 6 |
4 files changed, 130 insertions, 71 deletions
diff --git a/cpu/ppc4xx/405gp_pci.c b/cpu/ppc4xx/405gp_pci.c index 2837929..282e7a1 100644 --- a/cpu/ppc4xx/405gp_pci.c +++ b/cpu/ppc4xx/405gp_pci.c @@ -443,7 +443,7 @@ void pci_init_board(void) static struct pci_controller ppc440_hose = {0}; -void pci_440_init (struct pci_controller *hose) +int pci_440_init (struct pci_controller *hose) { int reg_num = 0; @@ -459,7 +459,7 @@ void pci_440_init (struct pci_controller *hose) if ((strap & SDR0_SDSTP1_PISE_MASK) == 0) { printf("PCI: SDR0_STRP1[PISE] not set.\n"); printf("PCI: Configuration aborted.\n"); - return; + return -1; } #elif defined(CONFIG_440GP) unsigned long strap; @@ -468,7 +468,7 @@ void pci_440_init (struct pci_controller *hose) if ((strap & CPC0_STRP1_PISE_MASK) == 0) { printf("PCI: CPC0_STRP1[PISE] not set.\n"); printf("PCI: Configuration aborted.\n"); - return; + return -1; } #endif #endif /* CONFIG_DISABLE_PISE_TEST */ @@ -477,7 +477,7 @@ void pci_440_init (struct pci_controller *hose) * PCI controller init *--------------------------------------------------------------------------*/ hose->first_busno = 0; - hose->last_busno = 0xff; + hose->last_busno = 0; /* PCI I/O space */ pci_set_region(hose->regions + reg_num++, @@ -515,7 +515,7 @@ void pci_440_init (struct pci_controller *hose) if (pci_pre_init (hose) == 0) { printf("PCI: Board-specific initialization failed.\n"); printf("PCI: Configuration aborted.\n"); - return; + return -1; } pci_register_hose( hose ); @@ -578,13 +578,16 @@ void pci_440_init (struct pci_controller *hose) #endif hose->last_busno = pci_hose_scan(hose); } + return hose->last_busno; } void pci_init_board(void) { - pci_440_init (&ppc440_hose); + int busno; + + busno = pci_440_init (&ppc440_hose); #if defined(CONFIG_440SPE) - pcie_setup_hoses(); + pcie_setup_hoses(busno + 1); #endif } diff --git a/cpu/ppc4xx/440spe_pcie.c b/cpu/ppc4xx/440spe_pcie.c index bf68cc1..158f1c5 100644 --- a/cpu/ppc4xx/440spe_pcie.c +++ b/cpu/ppc4xx/440spe_pcie.c @@ -40,73 +40,126 @@ enum { LNKW_X8 = 0x8 }; -static inline int pcie_in_8(const volatile unsigned char __iomem *addr) +static u8* pcie_get_base(struct pci_controller *hose, unsigned int devfn) { - int ret; - - PCIE_IN(lbzx, ret, addr); + u8 *base = (u8*)hose->cfg_data; + + /* use local configuration space for the first bus */ + if (PCI_BUS(devfn) == 0) { + if (hose->cfg_data == (u8*)CFG_PCIE0_CFGBASE) + base = (u8*)CFG_PCIE0_XCFGBASE; + if (hose->cfg_data == (u8*)CFG_PCIE1_CFGBASE) + base = (u8*)CFG_PCIE1_XCFGBASE; + if (hose->cfg_data == (u8*)CFG_PCIE2_CFGBASE) + base = (u8*)CFG_PCIE2_XCFGBASE; + } - return ret; + return base; } -static inline int pcie_in_le16(const volatile unsigned short __iomem *addr) +static void pcie_dmer_disable(void) { - int ret; - - PCIE_IN(lhbrx, ret, addr) - - return ret; + mtdcr (DCRN_PEGPL_CFG(DCRN_PCIE0_BASE), + mfdcr (DCRN_PEGPL_CFG(DCRN_PCIE0_BASE)) | GPL_DMER_MASK_DISA); + mtdcr (DCRN_PEGPL_CFG(DCRN_PCIE1_BASE), + mfdcr (DCRN_PEGPL_CFG(DCRN_PCIE1_BASE)) | GPL_DMER_MASK_DISA); + mtdcr (DCRN_PEGPL_CFG(DCRN_PCIE2_BASE), + mfdcr (DCRN_PEGPL_CFG(DCRN_PCIE2_BASE)) | GPL_DMER_MASK_DISA); } -static inline unsigned pcie_in_le32(const volatile unsigned __iomem *addr) +static void pcie_dmer_enable(void) { - unsigned ret; - - PCIE_IN(lwbrx, ret, addr); - - return ret; + mtdcr (DCRN_PEGPL_CFG (DCRN_PCIE0_BASE), + mfdcr (DCRN_PEGPL_CFG(DCRN_PCIE0_BASE)) & ~GPL_DMER_MASK_DISA); + mtdcr (DCRN_PEGPL_CFG (DCRN_PCIE1_BASE), + mfdcr (DCRN_PEGPL_CFG(DCRN_PCIE1_BASE)) & ~GPL_DMER_MASK_DISA); + mtdcr (DCRN_PEGPL_CFG (DCRN_PCIE2_BASE), + mfdcr (DCRN_PEGPL_CFG(DCRN_PCIE2_BASE)) & ~GPL_DMER_MASK_DISA); } - static int pcie_read_config(struct pci_controller *hose, unsigned int devfn, int offset, int len, u32 *val) { + u8 *address; *val = 0; + + /* + * Bus numbers are relative to hose->first_busno + */ + devfn -= PCI_BDF(hose->first_busno, 0, 0); + /* - * 440SPE implements only one function per port + * NOTICE: configuration space ranges are currenlty mapped only for + * the first 16 buses, so such limit must be imposed. In case more + * buses are required the TLB settings in board/amcc/<board>/init.S + * need to be altered accordingly (one bus takes 1 MB of memory space). */ - if (!((PCI_FUNC(devfn) == 0) && (PCI_DEV(devfn) == 1))) + if (PCI_BUS(devfn) >= 16) return 0; - devfn = PCI_BDF(0,0,0); + /* + * Only single device/single function is supported for the primary and + * secondary buses of the 440SPe host bridge. + */ + if ((!((PCI_FUNC(devfn) == 0) && (PCI_DEV(devfn) == 0))) && + ((PCI_BUS(devfn) == 0) || (PCI_BUS(devfn) == 1))) + return 0; + + address = pcie_get_base(hose, devfn); offset += devfn << 4; + /* + * Reading from configuration space of non-existing device can + * generate transaction errors. For the read duration we suppress + * assertion of machine check exceptions to avoid those. + */ + pcie_dmer_disable (); + switch (len) { case 1: - *val = pcie_in_8(hose->cfg_data + offset); + *val = in_8(hose->cfg_data + offset); break; case 2: - *val = pcie_in_le16((u16 *)(hose->cfg_data + offset)); + *val = in_le16((u16 *)(hose->cfg_data + offset)); break; default: - *val = pcie_in_le32((u32*)(hose->cfg_data + offset)); + *val = in_le32((u32*)(hose->cfg_data + offset)); break; } + + pcie_dmer_enable (); + return 0; } static int pcie_write_config(struct pci_controller *hose, unsigned int devfn, int offset, int len, u32 val) { + u8 *address; + + /* + * Bus numbers are relative to hose->first_busno + */ + devfn -= PCI_BDF(hose->first_busno, 0, 0); + /* - * 440SPE implements only one function per port + * Same constraints as in pcie_read_config(). */ - if (!((PCI_FUNC(devfn) == 0) && (PCI_DEV(devfn) == 1))) + if (PCI_BUS(devfn) >= 16) return 0; - devfn = PCI_BDF(0,0,0); + if ((!((PCI_FUNC(devfn) == 0) && (PCI_DEV(devfn) == 0))) && + ((PCI_BUS(devfn) == 0) || (PCI_BUS(devfn) == 1))) + return 0; + + address = pcie_get_base(hose, devfn); offset += devfn << 4; + /* + * Suppress MCK exceptions, similar to pcie_read_config() + */ + pcie_dmer_disable (); + switch (len) { case 1: out_8(hose->cfg_data + offset, val); @@ -118,6 +171,9 @@ static int pcie_write_config(struct pci_controller *hose, unsigned int devfn, out_le32((u32 *)(hose->cfg_data + offset), val); break; } + + pcie_dmer_enable (); + return 0; } @@ -126,7 +182,7 @@ int pcie_read_config_byte(struct pci_controller *hose,pci_dev_t dev,int offset,u u32 v; int rv; - rv = pcie_read_config(hose, dev, offset, 1, &v); + rv = pcie_read_config(hose, dev, offset, 1, &v); *val = (u8)v; return rv; } @@ -783,12 +839,12 @@ void ppc440spe_setup_pcie_rootpoint(struct pci_controller *hose, int port) volatile void *rmbase = NULL; pci_set_ops(hose, - pcie_read_config_byte, - pcie_read_config_word, - pcie_read_config_dword, - pcie_write_config_byte, - pcie_write_config_word, - pcie_write_config_dword); + pcie_read_config_byte, + pcie_read_config_word, + pcie_read_config_dword, + pcie_write_config_byte, + pcie_write_config_word, + pcie_write_config_dword); switch (port) { case 0: @@ -811,14 +867,9 @@ void ppc440spe_setup_pcie_rootpoint(struct pci_controller *hose, int port) /* * Set bus numbers on our root port */ - if (ppc440spe_revB()) { - out_8((u8 *)mbase + PCI_PRIMARY_BUS, 0); - out_8((u8 *)mbase + PCI_SECONDARY_BUS, 1); - out_8((u8 *)mbase + PCI_SUBORDINATE_BUS, 1); - } else { - out_8((u8 *)mbase + PCI_PRIMARY_BUS, 0); - out_8((u8 *)mbase + PCI_SECONDARY_BUS, 0); - } + out_8((u8 *)mbase + PCI_PRIMARY_BUS, 0); + out_8((u8 *)mbase + PCI_SECONDARY_BUS, 1); + out_8((u8 *)mbase + PCI_SUBORDINATE_BUS, 1); /* * Set up outbound translation to hose->mem_space from PLB @@ -875,6 +926,29 @@ void ppc440spe_setup_pcie_rootpoint(struct pci_controller *hose, int port) in_le16((u16 *)(mbase + PCI_COMMAND)) | PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); printf("PCIE:%d successfully set as rootpoint\n",port); + + /* Set Device and Vendor Id */ + switch (port) { + case 0: + out_le16(mbase + 0x200, 0xaaa0); + out_le16(mbase + 0x202, 0xbed0); + break; + case 1: + out_le16(mbase + 0x200, 0xaaa1); + out_le16(mbase + 0x202, 0xbed1); + break; + case 2: + out_le16(mbase + 0x200, 0xaaa2); + out_le16(mbase + 0x202, 0xbed2); + break; + default: + out_le16(mbase + 0x200, 0xaaa3); + out_le16(mbase + 0x202, 0xbed3); + } + + /* Set Class Code to PCI-PCI bridge and Revision Id to 1 */ + out_le32(mbase + 0x208, 0x06040001); + } int ppc440spe_setup_pcie_endpoint(struct pci_controller *hose, int port) @@ -952,8 +1026,8 @@ int ppc440spe_setup_pcie_endpoint(struct pci_controller *hose, int port) /* Enable I/O, Mem, and Busmaster cycles */ out_le16((u16 *)(mbase + PCI_COMMAND), - in_le16((u16 *)(mbase + PCI_COMMAND)) | - PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); + in_le16((u16 *)(mbase + PCI_COMMAND)) | + PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); out_le16(mbase + 0x200,0xcaad); /* Setting vendor ID */ out_le16(mbase + 0x202,0xfeed); /* Setting device ID */ attempts = 10; diff --git a/cpu/ppc4xx/440spe_pcie.h b/cpu/ppc4xx/440spe_pcie.h index eb7cecf..38745eb 100644 --- a/cpu/ppc4xx/440spe_pcie.h +++ b/cpu/ppc4xx/440spe_pcie.h @@ -38,6 +38,7 @@ #define DCRN_PEGPL_REGBAL(base) (base + 0x13) #define DCRN_PEGPL_REGMSK(base) (base + 0x14) #define DCRN_PEGPL_SPECIAL(base) (base + 0x15) +#define DCRN_PEGPL_CFG(base) (base + 0x16) /* * System DCRs (SDRs) @@ -161,20 +162,7 @@ mtdcr(DCRN_SDR0_CFGADDR, offset); \ mtdcr(DCRN_SDR0_CFGDATA,data);}) -#define PCIE_IN(opcode, ret, addr) \ - __asm__ __volatile__( \ - "sync\n" \ - #opcode " %0,0,%1\n" \ - "1: twi 0,%0,0\n" \ - "isync\n" \ - "b 3f\n" \ - "2: li %0,-1\n" \ - "3:\n" \ - ".section __ex_table,\"a\"\n" \ - ".balign 4\n" \ - ".long 1b,2b\n" \ - ".previous\n" \ - : "=r" (ret) : "r" (addr), "m" (*addr)); +#define GPL_DMER_MASK_DISA 0x02000000 int ppc440spe_init_pcie(void); int ppc440spe_init_pcie_rootport(int port); diff --git a/cpu/ppc4xx/traps.c b/cpu/ppc4xx/traps.c index f5365cb..38b6f89 100644 --- a/cpu/ppc4xx/traps.c +++ b/cpu/ppc4xx/traps.c @@ -151,12 +151,6 @@ MachineCheckException(struct pt_regs *regs) int uncorr_ecc = 0; #endif - /* Probing PCI(E) using config cycles may cause this exception - * when a device is not present. To gracefully recover in such - * scenarios config read/write routines need to be instrumented in - * order to return via fixup handler. For examples refer to - * pcie_in_8(), pcie_in_le16() and pcie_in_le32() - */ if ((fixup = search_exception_table(regs->nip)) != 0) { regs->nip = fixup; val = mfspr(MCSR); |