brintos

brintos / linux-shallow public Read only

0
0
Text · 5.2 KiB · a5f8ab9 Raw
255 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2#include <linux/types.h>3#include <linux/ioport.h>4#include <linux/slab.h>5#include <linux/export.h>6#include <linux/io.h>7#include <linux/mcb.h>8 9#include "mcb-internal.h"10 11#define for_each_chameleon_cell(dtype, p)	\12	for ((dtype) = get_next_dtype((p));	\13	     (dtype) != CHAMELEON_DTYPE_END;	\14	     (dtype) = get_next_dtype((p)))15 16static inline uint32_t get_next_dtype(void __iomem *p)17{18	uint32_t dtype;19 20	dtype = readl(p);21	return dtype >> 28;22}23 24static int chameleon_parse_bdd(struct mcb_bus *bus,25			struct chameleon_bar *cb,26			void __iomem *base)27{28	return 0;29}30 31static int chameleon_parse_gdd(struct mcb_bus *bus,32			struct chameleon_bar *cb,33			void __iomem *base, int bar_count)34{35	struct chameleon_gdd __iomem *gdd =36		(struct chameleon_gdd __iomem *) base;37	struct mcb_device *mdev;38	u32 dev_mapbase;39	u32 offset;40	u32 size;41	int ret;42	__le32 reg1;43	__le32 reg2;44 45	mdev = mcb_alloc_dev(bus);46	if (!mdev)47		return -ENOMEM;48 49	reg1 = readl(&gdd->reg1);50	reg2 = readl(&gdd->reg2);51	offset = readl(&gdd->offset);52	size = readl(&gdd->size);53 54	mdev->id = GDD_DEV(reg1);55	mdev->rev = GDD_REV(reg1);56	mdev->var = GDD_VAR(reg1);57	mdev->bar = GDD_BAR(reg2);58	mdev->group = GDD_GRP(reg2);59	mdev->inst = GDD_INS(reg2);60 61	/*62	 * If the BAR is missing, dev_mapbase is zero, or if the63	 * device is IO mapped we just print a warning and go on with the64	 * next device, instead of completely stop the gdd parser65	 */66	if (mdev->bar > bar_count - 1) {67		pr_info("No BAR for 16z%03d\n", mdev->id);68		ret = 0;69		goto err;70	}71 72	dev_mapbase = cb[mdev->bar].addr;73	if (!dev_mapbase) {74		pr_info("BAR not assigned for 16z%03d\n", mdev->id);75		ret = 0;76		goto err;77	}78 79	if (dev_mapbase & 0x01) {80		pr_info("IO mapped Device (16z%03d) not yet supported\n",81			mdev->id);82		ret = 0;83		goto err;84	}85 86	pr_debug("Found a 16z%03d\n", mdev->id);87 88	mdev->irq.start = GDD_IRQ(reg1);89	mdev->irq.end = GDD_IRQ(reg1);90	mdev->irq.flags = IORESOURCE_IRQ;91 92	mdev->mem.start = dev_mapbase + offset;93 94	mdev->mem.end = mdev->mem.start + size - 1;95	mdev->mem.flags = IORESOURCE_MEM;96 97	ret = mcb_device_register(bus, mdev);98	if (ret < 0)99		goto err;100 101	return 0;102 103err:104	mcb_free_dev(mdev);105 106	return ret;107}108 109static void chameleon_parse_bar(void __iomem *base,110				struct chameleon_bar *cb, int bar_count)111{112	char __iomem *p = base;113	int i;114 115	/* skip reg1 */116	p += sizeof(__le32);117 118	for (i = 0; i < bar_count; i++) {119		cb[i].addr = readl(p);120		cb[i].size = readl(p + 4);121 122		p += sizeof(struct chameleon_bar);123	}124}125 126static int chameleon_get_bar(void __iomem **base, phys_addr_t mapbase,127			     struct chameleon_bar **cb)128{129	struct chameleon_bar *c;130	int bar_count;131	__le32 reg;132	u32 dtype;133 134	/*135	 * For those devices which are not connected136	 * to the PCI Bus (e.g. LPC) there is a bar137	 * descriptor located directly after the138	 * chameleon header. This header is comparable139	 * to a PCI header.140	 */141	dtype = get_next_dtype(*base);142	if (dtype == CHAMELEON_DTYPE_BAR) {143		reg = readl(*base);144 145		bar_count = BAR_CNT(reg);146		if (bar_count <= 0 || bar_count > CHAMELEON_BAR_MAX)147			return -ENODEV;148 149		c = kcalloc(bar_count, sizeof(struct chameleon_bar),150			    GFP_KERNEL);151		if (!c)152			return -ENOMEM;153 154		chameleon_parse_bar(*base, c, bar_count);155		*base += BAR_DESC_SIZE(bar_count);156	} else {157		c = kzalloc(sizeof(struct chameleon_bar), GFP_KERNEL);158		if (!c)159			return -ENOMEM;160 161		bar_count = 1;162		c->addr = mapbase;163	}164 165	*cb = c;166 167	return bar_count;168}169 170int chameleon_parse_cells(struct mcb_bus *bus, phys_addr_t mapbase,171			void __iomem *base)172{173	struct chameleon_fpga_header *header;174	struct chameleon_bar *cb;175	void __iomem *p = base;176	int num_cells = 0;177	uint32_t dtype;178	int bar_count;179	int ret;180	u32 hsize;181	u32 table_size;182 183	hsize = sizeof(struct chameleon_fpga_header);184 185	header = kzalloc(hsize, GFP_KERNEL);186	if (!header)187		return -ENOMEM;188 189	/* Extract header information */190	memcpy_fromio(header, p, hsize);191	/* We only support chameleon v2 at the moment */192	header->magic = le16_to_cpu(header->magic);193	if (header->magic != CHAMELEONV2_MAGIC) {194		pr_err("Unsupported chameleon version 0x%x\n",195				header->magic);196		ret = -ENODEV;197		goto free_header;198	}199	p += hsize;200 201	bus->revision = header->revision;202	bus->model = header->model;203	bus->minor = header->minor;204	snprintf(bus->name, CHAMELEON_FILENAME_LEN + 1, "%s",205		 header->filename);206 207	bar_count = chameleon_get_bar(&p, mapbase, &cb);208	if (bar_count < 0) {209		ret = bar_count;210		goto free_header;211	}212 213	for_each_chameleon_cell(dtype, p) {214		switch (dtype) {215		case CHAMELEON_DTYPE_GENERAL:216			ret = chameleon_parse_gdd(bus, cb, p, bar_count);217			if (ret < 0)218				goto free_bar;219			p += sizeof(struct chameleon_gdd);220			break;221		case CHAMELEON_DTYPE_BRIDGE:222			chameleon_parse_bdd(bus, cb, p);223			p += sizeof(struct chameleon_bdd);224			break;225		case CHAMELEON_DTYPE_END:226			break;227		default:228			pr_err("Invalid chameleon descriptor type 0x%x\n",229				dtype);230			ret = -EINVAL;231			goto free_bar;232		}233		num_cells++;234	}235 236	if (num_cells == 0) {237		ret = -EINVAL;238		goto free_bar;239	}240 241	table_size = p - base;242	pr_debug("%d cell(s) found. Chameleon table size: 0x%04x bytes\n", num_cells, table_size);243	kfree(cb);244	kfree(header);245	return table_size;246 247free_bar:248	kfree(cb);249free_header:250	kfree(header);251 252	return ret;253}254EXPORT_SYMBOL_NS_GPL(chameleon_parse_cells, MCB);255