diff options
Diffstat (limited to 'drivers/parisc/lba_pci.c')
| -rw-r--r-- | drivers/parisc/lba_pci.c | 193 | 
1 files changed, 120 insertions, 73 deletions
diff --git a/drivers/parisc/lba_pci.c b/drivers/parisc/lba_pci.c index 3aeb3279c92..37e71ff6408 100644 --- a/drivers/parisc/lba_pci.c +++ b/drivers/parisc/lba_pci.c @@ -34,7 +34,7 @@  #include <linux/types.h>  #include <linux/kernel.h>  #include <linux/spinlock.h> -#include <linux/init.h>		/* for __init and __devinit */ +#include <linux/init.h>		/* for __init */  #include <linux/pci.h>  #include <linux/ioport.h>  #include <linux/slab.h> @@ -43,7 +43,6 @@  #include <asm/pdc.h>  #include <asm/pdcpat.h>  #include <asm/page.h> -#include <asm/system.h>  #include <asm/ropes.h>  #include <asm/hardware.h>	/* for register_parisc_driver() stuff */ @@ -190,8 +189,8 @@ lba_dump_res(struct resource *r, int d)  static int lba_device_present(u8 bus, u8 dfn, struct lba_device *d)  { -	u8 first_bus = d->hba.hba_bus->secondary; -	u8 last_sub_bus = d->hba.hba_bus->subordinate; +	u8 first_bus = d->hba.hba_bus->busn_res.start; +	u8 last_sub_bus = d->hba.hba_bus->busn_res.end;  	if ((bus < first_bus) ||  	    (bus > last_sub_bus) || @@ -365,7 +364,7 @@ lba_rd_cfg(struct lba_device *d, u32 tok, u8 reg, u32 size)  static int elroy_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 local_bus = (bus->parent == NULL) ? 0 : bus->busn_res.start;  	u32 tok = LBA_CFG_TOK(local_bus, devfn);  	void __iomem *data_reg = d->hba.base_addr + LBA_PCI_CFG_DATA; @@ -381,7 +380,7 @@ static int elroy_cfg_read(struct pci_bus *bus, unsigned int devfn, int pos, int  		return 0;  	} -	if (LBA_SKIP_PROBE(d) && !lba_device_present(bus->secondary, devfn, d)) { +	if (LBA_SKIP_PROBE(d) && !lba_device_present(bus->busn_res.start, devfn, d)) {  		DBG_CFG("%s(%x+%2x) -> -1 (b)\n", __func__, tok, pos);  		/* either don't want to look or know device isn't present. */  		*data = ~0U; @@ -432,7 +431,7 @@ lba_wr_cfg(struct lba_device *d, u32 tok, u8 reg, u32 data, u32 size)  static int elroy_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 local_bus = (bus->parent == NULL) ? 0 : bus->busn_res.start;  	u32 tok = LBA_CFG_TOK(local_bus,devfn);  	if ((pos > 255) || (devfn > 255)) @@ -445,7 +444,7 @@ static int elroy_cfg_write(struct pci_bus *bus, unsigned int devfn, int pos, int  		return 0;  	} -	if (LBA_SKIP_PROBE(d) && (!lba_device_present(bus->secondary, devfn, d))) { +	if (LBA_SKIP_PROBE(d) && (!lba_device_present(bus->busn_res.start, devfn, d))) {  		DBG_CFG("%s(%x+%2x) = 0x%x (b)\n", __func__, tok, pos,data);  		return 1; /* New Workaround */  	} @@ -482,7 +481,7 @@ static struct pci_ops elroy_cfg_ops = {  static int mercury_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 local_bus = (bus->parent == NULL) ? 0 : bus->busn_res.start;  	u32 tok = LBA_CFG_TOK(local_bus, devfn);  	void __iomem *data_reg = d->hba.base_addr + LBA_PCI_CFG_DATA; @@ -515,7 +514,7 @@ static int mercury_cfg_write(struct pci_bus *bus, unsigned int devfn, int pos, i  {  	struct lba_device *d = LBA_DEV(parisc_walk_tree(bus->bridge));  	void __iomem *data_reg = d->hba.base_addr + LBA_PCI_CFG_DATA; -	u32 local_bus = (bus->parent == NULL) ? 0 : bus->secondary; +	u32 local_bus = (bus->parent == NULL) ? 0 : bus->busn_res.start;  	u32 tok = LBA_CFG_TOK(local_bus,devfn);  	if ((pos > 255) || (devfn > 255)) @@ -614,6 +613,54 @@ truncate_pat_collision(struct resource *root, struct resource *new)  	return 0;	/* truncation successful */  } +/* + * extend_lmmio_len: extend lmmio range to maximum length + * + * This is needed at least on C8000 systems to get the ATI FireGL card + * working. On other systems we will currently not extend the lmmio space. + */ +static unsigned long +extend_lmmio_len(unsigned long start, unsigned long end, unsigned long lba_len) +{ +	struct resource *tmp; + +	pr_debug("LMMIO mismatch: PAT length = 0x%lx, MASK register = 0x%lx\n", +		end - start, lba_len); + +	lba_len = min(lba_len+1, 256UL*1024*1024); /* limit to 256 MB */ + +	pr_debug("LBA: lmmio_space [0x%lx-0x%lx] - original\n", start, end); + +	if (boot_cpu_data.cpu_type < mako) { +		pr_info("LBA: Not a C8000 system - not extending LMMIO range.\n"); +		return end; +	} + +	end += lba_len; +	if (end < start) /* fix overflow */ +		end = -1ULL; + +	pr_debug("LBA: lmmio_space [0x%lx-0x%lx] - current\n", start, end); + +	/* first overlap */ +	for (tmp = iomem_resource.child; tmp; tmp = tmp->sibling) { +		pr_debug("LBA: testing %pR\n", tmp); +		if (tmp->start == start) +			continue; /* ignore ourself */ +		if (tmp->end < start) +			continue; +		if (tmp->start > end) +			continue; +		if (end >= tmp->start) +			end = tmp->start - 1; +	} + +	pr_info("LBA: lmmio_space [0x%lx-0x%lx] - new\n", start, end); + +	/* return new end */ +	return end; +} +  #else  #define truncate_pat_collision(r,n)  (0)  #endif @@ -630,15 +677,14 @@ truncate_pat_collision(struct resource *root, struct resource *new)  static void  lba_fixup_bus(struct pci_bus *bus)  { -	struct list_head *ln; +	struct pci_dev *dev;  #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 platform_data 0x%p\n", -		bus, bus->secondary, bus->bridge->platform_data); +		bus, (int)bus->busn_res.start, bus->bridge->platform_data);  	/*  	** Properly Setup MMIO resources for this bus. @@ -653,7 +699,7 @@ lba_fixup_bus(struct pci_bus *bus)  		}  	} else {  		/* Host-PCI Bridge */ -		int err, i; +		int err;  		DBG("lba_fixup_bus() %s [%lx/%lx]/%lx\n",  			ldev->hba.io_space.name, @@ -669,11 +715,8 @@ lba_fixup_bus(struct pci_bus *bus)  			lba_dump_res(&ioport_resource, 2);  			BUG();  		} -		/* advertize Host bridge resources to PCI bus */ -		bus->resource[0] = &(ldev->hba.io_space); -		i = 1; -		if (ldev->hba.elmmio_space.start) { +		if (ldev->hba.elmmio_space.flags) {  			err = request_resource(&iomem_resource,  					&(ldev->hba.elmmio_space));  			if (err < 0) { @@ -685,35 +728,17 @@ lba_fixup_bus(struct pci_bus *bus)  				/* lba_dump_res(&iomem_resource, 2); */  				/* BUG(); */ -			} else -				bus->resource[i++] = &(ldev->hba.elmmio_space); +			}  		} - -		/*   Overlaps with elmmio can (and should) fail here. -		 *   We will prune (or ignore) the distributed range. -		 * -		 *   FIXME: SBA code should register all elmmio ranges first. -		 *      that would take care of elmmio ranges routed -		 *	to a different rope (already discovered) from -		 *	getting registered *after* LBA code has already -		 *	registered it's distributed lmmio range. -		 */ -		if (truncate_pat_collision(&iomem_resource, -				       	&(ldev->hba.lmmio_space))) { - -			printk(KERN_WARNING "LBA: lmmio_space [%lx/%lx] duplicate!\n", -					(long)ldev->hba.lmmio_space.start, -					(long)ldev->hba.lmmio_space.end); -		} else { +		if (ldev->hba.lmmio_space.flags) {  			err = request_resource(&iomem_resource, &(ldev->hba.lmmio_space));  			if (err < 0) {  				printk(KERN_ERR "FAILED: lba_fixup_bus() request for "  					"lmmio_space [%lx/%lx]\n",  					(long)ldev->hba.lmmio_space.start,  					(long)ldev->hba.lmmio_space.end); -			} else -				bus->resource[i++] = &(ldev->hba.lmmio_space); +			}  		}  #ifdef CONFIG_64BIT @@ -728,15 +753,13 @@ lba_fixup_bus(struct pci_bus *bus)  				lba_dump_res(&iomem_resource, 2);  				BUG();  			} -			bus->resource[i++] = &(ldev->hba.gmmio_space);  		}  #endif  	} -	list_for_each(ln, &bus->devices) { +	list_for_each_entry(dev, &bus->devices, bus_list) {  		int i; -		struct pci_dev *dev = pci_dev_b(ln);  		DBG("lba_fixup_bus() %s\n", pci_name(dev)); @@ -748,27 +771,6 @@ lba_fixup_bus(struct pci_bus *bus)  			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); -			} else { -				DBG("lba_fixup_bus() WTF? 0x%lx [%lx/%lx] XXX", -					res->flags, res->start, res->end); -			} -  			/*  			** FIXME: this will result in whinging for devices  			** that share expansion ROMs (think quad tulip), but @@ -815,7 +817,7 @@ lba_fixup_bus(struct pci_bus *bus)  	}  	/* Lastly enable FBB/PERR/SERR on all devices too */ -	list_for_each(ln, &bus->devices) { +	list_for_each_entry(dev, &bus->devices, bus_list) {  		(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); @@ -1034,11 +1036,20 @@ lba_pat_resources(struct parisc_device *pa_dev, struct lba_device *lba_dev)  		case PAT_PBNUM:  			lba_dev->hba.bus_num.start = p->start;  			lba_dev->hba.bus_num.end   = p->end; +			lba_dev->hba.bus_num.flags = IORESOURCE_BUS;  			break;  		case PAT_LMMIO:  			/* used to fix up pre-initialized MEM BARs */ -			if (!lba_dev->hba.lmmio_space.start) { +			if (!lba_dev->hba.lmmio_space.flags) { +				unsigned long lba_len; + +				lba_len = ~READ_REG32(lba_dev->hba.base_addr +						+ LBA_LMMIO_MASK); +				if ((p->end - p->start) != lba_len) +					p->end = extend_lmmio_len(p->start, +						p->end, lba_len); +  				sprintf(lba_dev->hba.lmmio_name,  						"PCI%02x LMMIO",  						(int)lba_dev->hba.bus_num.start); @@ -1046,7 +1057,7 @@ lba_pat_resources(struct parisc_device *pa_dev, struct lba_device *lba_dev)  					io->start;  				r = &lba_dev->hba.lmmio_space;  				r->name = lba_dev->hba.lmmio_name; -			} else if (!lba_dev->hba.elmmio_space.start) { +			} else if (!lba_dev->hba.elmmio_space.flags) {  				sprintf(lba_dev->hba.elmmio_name,  						"PCI%02x ELMMIO",  						(int)lba_dev->hba.bus_num.start); @@ -1141,6 +1152,7 @@ lba_legacy_resources(struct parisc_device *pa_dev, struct lba_device *lba_dev)  	r->name = "LBA PCI Busses";  	r->start = lba_num & 0xff;  	r->end = (lba_num>>8) & 0xff; +	r->flags = IORESOURCE_BUS;  	/* Set up local PCI Bus resources - we don't need them for  	** Legacy boxes but it's nice to see in /proc/iomem. @@ -1404,12 +1416,14 @@ static int __init  lba_driver_probe(struct parisc_device *dev)  {  	struct lba_device *lba_dev; +	LIST_HEAD(resources);  	struct pci_bus *lba_bus;  	struct pci_ops *cfg_ops;  	u32 func_class;  	void *tmp_obj;  	char *version;  	void __iomem *addr = ioremap_nocache(dev->hpa.start, 4096); +	int max;  	/* Read HW Rev First */  	func_class = READ_REG32(addr + LBA_FCLASS); @@ -1518,10 +1532,46 @@ lba_driver_probe(struct parisc_device *dev)  	if (lba_dev->hba.bus_num.start < lba_next_bus)  		lba_dev->hba.bus_num.start = lba_next_bus; +	/*   Overlaps with elmmio can (and should) fail here. +	 *   We will prune (or ignore) the distributed range. +	 * +	 *   FIXME: SBA code should register all elmmio ranges first. +	 *      that would take care of elmmio ranges routed +	 *	to a different rope (already discovered) from +	 *	getting registered *after* LBA code has already +	 *	registered it's distributed lmmio range. +	 */ +	if (truncate_pat_collision(&iomem_resource, +				   &(lba_dev->hba.lmmio_space))) { +		printk(KERN_WARNING "LBA: lmmio_space [%lx/%lx] duplicate!\n", +				(long)lba_dev->hba.lmmio_space.start, +				(long)lba_dev->hba.lmmio_space.end); +		lba_dev->hba.lmmio_space.flags = 0; +	} + +	pci_add_resource_offset(&resources, &lba_dev->hba.io_space, +				HBA_PORT_BASE(lba_dev->hba.hba_num)); +	if (lba_dev->hba.elmmio_space.flags) +		pci_add_resource_offset(&resources, &lba_dev->hba.elmmio_space, +					lba_dev->hba.lmmio_space_offset); +	if (lba_dev->hba.lmmio_space.flags) +		pci_add_resource_offset(&resources, &lba_dev->hba.lmmio_space, +					lba_dev->hba.lmmio_space_offset); +	if (lba_dev->hba.gmmio_space.flags) +		pci_add_resource(&resources, &lba_dev->hba.gmmio_space); + +	pci_add_resource(&resources, &lba_dev->hba.bus_num); +  	dev->dev.platform_data = lba_dev;  	lba_bus = lba_dev->hba.hba_bus = -		pci_scan_bus_parented(&dev->dev, lba_dev->hba.bus_num.start, -				cfg_ops, NULL); +		pci_create_root_bus(&dev->dev, lba_dev->hba.bus_num.start, +				    cfg_ops, NULL, &resources); +	if (!lba_bus) { +		pci_free_resource_list(&resources); +		return 0; +	} + +	max = pci_scan_child_bus(lba_bus);  	/* This is in lieu of calling pci_assign_unassigned_resources() */  	if (is_pdc_pat()) { @@ -1540,7 +1590,6 @@ lba_driver_probe(struct parisc_device *dev)  		lba_dump_res(&lba_dev->hba.lmmio_space, 2);  #endif  	} -	pci_enable_bridges(lba_bus);  	/*  	** Once PCI register ops has walked the bus, access to config @@ -1551,10 +1600,8 @@ lba_driver_probe(struct parisc_device *dev)  		lba_dev->flags |= LBA_FLAG_SKIP_PROBE;  	} -	if (lba_bus) { -		lba_next_bus = lba_bus->subordinate + 1; -		pci_bus_add_devices(lba_bus); -	} +	lba_next_bus = max + 1; +	pci_bus_add_devices(lba_bus);  	/* Whew! Finally done! Tell services we got this one covered. */  	return 0;  | 
