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