diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/mcb/mcb-internal.h | 9 | ||||
-rw-r--r-- | drivers/mcb/mcb-parse.c | 126 |
2 files changed, 121 insertions, 14 deletions
diff --git a/drivers/mcb/mcb-internal.h b/drivers/mcb/mcb-internal.h index 5254e0285725..d6e6933b19f1 100644 --- a/drivers/mcb/mcb-internal.h +++ b/drivers/mcb/mcb-internal.h @@ -112,6 +112,15 @@ struct chameleon_bdd { u32 size; } __packed; +struct chameleon_bar { + u32 addr; + u32 size; +}; + +#define BAR_CNT(x) ((x) & 0x07) +#define CHAMELEON_BAR_MAX 6 +#define BAR_DESC_SIZE(x) ((x) * sizeof(struct chameleon_bar) + sizeof(__le32)) + int chameleon_parse_cells(struct mcb_bus *bus, phys_addr_t mapbase, void __iomem *base); diff --git a/drivers/mcb/mcb-parse.c b/drivers/mcb/mcb-parse.c index dbecbed0d258..4ca2739b4fad 100644 --- a/drivers/mcb/mcb-parse.c +++ b/drivers/mcb/mcb-parse.c @@ -26,19 +26,20 @@ static inline uint32_t get_next_dtype(void __iomem *p) } static int chameleon_parse_bdd(struct mcb_bus *bus, - phys_addr_t mapbase, + struct chameleon_bar *cb, void __iomem *base) { return 0; } static int chameleon_parse_gdd(struct mcb_bus *bus, - phys_addr_t mapbase, - void __iomem *base) + struct chameleon_bar *cb, + void __iomem *base, int bar_count) { struct chameleon_gdd __iomem *gdd = (struct chameleon_gdd __iomem *) base; struct mcb_device *mdev; + u32 dev_mapbase; u32 offset; u32 size; int ret; @@ -61,13 +62,39 @@ static int chameleon_parse_gdd(struct mcb_bus *bus, mdev->group = GDD_GRP(reg2); mdev->inst = GDD_INS(reg2); + /* + * If the BAR is missing, dev_mapbase is zero, or if the + * device is IO mapped we just print a warning and go on with the + * next device, instead of completely stop the gdd parser + */ + if (mdev->bar > bar_count - 1) { + pr_info("No BAR for 16z%03d\n", mdev->id); + ret = 0; + goto err; + } + + dev_mapbase = cb[mdev->bar].addr; + if (!dev_mapbase) { + pr_info("BAR not assigned for 16z%03d\n", mdev->id); + ret = 0; + goto err; + } + + if (dev_mapbase & 0x01) { + pr_info("IO mapped Device (16z%03d) not yet supported\n", + mdev->id); + ret = 0; + goto err; + } + pr_debug("Found a 16z%03d\n", mdev->id); mdev->irq.start = GDD_IRQ(reg1); mdev->irq.end = GDD_IRQ(reg1); mdev->irq.flags = IORESOURCE_IRQ; - mdev->mem.start = mapbase + offset; + mdev->mem.start = dev_mapbase + offset; + mdev->mem.end = mdev->mem.start + size - 1; mdev->mem.flags = IORESOURCE_MEM; @@ -85,13 +112,76 @@ err: return ret; } +static void chameleon_parse_bar(void __iomem *base, + struct chameleon_bar *cb, int bar_count) +{ + char __iomem *p = base; + int i; + + /* skip reg1 */ + p += sizeof(__le32); + + for (i = 0; i < bar_count; i++) { + cb[i].addr = readl(p); + cb[i].size = readl(p + 4); + + p += sizeof(struct chameleon_bar); + } +} + +static int chameleon_get_bar(char __iomem **base, phys_addr_t mapbase, + struct chameleon_bar **cb) +{ + struct chameleon_bar *c; + int bar_count; + __le32 reg; + u32 dtype; + + /* + * For those devices which are not connected + * to the PCI Bus (e.g. LPC) there is a bar + * descriptor located directly after the + * chameleon header. This header is comparable + * to a PCI header. + */ + dtype = get_next_dtype(*base); + if (dtype == CHAMELEON_DTYPE_BAR) { + reg = readl(*base); + + bar_count = BAR_CNT(reg); + if (bar_count <= 0 && bar_count > CHAMELEON_BAR_MAX) + return -ENODEV; + + c = kcalloc(bar_count, sizeof(struct chameleon_bar), + GFP_KERNEL); + if (!c) + return -ENOMEM; + + chameleon_parse_bar(*base, c, bar_count); + *base += BAR_DESC_SIZE(bar_count); + } else { + c = kzalloc(sizeof(struct chameleon_bar), GFP_KERNEL); + if (!c) + return -ENOMEM; + + bar_count = 1; + c->addr = mapbase; + } + + *cb = c; + + return bar_count; +} + int chameleon_parse_cells(struct mcb_bus *bus, phys_addr_t mapbase, void __iomem *base) { - char __iomem *p = base; struct chameleon_fpga_header *header; - uint32_t dtype; + struct chameleon_bar *cb; + char __iomem *p = base; int num_cells = 0; + uint32_t dtype; + int bar_count; int ret = 0; u32 hsize; @@ -108,8 +198,8 @@ int chameleon_parse_cells(struct mcb_bus *bus, phys_addr_t mapbase, if (header->magic != CHAMELEONV2_MAGIC) { pr_err("Unsupported chameleon version 0x%x\n", header->magic); - kfree(header); - return -ENODEV; + ret = -ENODEV; + goto free_header; } p += hsize; @@ -119,16 +209,20 @@ int chameleon_parse_cells(struct mcb_bus *bus, phys_addr_t mapbase, snprintf(bus->name, CHAMELEON_FILENAME_LEN + 1, "%s", header->filename); + bar_count = chameleon_get_bar(&p, mapbase, &cb); + if (bar_count < 0) + goto free_header; + for_each_chameleon_cell(dtype, p) { switch (dtype) { case CHAMELEON_DTYPE_GENERAL: - ret = chameleon_parse_gdd(bus, mapbase, p); + ret = chameleon_parse_gdd(bus, cb, p, bar_count); if (ret < 0) - goto out; + goto free_bar; p += sizeof(struct chameleon_gdd); break; case CHAMELEON_DTYPE_BRIDGE: - chameleon_parse_bdd(bus, mapbase, p); + chameleon_parse_bdd(bus, cb, p); p += sizeof(struct chameleon_bdd); break; case CHAMELEON_DTYPE_END: @@ -136,8 +230,8 @@ int chameleon_parse_cells(struct mcb_bus *bus, phys_addr_t mapbase, default: pr_err("Invalid chameleon descriptor type 0x%x\n", dtype); - kfree(header); - return -EINVAL; + ret = -EINVAL; + goto free_bar; } num_cells++; } @@ -145,11 +239,15 @@ int chameleon_parse_cells(struct mcb_bus *bus, phys_addr_t mapbase, if (num_cells == 0) num_cells = -EINVAL; + kfree(cb); kfree(header); return num_cells; -out: +free_bar: + kfree(cb); +free_header: kfree(header); + return ret; } EXPORT_SYMBOL_GPL(chameleon_parse_cells); |