diff options
Diffstat (limited to 'drivers/pci/hotplug/shpchp_pci.c')
| -rw-r--r-- | drivers/pci/hotplug/shpchp_pci.c | 96 | 
1 files changed, 43 insertions, 53 deletions
diff --git a/drivers/pci/hotplug/shpchp_pci.c b/drivers/pci/hotplug/shpchp_pci.c index a2ccfcd3c29..469454e0cc4 100644 --- a/drivers/pci/hotplug/shpchp_pci.c +++ b/drivers/pci/hotplug/shpchp_pci.c @@ -34,99 +34,89 @@  #include "../pci.h"  #include "shpchp.h" -int __ref shpchp_configure_device(struct slot *p_slot) +int shpchp_configure_device(struct slot *p_slot)  {  	struct pci_dev *dev; -	struct pci_bus *parent = p_slot->ctrl->pci_dev->subordinate; -	int num, fn;  	struct controller *ctrl = p_slot->ctrl; +	struct pci_dev *bridge = ctrl->pci_dev; +	struct pci_bus *parent = bridge->subordinate; +	int num, ret = 0; + +	pci_lock_rescan_remove();  	dev = pci_get_slot(parent, PCI_DEVFN(p_slot->device, 0));  	if (dev) { -		ctrl_err(ctrl, "Device %s already exists " -			 "at %04x:%02x:%02x, cannot hot-add\n", pci_name(dev), -			 pci_domain_nr(parent), p_slot->bus, p_slot->device); +		ctrl_err(ctrl, "Device %s already exists at %04x:%02x:%02x, cannot hot-add\n", +			 pci_name(dev), pci_domain_nr(parent), +			 p_slot->bus, p_slot->device);  		pci_dev_put(dev); -		return -EINVAL; +		ret = -EINVAL; +		goto out;  	}  	num = pci_scan_slot(parent, PCI_DEVFN(p_slot->device, 0));  	if (num == 0) {  		ctrl_err(ctrl, "No new device found\n"); -		return -ENODEV; +		ret = -ENODEV; +		goto out;  	} -	for (fn = 0; fn < 8; fn++) { -		dev = pci_get_slot(parent, PCI_DEVFN(p_slot->device, fn)); -		if (!dev) +	list_for_each_entry(dev, &parent->devices, bus_list) { +		if (PCI_SLOT(dev->devfn) != p_slot->device) +			continue; +		if (pci_is_bridge(dev)) +			pci_hp_add_bridge(dev); +	} + +	pci_assign_unassigned_bridge_resources(bridge); + +	list_for_each_entry(dev, &parent->devices, bus_list) { +		if (PCI_SLOT(dev->devfn) != p_slot->device)  			continue; -		if ((dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) || -				(dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)) { -			/* Find an unused bus number for the new bridge */ -			struct pci_bus *child; -			unsigned char busnr, start = parent->secondary; -			unsigned char end = parent->subordinate; -			for (busnr = start; busnr <= end; busnr++) { -				if (!pci_find_bus(pci_domain_nr(parent), -							busnr)) -					break; -			} -			if (busnr > end) { -				ctrl_err(ctrl, -					 "No free bus for hot-added bridge\n"); -				pci_dev_put(dev); -				continue; -			} -			child = pci_add_new_bus(parent, dev, busnr); -			if (!child) { -				ctrl_err(ctrl, "Cannot add new bus for %s\n", -					 pci_name(dev)); -				pci_dev_put(dev); -				continue; -			} -			child->subordinate = pci_do_scan_bus(child); -			pci_bus_size_bridges(child); -		}  		pci_configure_slot(dev); -		pci_dev_put(dev);  	} -	pci_bus_assign_resources(parent);  	pci_bus_add_devices(parent); -	pci_enable_bridges(parent); -	return 0; + + out: +	pci_unlock_rescan_remove(); +	return ret;  }  int shpchp_unconfigure_device(struct slot *p_slot)  {  	int rc = 0; -	int j;  	u8 bctl = 0;  	struct pci_bus *parent = p_slot->ctrl->pci_dev->subordinate; +	struct pci_dev *dev, *temp;  	struct controller *ctrl = p_slot->ctrl;  	ctrl_dbg(ctrl, "%s: domain:bus:dev = %04x:%02x:%02x\n",  		 __func__, pci_domain_nr(parent), p_slot->bus, p_slot->device); -	for (j = 0; j < 8 ; j++) { -		struct pci_dev *temp = pci_get_slot(parent, -				(p_slot->device << 3) | j); -		if (!temp) +	pci_lock_rescan_remove(); + +	list_for_each_entry_safe(dev, temp, &parent->devices, bus_list) { +		if (PCI_SLOT(dev->devfn) != p_slot->device)  			continue; -		if (temp->hdr_type == PCI_HEADER_TYPE_BRIDGE) { -			pci_read_config_byte(temp, PCI_BRIDGE_CONTROL, &bctl); + +		pci_dev_get(dev); +		if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) { +			pci_read_config_byte(dev, PCI_BRIDGE_CONTROL, &bctl);  			if (bctl & PCI_BRIDGE_CTL_VGA) {  				ctrl_err(ctrl,  					 "Cannot remove display device %s\n", -					 pci_name(temp)); -				pci_dev_put(temp); +					 pci_name(dev)); +				pci_dev_put(dev);  				rc = -EINVAL;  				break;  			}  		} -		pci_remove_bus_device(temp); -		pci_dev_put(temp); +		pci_stop_and_remove_bus_device(dev); +		pci_dev_put(dev);  	} + +	pci_unlock_rescan_remove();  	return rc;  }  | 
