Merge branch 'remotes/lorenzo/pci/keembay'
[linux-2.6-microblaze.git] / drivers / pci / ecam.c
index d2a1920..1c40d25 100644 (file)
@@ -32,7 +32,7 @@ struct pci_config_window *pci_ecam_create(struct device *dev,
        struct pci_config_window *cfg;
        unsigned int bus_range, bus_range_max, bsz;
        struct resource *conflict;
-       int i, err;
+       int err;
 
        if (busr->start > busr->end)
                return ERR_PTR(-EINVAL);
@@ -50,6 +50,7 @@ struct pci_config_window *pci_ecam_create(struct device *dev,
        cfg->busr.start = busr->start;
        cfg->busr.end = busr->end;
        cfg->busr.flags = IORESOURCE_BUS;
+       cfg->bus_shift = bus_shift;
        bus_range = resource_size(&cfg->busr);
        bus_range_max = resource_size(cfgres) >> bus_shift;
        if (bus_range > bus_range_max) {
@@ -77,13 +78,6 @@ struct pci_config_window *pci_ecam_create(struct device *dev,
                cfg->winp = kcalloc(bus_range, sizeof(*cfg->winp), GFP_KERNEL);
                if (!cfg->winp)
                        goto err_exit_malloc;
-               for (i = 0; i < bus_range; i++) {
-                       cfg->winp[i] =
-                               pci_remap_cfgspace(cfgres->start + i * bsz,
-                                                  bsz);
-                       if (!cfg->winp[i])
-                               goto err_exit_iomap;
-               }
        } else {
                cfg->win = pci_remap_cfgspace(cfgres->start, bus_range * bsz);
                if (!cfg->win)
@@ -129,6 +123,44 @@ void pci_ecam_free(struct pci_config_window *cfg)
 }
 EXPORT_SYMBOL_GPL(pci_ecam_free);
 
+static int pci_ecam_add_bus(struct pci_bus *bus)
+{
+       struct pci_config_window *cfg = bus->sysdata;
+       unsigned int bsz = 1 << cfg->bus_shift;
+       unsigned int busn = bus->number;
+       phys_addr_t start;
+
+       if (!per_bus_mapping)
+               return 0;
+
+       if (busn < cfg->busr.start || busn > cfg->busr.end)
+               return -EINVAL;
+
+       busn -= cfg->busr.start;
+       start = cfg->res.start + busn * bsz;
+
+       cfg->winp[busn] = pci_remap_cfgspace(start, bsz);
+       if (!cfg->winp[busn])
+               return -ENOMEM;
+
+       return 0;
+}
+
+static void pci_ecam_remove_bus(struct pci_bus *bus)
+{
+       struct pci_config_window *cfg = bus->sysdata;
+       unsigned int busn = bus->number;
+
+       if (!per_bus_mapping || busn < cfg->busr.start || busn > cfg->busr.end)
+               return;
+
+       busn -= cfg->busr.start;
+       if (cfg->winp[busn]) {
+               iounmap(cfg->winp[busn]);
+               cfg->winp[busn] = NULL;
+       }
+}
+
 /*
  * Function to implement the pci_ops ->map_bus method
  */
@@ -167,6 +199,8 @@ EXPORT_SYMBOL_GPL(pci_ecam_map_bus);
 /* ECAM ops */
 const struct pci_ecam_ops pci_generic_ecam_ops = {
        .pci_ops        = {
+               .add_bus        = pci_ecam_add_bus,
+               .remove_bus     = pci_ecam_remove_bus,
                .map_bus        = pci_ecam_map_bus,
                .read           = pci_generic_config_read,
                .write          = pci_generic_config_write,
@@ -178,6 +212,8 @@ EXPORT_SYMBOL_GPL(pci_generic_ecam_ops);
 /* ECAM ops for 32-bit access only (non-compliant) */
 const struct pci_ecam_ops pci_32b_ops = {
        .pci_ops        = {
+               .add_bus        = pci_ecam_add_bus,
+               .remove_bus     = pci_ecam_remove_bus,
                .map_bus        = pci_ecam_map_bus,
                .read           = pci_generic_config_read32,
                .write          = pci_generic_config_write32,
@@ -187,6 +223,8 @@ const struct pci_ecam_ops pci_32b_ops = {
 /* ECAM ops for 32-bit read only (non-compliant) */
 const struct pci_ecam_ops pci_32b_read_ops = {
        .pci_ops        = {
+               .add_bus        = pci_ecam_add_bus,
+               .remove_bus     = pci_ecam_remove_bus,
                .map_bus        = pci_ecam_map_bus,
                .read           = pci_generic_config_read32,
                .write          = pci_generic_config_write,