linux/drivers/mcb/mcb-parse.c
<<
>>
Prefs
   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_GPL(chameleon_parse_cells);
 257