📄 lba_pci.c
字号:
case sizeof(u8): data = (u32) READ_REG8(d->hba.base_addr + LBA_PCI_CFG_DATA + (reg & 3)); break; case sizeof(u16): data = (u32) READ_REG16(d->hba.base_addr + LBA_PCI_CFG_DATA + (reg & 2)); break; case sizeof(u32): data = READ_REG32(d->hba.base_addr + LBA_PCI_CFG_DATA); break; default: break; /* leave data as -1 */ } } LBA_CFG_RESTORE(d, d->hba.base_addr); return(data);}#define LBA_CFG_RD(size, mask) \static int lba_cfg_read##size (struct pci_dev *dev, int pos, u##size *data) \{ \ struct lba_device *d = LBA_DEV(dev->bus->sysdata); \ u32 local_bus = (dev->bus->parent == NULL) ? 0 : dev->bus->secondary; \ u32 tok = LBA_CFG_TOK(local_bus,dev->devfn); \ \/* FIXME: B2K/C3600 workaround is always use old method... */ \ /* if (!LBA_TR4PLUS(d) && !LBA_SKIP_PROBE(d)) */ { \ /* original - Generate config cycle on broken elroy \ with risk we will miss PCI bus errors. */ \ *data = (u##size) lba_rd_cfg(d, tok, pos, sizeof(u##size)); \ DBG_CFG("%s(%s+%2x) -> 0x%x (a)\n", __FUNCTION__, dev->slot_name, pos, *data); \ return(*data == (u##size) -1); \ } \ \ if (LBA_SKIP_PROBE(d) && (!lba_device_present(dev->bus->secondary, dev->devfn, d))) \ { \ DBG_CFG("%s(%s+%2x) -> -1 (b)\n", __FUNCTION__, dev->slot_name, pos); \ /* either don't want to look or know device isn't present. */ \ *data = (u##size) -1; \ return(0); \ } \ \ /* Basic Algorithm \ ** Should only get here on fully working LBA rev. \ ** This is how simple the code should have been. \ */ \ LBA_CFG_TR4_ADDR_SETUP(d, tok | pos); \ *data = READ_REG##size(d->hba.base_addr + LBA_PCI_CFG_DATA + (pos & mask));\ DBG_CFG("%s(%s+%2x) -> 0x%x (c)\n", __FUNCTION__, dev->slot_name, pos, *data);\ return(*data == (u##size) -1); \}LBA_CFG_RD( 8, 3) LBA_CFG_RD(16, 2) LBA_CFG_RD(32, 0) static voidlba_wr_cfg( struct lba_device *d, u32 tok, u8 reg, u32 data, u32 size){ int error = 0; u32 arb_mask = 0; u32 error_config = 0; u32 status_control = 0; ASSERT((size == sizeof(u8)) || (size == sizeof(u16)) || (size == sizeof(u32))); if ((size != sizeof(u8)) && (size != sizeof(u16)) && (size != sizeof(u32))) { return; } LBA_CFG_SETUP(d, tok); LBA_CFG_ADDR_SETUP(d, tok | reg); switch (size) { case sizeof(u8): WRITE_REG8((u8) data, d->hba.base_addr + LBA_PCI_CFG_DATA + (reg&3)); break; case sizeof(u16): WRITE_REG16((u8) data, d->hba.base_addr + LBA_PCI_CFG_DATA +(reg&2)); break; case sizeof(u32): WRITE_REG32(data, d->hba.base_addr + LBA_PCI_CFG_DATA); break; default: break; } LBA_CFG_MASTER_ABORT_CHECK(d, d->hba.base_addr, tok, error); LBA_CFG_RESTORE(d, d->hba.base_addr);}/* * LBA 4.0 config write code implements non-postable semantics * by doing a read of CONFIG ADDR after the write. */#define LBA_CFG_WR(size, mask) \static int lba_cfg_write##size (struct pci_dev *dev, int pos, u##size data) \{ \ struct lba_device *d = LBA_DEV(dev->bus->sysdata); \ u32 local_bus = (dev->bus->parent == NULL) ? 0 : dev->bus->secondary; \ u32 tok = LBA_CFG_TOK(local_bus,dev->devfn); \ \ ASSERT((tok & 0xff) == 0); \ ASSERT(pos < 0x100); \ \ if (!LBA_TR4PLUS(d) && !LBA_SKIP_PROBE(d)) { \ /* Original Workaround */ \ lba_wr_cfg(d, tok, pos, (u32) data, sizeof(u##size)); \ DBG_CFG("%s(%s+%2x) = 0x%x (a)\n", __FUNCTION__, dev->slot_name, pos, data); \ return 0; \ } \ \ if (LBA_SKIP_PROBE(d) && (!lba_device_present(dev->bus->secondary, dev->devfn, d))) { \ DBG_CFG("%s(%s+%2x) = 0x%x (b)\n", __FUNCTION__, dev->slot_name, pos, data); \ return 1; /* New Workaround */ \ } \ \ DBG_CFG("%s(%s+%2x) = 0x%x (c)\n", __FUNCTION__, dev->slot_name, pos, data); \ /* Basic Algorithm */ \ LBA_CFG_TR4_ADDR_SETUP(d, tok | pos); \ WRITE_REG##size(data, d->hba.base_addr + LBA_PCI_CFG_DATA + (pos & mask)); \ lba_t32 = READ_REG32(d->hba.base_addr + LBA_PCI_CFG_ADDR); \ return 0; \}LBA_CFG_WR( 8, 3) LBA_CFG_WR(16, 2) LBA_CFG_WR(32, 0) static struct pci_ops lba_cfg_ops = { read_byte: lba_cfg_read8, read_word: lba_cfg_read16, read_dword: lba_cfg_read32, write_byte: lba_cfg_write8, write_word: lba_cfg_write16, write_dword: lba_cfg_write32};static voidlba_bios_init(void){ DBG(MODULE_NAME ": lba_bios_init\n");}#ifdef __LP64__/*** Determine if a device is already configured.** If so, reserve it resources.**** Read PCI cfg command register and see if I/O or MMIO is enabled.** PAT has to enable the devices it's using.**** Note: resources are fixed up before we try to claim them.*/static voidlba_claim_dev_resources(struct pci_dev *dev){ u16 cmd; int i, srch_flags; (void) lba_cfg_read16(dev, PCI_COMMAND, &cmd); srch_flags = (cmd & PCI_COMMAND_IO) ? IORESOURCE_IO : 0; if (cmd & PCI_COMMAND_MEMORY) srch_flags |= IORESOURCE_MEM; if (!srch_flags) return; for (i = 0; i <= PCI_ROM_RESOURCE; i++) { if (dev->resource[i].flags & srch_flags) { pci_claim_resource(dev, i); DBG(" claimed %s %d [%lx,%lx]/%x\n", dev->slot_name, i, dev->resource[i].start, dev->resource[i].end, (int) dev->resource[i].flags ); } }}#endif/*** The algorithm is generic code.** But it needs to access local data structures to get the IRQ base.** Could make this a "pci_fixup_irq(bus, region)" but not sure** it's worth it.**** Called by do_pci_scan_bus() immediately after each PCI bus is walked.** Resources aren't allocated until recursive buswalk below HBA is completed.*/static voidlba_fixup_bus(struct pci_bus *bus){ struct list_head *ln;#ifdef FBB_SUPPORT u16 fbb_enable = PCI_STATUS_FAST_BACK; u16 status;#endif struct lba_device *ldev = LBA_DEV(bus->sysdata); int lba_portbase = HBA_PORT_BASE(ldev->hba.hba_num); DBG("lba_fixup_bus(0x%p) bus %d sysdata 0x%p\n", bus, bus->secondary, bus->sysdata); /* ** Properly Setup MMIO resources for this bus. ** pci_alloc_primary_bus() mangles this. */ if (NULL == bus->self) { int err; DBG("lba_fixup_bus() %s [%lx/%lx]/%x\n", ldev->hba.io_space.name, ldev->hba.io_space.start, ldev->hba.io_space.end, (int) ldev->hba.io_space.flags); DBG("lba_fixup_bus() %s [%lx/%lx]/%x\n", ldev->hba.lmmio_space.name, ldev->hba.lmmio_space.start, ldev->hba.lmmio_space.end, (int) ldev->hba.lmmio_space.flags); err = request_resource(&ioport_resource, &(ldev->hba.io_space)); if (err < 0) { BUG(); lba_dump_res(&ioport_resource, 2); } err = request_resource(&iomem_resource, &(ldev->hba.lmmio_space)); if (err < 0) { BUG(); lba_dump_res(&iomem_resource, 2); } bus->resource[0] = &(ldev->hba.io_space); bus->resource[1] = &(ldev->hba.lmmio_space); } else { pci_read_bridge_bases(bus); /* Turn off downstream PreFetchable Memory range by default */ bus->resource[2]->start = 0; bus->resource[2]->end = 0; } list_for_each(ln, &bus->devices) { int i; struct pci_dev *dev = pci_dev_b(ln); DBG("lba_fixup_bus() %s\n", dev->name); /* Virtualize Device/Bridge Resources. */ for (i = 0; i < PCI_NUM_RESOURCES; i++) { struct resource *res = &dev->resource[i]; /* If resource not allocated - skip it */ if (!res->start) continue; if (res->flags & IORESOURCE_IO) { DBG("lba_fixup_bus() I/O Ports [%lx/%lx] -> ", res->start, res->end); res->start |= lba_portbase; res->end |= lba_portbase; DBG("[%lx/%lx]\n", res->start, res->end); } else if (res->flags & IORESOURCE_MEM) { /* ** Convert PCI (IO_VIEW) addresses to ** processor (PA_VIEW) addresses */ DBG("lba_fixup_bus() MMIO [%lx/%lx] -> ", res->start, res->end); res->start = PCI_HOST_ADDR(HBA_DATA(ldev), res->start); res->end = PCI_HOST_ADDR(HBA_DATA(ldev), res->end); DBG("[%lx/%lx]\n", res->start, res->end); } }#ifdef FBB_SUPPORT /* ** If one device does not support FBB transfers, ** No one on the bus can be allowed to use them. */ (void) lba_cfg_read16(dev, PCI_STATUS, &status); fbb_enable &= status;#endif#ifdef __LP64__ if (is_pdc_pat()) { /* Claim resources for PDC's devices */ lba_claim_dev_resources(dev); }#endif /* ** P2PB's have no IRQs. ignore them. */ if ((dev->class >> 8) == PCI_CLASS_BRIDGE_PCI) continue; /* Adjust INTERRUPT_LINE for this dev */ iosapic_fixup_irq(ldev->iosapic_obj, dev); }#ifdef FBB_SUPPORT/* FIXME/REVISIT - finish figuring out to set FBB on both** pci_setup_bridge() clobbers PCI_BRIDGE_CONTROL.** Can't fixup here anyway....garr...*/ if (fbb_enable) { if (bus->self) { u8 control; /* enable on PPB */ (void) lba_cfg_read8(bus->self, PCI_BRIDGE_CONTROL, &control); (void) lba_cfg_write8(bus->self, PCI_BRIDGE_CONTROL, control | PCI_STATUS_FAST_BACK); } else { /* enable on LBA */ } fbb_enable = PCI_COMMAND_FAST_BACK; } /* Lastly enable FBB/PERR/SERR on all devices too */ list_for_each(ln, &bus->devices) { (void) lba_cfg_read16(dev, PCI_COMMAND, &status); status |= PCI_COMMAND_PARITY | PCI_COMMAND_SERR | fbb_enable; (void) lba_cfg_write16(dev, PCI_COMMAND, status); }#endif}struct pci_bios_ops lba_bios_ops = { init: lba_bios_init, fixup_bus: lba_fixup_bus,};/*********************************************************** LBA Sprockets "I/O Port" Space Accessor Functions**** This set of accessor functions is intended for use with** "legacy firmware" (ie Sprockets on Allegro/Forte boxes).**** Many PCI devices don't require use of I/O port space (eg Tulip,** NCR720) since they export the same registers to both MMIO and** I/O port space. In general I/O port space is slower than** MMIO since drivers are designed so PIO writes can be posted.**********************************************************/#define LBA_PORT_IN(size, mask) \static u##size lba_astro_in##size (struct pci_hba_data *d, u16 addr) \{ \ u##size t; \ t = READ_REG##size(LBA_ASTRO_PORT_BASE + addr); \ DBG_PORT(" 0x%x\n", t); \ return (t); \}LBA_PORT_IN( 8, 3)LBA_PORT_IN(16, 2)LBA_PORT_IN(32, 0)/*** BUG X4107: Ordering broken - DMA RD return can bypass PIO WR**** Fixed in Elroy 2.2. The READ_U32(..., LBA_FUNC_ID) below is** guarantee non-postable completion semantics - not avoid X4107.** The READ_U32 only guarantees the write data gets to elroy but** out to the PCI bus. We can't read stuff from I/O port space** since we don't know what has side-effects. Attempting to read** from configuration space would be suicidal given the number of** bugs in that elroy functionality.**** Description:** DMA read results can improperly pass PIO writes (X4107). The** result of this bug is that if a processor modifies a location in** memory after having issued PIO writes, the PIO writes are not** guaranteed to be completed before a PCI device is allowed to see** the modified data in a DMA read.**** Note that IKE bug X3719 in TR1 IKEs will result in the same** symptom.**** Workaround:** The workaround for this bug is to always follow a PIO write with** a PIO read to the same bus before starting DMA on that PCI bus.***/#define LBA_PORT_OUT(size, mask) \static void lba_astro_out##size (struct pci_hba_data *d, u16 addr, u##size val) \{ \ ASSERT(d != NULL); \ DBG_PORT("%s(0x%p, 0x%x, 0x%x)\n", __FUNCTION__, d, addr, val); \ WRITE_REG##size(val, LBA_ASTRO_PORT_BASE + addr); \ if (LBA_DEV(d)->hw_rev < 3) \ lba_t32 = READ_U32(d->base_addr + LBA_FUNC_ID); \}LBA_PORT_OUT( 8, 3)LBA_PORT_OUT(16, 2)LBA_PORT_OUT(32, 0)static struct pci_port_ops lba_astro_port_ops = { inb: lba_astro_in8, inw: lba_astro_in16, inl: lba_astro_in32, outb: lba_astro_out8, outw: lba_astro_out16, outl: lba_astro_out32};#ifdef __LP64__#define PIOP_TO_GMMIO(lba, addr) \ ((lba)->iop_base + (((addr)&0xFFFC)<<10) + ((addr)&3))/*********************************************************** LBA PAT "I/O Port" Space Accessor Functions**** This set of accessor functions is intended for use with** "PAT PDC" firmware (ie Prelude/Rhapsody/Piranha boxes).**** This uses the PIOP space located in the first 64MB of GMMIO.** Each rope gets a full 64*KB* (ie 4 bytes per page) this way.** bits 1:0 stay the same. bits 15:2 become 25:12.** Then add the base and we can generate an I/O Port cycle.********************************************************/#undef LBA_PORT_IN#define LBA_PORT_IN(size, mask) \static u##size lba_pat_in##size (struct pci_hba_data *l, u16 addr) \{ \ u##size t; \ ASSERT(bus != NULL); \ DBG_PORT("%s(0x%p, 0x%x) ->", __FUNCTION__, l, addr); \ t = READ_REG##size(PIOP_TO_GMMIO(LBA_DEV(l), addr)); \ DBG_PORT(" 0x%x\n", t); \ return (t); \}LBA_PORT_IN( 8, 3)LBA_PORT_IN(16, 2)LBA_PORT_IN(32, 0)#undef LBA_PORT_OUT#define LBA_PORT_OUT(size, mask) \static void lba_pat_out##size (struct pci_hba_data *l, u16 addr, u##size val) \{ \ void *where = (void *) PIOP_TO_GMMIO(LBA_DEV(l), addr); \ ASSERT(bus != NULL); \ DBG_PORT("%s(0x%p, 0x%x, 0x%x)\n", __FUNCTION__, l, addr, val); \ WRITE_REG##size(val, where); \ /* flush the I/O down to the elroy at least */ \ lba_t32 = READ_U32(l->base_addr + LBA_FUNC_ID); \}LBA_PORT_OUT( 8, 3)LBA_PORT_OUT(16, 2)LBA_PORT_OUT(32, 0)static struct pci_port_ops lba_pat_port_ops = { inb: lba_pat_in8, inw: lba_pat_in16, inl: lba_pat_in32, outb: lba_pat_out8, outw: lba_pat_out16, outl: lba_pat_out32};
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -