/* $XFree86: xc/programs/Xserver/hw/xfree86/os-support/pmax/pmax_pci.c,v 1.6 2002/07/24 19:06:53 tsi Exp $ */ /* * Copyright 1998 by Concurrent Computer Corporation * * Permission to use, copy, modify, distribute, and sell this software * and its documentation for any purpose is hereby granted without fee, * provided that the above copyright notice appear in all copies and that * both that copyright notice and this permission notice appear in * supporting documentation, and that the name of Concurrent Computer * Corporation not be used in advertising or publicity pertaining to * distribution of the software without specific, written prior * permission. Concurrent Computer Corporation makes no representations * about the suitability of this software for any purpose. It is * provided "as is" without express or implied warranty. * * CONCURRENT COMPUTER CORPORATION DISCLAIMS ALL WARRANTIES WITH REGARD * TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL CONCURRENT COMPUTER CORPORATION BE * LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY * DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. * * Copyright 1998 by Metro Link Incorporated * * Permission to use, copy, modify, distribute, and sell this software * and its documentation for any purpose is hereby granted without fee, * provided that the above copyright notice appear in all copies and that * both that copyright notice and this permission notice appear in * supporting documentation, and that the name of Metro Link * Incorporated not be used in advertising or publicity pertaining to * distribution of the software without specific, written prior * permission. Metro Link Incorporated makes no representations * about the suitability of this software for any purpose. It is * provided "as is" without express or implied warranty. * * METRO LINK INCORPORATED DISCLAIMS ALL WARRANTIES WITH REGARD * TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL METRO LINK INCORPORATED BE * LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY * DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #ifdef HAVE_XORG_CONFIG_H #include #endif #include #include "os.h" #include "compiler.h" #include "xf86.h" #include "xf86Priv.h" #include "xf86_OSlib.h" #include "Pci.h" #include #include /* * Night Hawk 6400/6408 platform support */ #undef NH640X_PCI_MFDEV_SUPPORT #undef NH640X_PCI_BRIDGE_SUPPORT static void nh640xPciInit(void); static PCITAG nh640xPciFindNext(void); static PCITAG nh640xPciFindFirst(void); static CARD32 nh6400PciReadLong(PCITAG tag, int offset); static void nh6400PciWriteLong(PCITAG tag, int offset, CARD32 val); static ADDRESS nh6400BusToHostAddr(PCITAG tag, ADDRESS addr); static ADDRESS nh6400HostToBusAddr(PCITAG tag, ADDRESS addr); static CARD32 nh6408PciReadLong(PCITAG tag, int offset); static void nh6408PciWriteLong(PCITAG tag, int offset, CARD32 val); static ADDRESS nh6408BusToHostAddr(PCITAG tag, ADDRESS addr); static ADDRESS nh6408HostToBusAddr(PCITAG tag, ADDRESS addr); static pciBusFuncs_t nh6400_pci_funcs = { nh6400PciReadLong, nh6400PciWriteLong, nh6400HostToBusAddr, nh6400BusToHostAddr }; static pciBusFuncs_t nh6408_pci_funcs = { nh6408PciReadLong, nh6408PciWriteLong, nh6408HostToBusAddr, nh6408BusToHostAddr }; /* * NH640x CFG address and data register offsets from base */ #define NH6400_PCI_CFG_ADDR_REG_OFF 0 #define NH6400_PCI_CFG_TYPE0_DATA_REG_OFF 0x40 #define NH6400_PCI_CFG_TYPE1_DATA_REG_OFF 0x80 #define NH6408_PCI_CFG_ADDR_REG_OFF 0 #define NH6408_PCI_CFG_DATA_REG_OFF 0x10000 /* * Possible cfg addr values for NH640x GMEM PMC ports */ static unsigned long nh6400_pmc_cfgaddrs[] = { PCI_CFGMECH1_TYPE0_CFGADDR(0,0,0) }; /* * Possible cfg addr values for devices on a secondary bus * (e.g. behind DEC 21152 PCI-to-PCI bridge) */ static unsigned long dec_cfgaddrs[] = { PCI_CFGMECH1_TYPE1_CFGADDR(1,0,0,0), PCI_CFGMECH1_TYPE1_CFGADDR(1,1,0,0), PCI_CFGMECH1_TYPE1_CFGADDR(1,2,0,0), PCI_CFGMECH1_TYPE1_CFGADDR(1,3,0,0), PCI_CFGMECH1_TYPE1_CFGADDR(1,4,0,0), PCI_CFGMECH1_TYPE1_CFGADDR(1,5,0,0), PCI_CFGMECH1_TYPE1_CFGADDR(1,6,0,0), PCI_CFGMECH1_TYPE1_CFGADDR(1,7,0,0), PCI_CFGMECH1_TYPE1_CFGADDR(1,8,0,0), PCI_CFGMECH1_TYPE1_CFGADDR(1,9,0,0), PCI_CFGMECH1_TYPE1_CFGADDR(1,10,0,0), PCI_CFGMECH1_TYPE1_CFGADDR(1,11,0,0), PCI_CFGMECH1_TYPE1_CFGADDR(1,12,0,0), PCI_CFGMECH1_TYPE1_CFGADDR(1,13,0,0), PCI_CFGMECH1_TYPE1_CFGADDR(1,14,0,0), PCI_CFGMECH1_TYPE1_CFGADDR(1,15,0,0) }; /* * Data structure holding information about various nh640x PCI buses */ struct nh640x_pci_info { int busnum; int type; unsigned long num_cfg_addrs; unsigned long *cfg_addrs; int primary_bus; unsigned long cfgPhysBase; unsigned long memBase; unsigned long ioBase; unsigned long ioSize; unsigned char *cfgAddrReg; /* After mapping */ }; /* Type */ #define PRIMARY_PCI 0 #define SECONDARY_PCI 1 static struct nh640x_pci_info nh6400_pci_info[] = { /* pci4 */ { 4, PRIMARY_PCI, 1, nh6400_pmc_cfgaddrs, 0, 0xa0000000, 0xa1000000, 0, 0xa2000000 }, /* pci12 */ { 12, SECONDARY_PCI, 16, dec_cfgaddrs, 4 }, #if 0 /* pci5 */ { 5, PRIMARY_PCI, 1, nh6400_pmc_cfgaddrs, 0, 0xb0000000, 0xb1000000, 0, 0xb2000000 }, /* pci13 */ { 13, SECONDARY_PCI, 16, dec_cfgaddrs, 5 }, #endif }; #define NH6400_NUM_PCI_EXPANSION_BUSES (sizeof(nh6400_pci_info)/sizeof(struct nh640x_pci_info)) static struct nh640x_pci_info nh6408_pci_info[] = { /* pci8 */ { 8, PRIMARY_PCI, 1, nh6400_pmc_cfgaddrs, 0, 0x98040000, 0x9a800000, 65536, 0xa0000000 }, /* pci12 */ { 12, SECONDARY_PCI, 16, dec_cfgaddrs, 8, }, #if 0 /* pci9 */ { 9, PRIMARY_PCI, 1, nh6400_pmc_cfgaddrs, 0, 0x99040000, 0x9b800000, 65536, 0xb0000000 }, /* pci13 */ { 13, SECONDARY_PCI, 16, dec_cfgaddrs, 9, }, #endif }; #define NH6408_NUM_PCI_EXPANSION_BUSES (sizeof(nh6408_pci_info)/sizeof(struct nh640x_pci_info)) extern unsigned long pmax_sys_type; #define MOTOPPC_IO_BASE 0x80000000L /* Start of PCI/ISA I/O region */ extern void * pmax_iomap(unsigned long, unsigned long); extern unsigned long ioSize; extern volatile unsigned char *ioBase; void pmaxPciInit(void) { extern void motoppcPciInit(void); extern void nh640xPciInit(void); extern void nh6800tPciInit(void); extern unsigned long motoPciMemBase; extern unsigned long motoPciMemLen; extern unsigned long motoPciMemBaseCPU; /* * Determine type of machine */ switch(pmax_sys_type) { case MODEL_NH6400: case MODEL_NH6408: nh640xPciInit(); break; case MODEL_NH6800T: nh6800tPciInit(); break; case MODEL_PH620: case MODEL_PH640: case MODEL_MMTX: motoPciMemBase = 0; motoPciMemLen = 0x20000000; motoPciMemBaseCPU = 0xa0000000; /*FALLTHROUGH*/ case MODEL_MPWR: case MODEL_PH610: case MODEL_MPWR2: motoppcPciInit(); break; default: FatalError("pmaxPciInit: Unsupported machine type\n"); break; } } void ppcPciIoMap(int pcibus) { int primary_bus; if (ioBase != MAP_FAILED) munmap((void*)ioBase,ioSize); if (!pciBusInfo[pcibus]) return; primary_bus = pciBusInfo[pcibus]->primary_bus; if (!pciBusInfo[primary_bus]) return; ioSize = min(pciBusInfo[primary_bus]->ppc_io_size, 64 * 1024); if (ioSize) { ioBase = (unsigned char *)pmax_iomap(pciBusInfo[primary_bus]->ppc_io_base, ioSize); if (ioBase == MAP_FAILED) ioSize = 0; } } static void nh640xPciInit(void) { int i,n; struct nh640x_pci_info *infop; pciBusFuncs_p functions; switch (pmax_sys_type) { case MODEL_NH6400: n = NH6400_NUM_PCI_EXPANSION_BUSES; infop = nh6400_pci_info; functions = &nh6400_pci_funcs; break; case MODEL_NH6408: n = NH6408_NUM_PCI_EXPANSION_BUSES; infop = nh6408_pci_info; functions = &nh6408_pci_funcs; break; default: FatalError("Unknown Power MAXION system type\n"); /*NOTREACHED*/ } /* * Initialize entries in pciBusInfo[] table for each defined PCI bus. * This table is actually sparse because undefined or inaccessible * pci buses are left as NULL entries. Of course, pciFindNext() is * aware of this convention, and will skip the undefined buses. */ for (i=0; ibusnum; pciBusInfo_t *busp; if (pciBusInfo[bus]) busp = pciBusInfo[bus]; else busp = xalloc(sizeof(pciBusInfo_t)); if (!busp) FatalError("nh640xPciInit: xalloc failed\n"); /* Initialize pci bus info */ busp->configMech = PCI_CFG_MECH_OTHER; busp->numDevices = infop->num_cfg_addrs; busp->secondary = (infop->type == SECONDARY_PCI ? TRUE : FALSE); busp->primary_bus = infop->primary_bus; busp->funcs = functions; busp->pciBusPriv = infop; /* Initialize I/O base/size info */ if (busp->secondary) { pciBusInfo_t *pri_busp = pciBusInfo[busp->primary_bus]; if (pri_busp) { busp->ppc_io_base = pri_busp->ppc_io_base; busp->ppc_io_size = pri_busp->ppc_io_size; } } else if (infop->ioSize) { busp->ppc_io_size = infop->ioSize; busp->ppc_io_base = infop->ioBase; } pciBusInfo[bus] = busp; /* * Adjust pciNumBuses to reflect the highest defined entry in pciBusInfo */ if (pciNumBuses < bus) pciNumBuses = bus + 1; } pciFindFirstFP = nh640xPciFindFirst; pciFindNextFP = nh640xPciFindNext; } static PCITAG nh640xPciFindNext(void) { unsigned long devid, tmp; unsigned char base_class, sub_class, sec_bus, pri_bus; for (;;) { if (pciBusNum == -1) { /* * Start at top of the order */ pciBusNum = 0; pciFuncNum = 0; pciDevNum = 0; } else { #ifdef NH640X_PCI_MFDEV_SUPPORT /* * Somewhere in middle of order. Determine who's * next up */ if (pciFuncNum == 0) { /* * Is current dev a multifunction device? */ if (pciMfDev(pciBusNum, pciDevNum)) /* Probe for other functions */ pciFuncNum = 1; else /* No more functions this device. Next device please */ pciDevNum ++; } else if (++pciFuncNum >= 8) { /* No more functions for this device. Next device please */ pciFuncNum = 0; pciDevNum ++; } #else /* NH640X_PCI_MFDEV_SUPPORT */ pciDevNum++; #endif /* NH640X_PCI_MFDEV_SUPPORT */ if (!pciBusInfo[pciBusNum] || pciDevNum >= pciBusInfo[pciBusNum]->numDevices) { /* * No more devices for this bus. Next bus please */ if (++pciBusNum >= pciNumBuses) /* No more buses. All done for now */ return(PCI_NOT_FOUND); pciDevNum = 0; } } if (!pciBusInfo[pciBusNum]) continue; /* Undefined bus, next bus/device please */ /* * At this point, pciBusNum, pciDevNum, and pciFuncNum have been * advanced to the next device. Compute the tag, and read the * device/vendor ID field. */ pciDeviceTag = PCI_MAKE_TAG(pciBusNum, pciDevNum, pciFuncNum); devid = pciReadLong(pciDeviceTag, 0); if (devid == 0xffffffff) continue; /* Nobody home. Next device please */ #ifdef NH640X_PCI_BRIDGE_SUPPORT /* * Before checking for a specific devid, look for enabled * PCI to PCI bridge devices. If one is found, create and * initialize a bus info record (if one does not already exist). */ tmp = pciReadLong(pciDeviceTag, PCI_CLASS_CODE_REG); base_class = PCI_EXTRACT_BASE_CLASS(tmp); sub_class = PCI_EXTRACT_SUBCLASS(tmp); if (base_class == PCI_CLASS_BRIDGE && sub_class == PCI_SUBCLASS_BRIDGE_PCI) { tmp = pciReadLong(pciDeviceTag, PCI_BRIDGE_BUS_REG); sec_bus = PCI_SECONDARY_BUS_EXTRACT(tmp); pri_bus = PCI_PRIMARY_BUS_EXTRACT(tmp); if (sec_bus > 0 && sec_bus < PCI_MAX_BUSES && pcibusInfo[pri_bus]) { /* * Found a secondary PCI bus */ if (!pciBusInfo[sec_bus]) { pciBusInfo[sec_bus] = xalloc(sizeof(pciBusInfo_t)); if (!pciBusInfo[sec_bus]) FatalError("nh640xPciFindNext: alloc failed\n!!!"); } /* Copy parents settings... */ *pciBusInfo[sec_bus] = *pcibusInfo[pri_bus]; /* ...but not everything same as parent */ pciBusInfo[sec_bus]->primary_bus = pri_bus; pciBusInfo[sec_bus]->secondary = TRUE; pciBusInfo[sec_bus]->numDevices = 32; if (pciNumBuses < sec_num) pciNumBuses = sec_num+1; } } #endif /* NH640X_PCI_BRIDGE_SUPPORT */ /* * Does this device match the requested devid after * applying mask? */ if ((devid & pciDevidMask) == pciDevid) { /* Yes - Return it. Otherwise, next device */ /* However, before returning it, try to map */ /* I/O region for this PCI bus */ ppcPciIoMap(PCI_BUS_FROM_TAG(pciDeviceTag)); return(pciDeviceTag); /* got a match */ } } /* for */ /*NOTREACHED*/ } static PCITAG nh640xPciFindFirst(void) { pciBusNum = -1; return(nh640xPciFindNext()); } static unsigned long nh6400PciReadLong(PCITAG tag, int offset) { unsigned long tmp; char *base; int devnum, bus, func, data_reg_offset, ndevs; unsigned long cfgaddr; pciBusInfo_t *busp, *pri_busp; struct nh640x_pci_info *infop, *pri_infop; bus = PCI_BUS_FROM_TAG(tag); devnum = PCI_DEV_FROM_TAG(tag); func = PCI_FUNC_FROM_TAG(tag); xf86MsgVerb(3, X_INFO, "nh6400PciReadLong: bus=%d, devnum=%d, func=%d, offset=0x%x\n", bus, devnum, func, offset); if (bus >= pciNumBuses || !pciBusInfo[bus]) { xf86Msg(X_WARNING, "nh6400PciReadLong: bus pci%d not defined!!!\n", bus); return(0xffffffff); } busp = pciBusInfo[bus]; infop = (struct nh640x_pci_info *)busp->pciBusPriv; if (busp->secondary) { /* * Secondary PCI bus behind a pci-to-pci bridge */ pri_busp = pciBusInfo[busp->primary_bus]; pri_infop = (struct nh640x_pci_info *)pri_busp->pciBusPriv; ndevs = 16; data_reg_offset = NH6400_PCI_CFG_TYPE1_DATA_REG_OFF; /* For Type 1 cfg cycles */ if (!pri_busp) { xf86Msg(X_WARNING, "nh6400PciReadLong: pci%d's primary parent [pci%d] " "is not defined!!!\n", bus, busp->primary_bus); return(0xffffffff); } } else { pri_busp = busp; pri_infop = infop; ndevs = infop->num_cfg_addrs; data_reg_offset = NH6400_PCI_CFG_TYPE0_DATA_REG_OFF; /* For Type 0 cfg cycles */ } if (devnum >= ndevs) { xf86Msg(X_WARNING, "nh6400PciReadLong: devnum %d out of range for bus pci%d\n", devnum, bus); return(0xffffffff); } /* * Make sure the cfg address and data registers for this bus are mapped * Secondary buses just use the primary parents addreses */ if (!infop->cfgAddrReg) { if (!pri_infop->cfgAddrReg) { pri_infop->cfgAddrReg = pmax_iomap(pri_infop->cfgPhysBase, 0x1000); if (pri_infop->cfgAddrReg == MAP_FAILED) { FatalError("nh6400PciReadLong: Cannot map PCI cfg regs @ 0x%08x\n", pri_infop->cfgPhysBase); /*NOTREACHED*/ } } infop->cfgAddrReg = pri_infop->cfgAddrReg; infop->cfgPhysBase = pri_infop->cfgPhysBase; } base = infop->cfgAddrReg; if (busp->secondary) { /* cfgaddr = PCI_CFGMECH1_TYPE1_CFGADDR(bus,devnum,func,offset); */ cfgaddr = PCI_CFGMECH1_TYPE1_CFGADDR(1,devnum,func,offset); /* Must use bus=1 for now - glb */ } else { cfgaddr = infop->cfg_addrs[devnum] + offset; } xf86MsgVerb(X_INFO, 3, "nh6400PciReadLong: Writing cfgaddr=0x%x to 0x%x (phys=0x%x)\n", cfgaddr, base, infop->cfgPhysBase); /* There may not be any OS interaction while interrupts are disabled */ xf86DisableInterrupts(); *((unsigned long *)(base)) = pciByteSwap(cfgaddr); /* Set cfg address */ eieio(); if (!badaddr(base + data_reg_offset, 4, 0)) { tmp = *((unsigned long *)(base + data_reg_offset)); eieio(); } xf86EnableInterrupts(); xf86MsgVerb(X_INFO, 3, "nh6400PciReadLong: Read value=0x%x from 0x%x (phys=0x%x)\n", pciByteSwap(tmp), base + data_reg_offset, infop->cfgPhysBase + data_reg_offset); return(pciByteSwap(tmp)); } static void nh6400PciWriteLong(PCITAG tag, int offset, unsigned long val) { char *base; int devnum, bus, func, data_reg_offset, ndevs; unsigned long cfgaddr; pciBusInfo_t *busp, *pri_busp; struct nh640x_pci_info *infop, *pri_infop; bus = PCI_BUS_FROM_TAG(tag); devnum = PCI_DEV_FROM_TAG(tag); func = PCI_FUNC_FROM_TAG(tag); xf86MsgVerb(X_INFO, 3, "nh6400PciWriteLong: bus=%d, devnum=%d, func=%d, offset=0x%x, " val=0x%x\n", bus, devnum, func, offset, val); if (bus >= pciNumBuses || !pciBusInfo[bus]) { xf86Msg(X_WARNING, "nh6400PciWriteLong: bus pci%d not defined!!!\n", bus); return; } busp = pciBusInfo[bus]; infop = (struct nh640x_pci_info *)busp->pciBusPriv; if (busp->secondary) { /* * Secondary PCI bus behind a pci-to-pci bridge */ pri_busp = pciBusInfo[busp->primary_bus]; pri_infop = (struct nh640x_pci_info *)pri_busp->pciBusPriv; ndevs = 16; data_reg_offset = NH6400_PCI_CFG_TYPE1_DATA_REG_OFF; /* For Type 1 cfg cycles */ if (!pri_busp) { xf86Msg(X_WARNING, "nh6400PciWriteLong: pci%d's primary parent [pci%d]" " is not defined!!!\n", bus, busp->primary_bus); return; } } else { pri_busp = busp; pri_infop = infop; ndevs = infop->num_cfg_addrs; data_reg_offset = NH6400_PCI_CFG_TYPE0_DATA_REG_OFF; /* For Type 0 cfg cycles */ } if (devnum >= ndevs) { xf86Msg(X_WARNING, "nh6400PciWriteLong: devnum %d out of range for bus pci%d\n", devnum, bus); return; } /* * Make sure the cfg address and data registers for this bus are mapped * Secondary buses just use the primary parents addreses */ if (!infop->cfgAddrReg) { if (!pri_infop->cfgAddrReg) { pri_infop->cfgAddrReg = pmax_iomap(pri_infop->cfgPhysBase, 0x1000); if (pri_infop->cfgAddrReg == MAP_FAILED) { FatalError("nh6400PciWriteLong: Cannot map PCI cfg regs @ 0x%08x\n", pri_infop->cfgPhysBase); /*NOTREACHED*/ } } infop->cfgAddrReg = pri_infop->cfgAddrReg; infop->cfgPhysBase = pri_infop->cfgPhysBase; } base = infop->cfgAddrReg; if (busp->secondary) { /* cfgaddr = PCI_CFGMECH1_TYPE1_CFGADDR(bus,devnum,func,offset); */ cfgaddr = PCI_CFGMECH1_TYPE1_CFGADDR(1,devnum,func,offset); /* Must use bus=1 for now - glb */ } else { cfgaddr = infop->cfg_addrs[devnum] + offset; } xf86MsgVerb(X_INFO, 3, "nh6400PciWriteLong: Writing cfgaddr=0x%x to 0x%x (phys=0x%x)\n", cfgaddr, base, infop->cfgPhysBase); /* There may not be any OS interaction while interrupts are disabled */ xf86DisableInterrupts(); *((unsigned long *)(base)) = pciByteSwap(cfgaddr); /* Set cfg address */ eieio(); *((unsigned long *)(base + data_reg_offset)) = pciByteSwap(val); eieio(); xf86EnableInterrupts(); xf86MsgVerb(X_INFO, 3, "nh6400PciWriteLong: Wrote value=0x%x to 0x%x (phys=0x%x)\n", val, base + data_reg_offset, infop->cfgPhysBase + data_reg_offset); } /* * These next two functions are for debugging purposes only because * the nh6400 does not translate passed to/from a PCI domain. However, * we do do some bounds checking to make sure things are where they * should be. */ static ADDRESS nh6400BusToHostAddr(PCITAG tag, ADDRESS addr) { unsigned long addr_l = (unsigned long)addr; int bus = PCI_BUS_FROM_TAG(tag); struct nh640x_pci_info *infop; int pri_bus; unsigned long membase; if (!pciBusInfo[bus]) FatalError("nh6400BusToHostAddr: pci%d not defined!!\n", bus); if (pciBusInfo[bus]->secondary) { pri_bus = pciBusInfo[bus]->primary_bus; if (!pciBusInfo[pri_bus]) FatalError("nh6400BusToHostAddr: Primary bus pci%d not defined!!\n", pri_bus); } else pri_bus = bus; infop = (struct nh640x_pci_info *)pciBusInfo[pri_bus]->pciBusPriv; membase = infop->memBase; if (addr_l < 0x80000000) /* * NH6400 host memory addresses are 0-0x7fffffff */ return(addr); else if (addr_l >= membase && addr_l < membase + 0x0e000000) /* * NH6400 host can access PCI memory space addresses * [memBase, memBase+0x0dffffff] */ return(addr); else /* Other addresses are not valid */ FatalError("nh6400BusToHostAddr: Bus address 0x%x not visible to NH6400 host\n", addr_l); /*NOTREACHED*/ } static ADDRESS nh6400HostToBusAddr(PCITAG tag, ADDRESS addr) { unsigned long addr_l = (unsigned long) addr; int bus = PCI_BUS_FROM_TAG(tag); struct nh640x_pci_info *infop; int pri_bus; unsigned long membase; if (!pciBusInfo[bus]) FatalError("nh6400HostToBusAddr: pci%d not defined!!\n", bus); if (pciBusInfo[bus]->secondary) { pri_bus = pciBusInfo[bus]->primary_bus; if (!pciBusInfo[pri_bus]) FatalError("nh6400HostToBusAddr: Primary bus pci%d not defined!!\n", pri_bus); } else pri_bus = bus; infop = (struct nh640x_pci_info *)pciBusInfo[pri_bus]->pciBusPriv; membase = infop->memBase; if (addr_l < 0x80000000) /* * NH6400 host memory addresses are 0-0x7fffffff */ return(addr); else if (addr_l >= membase && addr_l < membase + 0x0e000000) /* * NH6400 host can access PCI memory space addresses * [memBase, memBase+0x0dffffff] */ return(addr); else /* Other addresses are not valid */ FatalError("nh6400HostToBusAddr: Bus address 0x%x not visible to NH6400 host\n", addr_l); /*NOTREACHED*/ } /* * NH6408 platform support */ static unsigned long nh6408PciReadLong(PCITAG tag, int offset) { unsigned long tmp; char *base; int devnum, bus, func, ndevs; unsigned long cfgaddr; pciBusInfo_t *busp, *pri_busp; struct nh640x_pci_info *infop, *pri_infop; bus = PCI_BUS_FROM_TAG(tag); devnum = PCI_DEV_FROM_TAG(tag); func = PCI_FUNC_FROM_TAG(tag); xf86MsgVerb(X_INFO, "nh6408PciReadLong: bus=%d, devnum=%d, func=%d, offset=0x%x\n", bus, devnum, func, offset); if (bus >= pciNumBuses || !pciBusInfo[bus]) { xf86Msg(X_WARNING, "nh6408PciReadLong: bus pci%d not defined!!!\n", bus); return(0xffffffff); } busp = pciBusInfo[bus]; infop = (struct nh640x_pci_info *)busp->pciBusPriv; if (busp->secondary) { /* * Secondary PCI bus behind a pci-to-pci bridge */ pri_busp = pciBusInfo[busp->primary_bus]; pri_infop = (struct nh640x_pci_info *)pri_busp->pciBusPriv; ndevs = 16; if (!pri_busp) { xf86Msg(X_WARNING, "nh6408PciReadLong: pci%d's primary parent [pci%d] " "is not defined!!!\n", bus, busp->primary_bus); return(0xffffffff); } } else { pri_busp = busp; pri_infop = infop; ndevs = infop->num_cfg_addrs; } if (devnum >= ndevs) { xf86Msg(X_WARNING "nh6408PciReadLong: devnum %d out of range for bus pci%d\n", devnum, bus); return(0xffffffff); } /* * Make sure the cfg address and data registers for this bus are mapped * Secondary buses just use the primary parents addreses */ if (!infop->cfgAddrReg) { if (!pri_infop->cfgAddrReg) { pri_infop->cfgAddrReg = pmax_iomap(pri_infop->cfgPhysBase, 0x11000); if (pri_infop->cfgAddrReg == MAP_FAILED) { FatalError("nh6408PciReadLong: Cannot map PCI cfg regs @ 0x%08x\n", pri_infop->cfgPhysBase); /*NOTREACHED*/ } } infop->cfgAddrReg = pri_infop->cfgAddrReg; infop->cfgPhysBase = pri_infop->cfgPhysBase; } base = infop->cfgAddrReg; if (busp->secondary) { /* cfgaddr = PCI_CFGMECH1_TYPE1_CFGADDR(bus,devnum,func,offset); */ cfgaddr = PCI_CFGMECH1_TYPE1_CFGADDR(1,devnum,func,offset); /* Must use bus=1 for now - glb */ } else { cfgaddr = infop->cfg_addrs[devnum] + offset; } xf86MsgVerb(X_INFO, 3, "nh6408PciReadLong: Writing cfgaddr=0x%x to 0x%x (phys=0x%x)\n", cfgaddr, base, infop->cfgPhysBase); /* There may not be any OS interaction while interrupts are disabled */ xf86DisableInterrupts(); *((unsigned long *)(base)) = pciByteSwap(cfgaddr); /* Set cfg address */ eieio(); if (!badaddr(base + NH6408_PCI_CFG_DATA_REG_OFF, 4, 0)) { tmp = *((unsigned long *)(base + NH6408_PCI_CFG_DATA_REG_OFF)); eieio(); } xf86EnableInterrupts(); xf86MsgVerb(X_INFO, 3, "nh6408PciReadLong: Read value=0x%x from 0x%x (phys=0x%x)\n", pciByteSwap(tmp), base + NH6408_PCI_CFG_DATA_REG_OFF, infop->cfgPhysBase + NH6408_PCI_CFG_DATA_REG_OFF); return(pciByteSwap(tmp)); } static void nh6408PciWriteLong(PCITAG tag, int offset, unsigned long val) { char *base; int devnum, bus, func, ndevs; unsigned long cfgaddr; pciBusInfo_t *busp, *pri_busp; struct nh640x_pci_info *infop, *pri_infop; bus = PCI_BUS_FROM_TAG(tag); devnum = PCI_DEV_FROM_TAG(tag); func = PCI_FUNC_FROM_TAG(tag); xf86MsgVerb(X_INFO, "nh6408PciWriteLong: bus=%d, devnum=%d, func=%d, offset=0x%x, " "val=0x%x\n", bus, devnum, func, offset, val); if (bus >= pciNumBuses || !pciBusInfo[bus]) { xf86Msg(X_WARNING, "nh6408PciWriteLong: bus pci%d not defined!!!\n", bus); return; } busp = pciBusInfo[bus]; infop = (struct nh640x_pci_info *)busp->pciBusPriv; if (busp->secondary) { /* * Secondary PCI bus behind a pci-to-pci bridge */ pri_busp = pciBusInfo[busp->primary_bus]; pri_infop = (struct nh640x_pci_info *)pri_busp->pciBusPriv; ndevs = 16; if (!pri_busp) { xf86Msg(X_WARNING, "nh6408PciWriteLong: pci%d's primary parent [pci%d] " is not defined!!!\n", bus, busp->primary_bus); return; } } else { pri_busp = busp; pri_infop = infop; ndevs = infop->num_cfg_addrs; } if (devnum >= ndevs) { xf86Msg(X_WARNING, "nh6408PciWriteLong: devnum %d out of range for bus pci%d\n", devnum, bus); return; } /* * Make sure the cfg address and data registers for this bus are mapped * Secondary buses just use the primary parents addreses */ if (!infop->cfgAddrReg) { if (!pri_infop->cfgAddrReg) { pri_infop->cfgAddrReg = pmax_iomap(pri_infop->cfgPhysBase, 0x11000); if (pri_infop->cfgAddrReg == MAP_FAILED) { FatalError("nh6408PciWriteLong: Cannot map PCI cfg regs @ 0x%08x\n", pri_infop->cfgPhysBase); /*NOTREACHED*/ } } infop->cfgAddrReg = pri_infop->cfgAddrReg; infop->cfgPhysBase = pri_infop->cfgPhysBase; } base = infop->cfgAddrReg; if (busp->secondary) { /* cfgaddr = PCI_CFGMECH1_TYPE1_CFGADDR(bus,devnum,0,offset); */ cfgaddr = PCI_CFGMECH1_TYPE1_CFGADDR(1,devnum,0,offset); } else { cfgaddr = infop->cfg_addrs[devnum] + offset; } xf86MsgVerb(X_INFO, 3, "nh6408PciWriteLong: Writing cfgaddr=0x%x to 0x%x (phys=0x%x)\n", cfgaddr, base, infop->cfgPhysBase); /* There may not be any OS interaction while interrupts are disabled */ xf86DisableInterrupts(); *((unsigned long *)(base)) = pciByteSwap(cfgaddr); /* Set cfg address */ eieio(); *((unsigned long *)(base + NH6408_PCI_CFG_DATA_REG_OFF)) = pciByteSwap(val); eieio(); xf86EnableInterrupts(); xf86MsgVerb(X_INFO, 3, "nh6408PciWriteLong: Wrote value=0x%x to 0x%x (phys=0x%x)\n", val, base + NH6408_PCI_CFG_DATA_REG_OFF, infop->cfgPhysBase + NH6408_PCI_CFG_DATA_REG_OFF); } static ADDRESS nh6408BusToHostAddr(PCITAG tag, ADDRESS addr) { unsigned long addr_l = (unsigned long)addr; int bus = PCI_BUS_FROM_TAG(tag); int pri_bus; struct nh640x_pci_info *infop; unsigned long membase; if (!pciBusInfo[bus]) FatalError("nh6408BusToHostAddr: pci%d not defined!!\n", bus); if (pciBusInfo[bus]->secondary) { pri_bus = pciBusInfo[bus]->primary_bus; if (!pciBusInfo[pri_bus]) FatalError("nh6408BusToHostAddr: Primary bus pci%d not defined!!\n", pri_bus); } else pri_bus = bus; infop = (struct nh640x_pci_info *)pciBusInfo[pri_bus]->pciBusPriv; membase = infop->memBase; if (addr_l < 0x10000000) /* * NH6408 host sees PCI memory space addresses 0-0x0fffffff * at the primary PCI buses "memBase" [memBase, memBase+0x0fffffff] */ return((ADDRESS)(membase + addr_l)); else if (addr_l >= 0x80000000) /* * NH6408 host memory addresses 0-0x7fffffff are seen at * 0x80000000-0xffffffff on PCI */ return((ADDRESS)(addr_l & 0x7fffffff)); else /* Other addresses are not valid */ FatalError("nh6408BusToHostAddr: Bus address 0x%x not visible to NH6408 host\n", addr_l); /*NOTREACHED*/ } static ADDRESS nh6408HostToBusAddr(PCITAG tag, ADDRESS addr) { unsigned long addr_l = (unsigned long)addr; int bus = PCI_BUS_FROM_TAG(tag); int pri_bus; struct nh640x_pci_info *infop; unsigned long membase; if (!pciBusInfo[bus]) FatalError("nh6408HostToBusAddr: pci%d not defined!!\n", bus); if (pciBusInfo[bus]->secondary) { pri_bus = pciBusInfo[bus]->primary_bus; if (!pciBusInfo[pri_bus]) FatalError("nh6408HostToBusAddr: Primary bus pci%d not defined!!\n", pri_bus); } else pri_bus = bus; infop = (struct nh640x_pci_info *)pciBusInfo[pri_bus]->pciBusPriv; membase = infop->memBase; if (addr_l < 0x80000000) /* * NH6408 host memory addresses 0-0x7fffffff are seen at * 0x80000000-0xffffffff on PCI */ return((ADDRESS)(addr_l | 0x80000000)); else if (addr_l >= membase && addr_l < (membase + 0x10000000)) /* * NH6408 host sees PCI memory space addresses 0-0x0fffffff * at the primary PCI buses "memBase" [memBase, memBase+0x0fffffff] */ return((ADDRESS)(addr_l - membase)); else /* Other addresses are not valid */ FatalError("nh6408HostToBusAddr: Host address 0x%x not visible to pci%d\n", addr_l, bus); /*NOTREACHED*/ } /* * NH6800 (Turbo) support */ static void nh6800tPciInit(void) { FatalError("nh6800tPciInit: NH6800TURBO not supported (yet)!!!\n"); }