aboutsummaryrefslogtreecommitdiff
path: root/drivers/pci/host/pci-host-generic.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pci/host/pci-host-generic.c')
-rw-r--r--drivers/pci/host/pci-host-generic.c230
1 files changed, 66 insertions, 164 deletions
diff --git a/drivers/pci/host/pci-host-generic.c b/drivers/pci/host/pci-host-generic.c
index 3d2076f59911..7f8b9ebc5453 100644
--- a/drivers/pci/host/pci-host-generic.c
+++ b/drivers/pci/host/pci-host-generic.c
@@ -32,13 +32,22 @@ struct gen_pci_cfg_bus_ops {
struct gen_pci_cfg_windows {
struct resource res;
- struct resource bus_range;
+ struct resource *bus_range;
void __iomem **win;
const struct gen_pci_cfg_bus_ops *ops;
};
+/*
+ * ARM pcibios functions expect the ARM struct pci_sys_data as the PCI
+ * sysdata. Add pci_sys_data as the first element in struct gen_pci so
+ * that when we use a gen_pci pointer as sysdata, it is also a pointer to
+ * a struct pci_sys_data.
+ */
struct gen_pci {
+#ifdef CONFIG_ARM
+ struct pci_sys_data sys;
+#endif
struct pci_host_bridge host;
struct gen_pci_cfg_windows cfg;
struct list_head resources;
@@ -48,9 +57,8 @@ static void __iomem *gen_pci_map_cfg_bus_cam(struct pci_bus *bus,
unsigned int devfn,
int where)
{
- struct pci_sys_data *sys = bus->sysdata;
- struct gen_pci *pci = sys->private_data;
- resource_size_t idx = bus->number - pci->cfg.bus_range.start;
+ struct gen_pci *pci = bus->sysdata;
+ resource_size_t idx = bus->number - pci->cfg.bus_range->start;
return pci->cfg.win[idx] + ((devfn << 8) | where);
}
@@ -64,9 +72,8 @@ static void __iomem *gen_pci_map_cfg_bus_ecam(struct pci_bus *bus,
unsigned int devfn,
int where)
{
- struct pci_sys_data *sys = bus->sysdata;
- struct gen_pci *pci = sys->private_data;
- resource_size_t idx = bus->number - pci->cfg.bus_range.start;
+ struct gen_pci *pci = bus->sysdata;
+ resource_size_t idx = bus->number - pci->cfg.bus_range->start;
return pci->cfg.win[idx] + ((devfn << 12) | where);
}
@@ -76,55 +83,9 @@ static struct gen_pci_cfg_bus_ops gen_pci_cfg_ecam_bus_ops = {
.map_bus = gen_pci_map_cfg_bus_ecam,
};
-static int gen_pci_config_read(struct pci_bus *bus, unsigned int devfn,
- int where, int size, u32 *val)
-{
- void __iomem *addr;
- struct pci_sys_data *sys = bus->sysdata;
- struct gen_pci *pci = sys->private_data;
-
- addr = pci->cfg.ops->map_bus(bus, devfn, where);
-
- switch (size) {
- case 1:
- *val = readb(addr);
- break;
- case 2:
- *val = readw(addr);
- break;
- default:
- *val = readl(addr);
- }
-
- return PCIBIOS_SUCCESSFUL;
-}
-
-static int gen_pci_config_write(struct pci_bus *bus, unsigned int devfn,
- int where, int size, u32 val)
-{
- void __iomem *addr;
- struct pci_sys_data *sys = bus->sysdata;
- struct gen_pci *pci = sys->private_data;
-
- addr = pci->cfg.ops->map_bus(bus, devfn, where);
-
- switch (size) {
- case 1:
- writeb(val, addr);
- break;
- case 2:
- writew(val, addr);
- break;
- default:
- writel(val, addr);
- }
-
- return PCIBIOS_SUCCESSFUL;
-}
-
static struct pci_ops gen_pci_ops = {
- .read = gen_pci_config_read,
- .write = gen_pci_config_write,
+ .read = pci_generic_config_read,
+ .write = pci_generic_config_write,
};
static const struct of_device_id gen_pci_of_match[] = {
@@ -138,106 +99,50 @@ static const struct of_device_id gen_pci_of_match[] = {
};
MODULE_DEVICE_TABLE(of, gen_pci_of_match);
-static int gen_pci_calc_io_offset(struct device *dev,
- struct of_pci_range *range,
- struct resource *res,
- resource_size_t *offset)
-{
- static atomic_t wins = ATOMIC_INIT(0);
- int err, idx, max_win;
- unsigned int window;
-
- if (!PAGE_ALIGNED(range->cpu_addr))
- return -EINVAL;
-
- max_win = (IO_SPACE_LIMIT + 1) / SZ_64K;
- idx = atomic_inc_return(&wins);
- if (idx > max_win)
- return -ENOSPC;
-
- window = (idx - 1) * SZ_64K;
- err = pci_ioremap_io(window, range->cpu_addr);
- if (err)
- return err;
-
- of_pci_range_to_resource(range, dev->of_node, res);
- res->start = window;
- res->end = res->start + range->size - 1;
- *offset = window - range->pci_addr;
- return 0;
-}
-
-static int gen_pci_calc_mem_offset(struct device *dev,
- struct of_pci_range *range,
- struct resource *res,
- resource_size_t *offset)
-{
- of_pci_range_to_resource(range, dev->of_node, res);
- *offset = range->cpu_addr - range->pci_addr;
- return 0;
-}
-
static void gen_pci_release_of_pci_ranges(struct gen_pci *pci)
{
- struct pci_host_bridge_window *win;
-
- list_for_each_entry(win, &pci->resources, list)
- release_resource(win->res);
-
pci_free_resource_list(&pci->resources);
}
static int gen_pci_parse_request_of_pci_ranges(struct gen_pci *pci)
{
- struct of_pci_range range;
- struct of_pci_range_parser parser;
int err, res_valid = 0;
struct device *dev = pci->host.dev.parent;
struct device_node *np = dev->of_node;
+ resource_size_t iobase;
+ struct pci_host_bridge_window *win;
- if (of_pci_range_parser_init(&parser, np)) {
- dev_err(dev, "missing \"ranges\" property\n");
- return -EINVAL;
- }
-
- for_each_of_pci_range(&parser, &range) {
- struct resource *parent, *res;
- resource_size_t offset;
- u32 restype = range.flags & IORESOURCE_TYPE_BITS;
+ err = of_pci_get_host_bridge_resources(np, 0, 0xff, &pci->resources,
+ &iobase);
+ if (err)
+ return err;
- res = devm_kmalloc(dev, sizeof(*res), GFP_KERNEL);
- if (!res) {
- err = -ENOMEM;
- goto out_release_res;
- }
+ list_for_each_entry(win, &pci->resources, list) {
+ struct resource *parent, *res = win->res;
- switch (restype) {
+ switch (resource_type(res)) {
case IORESOURCE_IO:
parent = &ioport_resource;
- err = gen_pci_calc_io_offset(dev, &range, res, &offset);
+ err = pci_remap_iospace(res, iobase);
+ if (err) {
+ dev_warn(dev, "error %d: failed to map resource %pR\n",
+ err, res);
+ continue;
+ }
break;
case IORESOURCE_MEM:
parent = &iomem_resource;
- err = gen_pci_calc_mem_offset(dev, &range, res, &offset);
- res_valid |= !(res->flags & IORESOURCE_PREFETCH || err);
+ res_valid |= !(res->flags & IORESOURCE_PREFETCH);
break;
+ case IORESOURCE_BUS:
+ pci->cfg.bus_range = res;
default:
- err = -EINVAL;
continue;
}
- if (err) {
- dev_warn(dev,
- "error %d: failed to add resource [type 0x%x, %lld bytes]\n",
- err, restype, range.size);
- continue;
- }
-
- err = request_resource(parent, res);
+ err = devm_request_resource(dev, parent, res);
if (err)
goto out_release_res;
-
- pci_add_resource_offset(&pci->resources, res, offset);
}
if (!res_valid) {
@@ -262,38 +167,30 @@ static int gen_pci_parse_map_cfg_windows(struct gen_pci *pci)
struct device *dev = pci->host.dev.parent;
struct device_node *np = dev->of_node;
- if (of_pci_parse_bus_range(np, &pci->cfg.bus_range))
- pci->cfg.bus_range = (struct resource) {
- .name = np->name,
- .start = 0,
- .end = 0xff,
- .flags = IORESOURCE_BUS,
- };
-
err = of_address_to_resource(np, 0, &pci->cfg.res);
if (err) {
dev_err(dev, "missing \"reg\" property\n");
return err;
}
- pci->cfg.win = devm_kcalloc(dev, resource_size(&pci->cfg.bus_range),
+ /* Limit the bus-range to fit within reg */
+ bus_max = pci->cfg.bus_range->start +
+ (resource_size(&pci->cfg.res) >> pci->cfg.ops->bus_shift) - 1;
+ pci->cfg.bus_range->end = min_t(resource_size_t,
+ pci->cfg.bus_range->end, bus_max);
+
+ pci->cfg.win = devm_kcalloc(dev, resource_size(pci->cfg.bus_range),
sizeof(*pci->cfg.win), GFP_KERNEL);
if (!pci->cfg.win)
return -ENOMEM;
- /* Limit the bus-range to fit within reg */
- bus_max = pci->cfg.bus_range.start +
- (resource_size(&pci->cfg.res) >> pci->cfg.ops->bus_shift) - 1;
- pci->cfg.bus_range.end = min_t(resource_size_t, pci->cfg.bus_range.end,
- bus_max);
-
/* Map our Configuration Space windows */
if (!devm_request_mem_region(dev, pci->cfg.res.start,
resource_size(&pci->cfg.res),
"Configuration Space"))
return -ENOMEM;
- bus_range = &pci->cfg.bus_range;
+ bus_range = pci->cfg.bus_range;
for (busn = bus_range->start; busn <= bus_range->end; ++busn) {
u32 idx = busn - bus_range->start;
u32 sz = 1 << pci->cfg.ops->bus_shift;
@@ -305,18 +202,9 @@ static int gen_pci_parse_map_cfg_windows(struct gen_pci *pci)
return -ENOMEM;
}
- /* Register bus resource */
- pci_add_resource(&pci->resources, bus_range);
return 0;
}
-static int gen_pci_setup(int nr, struct pci_sys_data *sys)
-{
- struct gen_pci *pci = sys->private_data;
- list_splice_init(&pci->resources, &sys->resources);
- return 1;
-}
-
static int gen_pci_probe(struct platform_device *pdev)
{
int err;
@@ -326,13 +214,7 @@ static int gen_pci_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
struct gen_pci *pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL);
- struct hw_pci hw = {
- .nr_controllers = 1,
- .private_data = (void **)&pci,
- .setup = gen_pci_setup,
- .map_irq = of_irq_parse_and_map_pci,
- .ops = &gen_pci_ops,
- };
+ struct pci_bus *bus, *child;
if (!pci)
return -ENOMEM;
@@ -353,6 +235,7 @@ static int gen_pci_probe(struct platform_device *pdev)
of_id = of_match_node(gen_pci_of_match, np);
pci->cfg.ops = of_id->data;
+ gen_pci_ops.map_bus = pci->cfg.ops->map_bus;
pci->host.dev.parent = dev;
INIT_LIST_HEAD(&pci->host.windows);
INIT_LIST_HEAD(&pci->resources);
@@ -369,14 +252,33 @@ static int gen_pci_probe(struct platform_device *pdev)
return err;
}
- pci_common_init_dev(dev, &hw);
+ /* Do not reassign resources if probe only */
+ if (!pci_has_flag(PCI_PROBE_ONLY))
+ pci_add_flags(PCI_REASSIGN_ALL_RSRC | PCI_REASSIGN_ALL_BUS);
+
+ bus = pci_scan_root_bus(dev, 0, &gen_pci_ops, pci, &pci->resources);
+ if (!bus) {
+ dev_err(dev, "Scanning rootbus failed");
+ return -ENODEV;
+ }
+
+ pci_fixup_irqs(pci_common_swizzle, of_irq_parse_and_map_pci);
+
+ if (!pci_has_flag(PCI_PROBE_ONLY)) {
+ pci_bus_size_bridges(bus);
+ pci_bus_assign_resources(bus);
+
+ list_for_each_entry(child, &bus->children, node)
+ pcie_bus_configure_settings(child);
+ }
+
+ pci_bus_add_devices(bus);
return 0;
}
static struct platform_driver gen_pci_driver = {
.driver = {
.name = "pci-host-generic",
- .owner = THIS_MODULE,
.of_match_table = gen_pci_of_match,
},
.probe = gen_pci_probe,