Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 #include <linux/types.h>
0003 #include <linux/ioport.h>
0004 #include <linux/slab.h>
0005 #include <linux/export.h>
0006 #include <linux/io.h>
0007 #include <linux/mcb.h>
0008 
0009 #include "mcb-internal.h"
0010 
0011 struct mcb_parse_priv {
0012     phys_addr_t mapbase;
0013     void __iomem *base;
0014 };
0015 
0016 #define for_each_chameleon_cell(dtype, p)   \
0017     for ((dtype) = get_next_dtype((p)); \
0018          (dtype) != CHAMELEON_DTYPE_END;    \
0019          (dtype) = get_next_dtype((p)))
0020 
0021 static inline uint32_t get_next_dtype(void __iomem *p)
0022 {
0023     uint32_t dtype;
0024 
0025     dtype = readl(p);
0026     return dtype >> 28;
0027 }
0028 
0029 static int chameleon_parse_bdd(struct mcb_bus *bus,
0030             struct chameleon_bar *cb,
0031             void __iomem *base)
0032 {
0033     return 0;
0034 }
0035 
0036 static int chameleon_parse_gdd(struct mcb_bus *bus,
0037             struct chameleon_bar *cb,
0038             void __iomem *base, int bar_count)
0039 {
0040     struct chameleon_gdd __iomem *gdd =
0041         (struct chameleon_gdd __iomem *) base;
0042     struct mcb_device *mdev;
0043     u32 dev_mapbase;
0044     u32 offset;
0045     u32 size;
0046     int ret;
0047     __le32 reg1;
0048     __le32 reg2;
0049 
0050     mdev = mcb_alloc_dev(bus);
0051     if (!mdev)
0052         return -ENOMEM;
0053 
0054     reg1 = readl(&gdd->reg1);
0055     reg2 = readl(&gdd->reg2);
0056     offset = readl(&gdd->offset);
0057     size = readl(&gdd->size);
0058 
0059     mdev->id = GDD_DEV(reg1);
0060     mdev->rev = GDD_REV(reg1);
0061     mdev->var = GDD_VAR(reg1);
0062     mdev->bar = GDD_BAR(reg2);
0063     mdev->group = GDD_GRP(reg2);
0064     mdev->inst = GDD_INS(reg2);
0065 
0066     /*
0067      * If the BAR is missing, dev_mapbase is zero, or if the
0068      * device is IO mapped we just print a warning and go on with the
0069      * next device, instead of completely stop the gdd parser
0070      */
0071     if (mdev->bar > bar_count - 1) {
0072         pr_info("No BAR for 16z%03d\n", mdev->id);
0073         ret = 0;
0074         goto err;
0075     }
0076 
0077     dev_mapbase = cb[mdev->bar].addr;
0078     if (!dev_mapbase) {
0079         pr_info("BAR not assigned for 16z%03d\n", mdev->id);
0080         ret = 0;
0081         goto err;
0082     }
0083 
0084     if (dev_mapbase & 0x01) {
0085         pr_info("IO mapped Device (16z%03d) not yet supported\n",
0086             mdev->id);
0087         ret = 0;
0088         goto err;
0089     }
0090 
0091     pr_debug("Found a 16z%03d\n", mdev->id);
0092 
0093     mdev->irq.start = GDD_IRQ(reg1);
0094     mdev->irq.end = GDD_IRQ(reg1);
0095     mdev->irq.flags = IORESOURCE_IRQ;
0096 
0097     mdev->mem.start = dev_mapbase + offset;
0098 
0099     mdev->mem.end = mdev->mem.start + size - 1;
0100     mdev->mem.flags = IORESOURCE_MEM;
0101 
0102     mdev->is_added = false;
0103 
0104     ret = mcb_device_register(bus, mdev);
0105     if (ret < 0)
0106         goto err;
0107 
0108     return 0;
0109 
0110 err:
0111     mcb_free_dev(mdev);
0112 
0113     return ret;
0114 }
0115 
0116 static void chameleon_parse_bar(void __iomem *base,
0117                 struct chameleon_bar *cb, int bar_count)
0118 {
0119     char __iomem *p = base;
0120     int i;
0121 
0122     /* skip reg1 */
0123     p += sizeof(__le32);
0124 
0125     for (i = 0; i < bar_count; i++) {
0126         cb[i].addr = readl(p);
0127         cb[i].size = readl(p + 4);
0128 
0129         p += sizeof(struct chameleon_bar);
0130     }
0131 }
0132 
0133 static int chameleon_get_bar(char __iomem **base, phys_addr_t mapbase,
0134                  struct chameleon_bar **cb)
0135 {
0136     struct chameleon_bar *c;
0137     int bar_count;
0138     __le32 reg;
0139     u32 dtype;
0140 
0141     /*
0142      * For those devices which are not connected
0143      * to the PCI Bus (e.g. LPC) there is a bar
0144      * descriptor located directly after the
0145      * chameleon header. This header is comparable
0146      * to a PCI header.
0147      */
0148     dtype = get_next_dtype(*base);
0149     if (dtype == CHAMELEON_DTYPE_BAR) {
0150         reg = readl(*base);
0151 
0152         bar_count = BAR_CNT(reg);
0153         if (bar_count <= 0 || bar_count > CHAMELEON_BAR_MAX)
0154             return -ENODEV;
0155 
0156         c = kcalloc(bar_count, sizeof(struct chameleon_bar),
0157                 GFP_KERNEL);
0158         if (!c)
0159             return -ENOMEM;
0160 
0161         chameleon_parse_bar(*base, c, bar_count);
0162         *base += BAR_DESC_SIZE(bar_count);
0163     } else {
0164         c = kzalloc(sizeof(struct chameleon_bar), GFP_KERNEL);
0165         if (!c)
0166             return -ENOMEM;
0167 
0168         bar_count = 1;
0169         c->addr = mapbase;
0170     }
0171 
0172     *cb = c;
0173 
0174     return bar_count;
0175 }
0176 
0177 int chameleon_parse_cells(struct mcb_bus *bus, phys_addr_t mapbase,
0178             void __iomem *base)
0179 {
0180     struct chameleon_fpga_header *header;
0181     struct chameleon_bar *cb;
0182     char __iomem *p = base;
0183     int num_cells = 0;
0184     uint32_t dtype;
0185     int bar_count;
0186     int ret;
0187     u32 hsize;
0188 
0189     hsize = sizeof(struct chameleon_fpga_header);
0190 
0191     header = kzalloc(hsize, GFP_KERNEL);
0192     if (!header)
0193         return -ENOMEM;
0194 
0195     /* Extract header information */
0196     memcpy_fromio(header, p, hsize);
0197     /* We only support chameleon v2 at the moment */
0198     header->magic = le16_to_cpu(header->magic);
0199     if (header->magic != CHAMELEONV2_MAGIC) {
0200         pr_err("Unsupported chameleon version 0x%x\n",
0201                 header->magic);
0202         ret = -ENODEV;
0203         goto free_header;
0204     }
0205     p += hsize;
0206 
0207     bus->revision = header->revision;
0208     bus->model = header->model;
0209     bus->minor = header->minor;
0210     snprintf(bus->name, CHAMELEON_FILENAME_LEN + 1, "%s",
0211          header->filename);
0212 
0213     bar_count = chameleon_get_bar(&p, mapbase, &cb);
0214     if (bar_count < 0) {
0215         ret = bar_count;
0216         goto free_header;
0217     }
0218 
0219     for_each_chameleon_cell(dtype, p) {
0220         switch (dtype) {
0221         case CHAMELEON_DTYPE_GENERAL:
0222             ret = chameleon_parse_gdd(bus, cb, p, bar_count);
0223             if (ret < 0)
0224                 goto free_bar;
0225             p += sizeof(struct chameleon_gdd);
0226             break;
0227         case CHAMELEON_DTYPE_BRIDGE:
0228             chameleon_parse_bdd(bus, cb, p);
0229             p += sizeof(struct chameleon_bdd);
0230             break;
0231         case CHAMELEON_DTYPE_END:
0232             break;
0233         default:
0234             pr_err("Invalid chameleon descriptor type 0x%x\n",
0235                 dtype);
0236             ret = -EINVAL;
0237             goto free_bar;
0238         }
0239         num_cells++;
0240     }
0241 
0242     if (num_cells == 0)
0243         num_cells = -EINVAL;
0244 
0245     kfree(cb);
0246     kfree(header);
0247     return num_cells;
0248 
0249 free_bar:
0250     kfree(cb);
0251 free_header:
0252     kfree(header);
0253 
0254     return ret;
0255 }
0256 EXPORT_SYMBOL_NS_GPL(chameleon_parse_cells, MCB);