| #include <linux/types.h> |
| #include <linux/ioport.h> |
| #include <linux/slab.h> |
| #include <linux/export.h> |
| #include <linux/io.h> |
| #include <linux/mcb.h> |
| |
| #include "mcb-internal.h" |
| |
| struct mcb_parse_priv { |
| phys_addr_t mapbase; |
| void __iomem *base; |
| }; |
| |
| #define for_each_chameleon_cell(dtype, p) \ |
| for ((dtype) = get_next_dtype((p)); \ |
| (dtype) != CHAMELEON_DTYPE_END; \ |
| (dtype) = get_next_dtype((p))) |
| |
| static inline uint32_t get_next_dtype(void __iomem *p) |
| { |
| uint32_t dtype; |
| |
| dtype = readl(p); |
| return dtype >> 28; |
| } |
| |
| static int chameleon_parse_bdd(struct mcb_bus *bus, |
| phys_addr_t mapbase, |
| void __iomem *base) |
| { |
| return 0; |
| } |
| |
| static int chameleon_parse_gdd(struct mcb_bus *bus, |
| phys_addr_t mapbase, |
| void __iomem *base) |
| { |
| struct chameleon_gdd __iomem *gdd = |
| (struct chameleon_gdd __iomem *) base; |
| struct mcb_device *mdev; |
| u32 offset; |
| u32 size; |
| int ret; |
| __le32 reg1; |
| __le32 reg2; |
| |
| mdev = mcb_alloc_dev(bus); |
| if (!mdev) |
| return -ENOMEM; |
| |
| reg1 = readl(&gdd->reg1); |
| reg2 = readl(&gdd->reg2); |
| offset = readl(&gdd->offset); |
| size = readl(&gdd->size); |
| |
| mdev->id = GDD_DEV(reg1); |
| mdev->rev = GDD_REV(reg1); |
| mdev->var = GDD_VAR(reg1); |
| mdev->bar = GDD_BAR(reg1); |
| mdev->group = GDD_GRP(reg2); |
| mdev->inst = GDD_INS(reg2); |
| |
| 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.end = mdev->mem.start + size - 1; |
| mdev->mem.flags = IORESOURCE_MEM; |
| |
| mdev->is_added = false; |
| |
| ret = mcb_device_register(bus, mdev); |
| if (ret < 0) |
| goto err; |
| |
| return 0; |
| |
| err: |
| mcb_free_dev(mdev); |
| |
| return ret; |
| } |
| |
| 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; |
| int num_cells = 0; |
| int ret = 0; |
| u32 hsize; |
| |
| hsize = sizeof(struct chameleon_fpga_header); |
| |
| header = kzalloc(hsize, GFP_KERNEL); |
| if (!header) |
| return -ENOMEM; |
| |
| /* Extract header information */ |
| memcpy_fromio(header, p, hsize); |
| /* We only support chameleon v2 at the moment */ |
| header->magic = le16_to_cpu(header->magic); |
| if (header->magic != CHAMELEONV2_MAGIC) { |
| pr_err("Unsupported chameleon version 0x%x\n", |
| header->magic); |
| kfree(header); |
| return -ENODEV; |
| } |
| p += hsize; |
| |
| pr_debug("header->revision = %d\n", header->revision); |
| pr_debug("header->model = 0x%x ('%c')\n", header->model, |
| header->model); |
| pr_debug("header->minor = %d\n", header->minor); |
| pr_debug("header->bus_type = 0x%x\n", header->bus_type); |
| |
| |
| pr_debug("header->magic = 0x%x\n", header->magic); |
| pr_debug("header->filename = \"%.*s\"\n", CHAMELEON_FILENAME_LEN, |
| header->filename); |
| |
| for_each_chameleon_cell(dtype, p) { |
| switch (dtype) { |
| case CHAMELEON_DTYPE_GENERAL: |
| ret = chameleon_parse_gdd(bus, mapbase, p); |
| if (ret < 0) |
| goto out; |
| p += sizeof(struct chameleon_gdd); |
| break; |
| case CHAMELEON_DTYPE_BRIDGE: |
| chameleon_parse_bdd(bus, mapbase, p); |
| p += sizeof(struct chameleon_bdd); |
| break; |
| case CHAMELEON_DTYPE_END: |
| break; |
| default: |
| pr_err("Invalid chameleon descriptor type 0x%x\n", |
| dtype); |
| return -EINVAL; |
| } |
| num_cells++; |
| } |
| |
| if (num_cells == 0) |
| num_cells = -EINVAL; |
| |
| kfree(header); |
| return num_cells; |
| |
| out: |
| kfree(header); |
| return ret; |
| } |
| EXPORT_SYMBOL_GPL(chameleon_parse_cells); |