lkml.org 
[lkml]   [2015]   [Jul]   [15]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 5/5] PCI: Add host drivers for Cavium ThunderX processors.
    Date
    From: David Daney <david.daney@cavium.com>

    Signed-off-by: David Daney <david.daney@cavium.com>
    ---
    drivers/pci/host/Kconfig | 12 +
    drivers/pci/host/Makefile | 2 +
    drivers/pci/host/pcie-thunder-pem.c | 462 ++++++++++++++++++++++++++++++++++++
    drivers/pci/host/pcie-thunder.c | 422 ++++++++++++++++++++++++++++++++
    4 files changed, 898 insertions(+)
    create mode 100644 drivers/pci/host/pcie-thunder-pem.c
    create mode 100644 drivers/pci/host/pcie-thunder.c

    diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
    index c132bdd..06e26ad 100644
    --- a/drivers/pci/host/Kconfig
    +++ b/drivers/pci/host/Kconfig
    @@ -145,4 +145,16 @@ config PCIE_IPROC_BCMA
    Say Y here if you want to use the Broadcom iProc PCIe controller
    through the BCMA bus interface

    +config PCI_THUNDER_PEM
    + bool
    +
    +config PCI_THUNDER
    + bool "Thunder PCIe host controller"
    + depends on ARM64 || COMPILE_TEST
    + depends on OF_PCI
    + select PCI_MSI
    + select PCI_THUNDER_PEM
    + help
    + Say Y here if you want internal PCI support on Thunder SoC.
    +
    endmenu
    diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
    index 140d66f..a355155 100644
    --- a/drivers/pci/host/Makefile
    +++ b/drivers/pci/host/Makefile
    @@ -17,3 +17,5 @@ obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
    obj-$(CONFIG_PCIE_IPROC) += pcie-iproc.o
    obj-$(CONFIG_PCIE_IPROC_PLATFORM) += pcie-iproc-platform.o
    obj-$(CONFIG_PCIE_IPROC_BCMA) += pcie-iproc-bcma.o
    +obj-$(CONFIG_PCI_THUNDER) += pcie-thunder.o
    +obj-$(CONFIG_PCI_THUNDER_PEM) += pcie-thunder-pem.o
    diff --git a/drivers/pci/host/pcie-thunder-pem.c b/drivers/pci/host/pcie-thunder-pem.c
    new file mode 100644
    index 0000000..7861a8a
    --- /dev/null
    +++ b/drivers/pci/host/pcie-thunder-pem.c
    @@ -0,0 +1,462 @@
    +/*
    + * PCIe host controller driver for Cavium Thunder SOC
    + *
    + * Copyright (C) 2014,2015 Cavium Inc.
    + *
    + * This program is free software; you can redistribute it and/or
    + * modify it under the terms of the GNU General Public License as
    + * published by the Free Software Foundation; either version 2 of
    + * the License, or (at your option) any later version.
    + */
    +/* #define DEBUG 1 */
    +#include <linux/kernel.h>
    +#include <linux/module.h>
    +#include <linux/delay.h>
    +#include <linux/irq.h>
    +#include <linux/pci.h>
    +#include <linux/irqdomain.h>
    +#include <linux/msi.h>
    +#include <linux/irqchip/arm-gic-v3.h>
    +
    +#define THUNDER_SLI_S2M_REG_ACC_BASE 0x874001000000ull
    +
    +#define THUNDER_GIC 0x801000000000ull
    +#define THUNDER_GICD_SETSPI_NSR 0x801000000040ull
    +#define THUNDER_GICD_CLRSPI_NSR 0x801000000048ull
    +
    +#define THUNDER_GSER_PCIE_MASK 0x01
    +
    +#define PEM_CTL_STATUS 0x000
    +#define PEM_RD_CFG 0x030
    +#define P2N_BAR0_START 0x080
    +#define P2N_BAR1_START 0x088
    +#define P2N_BAR2_START 0x090
    +#define BAR_CTL 0x0a8
    +#define BAR2_MASK 0x0b0
    +#define BAR1_INDEX 0x100
    +#define PEM_CFG 0x410
    +#define PEM_ON 0x420
    +
    +struct thunder_pem {
    + struct list_head list; /* on thunder_pem_buses */
    + bool connected;
    + unsigned int id;
    + unsigned int sli;
    + unsigned int sli_group;
    + unsigned int node;
    + u64 sli_window_base;
    + void __iomem *bar0;
    + void __iomem *bar4;
    + void __iomem *sli_s2m;
    + void __iomem *cfgregion;
    + struct pci_bus *bus;
    + int vwire_irqs[4];
    + u32 vwire_data[4];
    +};
    +
    +static LIST_HEAD(thunder_pem_buses);
    +
    +static struct pci_device_id thunder_pem_pci_table[] = {
    + {PCI_VENDOR_ID_CAVIUM, 0xa020, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
    + {0,}
    +};
    +MODULE_DEVICE_TABLE(pci, thunder_pem_pci_table);
    +
    +enum slix_s2m_ctype {
    + CTYPE_MEMORY = 0,
    + CTYPE_CONFIG = 1,
    + CTYPE_IO = 2
    +};
    +
    +static u64 slix_s2m_reg_val(unsigned mac, enum slix_s2m_ctype ctype,
    + bool merge, bool relaxed, bool snoop, u32 ba_msb)
    +{
    + u64 v;
    +
    + v = (u64)(mac % 3) << 49;
    + v |= (u64)ctype << 53;
    + if (!merge)
    + v |= 1ull << 48;
    + if (relaxed)
    + v |= 5ull << 40;
    + if (!snoop)
    + v |= 5ull << 41;
    + v |= (u64)ba_msb;
    +
    + return v;
    +}
    +
    +static u32 thunder_pcierc_config_read(struct thunder_pem *pem, u32 reg, int size)
    +{
    + unsigned int val;
    +
    + writeq(reg & ~3u, pem->bar0 + PEM_RD_CFG);
    + val = readq(pem->bar0 + PEM_RD_CFG) >> 32;
    +
    + if (size == 1)
    + val = (val >> (8 * (reg & 3))) & 0xff;
    + else if (size == 2)
    + val = (val >> (8 * (reg & 3))) & 0xffff;
    +
    + return val;
    +}
    +
    +static int thunder_pem_read_config(struct pci_bus *bus, unsigned int devfn,
    + int reg, int size, u32 *val)
    +{
    + void __iomem *addr;
    + struct thunder_pem *pem = bus->sysdata;
    + unsigned int busnr = bus->number;
    +
    + if (busnr > 255 || devfn > 255 || reg > 4095)
    + return PCIBIOS_DEVICE_NOT_FOUND;
    +
    + addr = pem->cfgregion + ((busnr << 24) | (devfn << 16) | reg);
    +
    + switch (size) {
    + case 1:
    + *val = readb(addr);
    + break;
    + case 2:
    + *val = readw(addr);
    + break;
    + case 4:
    + *val = readl(addr);
    + break;
    + default:
    + return PCIBIOS_BAD_REGISTER_NUMBER;
    + }
    +
    + return PCIBIOS_SUCCESSFUL;
    +}
    +
    +static int thunder_pem_write_config(struct pci_bus *bus, unsigned int devfn,
    + int reg, int size, u32 val)
    +{
    + void __iomem *addr;
    + struct thunder_pem *pem = bus->sysdata;
    + unsigned int busnr = bus->number;
    +
    + if (busnr > 255 || devfn > 255 || reg > 4095)
    + return PCIBIOS_DEVICE_NOT_FOUND;
    +
    + addr = pem->cfgregion + ((busnr << 24) | (devfn << 16) | reg);
    +
    + switch (size) {
    + case 1:
    + writeb(val, addr);
    + break;
    + case 2:
    + writew(val, addr);
    + break;
    + case 4:
    + writel(val, addr);
    + break;
    + default:
    + return PCIBIOS_BAD_REGISTER_NUMBER;
    + }
    +
    + return PCIBIOS_SUCCESSFUL;
    +}
    +
    +static struct pci_ops thunder_pem_ops = {
    + .read = thunder_pem_read_config,
    + .write = thunder_pem_write_config,
    +};
    +
    +static struct thunder_pem *thunder_pem_from_dev(struct pci_dev *dev)
    +{
    + struct thunder_pem *pem;
    + struct pci_bus *bus = dev->bus;
    +
    + while (!pci_is_root_bus(bus))
    + bus = bus->parent;
    +
    + list_for_each_entry(pem, &thunder_pem_buses, list) {
    + if (pem->bus == bus)
    + return pem;
    + }
    + return NULL;
    +}
    +
    +int thunder_pem_requester_id(struct pci_dev *dev)
    +{
    + struct thunder_pem *pem = thunder_pem_from_dev(dev);
    +
    + if (!pem)
    + return -ENODEV;
    +
    + if (pem->id < 3)
    + return ((1 << 16) |
    + ((dev)->bus->number << 8) |
    + (dev)->devfn);
    +
    + if (pem->id < 6)
    + return ((3 << 16) |
    + ((dev)->bus->number << 8) |
    + (dev)->devfn);
    +
    + if (pem->id < 9)
    + return ((1 << 19) | (1 << 16) |
    + ((dev)->bus->number << 8) |
    + (dev)->devfn);
    +
    + if (pem->id < 12)
    + return ((1 << 19) |
    + (3 << 16) |
    + ((dev)->bus->number << 8) |
    + (dev)->devfn);
    + return -ENODEV;
    +}
    +
    +static int thunder_pem_pcibios_add_device(struct pci_dev *dev)
    +{
    + struct thunder_pem *pem;
    + u8 pin;
    +
    + pem = thunder_pem_from_dev(dev);
    + if (!pem)
    + return 0;
    +
    + pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin);
    +
    + /* Cope with illegal. */
    + if (pin > 4)
    + pin = 1;
    +
    + dev->irq = pin > 0 ? pem->vwire_irqs[pin - 1] : 0;
    +
    + if (pin)
    + dev_dbg(&dev->dev, "assigning IRQ %02d\n", dev->irq);
    +
    + pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq);
    +
    + return 0;
    +}
    +
    +static int thunder_pem_pci_probe(struct pci_dev *pdev,
    + const struct pci_device_id *ent)
    +{
    + struct thunder_pem *pem;
    + resource_size_t bar0_start;
    + u64 regval;
    + u64 sliaddr, pciaddr;
    + u32 cfgval;
    + int primary_bus;
    + int i;
    + int ret = 0;
    + struct resource *res;
    + LIST_HEAD(resources);
    +
    + set_pcibios_add_device(thunder_pem_pcibios_add_device);
    +
    + pem = devm_kzalloc(&pdev->dev, sizeof(*pem), GFP_KERNEL);
    + if (!pem)
    + return -ENOMEM;
    +
    + pci_set_drvdata(pdev, pem);
    +
    + bar0_start = pci_resource_start(pdev, 0);
    + pem->node = (bar0_start >> 44) & 3;
    + pem->id = ((bar0_start >> 24) & 7) + (6 * pem->node);
    + pem->sli = pem->id % 3;
    + pem->sli_group = (pem->id / 3) % 2;
    + pem->sli_window_base = 0x880000000000ull | (((u64)pem->node) << 44) | ((u64)pem->sli_group << 40);
    + pem->sli_window_base += 0x4000000000 * pem->sli;
    +
    + ret = pci_enable_device_mem(pdev);
    + if (ret)
    + goto out;
    +
    + pem->bar0 = pcim_iomap(pdev, 0, 0x100000);
    + if (!pem->bar0) {
    + ret = -ENOMEM;
    + goto out;
    + }
    +
    + pem->bar4 = pcim_iomap(pdev, 4, 0x100000);
    + if (!pem->bar0) {
    + ret = -ENOMEM;
    + goto out;
    + }
    +
    + sliaddr = THUNDER_SLI_S2M_REG_ACC_BASE | ((u64)pem->node << 44) | ((u64)pem->sli_group << 36);
    +
    + regval = readq(pem->bar0 + PEM_ON);
    + if (!(regval & 1)) {
    + dev_notice(&pdev->dev, "PEM%u_ON not set, skipping...\n", pem->id);
    + goto out;
    + }
    +
    + regval = readq(pem->bar0 + PEM_CTL_STATUS);
    + regval |= 0x10; /* Set Link Enable bit */
    + writeq(regval, pem->bar0 + PEM_CTL_STATUS);
    +
    + udelay(1000);
    +
    + cfgval = thunder_pcierc_config_read(pem, 32 * 4, 4); /* PCIERC_CFG032 */
    +
    + if (((cfgval >> 29 & 0x1) == 0x0) || ((cfgval >> 27 & 0x1) == 0x1)) {
    + dev_notice(&pdev->dev, "PEM%u Link Timeout, skipping...\n", pem->id);
    + goto out;
    + }
    +
    + pem->sli_s2m = devm_ioremap(&pdev->dev, sliaddr, 0x1000);
    + if (!pem->sli_s2m) {
    + ret = -ENOMEM;
    + goto out;
    + }
    +
    + pem->cfgregion = devm_ioremap(&pdev->dev, pem->sli_window_base, 0x100000000ull);
    + if (!pem->cfgregion) {
    + ret = -ENOMEM;
    + goto out;
    + }
    + regval = slix_s2m_reg_val(pem->sli, CTYPE_CONFIG, false, false, false, 0);
    + writeq(regval, pem->sli_s2m + 0x10 * ((0x40 * pem->sli) + 0));
    +
    + cfgval = thunder_pcierc_config_read(pem, 6 * 4, 4); /* PCIERC_CFG006 */
    + primary_bus = (cfgval >> 8) & 0xff;
    +
    + res = kzalloc(sizeof(*res), GFP_KERNEL);
    + if (!res) {
    + ret = -ENOMEM;
    + goto out;
    + }
    + res->start = primary_bus;
    + res->end = 255;
    + res->flags = IORESOURCE_BUS;
    + pci_add_resource(&resources, res);
    +
    +
    + res = kzalloc(sizeof(*res), GFP_KERNEL);
    + if (!res) {
    + ret = -ENOMEM;
    + goto out;
    + }
    + res->start = 0x100000 * pem->id;
    + res->end = res->start + 0x100000 - 1;
    + res->flags = IORESOURCE_IO;
    + pci_add_resource(&resources, res);
    + regval = slix_s2m_reg_val(pem->sli, CTYPE_IO, false, false, false, 0);
    + writeq(regval, pem->sli_s2m + 0x10 * ((0x40 * pem->sli) + 1));
    +
    + res = kzalloc(sizeof(*res), GFP_KERNEL);
    + if (!res) {
    + ret = -ENOMEM;
    + goto out;
    + }
    + pciaddr = 0x10000000ull;
    + res->start = pem->sli_window_base + 0x1000000000ull + pciaddr;
    + res->end = res->start + 0x1000000000ull - pciaddr - 1;
    + res->flags = IORESOURCE_MEM;
    + pci_add_resource_offset(&resources, res, res->start - pciaddr);
    + for (i = 0; i < 16; i++) {
    + regval = slix_s2m_reg_val(pem->sli, CTYPE_MEMORY, false, false, false, i);
    + writeq(regval, pem->sli_s2m + 0x10 * ((0x40 * pem->sli) + (0x10 + i)));
    + }
    +
    + res = kzalloc(sizeof(*res), GFP_KERNEL);
    + if (!res) {
    + ret = -ENOMEM;
    + goto out;
    + }
    + pciaddr = 0x1000000000ull;
    + res->start = pem->sli_window_base + 0x1000000000ull + pciaddr;
    + res->end = res->start + 0x1000000000ull - 1;
    + res->flags = IORESOURCE_MEM | IORESOURCE_PREFETCH;
    + pci_add_resource_offset(&resources, res, res->start - pciaddr);
    + for (i = 0; i < 16; i++) {
    + regval = slix_s2m_reg_val(pem->sli, CTYPE_MEMORY, true, true, true, i + 0x10);
    + writeq(regval, pem->sli_s2m + 0x10 * ((0x40 * pem->sli) + (0x20 + i)));
    + }
    +
    + writeq(0, pem->bar0 + P2N_BAR0_START);
    + writeq(0, pem->bar0 + P2N_BAR1_START);
    + writeq(0, pem->bar0 + P2N_BAR2_START);
    +
    + regval = 0x10; /* BAR_CTL[BAR1_SIZ] = 1 (64MB) */
    + regval |= 0x8; /* BAR_CTL[BAR2_ENB] = 1 */
    + writeq(regval, pem->bar0 + BAR_CTL);
    +
    + /* 1st 4MB region -> GIC registers so 32-bit MSI can reach the GIC. */
    + regval = (THUNDER_GIC + (((u64)pem->node) << 44)) >> 18;
    + /* BAR1_INDEX[ADDR_V] = 1 */
    + regval |= 1;
    + writeq(regval, pem->bar0 + BAR1_INDEX);
    + /* Remaining regions linear mapping to physical address space */
    + for (i = 1; i < 16; i++) {
    + regval = (i << 4) | 1;
    + writeq(regval, pem->bar0 + BAR1_INDEX + 8 * i);
    + }
    +
    + pem->bus = pci_create_root_bus(&pdev->dev, primary_bus, &thunder_pem_ops, pem, &resources);
    + if (!pem->bus) {
    + ret = -ENODEV;
    + goto err_root_bus;
    + }
    + pem->bus->is_pcierc = 1;
    + list_add_tail(&pem->list, &thunder_pem_buses);
    +
    + for (i = 0; i < 3; i++) {
    + pem->vwire_data[i] = 40 + 4 * pem->id + i;
    + pem->vwire_irqs[i] = irq_create_mapping(gic_get_irq_domain(), pem->vwire_data[i]);
    + if (!pem->vwire_irqs[i]) {
    + dev_err(&pdev->dev, "Error: No irq mapping for %u\n", pem->vwire_data[i]);
    + continue;
    + }
    + irq_set_irq_type(pem->vwire_irqs[i], IRQ_TYPE_LEVEL_HIGH);
    +
    + writeq(THUNDER_GICD_SETSPI_NSR, pem->bar4 + 0 + (i + 2) * 32);
    + writeq(pem->vwire_data[i], pem->bar4 + 8 + (i + 2) * 32);
    + writeq(THUNDER_GICD_CLRSPI_NSR, pem->bar4 + 16 + (i + 2) * 32);
    + writeq(pem->vwire_data[i], pem->bar4 + 24 + (i + 2) * 32);
    + }
    + ret = pci_read_config_dword(pdev, 44 * 4, &cfgval);
    + if (WARN_ON(ret))
    + goto err_free_root_bus;
    + cfgval &= ~0x40000000; /* Clear FUNM */
    + cfgval |= 0x80000000; /* Set MSIXEN */
    + pci_write_config_dword(pdev, 44 * 4, cfgval);
    + pem->bus->msi = pdev->bus->msi;
    +
    + pci_scan_child_bus(pem->bus);
    + pci_bus_add_devices(pem->bus);
    + pci_assign_unassigned_root_bus_resources(pem->bus);
    +
    + return 0;
    +
    +err_free_root_bus:
    + pci_remove_root_bus(pem->bus);
    +err_root_bus:
    + pci_free_resource_list(&resources);
    +out:
    + return ret;
    +}
    +
    +static void thunder_pem_pci_remove(struct pci_dev *pdev)
    +{
    +}
    +
    +static struct pci_driver thunder_pem_driver = {
    + .name = "thunder_pem",
    + .id_table = thunder_pem_pci_table,
    + .probe = thunder_pem_pci_probe,
    + .remove = thunder_pem_pci_remove
    +};
    +
    +static int __init thunder_pcie_init(void)
    +{
    + int ret;
    +
    + ret = pci_register_driver(&thunder_pem_driver);
    +
    + return ret;
    +}
    +module_init(thunder_pcie_init);
    +
    +static void __exit thunder_pcie_exit(void)
    +{
    + pci_unregister_driver(&thunder_pem_driver);
    +}
    +module_exit(thunder_pcie_exit);
    diff --git a/drivers/pci/host/pcie-thunder.c b/drivers/pci/host/pcie-thunder.c
    new file mode 100644
    index 0000000..4abab5a
    --- /dev/null
    +++ b/drivers/pci/host/pcie-thunder.c
    @@ -0,0 +1,422 @@
    +/*
    + * PCIe host controller driver for Cavium Thunder SOC
    + *
    + * Copyright (C) 2014, 2015 Cavium Inc.
    + *
    + * This program is free software; you can redistribute it and/or
    + * modify it under the terms of the GNU General Public License as
    + * published by the Free Software Foundation; either version 2 of
    + * the License, or (at your option) any later version.
    + */
    +
    +#include <linux/kernel.h>
    +#include <linux/module.h>
    +#include <linux/of_irq.h>
    +#include <linux/of_pci.h>
    +#include <linux/pci.h>
    +#include <linux/platform_device.h>
    +#include <linux/msi.h>
    +#include <linux/irqchip/arm-gic-v3.h>
    +
    +#define PCI_DEVICE_ID_THUNDER_BRIDGE 0xa002
    +
    +#define THUNDER_PCIE_BUS_SHIFT 20
    +#define THUNDER_PCIE_DEV_SHIFT 15
    +#define THUNDER_PCIE_FUNC_SHIFT 12
    +
    +#define THUNDER_ECAM0_CFG_BASE 0x848000000000
    +#define THUNDER_ECAM1_CFG_BASE 0x849000000000
    +#define THUNDER_ECAM2_CFG_BASE 0x84a000000000
    +#define THUNDER_ECAM3_CFG_BASE 0x84b000000000
    +#define THUNDER_ECAM4_CFG_BASE 0x948000000000
    +#define THUNDER_ECAM5_CFG_BASE 0x949000000000
    +#define THUNDER_ECAM6_CFG_BASE 0x94a000000000
    +#define THUNDER_ECAM7_CFG_BASE 0x94b000000000
    +
    +struct thunder_pcie {
    + struct device_node *node;
    + struct device *dev;
    + void __iomem *cfg_base;
    + struct msi_controller *msi;
    + int ecam;
    + bool valid;
    +};
    +
    +int thunder_pem_requester_id(struct pci_dev *dev);
    +
    +static atomic_t thunder_pcie_ecam_probed;
    +
    +static u32 pci_requester_id_ecam(struct pci_dev *dev)
    +{
    + return (((pci_domain_nr(dev->bus) >> 2) << 19) |
    + ((pci_domain_nr(dev->bus) % 4) << 16) |
    + (dev->bus->number << 8) | dev->devfn);
    +}
    +
    +static u32 thunder_pci_requester_id(struct pci_dev *dev, u16 alias)
    +{
    + int ret;
    +
    + ret = thunder_pem_requester_id(dev);
    + if (ret >= 0)
    + return (u32)ret;
    +
    + return pci_requester_id_ecam(dev);
    +}
    +
    +/*
    + * This bridge is just for the sake of supporting ARI for
    + * downstream devices. No resources are attached to it.
    + * Copy upstream root bus resources to bridge which aide in
    + * resource claiming for downstream devices
    + */
    +static void pci_bridge_resource_fixup(struct pci_dev *dev)
    +{
    + struct pci_bus *bus;
    + int resno;
    +
    + bus = dev->subordinate;
    + for (resno = 0; resno < PCI_BRIDGE_RESOURCE_NUM; resno++) {
    + bus->resource[resno] = pci_bus_resource_n(bus->parent,
    + PCI_BRIDGE_RESOURCE_NUM + resno);
    + }
    +
    + for (resno = PCI_BRIDGE_RESOURCES;
    + resno <= PCI_BRIDGE_RESOURCE_END; resno++) {
    + dev->resource[resno].start = dev->resource[resno].end = 0;
    + dev->resource[resno].flags = 0;
    + }
    +}
    +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CAVIUM, PCI_DEVICE_ID_THUNDER_BRIDGE,
    + pci_bridge_resource_fixup);
    +
    +/*
    + * All PCIe devices in Thunder have fixed resources, shouldn't be reassigned.
    + * Also claim the device's valid resources to set 'res->parent' hierarchy.
    + */
    +static void pci_dev_resource_fixup(struct pci_dev *dev)
    +{
    + struct resource *res;
    + int resno;
    +
    + /*
    + * If the ECAM is not yet probed, we must be in a virtual
    + * machine. In that case, don't mark things as
    + * IORESOURCE_PCI_FIXED
    + */
    + if (!atomic_read(&thunder_pcie_ecam_probed))
    + return;
    +
    + for (resno = 0; resno < PCI_NUM_RESOURCES; resno++)
    + dev->resource[resno].flags |= IORESOURCE_PCI_FIXED;
    +
    + for (resno = 0; resno < PCI_BRIDGE_RESOURCES; resno++) {
    + res = &dev->resource[resno];
    + if (res->parent || !(res->flags & IORESOURCE_MEM))
    + continue;
    + pci_claim_resource(dev, resno);
    + }
    +}
    +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CAVIUM, PCI_ANY_ID,
    + pci_dev_resource_fixup);
    +
    +static void __iomem *thunder_pcie_get_cfg_addr(struct thunder_pcie *pcie,
    + unsigned int busnr,
    + unsigned int devfn, int reg)
    +{
    + return pcie->cfg_base +
    + ((busnr << THUNDER_PCIE_BUS_SHIFT)
    + | (PCI_SLOT(devfn) << THUNDER_PCIE_DEV_SHIFT)
    + | (PCI_FUNC(devfn) << THUNDER_PCIE_FUNC_SHIFT)) + reg;
    +}
    +
    +static int thunder_pcie_read_config(struct pci_bus *bus, unsigned int devfn,
    + int reg, int size, u32 *val)
    +{
    + struct thunder_pcie *pcie = bus->sysdata;
    + void __iomem *addr;
    + unsigned int busnr = bus->number;
    +
    + if (busnr > 255 || devfn > 255 || reg > 4095)
    + return PCIBIOS_DEVICE_NOT_FOUND;
    +
    + addr = thunder_pcie_get_cfg_addr(pcie, busnr, devfn, reg);
    +
    + switch (size) {
    + case 1:
    + *val = readb(addr);
    + break;
    + case 2:
    + *val = readw(addr);
    + break;
    + case 4:
    + *val = readl(addr);
    + break;
    + default:
    + return PCIBIOS_BAD_REGISTER_NUMBER;
    + }
    +
    + return PCIBIOS_SUCCESSFUL;
    +}
    +
    +static int thunder_pcie_write_config(struct pci_bus *bus, unsigned int devfn,
    + int reg, int size, u32 val)
    +{
    + struct thunder_pcie *pcie = bus->sysdata;
    + void __iomem *addr;
    + unsigned int busnr = bus->number;
    +
    + if (busnr > 255 || devfn > 255 || reg > 4095)
    + return PCIBIOS_DEVICE_NOT_FOUND;
    +
    + addr = thunder_pcie_get_cfg_addr(pcie, busnr, devfn, reg);
    +
    + switch (size) {
    + case 1:
    + writeb(val, addr);
    + break;
    + case 2:
    + writew(val, addr);
    + break;
    + case 4:
    + writel(val, addr);
    + break;
    + default:
    + return PCIBIOS_BAD_REGISTER_NUMBER;
    + }
    +
    + return PCIBIOS_SUCCESSFUL;
    +}
    +
    +static struct pci_ops thunder_pcie_ops = {
    + .read = thunder_pcie_read_config,
    + .write = thunder_pcie_write_config,
    +};
    +
    +static int thunder_pcie_msi_enable(struct thunder_pcie *pcie,
    + struct pci_bus *bus)
    +{
    + struct device_node *msi_node;
    +
    + msi_node = of_parse_phandle(pcie->node, "msi-parent", 0);
    + if (!msi_node)
    + return -ENODEV;
    +
    + pcie->msi = of_pci_find_msi_chip_by_node(msi_node);
    + if (!pcie->msi)
    + return -ENODEV;
    +
    + pcie->msi->dev = pcie->dev;
    + bus->msi = pcie->msi;
    +
    + return 0;
    +}
    +
    +static void thunder_pcie_config(struct thunder_pcie *pcie, u64 addr)
    +{
    + atomic_set(&thunder_pcie_ecam_probed, 1);
    + set_its_pci_requester_id(thunder_pci_requester_id);
    +
    + pcie->valid = true;
    +
    + switch (addr) {
    + case THUNDER_ECAM0_CFG_BASE:
    + pcie->ecam = 0;
    + break;
    + case THUNDER_ECAM1_CFG_BASE:
    + pcie->ecam = 1;
    + break;
    + case THUNDER_ECAM2_CFG_BASE:
    + pcie->ecam = 2;
    + break;
    + case THUNDER_ECAM3_CFG_BASE:
    + pcie->ecam = 3;
    + break;
    + case THUNDER_ECAM4_CFG_BASE:
    + pcie->ecam = 4;
    + break;
    + case THUNDER_ECAM5_CFG_BASE:
    + pcie->ecam = 5;
    + break;
    + case THUNDER_ECAM6_CFG_BASE:
    + pcie->ecam = 6;
    + break;
    + case THUNDER_ECAM7_CFG_BASE:
    + pcie->ecam = 7;
    + break;
    + default:
    + pcie->valid = false;
    + break;
    + }
    +}
    +
    +static int thunder_pcie_probe(struct platform_device *pdev)
    +{
    + struct thunder_pcie *pcie;
    + struct resource *cfg_base;
    + struct pci_bus *bus;
    + int ret = 0;
    + LIST_HEAD(res);
    +
    + pcie = devm_kzalloc(&pdev->dev, sizeof(*pcie), GFP_KERNEL);
    + if (!pcie)
    + return -ENOMEM;
    +
    + pcie->node = of_node_get(pdev->dev.of_node);
    + pcie->dev = &pdev->dev;
    +
    + /* Get controller's configuration space range */
    + cfg_base = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    +
    + thunder_pcie_config(pcie, cfg_base->start);
    +
    + pcie->cfg_base = devm_ioremap_resource(&pdev->dev, cfg_base);
    + if (IS_ERR(pcie->cfg_base)) {
    + ret = PTR_ERR(pcie->cfg_base);
    + goto err_ioremap;
    + }
    +
    + dev_info(&pdev->dev, "ECAM%d CFG BASE 0x%llx\n",
    + pcie->ecam, (u64)cfg_base->start);
    +
    + ret = of_pci_get_host_bridge_resources(pdev->dev.of_node,
    + 0, 255, &res, NULL);
    + if (ret)
    + goto err_root_bus;
    +
    + bus = pci_create_root_bus(&pdev->dev, 0, &thunder_pcie_ops, pcie, &res);
    + if (!bus) {
    + ret = -ENODEV;
    + goto err_root_bus;
    + }
    +
    + /* Set reference to MSI chip */
    + ret = thunder_pcie_msi_enable(pcie, bus);
    + if (ret) {
    + dev_err(&pdev->dev,
    + "Unable to set reference to MSI chip: ret=%d\n", ret);
    + goto err_msi;
    + }
    +
    + platform_set_drvdata(pdev, pcie);
    +
    + pci_scan_child_bus(bus);
    + pci_bus_add_devices(bus);
    +
    + return 0;
    +err_msi:
    + pci_remove_root_bus(bus);
    +err_root_bus:
    + pci_free_resource_list(&res);
    +err_ioremap:
    + of_node_put(pcie->node);
    + return ret;
    +}
    +
    +static const struct of_device_id thunder_pcie_of_match[] = {
    + { .compatible = "cavium,thunder-pcie", },
    + {},
    +};
    +MODULE_DEVICE_TABLE(of, thunder_pcie_of_match);
    +
    +static struct platform_driver thunder_pcie_driver = {
    + .driver = {
    + .name = "thunder-pcie",
    + .owner = THIS_MODULE,
    + .of_match_table = thunder_pcie_of_match,
    + },
    + .probe = thunder_pcie_probe,
    +};
    +module_platform_driver(thunder_pcie_driver);
    +
    +#ifdef CONFIG_ACPI
    +
    +static int
    +thunder_mmcfg_read_config(struct pci_mmcfg_region *cfg, unsigned int busnr,
    + unsigned int devfn, int reg, int len, u32 *value)
    +{
    + struct thunder_pcie *pcie = cfg->data;
    + void __iomem *addr;
    +
    + if (!pcie->valid) {
    + /* Not support for now */
    + pr_err("RC PEM not supported !!!\n");
    + return PCIBIOS_DEVICE_NOT_FOUND;
    + }
    +
    + addr = thunder_pcie_get_cfg_addr(pcie, busnr, devfn, reg);
    +
    + switch (len) {
    + case 1:
    + *value = readb(addr);
    + break;
    + case 2:
    + *value = readw(addr);
    + break;
    + case 4:
    + *value = readl(addr);
    + break;
    + default:
    + return PCIBIOS_BAD_REGISTER_NUMBER;
    + }
    +
    + return PCIBIOS_SUCCESSFUL;
    +}
    +
    +static int thunder_mmcfg_write_config(struct pci_mmcfg_region *cfg,
    + unsigned int busnr, unsigned int devfn, int reg, int len,
    + u32 value) {
    + struct thunder_pcie *pcie = cfg->data;
    + void __iomem *addr;
    +
    + if (!pcie->valid) {
    + /* Not support for now */
    + pr_err("RC PEM not supported !!!\n");
    + return PCIBIOS_DEVICE_NOT_FOUND;
    + }
    +
    + addr = thunder_pcie_get_cfg_addr(pcie, busnr, devfn, reg);
    +
    + switch (len) {
    + case 1:
    + writeb(value, addr);
    + break;
    + case 2:
    + writew(value, addr);
    + break;
    + case 4:
    + writel(value, addr);
    + break;
    + default:
    + return PCIBIOS_BAD_REGISTER_NUMBER;
    + }
    +
    + return PCIBIOS_SUCCESSFUL;
    +}
    +
    +static int thunder_acpi_mcfg_fixup(struct acpi_pci_root *root,
    + struct pci_mmcfg_region *cfg)
    +{
    + struct thunder_pcie *pcie;
    +
    + pcie = kzalloc(sizeof(*pcie), GFP_KERNEL);
    + if (!pcie)
    + return -ENOMEM;
    +
    + pcie->dev = &root->device->dev;
    +
    + thunder_pcie_config(pcie, cfg->address);
    +
    + pcie->cfg_base = cfg->virt;
    + cfg->data = pcie;
    + cfg->read = thunder_mmcfg_read_config;
    + cfg->write = thunder_mmcfg_write_config;
    +
    + return 0;
    +}
    +DECLARE_ACPI_MCFG_FIXUP("CAVIUM", "THUNDERX", thunder_acpi_mcfg_fixup);
    +#endif
    +
    +MODULE_AUTHOR("Sunil Goutham");
    +MODULE_DESCRIPTION("Cavium Thunder ECAM host controller driver");
    +MODULE_LICENSE("GPL v2");
    --
    1.9.1


    \
     
     \ /
      Last update: 2015-07-15 19:01    [W:3.162 / U:0.684 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site