You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
160 lines
3.3 KiB
160 lines
3.3 KiB
#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(reg2); |
|
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); |
|
kfree(header); |
|
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);
|
|
|