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