lba_pci.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,552 行 · 第 1/3 页

C
1,552
字号
	LBA_CFG_RESTORE(d, d->hba.base_addr);	return(data);}#ifdef CONFIG_PARISC64#define pat_cfg_addr(bus, devfn, addr) (((bus) << 16) | ((devfn) << 8) | (addr))static int pat_cfg_read(struct pci_bus *bus, unsigned int devfn, int pos, int size, u32 *data){	int tok = pat_cfg_addr(bus->number, devfn, pos);	u32 tmp;	int ret = pdc_pat_io_pci_cfg_read(tok, size, &tmp);	DBG_CFG("%s(%d:%d.%d+0x%02x) -> 0x%x %d\n", __FUNCTION__, bus->number, PCI_SLOT(devfn), PCI_FUNC(devfn), pos, tmp, ret);	switch (size) {		case 1: *data = (u8)  tmp; return (tmp == (u8)  ~0);		case 2: *data = (u16) tmp; return (tmp == (u16) ~0);		case 4: *data = (u32) tmp; return (tmp == (u32) ~0);	}	*data = ~0;	return (ret);}static int pat_cfg_write(struct pci_bus *bus, unsigned int devfn, int pos, int size, u32 data){	int tok = pat_cfg_addr(bus->number, devfn, pos);	int ret = pdc_pat_io_pci_cfg_write(tok, size, data);	DBG_CFG("%s(%d:%d.%d+0x%02x, 0x%lx/%d)\n", __FUNCTION__, bus->number, PCI_SLOT(devfn), PCI_FUNC(devfn), pos, data, size);	return (ret);}static struct pci_ops pat_cfg_ops = {	.read =		pat_cfg_read,	.write =	pat_cfg_write,};#else/* keep the compiler from complaining about undeclared variables */#define pat_cfg_ops lba_cfg_ops#endifstatic int lba_cfg_read(struct pci_bus *bus, unsigned int devfn, int pos, int size, u32 *data){	struct lba_device *d = LBA_DEV(parisc_walk_tree(bus->bridge));	u32 local_bus = (bus->parent == NULL) ? 0 : bus->secondary;	u32 tok = LBA_CFG_TOK(local_bus, 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 = lba_rd_cfg(d, tok, pos, size);		DBG_CFG("%s(%x+%2x) -> 0x%x (a)\n", __FUNCTION__, tok, pos, *data);		return(*data == ~0U);	}	if (LBA_SKIP_PROBE(d) && (!lba_device_present(bus->secondary, devfn, d)))	{		DBG_CFG("%s(%x+%2x) -> -1 (b)\n", __FUNCTION__, tok, pos);		/* either don't want to look or know device isn't present. */		*data = ~0U;		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);	switch(size) {	case 1: *(u8 *)  data = READ_REG8(d->hba.base_addr + LBA_PCI_CFG_DATA + (pos & 3));		break;	case 2: *(u16 *) data = READ_REG16(d->hba.base_addr + LBA_PCI_CFG_DATA + (pos & 2));		break;	case 4: *(u32 *) data = READ_REG32(d->hba.base_addr + LBA_PCI_CFG_DATA);	   break;	}	DBG_CFG("%s(%x+%2x) -> 0x%x (c)\n", __FUNCTION__, tok, pos, *data);	return(*data == ~0U);}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. */static int lba_cfg_write(struct pci_bus *bus, unsigned int devfn, int pos, int size, u32 data){	struct lba_device *d = LBA_DEV(parisc_walk_tree(bus->bridge));	u32 local_bus = (bus->parent == NULL) ? 0 : bus->secondary;	u32 tok = LBA_CFG_TOK(local_bus,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, size);		DBG_CFG("%s(%x+%2x) = 0x%x (a)\n", __FUNCTION__, tok, pos,data);		return 0;	}	if (LBA_SKIP_PROBE(d) && (!lba_device_present(bus->secondary, devfn, d))) {		DBG_CFG("%s(%x+%2x) = 0x%x (b)\n", __FUNCTION__, tok, pos,data);		return 1; /* New Workaround */	}	DBG_CFG("%s(%x+%2x) = 0x%x (c)\n", __FUNCTION__, tok, pos, data);	/* Basic Algorithm */	LBA_CFG_TR4_ADDR_SETUP(d, tok | pos);	switch(size) {	case 1: WRITE_REG8 (data, d->hba.base_addr + LBA_PCI_CFG_DATA + (pos & 3));		   break;	case 2: WRITE_REG16(data, d->hba.base_addr + LBA_PCI_CFG_DATA + (pos & 2));		   break;	case 4: WRITE_REG32(data, d->hba.base_addr + LBA_PCI_CFG_DATA);		   break;	}	/* flush posted write */	lba_t32 = READ_REG32(d->hba.base_addr + LBA_PCI_CFG_ADDR);	return 0;}static struct pci_ops lba_cfg_ops = {	.read =		lba_cfg_read,	.write =	lba_cfg_write,};static voidlba_bios_init(void){	DBG(MODULE_NAME ": lba_bios_init\n");}#ifdef CONFIG_PARISC64/*** 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) pci_read_config_word(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",				pci_name(dev), i,				dev->resource[i].start,				dev->resource[i].end,				(int) dev->resource[i].flags				);		}	}}#else#define lba_claim_dev_resources(dev)#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 status;#endif	struct lba_device *ldev = LBA_DEV(parisc_walk_tree(bus->bridge));	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->bridge->platform_data);	/*	** Properly Setup MMIO resources for this bus.	** pci_alloc_primary_bus() mangles this.	*/	if (bus->self) {		/* PCI-PCI Bridge */		pci_read_bridge_bases(bus);	} else {		/* Host-PCI Bridge */		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);		}#ifdef CONFIG_PARISC64		if (ldev->hba.gmmio_space.flags) {			err = request_resource(&iomem_resource, &(ldev->hba.gmmio_space));			if (err < 0) {				BUG();				lba_dump_res(&iomem_resource, 2);			}			bus->resource[2] = &(ldev->hba.gmmio_space);		}#endif		/* advertize Host bridge resources to PCI bus */		bus->resource[0] = &(ldev->hba.io_space);		bus->resource[1] = &(ldev->hba.lmmio_space);	}	list_for_each(ln, &bus->devices) {		int i;		struct pci_dev *dev = pci_dev_b(ln);		DBG("lba_fixup_bus() %s\n", pci_name(dev));		/* Virtualize Device/Bridge Resources. */		for (i = 0; i < PCI_BRIDGE_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) pci_read_config_word(dev, PCI_STATUS, &status);		bus->bridge_ctl &= ~(status & PCI_STATUS_FAST_BACK);#endif		if (is_pdc_pat()) {			/* Claim resources for PDC's devices */			lba_claim_dev_resources(dev);		}                /*		** 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) pci_read_config_byte(bus->self, PCI_BRIDGE_CONTROL, &control);			(void) pci_write_config_byte(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) pci_read_config_word(dev, PCI_COMMAND, &status);		status |= PCI_COMMAND_PARITY | PCI_COMMAND_SERR | fbb_enable;		(void) pci_write_config_word(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 CONFIG_PARISC64#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,

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?