| #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, | 
 | 			struct chameleon_bar *cb, | 
 | 			void __iomem *base) | 
 | { | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int chameleon_parse_gdd(struct mcb_bus *bus, | 
 | 			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; | 
 | 	__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); | 
 |  | 
 | 	/* | 
 | 	 * 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 = dev_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; | 
 | } | 
 |  | 
 | 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) | 
 | { | 
 | 	struct chameleon_fpga_header *header; | 
 | 	struct chameleon_bar *cb; | 
 | 	char __iomem *p = base; | 
 | 	int num_cells = 0; | 
 | 	uint32_t dtype; | 
 | 	int bar_count; | 
 | 	int ret; | 
 | 	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); | 
 | 		ret = -ENODEV; | 
 | 		goto free_header; | 
 | 	} | 
 | 	p += hsize; | 
 |  | 
 | 	bus->revision = header->revision; | 
 | 	bus->model = header->model; | 
 | 	bus->minor = header->minor; | 
 | 	snprintf(bus->name, CHAMELEON_FILENAME_LEN + 1, "%s", | 
 | 		 header->filename); | 
 |  | 
 | 	bar_count = chameleon_get_bar(&p, mapbase, &cb); | 
 | 	if (bar_count < 0) { | 
 | 		ret = bar_count; | 
 | 		goto free_header; | 
 | 	} | 
 |  | 
 | 	for_each_chameleon_cell(dtype, p) { | 
 | 		switch (dtype) { | 
 | 		case CHAMELEON_DTYPE_GENERAL: | 
 | 			ret = chameleon_parse_gdd(bus, cb, p, bar_count); | 
 | 			if (ret < 0) | 
 | 				goto free_bar; | 
 | 			p += sizeof(struct chameleon_gdd); | 
 | 			break; | 
 | 		case CHAMELEON_DTYPE_BRIDGE: | 
 | 			chameleon_parse_bdd(bus, cb, p); | 
 | 			p += sizeof(struct chameleon_bdd); | 
 | 			break; | 
 | 		case CHAMELEON_DTYPE_END: | 
 | 			break; | 
 | 		default: | 
 | 			pr_err("Invalid chameleon descriptor type 0x%x\n", | 
 | 				dtype); | 
 | 			ret = -EINVAL; | 
 | 			goto free_bar; | 
 | 		} | 
 | 		num_cells++; | 
 | 	} | 
 |  | 
 | 	if (num_cells == 0) | 
 | 		num_cells = -EINVAL; | 
 |  | 
 | 	kfree(cb); | 
 | 	kfree(header); | 
 | 	return num_cells; | 
 |  | 
 | free_bar: | 
 | 	kfree(cb); | 
 | free_header: | 
 | 	kfree(header); | 
 |  | 
 | 	return ret; | 
 | } | 
 | EXPORT_SYMBOL_GPL(chameleon_parse_cells); |