forked from Minki/linux
PCI: generic: Convert to DT resource parsing API
In order to consolidate DT configuration for PCI host controllers in the kernel, a new API, of_pci_get_host_bridge_resources(), was developed to allow parsing and assigning IO/BUS/MEM resources from DT, removing duplicated code present in the majority of PCI host driver implementations. Convert the existing PCI generic host controller driver to the new API. Most of the code parsing ranges and creating resources is now delegated to the of_pci_get_host_bridge_resources() API. The PCI host controller code filters the resulting resource list and maps IO space by using the newly introduced pci_ioremap_iospace() API. New code supports only one IO resource per generic host controller, which should cater for all existing host controller configurations. [bhelgaas: changelog] Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> Acked-by: Will Deacon <will.deacon@arm.com>
This commit is contained in:
parent
a5525b2403
commit
dbf9826d57
@ -32,7 +32,7 @@ struct gen_pci_cfg_bus_ops {
|
|||||||
|
|
||||||
struct gen_pci_cfg_windows {
|
struct gen_pci_cfg_windows {
|
||||||
struct resource res;
|
struct resource res;
|
||||||
struct resource bus_range;
|
struct resource *bus_range;
|
||||||
void __iomem **win;
|
void __iomem **win;
|
||||||
|
|
||||||
const struct gen_pci_cfg_bus_ops *ops;
|
const struct gen_pci_cfg_bus_ops *ops;
|
||||||
@ -50,7 +50,7 @@ static void __iomem *gen_pci_map_cfg_bus_cam(struct pci_bus *bus,
|
|||||||
{
|
{
|
||||||
struct pci_sys_data *sys = bus->sysdata;
|
struct pci_sys_data *sys = bus->sysdata;
|
||||||
struct gen_pci *pci = sys->private_data;
|
struct gen_pci *pci = sys->private_data;
|
||||||
resource_size_t idx = bus->number - pci->cfg.bus_range.start;
|
resource_size_t idx = bus->number - pci->cfg.bus_range->start;
|
||||||
|
|
||||||
return pci->cfg.win[idx] + ((devfn << 8) | where);
|
return pci->cfg.win[idx] + ((devfn << 8) | where);
|
||||||
}
|
}
|
||||||
@ -66,7 +66,7 @@ static void __iomem *gen_pci_map_cfg_bus_ecam(struct pci_bus *bus,
|
|||||||
{
|
{
|
||||||
struct pci_sys_data *sys = bus->sysdata;
|
struct pci_sys_data *sys = bus->sysdata;
|
||||||
struct gen_pci *pci = sys->private_data;
|
struct gen_pci *pci = sys->private_data;
|
||||||
resource_size_t idx = bus->number - pci->cfg.bus_range.start;
|
resource_size_t idx = bus->number - pci->cfg.bus_range->start;
|
||||||
|
|
||||||
return pci->cfg.win[idx] + ((devfn << 12) | where);
|
return pci->cfg.win[idx] + ((devfn << 12) | where);
|
||||||
}
|
}
|
||||||
@ -138,106 +138,50 @@ static const struct of_device_id gen_pci_of_match[] = {
|
|||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(of, 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)
|
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);
|
pci_free_resource_list(&pci->resources);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int gen_pci_parse_request_of_pci_ranges(struct gen_pci *pci)
|
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;
|
int err, res_valid = 0;
|
||||||
struct device *dev = pci->host.dev.parent;
|
struct device *dev = pci->host.dev.parent;
|
||||||
struct device_node *np = dev->of_node;
|
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)) {
|
err = of_pci_get_host_bridge_resources(np, 0, 0xff, &pci->resources,
|
||||||
dev_err(dev, "missing \"ranges\" property\n");
|
&iobase);
|
||||||
return -EINVAL;
|
if (err)
|
||||||
}
|
return err;
|
||||||
|
|
||||||
for_each_of_pci_range(&parser, &range) {
|
list_for_each_entry(win, &pci->resources, list) {
|
||||||
struct resource *parent, *res;
|
struct resource *parent, *res = win->res;
|
||||||
resource_size_t offset;
|
|
||||||
u32 restype = range.flags & IORESOURCE_TYPE_BITS;
|
|
||||||
|
|
||||||
res = devm_kmalloc(dev, sizeof(*res), GFP_KERNEL);
|
switch (resource_type(res)) {
|
||||||
if (!res) {
|
|
||||||
err = -ENOMEM;
|
|
||||||
goto out_release_res;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (restype) {
|
|
||||||
case IORESOURCE_IO:
|
case IORESOURCE_IO:
|
||||||
parent = &ioport_resource;
|
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;
|
break;
|
||||||
case IORESOURCE_MEM:
|
case IORESOURCE_MEM:
|
||||||
parent = &iomem_resource;
|
parent = &iomem_resource;
|
||||||
err = gen_pci_calc_mem_offset(dev, &range, res, &offset);
|
res_valid |= !(res->flags & IORESOURCE_PREFETCH);
|
||||||
res_valid |= !(res->flags & IORESOURCE_PREFETCH || err);
|
|
||||||
break;
|
break;
|
||||||
|
case IORESOURCE_BUS:
|
||||||
|
pci->cfg.bus_range = res;
|
||||||
default:
|
default:
|
||||||
err = -EINVAL;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (err) {
|
err = devm_request_resource(dev, parent, res);
|
||||||
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);
|
|
||||||
if (err)
|
if (err)
|
||||||
goto out_release_res;
|
goto out_release_res;
|
||||||
|
|
||||||
pci_add_resource_offset(&pci->resources, res, offset);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!res_valid) {
|
if (!res_valid) {
|
||||||
@ -262,14 +206,6 @@ static int gen_pci_parse_map_cfg_windows(struct gen_pci *pci)
|
|||||||
struct device *dev = pci->host.dev.parent;
|
struct device *dev = pci->host.dev.parent;
|
||||||
struct device_node *np = dev->of_node;
|
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);
|
err = of_address_to_resource(np, 0, &pci->cfg.res);
|
||||||
if (err) {
|
if (err) {
|
||||||
dev_err(dev, "missing \"reg\" property\n");
|
dev_err(dev, "missing \"reg\" property\n");
|
||||||
@ -277,12 +213,12 @@ static int gen_pci_parse_map_cfg_windows(struct gen_pci *pci)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Limit the bus-range to fit within reg */
|
/* Limit the bus-range to fit within reg */
|
||||||
bus_max = pci->cfg.bus_range.start +
|
bus_max = pci->cfg.bus_range->start +
|
||||||
(resource_size(&pci->cfg.res) >> pci->cfg.ops->bus_shift) - 1;
|
(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,
|
pci->cfg.bus_range->end = min_t(resource_size_t,
|
||||||
bus_max);
|
pci->cfg.bus_range->end, bus_max);
|
||||||
|
|
||||||
pci->cfg.win = devm_kcalloc(dev, resource_size(&pci->cfg.bus_range),
|
pci->cfg.win = devm_kcalloc(dev, resource_size(pci->cfg.bus_range),
|
||||||
sizeof(*pci->cfg.win), GFP_KERNEL);
|
sizeof(*pci->cfg.win), GFP_KERNEL);
|
||||||
if (!pci->cfg.win)
|
if (!pci->cfg.win)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
@ -293,7 +229,7 @@ static int gen_pci_parse_map_cfg_windows(struct gen_pci *pci)
|
|||||||
"Configuration Space"))
|
"Configuration Space"))
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
bus_range = &pci->cfg.bus_range;
|
bus_range = pci->cfg.bus_range;
|
||||||
for (busn = bus_range->start; busn <= bus_range->end; ++busn) {
|
for (busn = bus_range->start; busn <= bus_range->end; ++busn) {
|
||||||
u32 idx = busn - bus_range->start;
|
u32 idx = busn - bus_range->start;
|
||||||
u32 sz = 1 << pci->cfg.ops->bus_shift;
|
u32 sz = 1 << pci->cfg.ops->bus_shift;
|
||||||
@ -305,8 +241,6 @@ static int gen_pci_parse_map_cfg_windows(struct gen_pci *pci)
|
|||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Register bus resource */
|
|
||||||
pci_add_resource(&pci->resources, bus_range);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user