From tlnguyen@snoqualmie.dp.intel.com Tue Mar 29 12:14:02 2005 Date: Tue, 29 Mar 2005 13:36:43 -0800 From: long To: gregkh@suse.de Subject: [pcie] use device_for_each_child() to properly access child devices. Content-Length: 5866 Lines: 188 On Friday, March 25, 2005 8:47 PM Greg KH wrote: >Here's a fix for pci express. For some reason I don't think they are >using the driver model properly here, but I could be wrong... Thanks for making the changes. However, changes in functions: void pcie_port_device_remove(struct pci_dev *dev) and static int remove_iter(struct device *dev, void *data) are not correct. Please use the patch, which is based on kernel 2.6.12-rc1, below for a fix for these. Signed-off-by: Greg Kroah-Hartman Index: gregkh-2.6/drivers/pci/pcie/portdrv_core.c =================================================================== --- gregkh-2.6.orig/drivers/pci/pcie/portdrv_core.c 2005-04-20 21:25:10.000000000 -0700 +++ gregkh-2.6/drivers/pci/pcie/portdrv_core.c 2005-04-20 21:42:09.000000000 -0700 @@ -232,19 +232,16 @@ /* Initialize generic device interface */ device = &dev->device; memset(device, 0, sizeof(struct device)); - INIT_LIST_HEAD(&device->node); - INIT_LIST_HEAD(&device->children); - INIT_LIST_HEAD(&device->bus_list); device->bus = &pcie_port_bus_type; device->driver = NULL; - device->driver_data = NULL; + device->driver_data = NULL; device->release = release_pcie_device; /* callback to free pcie dev */ - sprintf(&device->bus_id[0], "pcie%02x", + sprintf(&device->bus_id[0], "pcie%02x", get_descriptor_id(port_type, service_type)); device->parent = &parent->dev; } -static struct pcie_device* alloc_pcie_device(struct pci_dev *parent, +static struct pcie_device* alloc_pcie_device(struct pci_dev *parent, int port_type, int service_type, int irq, int irq_mode) { struct pcie_device *device; @@ -270,9 +267,9 @@ pci_read_config_word(dev, pos + PCIE_CAPABILITIES_REG, ®); type = (reg >> 4) & PORT_TYPE_MASK; if ( type == PCIE_RC_PORT || type == PCIE_SW_UPSTREAM_PORT || - type == PCIE_SW_DOWNSTREAM_PORT ) + type == PCIE_SW_DOWNSTREAM_PORT ) return 0; - + return -ENODEV; } @@ -283,8 +280,8 @@ u16 reg16; /* Get port type */ - pci_read_config_word(dev, - pci_find_capability(dev, PCI_CAP_ID_EXP) + + pci_read_config_word(dev, + pci_find_capability(dev, PCI_CAP_ID_EXP) + PCIE_CAPABILITIES_REG, ®16); type = (reg16 >> 4) & PORT_TYPE_MASK; @@ -299,11 +296,11 @@ if (capabilities & (1 << i)) { child = alloc_pcie_device( dev, /* parent */ - type, /* port type */ + type, /* port type */ i, /* service type */ vectors[i], /* irq */ irq_mode /* interrupt mode */); - if (child) { + if (child) { status = device_register(&child->device); if (status) { kfree(child); @@ -317,84 +314,78 @@ } #ifdef CONFIG_PM -int pcie_port_device_suspend(struct pci_dev *dev, pm_message_t state) +static int suspend_iter(struct device *dev, void *data) { - struct list_head *head, *tmp; - struct device *parent, *child; - struct device_driver *driver; struct pcie_port_service_driver *service_driver; + u32 state = (u32)data; - parent = &dev->dev; - head = &parent->children; - tmp = head->next; - while (head != tmp) { - child = container_of(tmp, struct device, node); - tmp = tmp->next; - if (child->bus != &pcie_port_bus_type) - continue; - driver = child->driver; - if (!driver) - continue; - service_driver = to_service_driver(driver); - if (service_driver->suspend) - service_driver->suspend(to_pcie_device(child), state); - } - return 0; + if ((dev->bus == &pcie_port_bus_type) && + (dev->driver)) { + service_driver = to_service_driver(dev->driver); + if (service_driver->suspend) + service_driver->suspend(to_pcie_device(dev), state); + } + return 0; +} + +int pcie_port_device_suspend(struct pci_dev *dev, u32 state) +{ + device_for_each_child(&dev->dev, (void *)state, suspend_iter); + return 0; } -int pcie_port_device_resume(struct pci_dev *dev) -{ - struct list_head *head, *tmp; - struct device *parent, *child; - struct device_driver *driver; +static int resume_iter(struct device *dev, void *data) +{ struct pcie_port_service_driver *service_driver; - parent = &dev->dev; - head = &parent->children; - tmp = head->next; - while (head != tmp) { - child = container_of(tmp, struct device, node); - tmp = tmp->next; - if (child->bus != &pcie_port_bus_type) - continue; - driver = child->driver; - if (!driver) - continue; - service_driver = to_service_driver(driver); - if (service_driver->resume) - service_driver->resume(to_pcie_device(child)); + if ((dev->bus == &pcie_port_bus_type) && + (dev->driver)) { + service_driver = to_service_driver(dev->driver); + if (service_driver->resume) + service_driver->resume(to_pcie_device(dev)); } - return 0; + return 0; +} +int pcie_port_device_resume(struct pci_dev *dev) +{ + device_for_each_child(&dev->dev, NULL, resume_iter); + return 0; } #endif -void pcie_port_device_remove(struct pci_dev *dev) +static int remove_iter(struct device *dev, void *data) { - struct list_head *head, *tmp; - struct device *parent, *child; - struct device_driver *driver; struct pcie_port_service_driver *service_driver; - int interrupt_mode = PCIE_PORT_INTx_MODE; - parent = &dev->dev; - head = &parent->children; - tmp = head->next; - while (head != tmp) { - child = container_of(tmp, struct device, node); - tmp = tmp->next; - if (child->bus != &pcie_port_bus_type) - continue; - driver = child->driver; - if (driver) { - service_driver = to_service_driver(driver); - if (service_driver->remove) - service_driver->remove(to_pcie_device(child)); + if (dev->bus == &pcie_port_bus_type) { + if (dev->driver) { + service_driver = to_service_driver(dev->driver); + if (service_driver->remove) + service_driver->remove(to_pcie_device(dev)); } - interrupt_mode = (to_pcie_device(child))->interrupt_mode; - put_device(child); - device_unregister(child); + *(unsigned long*)data = (unsigned long)dev; + return 1; } + return 0; +} + +void pcie_port_device_remove(struct pci_dev *dev) +{ + struct device *device; + unsigned long device_addr; + int interrupt_mode = PCIE_PORT_INTx_MODE; + int status; + + do { + status = device_for_each_child(&dev->dev, &device_addr, remove_iter); + if (status) { + device = (struct device*)device_addr; + interrupt_mode = (to_pcie_device(device))->interrupt_mode; + put_device(device); + device_unregister(device); + } + } while (status); /* Switch to INTx by default if MSI enabled */ if (interrupt_mode == PCIE_PORT_MSIX_MODE) pci_disable_msix(dev); @@ -423,7 +414,7 @@ new->driver.resume = pcie_port_resume_service; return driver_register(&new->driver); -} +} void pcie_port_service_unregister(struct pcie_port_service_driver *new) {