arch/tile: improve support for PCI hotplug
Chris Metcalf [Mon, 2 May 2011 19:09:42 +0000 (15:09 -0400)]
Note that this is not complete hot-plug support; hot-unplug is not included.

Signed-off-by: Chris Metcalf <cmetcalf@tilera.com>

arch/tile/Kconfig
arch/tile/include/asm/pci.h
arch/tile/kernel/pci.c

index e32b0c2..635e1bf 100644 (file)
@@ -339,6 +339,14 @@ config NO_IOPORT
 
 source "drivers/pci/Kconfig"
 
+config HOTPLUG
+       bool "Support for hot-pluggable devices"
+       ---help---
+         Say Y here if you want to plug devices into your computer while
+         the system is running, and be able to use them quickly.  In many
+         cases, the devices can likewise be unplugged at any time too.
+         One well-known example of this is USB.
+
 source "drivers/pci/hotplug/Kconfig"
 
 endmenu
index c3fc458..7f03cef 100644 (file)
@@ -46,7 +46,8 @@ struct pci_controller {
  */
 #define PCI_DMA_BUS_IS_PHYS     1
 
-int __init tile_pci_init(void);
+int __devinit tile_pci_init(void);
+int __devinit pcibios_init(void);
 
 void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long max);
 static inline void pci_iounmap(struct pci_dev *dev, void __iomem *addr) {}
index ea38f0c..65add02 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2010 Tilera Corporation. All Rights Reserved.
+ * Copyright 2011 Tilera Corporation. All Rights Reserved.
  *
  *   This program is free software; you can redistribute it and/or
  *   modify it under the terms of the GNU General Public License
@@ -59,6 +59,7 @@ int __write_once tile_plx_gen1;
 
 static struct pci_controller controllers[TILE_NUM_PCIE];
 static int num_controllers;
+static int pci_scan_flags[TILE_NUM_PCIE];
 
 static struct pci_ops tile_cfg_ops;
 
@@ -79,7 +80,7 @@ EXPORT_SYMBOL(pcibios_align_resource);
  * controller_id is the controller number, config type is 0 or 1 for
  * config0 or config1 operations.
  */
-static int __init tile_pcie_open(int controller_id, int config_type)
+static int __devinit tile_pcie_open(int controller_id, int config_type)
 {
        char filename[32];
        int fd;
@@ -95,7 +96,7 @@ static int __init tile_pcie_open(int controller_id, int config_type)
 /*
  * Get the IRQ numbers from the HV and set up the handlers for them.
  */
-static int __init tile_init_irqs(int controller_id,
+static int __devinit tile_init_irqs(int controller_id,
                                 struct pci_controller *controller)
 {
        char filename[32];
@@ -139,71 +140,80 @@ static int __init tile_init_irqs(int controller_id,
  *
  * Returns the number of controllers discovered.
  */
-int __init tile_pci_init(void)
+int __devinit tile_pci_init(void)
 {
        int i;
 
        pr_info("PCI: Searching for controllers...\n");
 
+       /* Re-init number of PCIe controllers to support hot-plug feature. */
+       num_controllers = 0;
+
        /* Do any configuration we need before using the PCIe */
 
        for (i = 0; i < TILE_NUM_PCIE; i++) {
-               int hv_cfg_fd0 = -1;
-               int hv_cfg_fd1 = -1;
-               int hv_mem_fd = -1;
-               char name[32];
-               struct pci_controller *controller;
-
                /*
-                * Open the fd to the HV.  If it fails then this
-                * device doesn't exist.
+                * To see whether we need a real config op based on
+                * the results of pcibios_init(), to support PCIe hot-plug.
                 */
-               hv_cfg_fd0 = tile_pcie_open(i, 0);
-               if (hv_cfg_fd0 < 0)
-                       continue;
-               hv_cfg_fd1 = tile_pcie_open(i, 1);
-               if (hv_cfg_fd1 < 0) {
-                       pr_err("PCI: Couldn't open config fd to HV "
-                           "for controller %d\n", i);
-                       goto err_cont;
-               }
+               if (pci_scan_flags[i] == 0) {
+                       int hv_cfg_fd0 = -1;
+                       int hv_cfg_fd1 = -1;
+                       int hv_mem_fd = -1;
+                       char name[32];
+                       struct pci_controller *controller;
+
+                       /*
+                        * Open the fd to the HV.  If it fails then this
+                        * device doesn't exist.
+                        */
+                       hv_cfg_fd0 = tile_pcie_open(i, 0);
+                       if (hv_cfg_fd0 < 0)
+                               continue;
+                       hv_cfg_fd1 = tile_pcie_open(i, 1);
+                       if (hv_cfg_fd1 < 0) {
+                               pr_err("PCI: Couldn't open config fd to HV "
+                                   "for controller %d\n", i);
+                               goto err_cont;
+                       }
 
-               sprintf(name, "pcie/%d/mem", i);
-               hv_mem_fd = hv_dev_open((HV_VirtAddr)name, 0);
-               if (hv_mem_fd < 0) {
-                       pr_err("PCI: Could not open mem fd to HV!\n");
-                       goto err_cont;
-               }
+                       sprintf(name, "pcie/%d/mem", i);
+                       hv_mem_fd = hv_dev_open((HV_VirtAddr)name, 0);
+                       if (hv_mem_fd < 0) {
+                               pr_err("PCI: Could not open mem fd to HV!\n");
+                               goto err_cont;
+                       }
 
-               pr_info("PCI: Found PCI controller #%d\n", i);
+                       pr_info("PCI: Found PCI controller #%d\n", i);
 
-               controller = &controllers[num_controllers];
+                       controller = &controllers[i];
 
-               if (tile_init_irqs(i, controller)) {
-                       pr_err("PCI: Could not initialize "
-                              "IRQs, aborting.\n");
-                       goto err_cont;
-               }
+                       if (tile_init_irqs(i, controller)) {
+                               pr_err("PCI: Could not initialize "
+                                      "IRQs, aborting.\n");
+                               goto err_cont;
+                       }
 
-               controller->index = num_controllers;
-               controller->hv_cfg_fd[0] = hv_cfg_fd0;
-               controller->hv_cfg_fd[1] = hv_cfg_fd1;
-               controller->hv_mem_fd = hv_mem_fd;
-               controller->first_busno = 0;
-               controller->last_busno = 0xff;
-               controller->ops = &tile_cfg_ops;
+                       controller->index = i;
+                       controller->hv_cfg_fd[0] = hv_cfg_fd0;
+                       controller->hv_cfg_fd[1] = hv_cfg_fd1;
+                       controller->hv_mem_fd = hv_mem_fd;
+                       controller->first_busno = 0;
+                       controller->last_busno = 0xff;
+                       controller->ops = &tile_cfg_ops;
 
-               num_controllers++;
-               continue;
+                       num_controllers++;
+                       continue;
 
 err_cont:
-               if (hv_cfg_fd0 >= 0)
-                       hv_dev_close(hv_cfg_fd0);
-               if (hv_cfg_fd1 >= 0)
-                       hv_dev_close(hv_cfg_fd1);
-               if (hv_mem_fd >= 0)
-                       hv_dev_close(hv_mem_fd);
-               continue;
+                       if (hv_cfg_fd0 >= 0)
+                               hv_dev_close(hv_cfg_fd0);
+                       if (hv_cfg_fd1 >= 0)
+                               hv_dev_close(hv_cfg_fd1);
+                       if (hv_mem_fd >= 0)
+                               hv_dev_close(hv_mem_fd);
+                       continue;
+               }
        }
 
        /*
@@ -232,7 +242,7 @@ static int tile_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
 }
 
 
-static void __init fixup_read_and_payload_sizes(void)
+static void __devinit fixup_read_and_payload_sizes(void)
 {
        struct pci_dev *dev = NULL;
        int smallest_max_payload = 0x1; /* Tile maxes out at 256 bytes. */
@@ -282,7 +292,7 @@ static void __init fixup_read_and_payload_sizes(void)
  * The controllers have been set up by the time we get here, by a call to
  * tile_pci_init.
  */
-static int __init pcibios_init(void)
+int __devinit pcibios_init(void)
 {
        int i;
 
@@ -296,25 +306,31 @@ static int __init pcibios_init(void)
        mdelay(250);
 
        /* Scan all of the recorded PCI controllers.  */
-       for (i = 0; i < num_controllers; i++) {
-               struct pci_controller *controller = &controllers[i];
-               struct pci_bus *bus;
-
-               pr_info("PCI: initializing controller #%d\n", i);
-
+       for (i = 0; i < TILE_NUM_PCIE; i++) {
                /*
-                * This comes from the generic Linux PCI driver.
-                *
-                * It reads the PCI tree for this bus into the Linux
-                * data structures.
-                *
-                * This is inlined in linux/pci.h and calls into
-                * pci_scan_bus_parented() in probe.c.
+                * Do real pcibios init ops if the controller is initialized
+                * by tile_pci_init() successfully and not initialized by
+                * pcibios_init() yet to support PCIe hot-plug.
                 */
-               bus = pci_scan_bus(0, controller->ops, controller);
-               controller->root_bus = bus;
-               controller->last_busno = bus->subordinate;
-
+               if (pci_scan_flags[i] == 0 && controllers[i].ops != NULL) {
+                       struct pci_controller *controller = &controllers[i];
+                       struct pci_bus *bus;
+
+                       pr_info("PCI: initializing controller #%d\n", i);
+
+                       /*
+                        * This comes from the generic Linux PCI driver.
+                        *
+                        * It reads the PCI tree for this bus into the Linux
+                        * data structures.
+                        *
+                        * This is inlined in linux/pci.h and calls into
+                        * pci_scan_bus_parented() in probe.c.
+                        */
+                       bus = pci_scan_bus(0, controller->ops, controller);
+                       controller->root_bus = bus;
+                       controller->last_busno = bus->subordinate;
+               }
        }
 
        /* Do machine dependent PCI interrupt routing */
@@ -326,34 +342,45 @@ static int __init pcibios_init(void)
         * It allocates all of the resources (I/O memory, etc)
         * associated with the devices read in above.
         */
-
        pci_assign_unassigned_resources();
 
        /* Configure the max_read_size and max_payload_size values. */
        fixup_read_and_payload_sizes();
 
        /* Record the I/O resources in the PCI controller structure. */
-       for (i = 0; i < num_controllers; i++) {
-               struct pci_bus *root_bus = controllers[i].root_bus;
-               struct pci_bus *next_bus;
-               struct pci_dev *dev;
-
-               list_for_each_entry(dev, &root_bus->devices, bus_list) {
-                       /* Find the PCI host controller, ie. the 1st bridge. */
-                       if ((dev->class >> 8) == PCI_CLASS_BRIDGE_PCI &&
-                               (PCI_SLOT(dev->devfn) == 0)) {
-                               next_bus = dev->subordinate;
-                               controllers[i].mem_resources[0] =
-                                       *next_bus->resource[0];
-                               controllers[i].mem_resources[1] =
-                                        *next_bus->resource[1];
-                               controllers[i].mem_resources[2] =
-                                        *next_bus->resource[2];
-
-                               break;
+       for (i = 0; i < TILE_NUM_PCIE; i++) {
+               /*
+                * Do real pcibios init ops if the controller is initialized
+                * by tile_pci_init() successfully and not initialized by
+                * pcibios_init() yet to support PCIe hot-plug.
+                */
+               if (pci_scan_flags[i] == 0 && controllers[i].ops != NULL) {
+                       struct pci_bus *root_bus = controllers[i].root_bus;
+                       struct pci_bus *next_bus;
+                       struct pci_dev *dev;
+
+                       list_for_each_entry(dev, &root_bus->devices, bus_list) {
+                               /*
+                                * Find the PCI host controller, ie. the 1st
+                                * bridge.
+                                */
+                               if ((dev->class >> 8) == PCI_CLASS_BRIDGE_PCI &&
+                                       (PCI_SLOT(dev->devfn) == 0)) {
+                                       next_bus = dev->subordinate;
+                                       controllers[i].mem_resources[0] =
+                                               *next_bus->resource[0];
+                                       controllers[i].mem_resources[1] =
+                                                *next_bus->resource[1];
+                                       controllers[i].mem_resources[2] =
+                                                *next_bus->resource[2];
+
+                                       /* Setup flags. */
+                                       pci_scan_flags[i] = 1;
+
+                                       break;
+                               }
                        }
                }
-
        }
 
        return 0;
@@ -381,7 +408,7 @@ char __devinit *pcibios_setup(char *str)
 /*
  * This is called from the generic Linux layer.
  */
-void __init pcibios_update_irq(struct pci_dev *dev, int irq)
+void __devinit pcibios_update_irq(struct pci_dev *dev, int irq)
 {
        pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq);
 }