18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright 2004 Koninklijke Philips Electronics NV 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Conversion to platform driver and DT: 68c2ecf20Sopenharmony_ci * Copyright 2014 Linaro Ltd. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * 14/04/2005 Initial version, colin.king@philips.com 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci#include <linux/kernel.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/of_address.h> 138c2ecf20Sopenharmony_ci#include <linux/of_pci.h> 148c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 158c2ecf20Sopenharmony_ci#include <linux/pci.h> 168c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include "../pci.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistatic void __iomem *versatile_pci_base; 218c2ecf20Sopenharmony_cistatic void __iomem *versatile_cfg_base[2]; 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#define PCI_IMAP(m) (versatile_pci_base + ((m) * 4)) 248c2ecf20Sopenharmony_ci#define PCI_SMAP(m) (versatile_pci_base + 0x14 + ((m) * 4)) 258c2ecf20Sopenharmony_ci#define PCI_SELFID (versatile_pci_base + 0xc) 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define VP_PCI_DEVICE_ID 0x030010ee 288c2ecf20Sopenharmony_ci#define VP_PCI_CLASS_ID 0x0b400000 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistatic u32 pci_slot_ignore; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic int __init versatile_pci_slot_ignore(char *str) 338c2ecf20Sopenharmony_ci{ 348c2ecf20Sopenharmony_ci int retval; 358c2ecf20Sopenharmony_ci int slot; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci while ((retval = get_option(&str, &slot))) { 388c2ecf20Sopenharmony_ci if ((slot < 0) || (slot > 31)) 398c2ecf20Sopenharmony_ci pr_err("Illegal slot value: %d\n", slot); 408c2ecf20Sopenharmony_ci else 418c2ecf20Sopenharmony_ci pci_slot_ignore |= (1 << slot); 428c2ecf20Sopenharmony_ci } 438c2ecf20Sopenharmony_ci return 1; 448c2ecf20Sopenharmony_ci} 458c2ecf20Sopenharmony_ci__setup("pci_slot_ignore=", versatile_pci_slot_ignore); 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic void __iomem *versatile_map_bus(struct pci_bus *bus, 498c2ecf20Sopenharmony_ci unsigned int devfn, int offset) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci unsigned int busnr = bus->number; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci if (pci_slot_ignore & (1 << PCI_SLOT(devfn))) 548c2ecf20Sopenharmony_ci return NULL; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci return versatile_cfg_base[1] + ((busnr << 16) | (devfn << 8) | offset); 578c2ecf20Sopenharmony_ci} 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic struct pci_ops pci_versatile_ops = { 608c2ecf20Sopenharmony_ci .map_bus = versatile_map_bus, 618c2ecf20Sopenharmony_ci .read = pci_generic_config_read32, 628c2ecf20Sopenharmony_ci .write = pci_generic_config_write, 638c2ecf20Sopenharmony_ci}; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic int versatile_pci_probe(struct platform_device *pdev) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 688c2ecf20Sopenharmony_ci struct resource *res; 698c2ecf20Sopenharmony_ci struct resource_entry *entry; 708c2ecf20Sopenharmony_ci int i, myslot = -1, mem = 1; 718c2ecf20Sopenharmony_ci u32 val; 728c2ecf20Sopenharmony_ci void __iomem *local_pci_cfg_base; 738c2ecf20Sopenharmony_ci struct pci_host_bridge *bridge; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci bridge = devm_pci_alloc_host_bridge(dev, 0); 768c2ecf20Sopenharmony_ci if (!bridge) 778c2ecf20Sopenharmony_ci return -ENOMEM; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci versatile_pci_base = devm_platform_ioremap_resource(pdev, 0); 808c2ecf20Sopenharmony_ci if (IS_ERR(versatile_pci_base)) 818c2ecf20Sopenharmony_ci return PTR_ERR(versatile_pci_base); 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci versatile_cfg_base[0] = devm_platform_ioremap_resource(pdev, 1); 848c2ecf20Sopenharmony_ci if (IS_ERR(versatile_cfg_base[0])) 858c2ecf20Sopenharmony_ci return PTR_ERR(versatile_cfg_base[0]); 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 2); 888c2ecf20Sopenharmony_ci versatile_cfg_base[1] = devm_pci_remap_cfg_resource(dev, res); 898c2ecf20Sopenharmony_ci if (IS_ERR(versatile_cfg_base[1])) 908c2ecf20Sopenharmony_ci return PTR_ERR(versatile_cfg_base[1]); 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci resource_list_for_each_entry(entry, &bridge->windows) { 938c2ecf20Sopenharmony_ci if (resource_type(entry->res) == IORESOURCE_MEM) { 948c2ecf20Sopenharmony_ci writel(entry->res->start >> 28, PCI_IMAP(mem)); 958c2ecf20Sopenharmony_ci writel(__pa(PAGE_OFFSET) >> 28, PCI_SMAP(mem)); 968c2ecf20Sopenharmony_ci mem++; 978c2ecf20Sopenharmony_ci } 988c2ecf20Sopenharmony_ci } 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci /* 1018c2ecf20Sopenharmony_ci * We need to discover the PCI core first to configure itself 1028c2ecf20Sopenharmony_ci * before the main PCI probing is performed 1038c2ecf20Sopenharmony_ci */ 1048c2ecf20Sopenharmony_ci for (i = 0; i < 32; i++) { 1058c2ecf20Sopenharmony_ci if ((readl(versatile_cfg_base[0] + (i << 11) + PCI_VENDOR_ID) == VP_PCI_DEVICE_ID) && 1068c2ecf20Sopenharmony_ci (readl(versatile_cfg_base[0] + (i << 11) + PCI_CLASS_REVISION) == VP_PCI_CLASS_ID)) { 1078c2ecf20Sopenharmony_ci myslot = i; 1088c2ecf20Sopenharmony_ci break; 1098c2ecf20Sopenharmony_ci } 1108c2ecf20Sopenharmony_ci } 1118c2ecf20Sopenharmony_ci if (myslot == -1) { 1128c2ecf20Sopenharmony_ci dev_err(dev, "Cannot find PCI core!\n"); 1138c2ecf20Sopenharmony_ci return -EIO; 1148c2ecf20Sopenharmony_ci } 1158c2ecf20Sopenharmony_ci /* 1168c2ecf20Sopenharmony_ci * Do not to map Versatile FPGA PCI device into memory space 1178c2ecf20Sopenharmony_ci */ 1188c2ecf20Sopenharmony_ci pci_slot_ignore |= (1 << myslot); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci dev_info(dev, "PCI core found (slot %d)\n", myslot); 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci writel(myslot, PCI_SELFID); 1238c2ecf20Sopenharmony_ci local_pci_cfg_base = versatile_cfg_base[1] + (myslot << 11); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci val = readl(local_pci_cfg_base + PCI_COMMAND); 1268c2ecf20Sopenharmony_ci val |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE; 1278c2ecf20Sopenharmony_ci writel(val, local_pci_cfg_base + PCI_COMMAND); 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci /* 1308c2ecf20Sopenharmony_ci * Configure the PCI inbound memory windows to be 1:1 mapped to SDRAM 1318c2ecf20Sopenharmony_ci */ 1328c2ecf20Sopenharmony_ci writel(__pa(PAGE_OFFSET), local_pci_cfg_base + PCI_BASE_ADDRESS_0); 1338c2ecf20Sopenharmony_ci writel(__pa(PAGE_OFFSET), local_pci_cfg_base + PCI_BASE_ADDRESS_1); 1348c2ecf20Sopenharmony_ci writel(__pa(PAGE_OFFSET), local_pci_cfg_base + PCI_BASE_ADDRESS_2); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci /* 1378c2ecf20Sopenharmony_ci * For many years the kernel and QEMU were symbiotically buggy 1388c2ecf20Sopenharmony_ci * in that they both assumed the same broken IRQ mapping. 1398c2ecf20Sopenharmony_ci * QEMU therefore attempts to auto-detect old broken kernels 1408c2ecf20Sopenharmony_ci * so that they still work on newer QEMU as they did on old 1418c2ecf20Sopenharmony_ci * QEMU. Since we now use the correct (ie matching-hardware) 1428c2ecf20Sopenharmony_ci * IRQ mapping we write a definitely different value to a 1438c2ecf20Sopenharmony_ci * PCI_INTERRUPT_LINE register to tell QEMU that we expect 1448c2ecf20Sopenharmony_ci * real hardware behaviour and it need not be backwards 1458c2ecf20Sopenharmony_ci * compatible for us. This write is harmless on real hardware. 1468c2ecf20Sopenharmony_ci */ 1478c2ecf20Sopenharmony_ci writel(0, versatile_cfg_base[0] + PCI_INTERRUPT_LINE); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci pci_add_flags(PCI_REASSIGN_ALL_BUS); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci bridge->ops = &pci_versatile_ops; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci return pci_host_probe(bridge); 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistatic const struct of_device_id versatile_pci_of_match[] = { 1578c2ecf20Sopenharmony_ci { .compatible = "arm,versatile-pci", }, 1588c2ecf20Sopenharmony_ci { }, 1598c2ecf20Sopenharmony_ci}; 1608c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, versatile_pci_of_match); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_cistatic struct platform_driver versatile_pci_driver = { 1638c2ecf20Sopenharmony_ci .driver = { 1648c2ecf20Sopenharmony_ci .name = "versatile-pci", 1658c2ecf20Sopenharmony_ci .of_match_table = versatile_pci_of_match, 1668c2ecf20Sopenharmony_ci .suppress_bind_attrs = true, 1678c2ecf20Sopenharmony_ci }, 1688c2ecf20Sopenharmony_ci .probe = versatile_pci_probe, 1698c2ecf20Sopenharmony_ci}; 1708c2ecf20Sopenharmony_cimodule_platform_driver(versatile_pci_driver); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Versatile PCI driver"); 1738c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 174