diff options
Diffstat (limited to 'drivers/pci/hotplug/acpiphp_glue.c')
| -rw-r--r-- | drivers/pci/hotplug/acpiphp_glue.c | 292 | 
1 files changed, 63 insertions, 229 deletions
diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 3d6d4fd1e3c..4681d2c9b1d 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -325,8 +325,8 @@ static void init_bridge_misc(struct acpiphp_bridge *bridge)  		return;  	} -	/* install notify handler */ -	if (bridge->type != BRIDGE_TYPE_HOST) { +	/* install notify handler for P2P bridges */ +	if (!pci_is_root_bus(bridge->pci_bus)) {  		if ((bridge->flags & BRIDGE_HAS_EJ0) && bridge->func) {  			status = acpi_remove_notify_handler(bridge->func->handle,  						ACPI_SYSTEM_NOTIFY, @@ -369,27 +369,12 @@ static struct acpiphp_func *acpiphp_bridge_handle_to_function(acpi_handle handle  static inline void config_p2p_bridge_flags(struct acpiphp_bridge *bridge)  {  	acpi_handle dummy_handle; +	struct acpiphp_func *func;  	if (ACPI_SUCCESS(acpi_get_handle(bridge->handle, -					"_STA", &dummy_handle))) -		bridge->flags |= BRIDGE_HAS_STA; - -	if (ACPI_SUCCESS(acpi_get_handle(bridge->handle, -					"_EJ0", &dummy_handle))) +					"_EJ0", &dummy_handle))) {  		bridge->flags |= BRIDGE_HAS_EJ0; -	if (ACPI_SUCCESS(acpi_get_handle(bridge->handle, -					"_PS0", &dummy_handle))) -		bridge->flags |= BRIDGE_HAS_PS0; - -	if (ACPI_SUCCESS(acpi_get_handle(bridge->handle, -					"_PS3", &dummy_handle))) -		bridge->flags |= BRIDGE_HAS_PS3; - -	/* is this ejectable p2p bridge? */ -	if (bridge->flags & BRIDGE_HAS_EJ0) { -		struct acpiphp_func *func; -  		dbg("found ejectable p2p bridge\n");  		/* make link between PCI bridge and PCI function */ @@ -412,7 +397,6 @@ static void add_host_bridge(struct acpi_pci_root *root)  	if (bridge == NULL)  		return; -	bridge->type = BRIDGE_TYPE_HOST;  	bridge->handle = handle;  	bridge->pci_bus = root->bus; @@ -432,7 +416,6 @@ static void add_p2p_bridge(acpi_handle *handle)  		return;  	} -	bridge->type = BRIDGE_TYPE_P2P;  	bridge->handle = handle;  	config_p2p_bridge_flags(bridge); @@ -543,13 +526,15 @@ static void cleanup_bridge(struct acpiphp_bridge *bridge)  	acpi_status status;  	acpi_handle handle = bridge->handle; -	status = acpi_remove_notify_handler(handle, ACPI_SYSTEM_NOTIFY, +	if (!pci_is_root_bus(bridge->pci_bus)) { +		status = acpi_remove_notify_handler(handle, +					    ACPI_SYSTEM_NOTIFY,  					    handle_hotplug_event_bridge); -	if (ACPI_FAILURE(status)) -		err("failed to remove notify handler\n"); +		if (ACPI_FAILURE(status)) +			err("failed to remove notify handler\n"); +	} -	if ((bridge->type != BRIDGE_TYPE_HOST) && -	    ((bridge->flags & BRIDGE_HAS_EJ0) && bridge->func)) { +	if ((bridge->flags & BRIDGE_HAS_EJ0) && bridge->func) {  		status = acpi_install_notify_handler(bridge->func->handle,  						ACPI_SYSTEM_NOTIFY,  						handle_hotplug_event_func, @@ -630,9 +615,6 @@ static void remove_bridge(struct acpi_pci_root *root)  	bridge = acpiphp_handle_to_bridge(handle);  	if (bridge)  		cleanup_bridge(bridge); -	else -		acpi_remove_notify_handler(handle, ACPI_SYSTEM_NOTIFY, -					   handle_hotplug_event_bridge);  }  static int power_on_slot(struct acpiphp_slot *slot) @@ -734,34 +716,25 @@ static unsigned char acpiphp_max_busnr(struct pci_bus *bus)   */  static int acpiphp_bus_add(struct acpiphp_func *func)  { -	acpi_handle phandle; -	struct acpi_device *device, *pdevice; +	struct acpi_device *device;  	int ret_val; -	acpi_get_parent(func->handle, &phandle); -	if (acpi_bus_get_device(phandle, &pdevice)) { -		dbg("no parent device, assuming NULL\n"); -		pdevice = NULL; -	}  	if (!acpi_bus_get_device(func->handle, &device)) {  		dbg("bus exists... trim\n");  		/* this shouldn't be in here, so remove  		 * the bus then re-add it...  		 */ -		ret_val = acpi_bus_trim(device, 1); +		ret_val = acpi_bus_trim(device);  		dbg("acpi_bus_trim return %x\n", ret_val);  	} -	ret_val = acpi_bus_add(&device, pdevice, func->handle, -		ACPI_BUS_TYPE_DEVICE); -	if (ret_val) { -		dbg("error adding bus, %x\n", -			-ret_val); -		goto acpiphp_bus_add_out; -	} -	ret_val = acpi_bus_start(device); +	ret_val = acpi_bus_scan(func->handle); +	if (!ret_val) +		ret_val = acpi_bus_get_device(func->handle, &device); + +	if (ret_val) +		dbg("error adding bus, %x\n", -ret_val); -acpiphp_bus_add_out:  	return ret_val;  } @@ -781,7 +754,7 @@ static int acpiphp_bus_trim(acpi_handle handle)  		return retval;  	} -	retval = acpi_bus_trim(device, 1); +	retval = acpi_bus_trim(device);  	if (retval)  		err("cannot remove from acpi list\n"); @@ -806,6 +779,29 @@ static void acpiphp_set_acpi_region(struct acpiphp_slot *slot)  	}  } +static void check_hotplug_bridge(struct acpiphp_slot *slot, struct pci_dev *dev) +{ +	struct acpiphp_func *func; + +	if (!dev->subordinate) +		return; + +	/* quirk, or pcie could set it already */ +	if (dev->is_hotplug_bridge) +		return; + +	if (PCI_SLOT(dev->devfn) != slot->device) +		return; + +	list_for_each_entry(func, &slot->funcs, sibling) { +		if (PCI_FUNC(dev->devfn) == func->function) { +			/* check if this bridge has ejectable slots */ +			if ((detect_ejectable_slots(func->handle) > 0)) +				dev->is_hotplug_bridge = 1; +			break; +		} +	} +}  /**   * enable_device - enable, configure a slot   * @slot: slot to be enabled @@ -825,6 +821,9 @@ static int __ref enable_device(struct acpiphp_slot *slot)  	if (slot->flags & SLOT_ENABLED)  		goto err_exit; +	list_for_each_entry(func, &slot->funcs, sibling) +		acpiphp_bus_add(func); +  	num = pci_scan_slot(bus, PCI_DEVFN(slot->device, 0));  	if (num == 0) {  		/* Maybe only part of funcs are added. */ @@ -840,15 +839,14 @@ static int __ref enable_device(struct acpiphp_slot *slot)  			if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE ||  			    dev->hdr_type == PCI_HEADER_TYPE_CARDBUS) {  				max = pci_scan_bridge(bus, dev, max, pass); -				if (pass && dev->subordinate) +				if (pass && dev->subordinate) { +					check_hotplug_bridge(slot, dev);  					pci_bus_size_bridges(dev->subordinate); +				}  			}  		}  	} -	list_for_each_entry(func, &slot->funcs, sibling) -		acpiphp_bus_add(func); -  	pci_bus_assign_resources(bus);  	acpiphp_sanitize_bus(bus);  	acpiphp_set_hpp_values(bus); @@ -1106,73 +1104,11 @@ static void acpiphp_sanitize_bus(struct pci_bus *bus)  	}  } -/* Program resources in newly inserted bridge */ -static int acpiphp_configure_bridge (acpi_handle handle) -{ -	struct pci_bus *bus; - -	if (acpi_is_root_bridge(handle)) { -		struct acpi_pci_root *root = acpi_pci_find_root(handle); -		bus = root->bus; -	} else { -		struct pci_dev *pdev = acpi_get_pci_dev(handle); -		bus = pdev->subordinate; -		pci_dev_put(pdev); -	} - -	pci_bus_size_bridges(bus); -	pci_bus_assign_resources(bus); -	acpiphp_sanitize_bus(bus); -	acpiphp_set_hpp_values(bus); -	pci_enable_bridges(bus); -	return 0; -} - -static void handle_bridge_insertion(acpi_handle handle, u32 type) -{ -	struct acpi_device *device, *pdevice; -	acpi_handle phandle; - -	if ((type != ACPI_NOTIFY_BUS_CHECK) && -			(type != ACPI_NOTIFY_DEVICE_CHECK)) { -		err("unexpected notification type %d\n", type); -		return; -	} - -	acpi_get_parent(handle, &phandle); -	if (acpi_bus_get_device(phandle, &pdevice)) { -		dbg("no parent device, assuming NULL\n"); -		pdevice = NULL; -	} -	if (acpi_bus_add(&device, pdevice, handle, ACPI_BUS_TYPE_DEVICE)) { -		err("cannot add bridge to acpi list\n"); -		return; -	} -	if (!acpiphp_configure_bridge(handle) && -		!acpi_bus_start(device)) -		add_bridge(handle); -	else -		err("cannot configure and start bridge\n"); - -} -  /*   * ACPI event handlers   */  static acpi_status -count_sub_bridges(acpi_handle handle, u32 lvl, void *context, void **rv) -{ -	int *count = (int *)context; -	struct acpiphp_bridge *bridge; - -	bridge = acpiphp_handle_to_bridge(handle); -	if (bridge) -		(*count)++; -	return AE_OK ; -} - -static acpi_status  check_sub_bridges(acpi_handle handle, u32 lvl, void *context, void **rv)  {  	struct acpiphp_bridge *bridge; @@ -1190,66 +1126,20 @@ check_sub_bridges(acpi_handle handle, u32 lvl, void *context, void **rv)  	return AE_OK ;  } -struct acpiphp_hp_work { -	struct work_struct work; -	acpi_handle handle; -	u32 type; -	void *context; -}; - -static void alloc_acpiphp_hp_work(acpi_handle handle, u32 type, -				  void *context, -				  void (*func)(struct work_struct *work)) -{ -	struct acpiphp_hp_work *hp_work; -	int ret; - -	hp_work = kmalloc(sizeof(*hp_work), GFP_KERNEL); -	if (!hp_work) -		return; - -	hp_work->handle = handle; -	hp_work->type = type; -	hp_work->context = context; - -	INIT_WORK(&hp_work->work, func); -	ret = queue_work(kacpi_hotplug_wq, &hp_work->work); -	if (!ret) -		kfree(hp_work); -} -  static void _handle_hotplug_event_bridge(struct work_struct *work)  {  	struct acpiphp_bridge *bridge;  	char objname[64];  	struct acpi_buffer buffer = { .length = sizeof(objname),  				      .pointer = objname }; -	struct acpi_device *device; -	int num_sub_bridges = 0; -	struct acpiphp_hp_work *hp_work; +	struct acpi_hp_work *hp_work;  	acpi_handle handle;  	u32 type; -	hp_work = container_of(work, struct acpiphp_hp_work, work); +	hp_work = container_of(work, struct acpi_hp_work, work);  	handle = hp_work->handle;  	type = hp_work->type; - -	if (acpi_bus_get_device(handle, &device)) { -		/* This bridge must have just been physically inserted */ -		handle_bridge_insertion(handle, type); -		goto out; -	} - -	bridge = acpiphp_handle_to_bridge(handle); -	if (type == ACPI_NOTIFY_BUS_CHECK) { -		acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, ACPI_UINT32_MAX, -			count_sub_bridges, NULL, &num_sub_bridges, NULL); -	} - -	if (!bridge && !num_sub_bridges) { -		err("cannot get bridge info\n"); -		goto out; -	} +	bridge = (struct acpiphp_bridge *)hp_work->context;  	acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); @@ -1257,14 +1147,10 @@ static void _handle_hotplug_event_bridge(struct work_struct *work)  	case ACPI_NOTIFY_BUS_CHECK:  		/* bus re-enumerate */  		dbg("%s: Bus check notify on %s\n", __func__, objname); -		if (bridge) { -			dbg("%s: re-enumerating slots under %s\n", -				__func__, objname); -			acpiphp_check_bridge(bridge); -		} -		if (num_sub_bridges) -			acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, -				ACPI_UINT32_MAX, check_sub_bridges, NULL, NULL, NULL); +		dbg("%s: re-enumerating slots under %s\n", __func__, objname); +		acpiphp_check_bridge(bridge); +		acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, +			ACPI_UINT32_MAX, check_sub_bridges, NULL, NULL, NULL);  		break;  	case ACPI_NOTIFY_DEVICE_CHECK: @@ -1281,8 +1167,7 @@ static void _handle_hotplug_event_bridge(struct work_struct *work)  	case ACPI_NOTIFY_EJECT_REQUEST:  		/* request device eject */  		dbg("%s: Device eject notify on %s\n", __func__, objname); -		if ((bridge->type != BRIDGE_TYPE_HOST) && -		    (bridge->flags & BRIDGE_HAS_EJ0)) { +		if ((bridge->flags & BRIDGE_HAS_EJ0) && bridge->func) {  			struct acpiphp_slot *slot;  			slot = bridge->func->slot;  			if (!acpiphp_disable_slot(slot)) @@ -1310,7 +1195,6 @@ static void _handle_hotplug_event_bridge(struct work_struct *work)  		break;  	} -out:  	kfree(hp_work); /* allocated in handle_hotplug_event_bridge */  } @@ -1333,8 +1217,7 @@ static void handle_hotplug_event_bridge(acpi_handle handle, u32 type,  	 * For now just re-add this work to the kacpi_hotplug_wq so we  	 * don't deadlock on hotplug actions.  	 */ -	alloc_acpiphp_hp_work(handle, type, context, -			      _handle_hotplug_event_bridge); +	alloc_acpi_hp_work(handle, type, context, _handle_hotplug_event_bridge);  }  static void _handle_hotplug_event_func(struct work_struct *work) @@ -1343,20 +1226,17 @@ static void _handle_hotplug_event_func(struct work_struct *work)  	char objname[64];  	struct acpi_buffer buffer = { .length = sizeof(objname),  				      .pointer = objname }; -	struct acpiphp_hp_work *hp_work; +	struct acpi_hp_work *hp_work;  	acpi_handle handle;  	u32 type; -	void *context; -	hp_work = container_of(work, struct acpiphp_hp_work, work); +	hp_work = container_of(work, struct acpi_hp_work, work);  	handle = hp_work->handle;  	type = hp_work->type; -	context = hp_work->context; +	func = (struct acpiphp_func *)hp_work->context;  	acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); -	func = (struct acpiphp_func *)context; -  	switch (type) {  	case ACPI_NOTIFY_BUS_CHECK:  		/* bus re-enumerate */ @@ -1409,23 +1289,7 @@ static void handle_hotplug_event_func(acpi_handle handle, u32 type,  	 * For now just re-add this work to the kacpi_hotplug_wq so we  	 * don't deadlock on hotplug actions.  	 */ -	alloc_acpiphp_hp_work(handle, type, context, -			      _handle_hotplug_event_func); -} - -static acpi_status -find_root_bridges(acpi_handle handle, u32 lvl, void *context, void **rv) -{ -	int *count = (int *)context; - -	if (!acpi_is_root_bridge(handle)) -		return AE_OK; - -	(*count)++; -	acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY, -				    handle_hotplug_event_bridge, NULL); - -	return AE_OK ; +	alloc_acpi_hp_work(handle, type, context, _handle_hotplug_event_func);  }  static struct acpi_pci_driver acpi_pci_hp_driver = { @@ -1438,15 +1302,7 @@ static struct acpi_pci_driver acpi_pci_hp_driver = {   */  int __init acpiphp_glue_init(void)  { -	int num = 0; - -	acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, -			ACPI_UINT32_MAX, find_root_bridges, NULL, &num, NULL); - -	if (num <= 0) -		return -1; -	else -		acpi_pci_register_driver(&acpi_pci_hp_driver); +	acpi_pci_register_driver(&acpi_pci_hp_driver);  	return 0;  } @@ -1462,28 +1318,6 @@ void  acpiphp_glue_exit(void)  	acpi_pci_unregister_driver(&acpi_pci_hp_driver);  } - -/** - * acpiphp_get_num_slots - count number of slots in a system - */ -int __init acpiphp_get_num_slots(void) -{ -	struct acpiphp_bridge *bridge; -	int num_slots = 0; - -	list_for_each_entry(bridge, &bridge_list, list) { -		dbg("Bus %04x:%02x has %d slot%s\n", -				pci_domain_nr(bridge->pci_bus), -				bridge->pci_bus->number, bridge->nr_slots, -				bridge->nr_slots == 1 ? "" : "s"); -		num_slots += bridge->nr_slots; -	} - -	dbg("Total %d slots\n", num_slots); -	return num_slots; -} - -  /**   * acpiphp_enable_slot - power on slot   * @slot: ACPI PHP slot  |