18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci#include <linux/types.h> 38c2ecf20Sopenharmony_ci#include <linux/ioport.h> 48c2ecf20Sopenharmony_ci#include <linux/slab.h> 58c2ecf20Sopenharmony_ci#include <linux/export.h> 68c2ecf20Sopenharmony_ci#include <linux/io.h> 78c2ecf20Sopenharmony_ci#include <linux/mcb.h> 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include "mcb-internal.h" 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_cistruct mcb_parse_priv { 128c2ecf20Sopenharmony_ci phys_addr_t mapbase; 138c2ecf20Sopenharmony_ci void __iomem *base; 148c2ecf20Sopenharmony_ci}; 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#define for_each_chameleon_cell(dtype, p) \ 178c2ecf20Sopenharmony_ci for ((dtype) = get_next_dtype((p)); \ 188c2ecf20Sopenharmony_ci (dtype) != CHAMELEON_DTYPE_END; \ 198c2ecf20Sopenharmony_ci (dtype) = get_next_dtype((p))) 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistatic inline uint32_t get_next_dtype(void __iomem *p) 228c2ecf20Sopenharmony_ci{ 238c2ecf20Sopenharmony_ci uint32_t dtype; 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci dtype = readl(p); 268c2ecf20Sopenharmony_ci return dtype >> 28; 278c2ecf20Sopenharmony_ci} 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic int chameleon_parse_bdd(struct mcb_bus *bus, 308c2ecf20Sopenharmony_ci struct chameleon_bar *cb, 318c2ecf20Sopenharmony_ci void __iomem *base) 328c2ecf20Sopenharmony_ci{ 338c2ecf20Sopenharmony_ci return 0; 348c2ecf20Sopenharmony_ci} 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic int chameleon_parse_gdd(struct mcb_bus *bus, 378c2ecf20Sopenharmony_ci struct chameleon_bar *cb, 388c2ecf20Sopenharmony_ci void __iomem *base, int bar_count) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci struct chameleon_gdd __iomem *gdd = 418c2ecf20Sopenharmony_ci (struct chameleon_gdd __iomem *) base; 428c2ecf20Sopenharmony_ci struct mcb_device *mdev; 438c2ecf20Sopenharmony_ci u32 dev_mapbase; 448c2ecf20Sopenharmony_ci u32 offset; 458c2ecf20Sopenharmony_ci u32 size; 468c2ecf20Sopenharmony_ci int ret; 478c2ecf20Sopenharmony_ci __le32 reg1; 488c2ecf20Sopenharmony_ci __le32 reg2; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci mdev = mcb_alloc_dev(bus); 518c2ecf20Sopenharmony_ci if (!mdev) 528c2ecf20Sopenharmony_ci return -ENOMEM; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci reg1 = readl(&gdd->reg1); 558c2ecf20Sopenharmony_ci reg2 = readl(&gdd->reg2); 568c2ecf20Sopenharmony_ci offset = readl(&gdd->offset); 578c2ecf20Sopenharmony_ci size = readl(&gdd->size); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci mdev->id = GDD_DEV(reg1); 608c2ecf20Sopenharmony_ci mdev->rev = GDD_REV(reg1); 618c2ecf20Sopenharmony_ci mdev->var = GDD_VAR(reg1); 628c2ecf20Sopenharmony_ci mdev->bar = GDD_BAR(reg2); 638c2ecf20Sopenharmony_ci mdev->group = GDD_GRP(reg2); 648c2ecf20Sopenharmony_ci mdev->inst = GDD_INS(reg2); 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci /* 678c2ecf20Sopenharmony_ci * If the BAR is missing, dev_mapbase is zero, or if the 688c2ecf20Sopenharmony_ci * device is IO mapped we just print a warning and go on with the 698c2ecf20Sopenharmony_ci * next device, instead of completely stop the gdd parser 708c2ecf20Sopenharmony_ci */ 718c2ecf20Sopenharmony_ci if (mdev->bar > bar_count - 1) { 728c2ecf20Sopenharmony_ci pr_info("No BAR for 16z%03d\n", mdev->id); 738c2ecf20Sopenharmony_ci ret = 0; 748c2ecf20Sopenharmony_ci goto err; 758c2ecf20Sopenharmony_ci } 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci dev_mapbase = cb[mdev->bar].addr; 788c2ecf20Sopenharmony_ci if (!dev_mapbase) { 798c2ecf20Sopenharmony_ci pr_info("BAR not assigned for 16z%03d\n", mdev->id); 808c2ecf20Sopenharmony_ci ret = 0; 818c2ecf20Sopenharmony_ci goto err; 828c2ecf20Sopenharmony_ci } 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci if (dev_mapbase & 0x01) { 858c2ecf20Sopenharmony_ci pr_info("IO mapped Device (16z%03d) not yet supported\n", 868c2ecf20Sopenharmony_ci mdev->id); 878c2ecf20Sopenharmony_ci ret = 0; 888c2ecf20Sopenharmony_ci goto err; 898c2ecf20Sopenharmony_ci } 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci pr_debug("Found a 16z%03d\n", mdev->id); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci mdev->irq.start = GDD_IRQ(reg1); 948c2ecf20Sopenharmony_ci mdev->irq.end = GDD_IRQ(reg1); 958c2ecf20Sopenharmony_ci mdev->irq.flags = IORESOURCE_IRQ; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci mdev->mem.start = dev_mapbase + offset; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci mdev->mem.end = mdev->mem.start + size - 1; 1008c2ecf20Sopenharmony_ci mdev->mem.flags = IORESOURCE_MEM; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci ret = mcb_device_register(bus, mdev); 1038c2ecf20Sopenharmony_ci if (ret < 0) 1048c2ecf20Sopenharmony_ci goto err; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci return 0; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_cierr: 1098c2ecf20Sopenharmony_ci mcb_free_dev(mdev); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci return ret; 1128c2ecf20Sopenharmony_ci} 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistatic void chameleon_parse_bar(void __iomem *base, 1158c2ecf20Sopenharmony_ci struct chameleon_bar *cb, int bar_count) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci char __iomem *p = base; 1188c2ecf20Sopenharmony_ci int i; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci /* skip reg1 */ 1218c2ecf20Sopenharmony_ci p += sizeof(__le32); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci for (i = 0; i < bar_count; i++) { 1248c2ecf20Sopenharmony_ci cb[i].addr = readl(p); 1258c2ecf20Sopenharmony_ci cb[i].size = readl(p + 4); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci p += sizeof(struct chameleon_bar); 1288c2ecf20Sopenharmony_ci } 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic int chameleon_get_bar(void __iomem **base, phys_addr_t mapbase, 1328c2ecf20Sopenharmony_ci struct chameleon_bar **cb) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci struct chameleon_bar *c; 1358c2ecf20Sopenharmony_ci int bar_count; 1368c2ecf20Sopenharmony_ci __le32 reg; 1378c2ecf20Sopenharmony_ci u32 dtype; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci /* 1408c2ecf20Sopenharmony_ci * For those devices which are not connected 1418c2ecf20Sopenharmony_ci * to the PCI Bus (e.g. LPC) there is a bar 1428c2ecf20Sopenharmony_ci * descriptor located directly after the 1438c2ecf20Sopenharmony_ci * chameleon header. This header is comparable 1448c2ecf20Sopenharmony_ci * to a PCI header. 1458c2ecf20Sopenharmony_ci */ 1468c2ecf20Sopenharmony_ci dtype = get_next_dtype(*base); 1478c2ecf20Sopenharmony_ci if (dtype == CHAMELEON_DTYPE_BAR) { 1488c2ecf20Sopenharmony_ci reg = readl(*base); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci bar_count = BAR_CNT(reg); 1518c2ecf20Sopenharmony_ci if (bar_count <= 0 || bar_count > CHAMELEON_BAR_MAX) 1528c2ecf20Sopenharmony_ci return -ENODEV; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci c = kcalloc(bar_count, sizeof(struct chameleon_bar), 1558c2ecf20Sopenharmony_ci GFP_KERNEL); 1568c2ecf20Sopenharmony_ci if (!c) 1578c2ecf20Sopenharmony_ci return -ENOMEM; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci chameleon_parse_bar(*base, c, bar_count); 1608c2ecf20Sopenharmony_ci *base += BAR_DESC_SIZE(bar_count); 1618c2ecf20Sopenharmony_ci } else { 1628c2ecf20Sopenharmony_ci c = kzalloc(sizeof(struct chameleon_bar), GFP_KERNEL); 1638c2ecf20Sopenharmony_ci if (!c) 1648c2ecf20Sopenharmony_ci return -ENOMEM; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci bar_count = 1; 1678c2ecf20Sopenharmony_ci c->addr = mapbase; 1688c2ecf20Sopenharmony_ci } 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci *cb = c; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci return bar_count; 1738c2ecf20Sopenharmony_ci} 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ciint chameleon_parse_cells(struct mcb_bus *bus, phys_addr_t mapbase, 1768c2ecf20Sopenharmony_ci void __iomem *base) 1778c2ecf20Sopenharmony_ci{ 1788c2ecf20Sopenharmony_ci struct chameleon_fpga_header *header; 1798c2ecf20Sopenharmony_ci struct chameleon_bar *cb; 1808c2ecf20Sopenharmony_ci void __iomem *p = base; 1818c2ecf20Sopenharmony_ci int num_cells = 0; 1828c2ecf20Sopenharmony_ci uint32_t dtype; 1838c2ecf20Sopenharmony_ci int bar_count; 1848c2ecf20Sopenharmony_ci int ret; 1858c2ecf20Sopenharmony_ci u32 hsize; 1868c2ecf20Sopenharmony_ci u32 table_size; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci hsize = sizeof(struct chameleon_fpga_header); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci header = kzalloc(hsize, GFP_KERNEL); 1918c2ecf20Sopenharmony_ci if (!header) 1928c2ecf20Sopenharmony_ci return -ENOMEM; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci /* Extract header information */ 1958c2ecf20Sopenharmony_ci memcpy_fromio(header, p, hsize); 1968c2ecf20Sopenharmony_ci /* We only support chameleon v2 at the moment */ 1978c2ecf20Sopenharmony_ci header->magic = le16_to_cpu(header->magic); 1988c2ecf20Sopenharmony_ci if (header->magic != CHAMELEONV2_MAGIC) { 1998c2ecf20Sopenharmony_ci pr_err("Unsupported chameleon version 0x%x\n", 2008c2ecf20Sopenharmony_ci header->magic); 2018c2ecf20Sopenharmony_ci ret = -ENODEV; 2028c2ecf20Sopenharmony_ci goto free_header; 2038c2ecf20Sopenharmony_ci } 2048c2ecf20Sopenharmony_ci p += hsize; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci bus->revision = header->revision; 2078c2ecf20Sopenharmony_ci bus->model = header->model; 2088c2ecf20Sopenharmony_ci bus->minor = header->minor; 2098c2ecf20Sopenharmony_ci snprintf(bus->name, CHAMELEON_FILENAME_LEN + 1, "%s", 2108c2ecf20Sopenharmony_ci header->filename); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci bar_count = chameleon_get_bar(&p, mapbase, &cb); 2138c2ecf20Sopenharmony_ci if (bar_count < 0) { 2148c2ecf20Sopenharmony_ci ret = bar_count; 2158c2ecf20Sopenharmony_ci goto free_header; 2168c2ecf20Sopenharmony_ci } 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci for_each_chameleon_cell(dtype, p) { 2198c2ecf20Sopenharmony_ci switch (dtype) { 2208c2ecf20Sopenharmony_ci case CHAMELEON_DTYPE_GENERAL: 2218c2ecf20Sopenharmony_ci ret = chameleon_parse_gdd(bus, cb, p, bar_count); 2228c2ecf20Sopenharmony_ci if (ret < 0) 2238c2ecf20Sopenharmony_ci goto free_bar; 2248c2ecf20Sopenharmony_ci p += sizeof(struct chameleon_gdd); 2258c2ecf20Sopenharmony_ci break; 2268c2ecf20Sopenharmony_ci case CHAMELEON_DTYPE_BRIDGE: 2278c2ecf20Sopenharmony_ci chameleon_parse_bdd(bus, cb, p); 2288c2ecf20Sopenharmony_ci p += sizeof(struct chameleon_bdd); 2298c2ecf20Sopenharmony_ci break; 2308c2ecf20Sopenharmony_ci case CHAMELEON_DTYPE_END: 2318c2ecf20Sopenharmony_ci break; 2328c2ecf20Sopenharmony_ci default: 2338c2ecf20Sopenharmony_ci pr_err("Invalid chameleon descriptor type 0x%x\n", 2348c2ecf20Sopenharmony_ci dtype); 2358c2ecf20Sopenharmony_ci ret = -EINVAL; 2368c2ecf20Sopenharmony_ci goto free_bar; 2378c2ecf20Sopenharmony_ci } 2388c2ecf20Sopenharmony_ci num_cells++; 2398c2ecf20Sopenharmony_ci } 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci if (num_cells == 0) { 2428c2ecf20Sopenharmony_ci ret = -EINVAL; 2438c2ecf20Sopenharmony_ci goto free_bar; 2448c2ecf20Sopenharmony_ci } 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci table_size = p - base; 2478c2ecf20Sopenharmony_ci pr_debug("%d cell(s) found. Chameleon table size: 0x%04x bytes\n", num_cells, table_size); 2488c2ecf20Sopenharmony_ci kfree(cb); 2498c2ecf20Sopenharmony_ci kfree(header); 2508c2ecf20Sopenharmony_ci return table_size; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_cifree_bar: 2538c2ecf20Sopenharmony_ci kfree(cb); 2548c2ecf20Sopenharmony_cifree_header: 2558c2ecf20Sopenharmony_ci kfree(header); 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci return ret; 2588c2ecf20Sopenharmony_ci} 2598c2ecf20Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(chameleon_parse_cells, MCB); 260