* [PATCH 1/2] add new platform driver for PCI RC
2015-11-24 11:44 [PATCH 0/2] adding PCI support to AXS10x Joao Pinto
@ 2015-11-24 11:44 ` Joao Pinto
2015-11-24 12:15 ` kbuild test robot
2015-11-24 11:44 ` [PATCH 2/2] PCI support added to ARC Joao Pinto
1 sibling, 1 reply; 5+ messages in thread
From: Joao Pinto @ 2015-11-24 11:44 UTC (permalink / raw)
To: linux-pci, linux-kernel
Cc: bhelgaas, CARLOS.PALMINHA, Vineet.Gupta1, Alexey.Brodkin,
Joao Pinto
This patch adds a new driver that will be the reference platform driver for all
PCIe RC IP Protoyping Kits based on ARC SDP. This patch is composed by:
-Changes to pcie-designware driver adds a function that enables the feature
of starting the LTSSM (Link Train Status State) used by the new driver
-MAINTAINERS file was updated to include the new driver
-Documentation/devicetree/bindings/pci was updated to include the new driver
documentation
Signed-off-by: Joao Pinto <jpinto@synopsys.com>
---
.../devicetree/bindings/pci/pcie-snpsdev.txt | 28 ++
MAINTAINERS | 7 +
drivers/pci/host/Kconfig | 5 +
drivers/pci/host/Makefile | 1 +
drivers/pci/host/pcie-designware.c | 9 +
drivers/pci/host/pcie-designware.h | 1 +
drivers/pci/host/pcie-snpsdev.c | 342 +++++++++++++++++++++
7 files changed, 393 insertions(+)
create mode 100644 Documentation/devicetree/bindings/pci/pcie-snpsdev.txt
create mode 100644 drivers/pci/host/pcie-snpsdev.c
diff --git a/Documentation/devicetree/bindings/pci/pcie-snpsdev.txt b/Documentation/devicetree/bindings/pci/pcie-snpsdev.txt
new file mode 100644
index 0000000..b833c8f
--- /dev/null
+++ b/Documentation/devicetree/bindings/pci/pcie-snpsdev.txt
@@ -0,0 +1,28 @@
+Synopsys PCI RC IP Prototyping Kit
+----------------------------------
+
+This is the reference platform driver to be used in the Synopsys PCI RC IP
+Prototyping Kits.
+
+Required properties:
+- compatible: "snps,pcie-snpsdev";
+- reg: A list of physical regions to access the device.
+- interrupts: interrupt for the device.
+- #address-cells: must be 3.
+- #size-cells: must be 2.
+
+Example configuration:
+
+ pcie: pcie@0xdffff000 {
+ #interrupt-cells = <1>;
+ compatible = "snps,pcie-snpsdev";
+ reg = <0xdffff000 0x1000>;
+ interrupts = <25>, <24>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+ device_type = "pci";
+ ranges = <0x00000800 0 0xd0000000 0xd0000000 0 0x00002000
+ 0x81000000 0 0x00000000 0xde000000 0 0x00010000
+ 0x82000000 0 0xd0400000 0xd0400000 0 0x0d000000>;
+ num-lanes = <1>;
+ };
diff --git a/MAINTAINERS b/MAINTAINERS
index e9caa4b..d2e4506 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -8230,6 +8230,13 @@ S: Maintained
F: Documentation/devicetree/bindings/pci/hisilicon-pcie.txt
F: drivers/pci/host/pcie-hisi.c
+PCI DRIVER FOR SYNOPSYS PROTOTYPING DEVICE
+M: Joao Pinto <jpinto@synopsys.com>
+L: linux-pci@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/pci/pcie-snpsdev.txt
+F: drivers/pci/host/pcie-snpsdev.c
+
PCMCIA SUBSYSTEM
P: Linux PCMCIA Team
L: linux-pcmcia@lists.infradead.org
diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
index f131ba9..a874b1e 100644
--- a/drivers/pci/host/Kconfig
+++ b/drivers/pci/host/Kconfig
@@ -172,4 +172,9 @@ config PCI_HISI
help
Say Y here if you want PCIe controller support on HiSilicon HIP05 SoC
+config PCIE_SNPSDEV
+ bool "Platform Driver for Synopsys Device"
+ select PCIEPORTBUS
+ select PCIE_DW
+
endmenu
diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
index 9d4d3c6..e422f65 100644
--- a/drivers/pci/host/Makefile
+++ b/drivers/pci/host/Makefile
@@ -20,3 +20,4 @@ obj-$(CONFIG_PCIE_IPROC_BCMA) += pcie-iproc-bcma.o
obj-$(CONFIG_PCIE_ALTERA) += pcie-altera.o
obj-$(CONFIG_PCIE_ALTERA_MSI) += pcie-altera-msi.o
obj-$(CONFIG_PCI_HISI) += pcie-hisi.o
+obj-$(CONFIG_PCIE_SNPSDEV) += pcie-snpsdev.o
diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c
index 540f077..d9a7aa6 100644
--- a/drivers/pci/host/pcie-designware.c
+++ b/drivers/pci/host/pcie-designware.c
@@ -706,6 +706,15 @@ static struct pci_ops dw_pcie_ops = {
.write = dw_pcie_wr_conf,
};
+void dw_pcie_link_retrain(struct pcie_port *pp)
+{
+ u32 val = 0;
+
+ dw_pcie_readl_rc(pp, LINK_CONTROL_LINK_STATUS_REG, &val);
+ val = val | PCIE_RETRAIN_LINK_MASK;
+ dw_pcie_writel_rc(pp, val, LINK_CONTROL_LINK_STATUS_REG);
+}
+
void dw_pcie_setup_rc(struct pcie_port *pp)
{
u32 val;
diff --git a/drivers/pci/host/pcie-designware.h b/drivers/pci/host/pcie-designware.h
index 2356d29..249b631 100644
--- a/drivers/pci/host/pcie-designware.h
+++ b/drivers/pci/host/pcie-designware.h
@@ -79,5 +79,6 @@ void dw_pcie_msi_init(struct pcie_port *pp);
int dw_pcie_link_up(struct pcie_port *pp);
void dw_pcie_setup_rc(struct pcie_port *pp);
int dw_pcie_host_init(struct pcie_port *pp);
+void dw_pcie_link_retrain(struct pcie_port *pp);
#endif /* _PCIE_DESIGNWARE_H */
diff --git a/drivers/pci/host/pcie-snpsdev.c b/drivers/pci/host/pcie-snpsdev.c
new file mode 100644
index 0000000..5721da9
--- /dev/null
+++ b/drivers/pci/host/pcie-snpsdev.c
@@ -0,0 +1,342 @@
+/*
+ * PCIe RC driver for Synopsys Designware Core
+ *
+ * Copyright (C) 2015-2016 Synopsys, Inc. (www.synopsys.com)
+ *
+ * Authors: Manjunath Bettegowda <manjumb@synopsys.com>,
+ * Jie Deng <jiedeng@synopsys.com>
+ * Joao Pinto <jpinto@synopsys.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/resource.h>
+#include <linux/signal.h>
+#include <linux/types.h>
+
+#include "pcie-designware.h"
+
+#define to_snpsdev_pcie(x) container_of(x, struct snpsdev_pcie, pp)
+
+struct snpsdev_pcie {
+ void __iomem *mem_base; /* Memory Base to access Core's [RC]
+ * Config Space Layout
+ */
+ struct pcie_port pp; /* RC Root Port specific structure -
+ * DWC_PCIE_RC stuff
+ */
+};
+
+#define SIZE_1GB 0x40000000
+#define PCI_EQUAL_CONTROL_PHY 0x00000707
+
+/* PCIe Port Logic registers (memory-mapped) */
+#define PLR_OFFSET 0x700
+#define PCIE_PHY_DEBUG_R0 (PLR_OFFSET + 0x28) /* 0x728 */
+#define PCIE_PHY_DEBUG_R1 (PLR_OFFSET + 0x2c) /* 0x72c */
+
+/* PCIE PHY CONTROL REGISTER: Useful for cfg_phy_control GPIO outputs */
+#define PCIE_PHY_CTRL (PLR_OFFSET + 0x114) /* 0x814 */
+/* PCIE PHY STATUS REGISTER: Useful for phy_cfg_status GPIO inputs */
+#define PCIE_PHY_STAT (PLR_OFFSET + 0x110) /* 0x810 */
+
+static void snpsdev_pcie_fixup_bridge(struct pci_dev *dev)
+{
+ u32 slot_cap;
+
+ u16 caps_reg = pcie_caps_reg(dev) | PCI_EXP_FLAGS_SLOT;
+
+ pcie_capability_write_word(dev, PCI_EXP_FLAGS, caps_reg);
+ dev->pcie_flags_reg = caps_reg;
+
+ pcie_capability_read_dword(dev, PCI_EXP_SLTCAP, &slot_cap);
+
+ slot_cap = slot_cap & (~PCI_EXP_SLTCAP_SPLV);
+ slot_cap = slot_cap | (0x30 << 7);
+
+ pcie_capability_write_dword(dev, PCI_EXP_SLTCAP, slot_cap);
+
+ if (pcibios_enable_device(dev, ~0) < 0)
+ pr_err("PCI: synopsys device enable failed\n");
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, snpsdev_pcie_fixup_bridge);
+
+static void snpsdev_pcie_fixup_res(struct pci_dev *dev)
+{
+ struct resource *res;
+ resource_size_t size;
+ int bar;
+
+ for (bar = 0; bar < 6; bar++) {
+ res = dev->resource + bar;
+ size = resource_size(res);
+
+ if (size == SIZE_1GB) {
+ res->start = 0;
+ res->end = 0;
+ res->flags = 0;
+ }
+ }
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, snpsdev_pcie_fixup_res);
+
+/* This handler was created for future work */
+static irqreturn_t snpsdev_pcie_irq_handler(int irq, void *arg)
+{
+ return IRQ_NONE;
+}
+
+static irqreturn_t snpsdev_pcie_msi_irq_handler(int irq, void *arg)
+{
+ struct pcie_port *pp = arg;
+
+ dw_handle_msi_irq(pp);
+
+ return IRQ_HANDLED;
+}
+
+static void snpsdev_pcie_init_phy(struct pcie_port *pp)
+{
+ /* write Lane 0 Equalization Control fields register */
+ writel(PCI_EQUAL_CONTROL_PHY, pp->dbi_base + 0x154);
+}
+
+static int snpsdev_pcie_deassert_core_reset(struct pcie_port *pp)
+{
+ return 0;
+}
+
+/*
+ * snpsdev_pcie_host_init()
+ * Platform specific host/RC initialization
+ * a. Assert the core reset
+ * b. Assert and deassert phy reset and initialize the phy
+ * c. De-Assert the core reset
+ * d. Initializet the Root Port (BARs/Memory Or IO/ Interrupt/ Commnad Reg)
+ * e. Initiate Link startup procedure
+ *
+ */
+static void snpsdev_pcie_host_init(struct pcie_port *pp)
+{
+ int count = 0;
+
+ /* Initialize Phy (Reset/poweron/control-inputs ) */
+ snpsdev_pcie_init_phy(pp);
+
+ /* de-assert core reset */
+ snpsdev_pcie_deassert_core_reset(pp);
+
+ /* We expect the PCIE Link to be up by this time */
+ dw_pcie_setup_rc(pp);
+
+ /* Start LTSSM here */
+ dw_pcie_link_retrain(pp);
+
+ /* Check for Link up indication */
+ while (!dw_pcie_link_up(pp)) {
+ usleep_range(1000, 1100);
+ count++;
+ if (count == 20) {
+ dev_err(pp->dev, "phy link never came up\n");
+ dev_dbg(pp->dev,
+ "PL_DEBUG0: 0x%08x, DEBUG_R1: 0x%08x\n",
+ readl(pp->dbi_base + PCIE_PHY_DEBUG_R0),
+ readl(pp->dbi_base + PCIE_PHY_DEBUG_R1));
+ break;
+ }
+ }
+
+ if (IS_ENABLED(CONFIG_PCI_MSI))
+ dw_pcie_msi_init(pp);
+}
+/**
+ *
+ * Let all outof band signalling be handled by cfg_phy_control[31:0]
+ * which is selected through optional config attribute PHY_CONTROL_REG
+ *
+ * Monitor cxpl_debug_info as required to take necessary action
+ * This is available in the register PCIE_PHY_DEBUG_R0 & PCIE_PHY_DEBUG_R1
+ *
+ */
+static int snpsdev_pcie_link_up(struct pcie_port *pp)
+{
+ u32 status;
+
+ /* Bit number 36: reports LTSSM PHY Link UP; Available in bit 3 of
+ * PCIE_PHY_DEBUG_R1
+ */
+ status = readl(pp->dbi_base + PCIE_PHY_DEBUG_R1) & (0x1 << 4);
+ if (status != 0)
+ return 1;
+
+ /* TODO:Now Link is in L0;Initiate GEN2/GEN3 migration if RC Supports*/
+ return 0;
+}
+
+
+/**
+ * This is RC operation structure
+ * snpsdev_pcie_link_up: the function which initiates the phy link up procedure
+ * snpsdev_pcie_host_init: the function whihc does the host/RC Root port
+ * initialization.
+ */
+static struct pcie_host_ops snpsdev_pcie_host_ops = {
+ .link_up = snpsdev_pcie_link_up,
+ .host_init = snpsdev_pcie_host_init,
+};
+
+/**
+ * snpsdev_add_pcie_port
+ * This function
+ * a. installs the interrupt handler
+ * b. registers host operations int he pcie_port structure
+ */
+static int snpsdev_add_pcie_port(struct pcie_port *pp,
+ struct platform_device *pdev)
+{
+ int ret;
+
+ pp->irq = platform_get_irq(pdev, 1);
+
+ if (pp->irq < 0) {
+ if (pp->irq != -EPROBE_DEFER)
+ dev_err(&pdev->dev, "cannot get irq\n");
+ return pp->irq;
+ }
+
+ ret = devm_request_irq(&pdev->dev, pp->irq, snpsdev_pcie_irq_handler,
+ IRQF_SHARED, "snpsdev-pcie", pp);
+
+ if (ret) {
+ dev_err(&pdev->dev, "failed to request irq\n");
+ return ret;
+ }
+
+ if (IS_ENABLED(CONFIG_PCI_MSI)) {
+ pp->msi_irq = platform_get_irq(pdev, 0);
+
+ if (pp->msi_irq < 0) {
+ if (pp->msi_irq != -EPROBE_DEFER)
+ dev_err(&pdev->dev, "cannot get msi irq\n");
+ return pp->msi_irq;
+ }
+
+ ret = devm_request_irq(&pdev->dev, pp->msi_irq,
+ snpsdev_pcie_msi_irq_handler,
+ IRQF_SHARED, "snpsdev-pcie-msi", pp);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to request msi irq\n");
+ return ret;
+ }
+ }
+
+ pp->root_bus_nr = -1;
+ pp->ops = &snpsdev_pcie_host_ops;
+
+ /* Below function:
+ * Checks for range property from DT
+ * Gets the IO and MEMORY and CONFIG-Space ranges from DT
+ * Does IOREMAPS on the physical addresses
+ * Gets the num-lanes from DT
+ * Gets MSI capability from DT
+ * Calls the platform specific host initialization
+ * Program the correct class, BAR0, Link width, in Config space
+ * Then it calls pci common init routine
+ * Then it calls function to assign "unassigned resources"
+ */
+ ret = dw_pcie_host_init(pp);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to initialize host\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * snpsdev_pcie_rc_probe()
+ * This function gets called as part of pcie registration. if the id matches
+ * the platform driver framework will call this function.
+ *
+ * @pdev: Pointer to the platform_device structure
+ *
+ * Returns zero on success; Negative errorno on failure
+ */
+static int __init snpsdev_pcie_rc_probe(struct platform_device *pdev)
+{
+ struct snpsdev_pcie *snpsdev_pcie;
+ struct pcie_port *pp;
+ struct resource *dwc_pcie_rc_res; /* Resource from DT */
+ int ret;
+
+ snpsdev_pcie = devm_kzalloc(&pdev->dev, sizeof(*snpsdev_pcie),
+ GFP_KERNEL);
+ if (!snpsdev_pcie)
+ return -ENOMEM;
+
+ pp = &snpsdev_pcie->pp;
+ pp->dev = &pdev->dev;
+
+ dwc_pcie_rc_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ if (!dwc_pcie_rc_res)
+ return -ENODEV;
+
+ snpsdev_pcie->mem_base = devm_ioremap_resource(&pdev->dev,
+ dwc_pcie_rc_res);
+ if (IS_ERR(snpsdev_pcie->mem_base)) {
+ ret = PTR_ERR(snpsdev_pcie->mem_base);
+ return ret;
+ }
+ pp->dbi_base = snpsdev_pcie->mem_base;
+
+ ret = snpsdev_add_pcie_port(pp, pdev);
+ if (ret < 0)
+ return ret;
+
+ platform_set_drvdata(pdev, snpsdev_pcie);
+
+ return 0;
+}
+
+static int __exit snpsdev_pcie_rc_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static const struct of_device_id snpsdev_pcie_rc_of_match[] = {
+ { .compatible = "snps,pcie-snpsdev", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, snpsdev_pcie_rc_of_match);
+
+static struct platform_driver snpsdev_pcie_rc_driver = {
+ .remove = __exit_p(snpsdev_pcie_rc_remove),
+ .driver = {
+ .name = "pcie-snpsdev",
+ .owner = THIS_MODULE,
+ .of_match_table = snpsdev_pcie_rc_of_match,
+ },
+};
+
+static int __init snpsdev_pcie_init(void)
+{
+ return platform_driver_probe(&snpsdev_pcie_rc_driver,
+ snpsdev_pcie_rc_probe);
+}
+subsys_initcall(snpsdev_pcie_init);
+
+MODULE_AUTHOR("Manjunath Bettegowda <manjumb@synopsys.com>");
+MODULE_DESCRIPTION("Platform Driver for Synopsys Device");
+MODULE_LICENSE("GPL v2");
--
1.8.1.5
^ permalink raw reply related [flat|nested] 5+ messages in thread* [PATCH 2/2] PCI support added to ARC
2015-11-24 11:44 [PATCH 0/2] adding PCI support to AXS10x Joao Pinto
2015-11-24 11:44 ` [PATCH 1/2] add new platform driver for PCI RC Joao Pinto
@ 2015-11-24 11:44 ` Joao Pinto
1 sibling, 0 replies; 5+ messages in thread
From: Joao Pinto @ 2015-11-24 11:44 UTC (permalink / raw)
To: linux-pci, linux-kernel
Cc: bhelgaas, CARLOS.PALMINHA, Vineet.Gupta1, Alexey.Brodkin,
Joao Pinto
This patch adds PCI support to ARC and updates drivers/pci Makefile enabling
the ARC arch to use the generic PCI setup functions.
Signed-off-by: Joao Pinto <jpinto@synopsys.com>
---
arch/arc/Kconfig | 22 +++
arch/arc/include/asm/dma.h | 5 +
arch/arc/include/asm/io.h | 2 +
arch/arc/include/asm/mach/pci.h | 97 +++++++++++
arch/arc/include/asm/pci.h | 34 ++++
arch/arc/kernel/Makefile | 1 +
arch/arc/kernel/pcibios.c | 360 ++++++++++++++++++++++++++++++++++++++++
arch/arc/mm/ioremap.c | 29 +++-
arch/arc/plat-axs10x/Kconfig | 1 +
drivers/pci/Makefile | 1 +
10 files changed, 551 insertions(+), 1 deletion(-)
create mode 100644 arch/arc/include/asm/mach/pci.h
create mode 100644 arch/arc/include/asm/pci.h
create mode 100644 arch/arc/kernel/pcibios.c
diff --git a/arch/arc/Kconfig b/arch/arc/Kconfig
index 2c2ac3f..5b526a3 100644
--- a/arch/arc/Kconfig
+++ b/arch/arc/Kconfig
@@ -19,6 +19,7 @@ config ARC
select GENERIC_FIND_FIRST_BIT
# for now, we don't need GENERIC_IRQ_PROBE, CONFIG_GENERIC_IRQ_CHIP
select GENERIC_IRQ_SHOW
+ select GENERIC_PCI_IOMAP
select GENERIC_PENDING_IRQ if SMP
select GENERIC_SMP_IDLE_THREAD
select HAVE_ARCH_KGDB
@@ -39,6 +40,9 @@ config ARC
select PERF_USE_VMALLOC
select HAVE_DEBUG_STACKOVERFLOW
+config MIGHT_HAVE_PCI
+ bool
+
config TRACE_IRQFLAGS_SUPPORT
def_bool y
@@ -110,6 +114,24 @@ config ISA_ARCV2
endchoice
+menu "Bus Support"
+
+config PCI
+ bool "PCI support" if MIGHT_HAVE_PCI
+ help
+ PCI is the name of a bus system, i.e. the way the CPU talks to the other stuff inside
+ your box.Find out if your board/platform have PCI.
+ Note: PCIE support for Synopsys Device will be available only when
+ HAPS DX is configured with PCIE RC bitmap. If you have PCI, say Y, otherwise N.
+
+config PCI_SYSCALL
+ def_bool PCI
+
+source "drivers/pci/Kconfig"
+source "drivers/pci/pcie/Kconfig"
+
+endmenu
+
menu "ARC CPU Configuration"
choice
diff --git a/arch/arc/include/asm/dma.h b/arch/arc/include/asm/dma.h
index ca7c451..37942fa 100644
--- a/arch/arc/include/asm/dma.h
+++ b/arch/arc/include/asm/dma.h
@@ -10,5 +10,10 @@
#define ASM_ARC_DMA_H
#define MAX_DMA_ADDRESS 0xC0000000
+#ifdef CONFIG_PCI
+extern int isa_dma_bridge_buggy;
+#else
+#define isa_dma_bridge_buggy (0)
+#endif
#endif
diff --git a/arch/arc/include/asm/io.h b/arch/arc/include/asm/io.h
index 694ece8..d86c2e3 100644
--- a/arch/arc/include/asm/io.h
+++ b/arch/arc/include/asm/io.h
@@ -17,6 +17,8 @@ extern void __iomem *ioremap(unsigned long physaddr, unsigned long size);
extern void __iomem *ioremap_prot(phys_addr_t offset, unsigned long size,
unsigned long flags);
extern void iounmap(const void __iomem *addr);
+extern void __iomem *ioport_map(unsigned long port, unsigned int size);
+extern int pci_ioremap_io(unsigned int offset, phys_addr_t phys_addr);
#define ioremap_nocache(phy, sz) ioremap(phy, sz)
#define ioremap_wc(phy, sz) ioremap(phy, sz)
diff --git a/arch/arc/include/asm/mach/pci.h b/arch/arc/include/asm/mach/pci.h
new file mode 100644
index 0000000..9e75277
--- /dev/null
+++ b/arch/arc/include/asm/mach/pci.h
@@ -0,0 +1,97 @@
+/*
+ * arch/arc/include/asm/mach/pci.h
+ *
+ * Copyright (C) 2004-2014 Synopsys, Inc. (www.synopsys.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __ASM_MACH_PCI_H
+#define __ASM_MACH_PCI_H
+
+#include <linux/ioport.h>
+
+struct pci_sys_data;
+struct pci_ops;
+struct pci_bus;
+struct device;
+
+struct hw_pci {
+#ifdef CONFIG_PCI_DOMAINS
+ int domain;
+#endif
+ struct pci_ops *ops;
+ int nr_controllers;
+ void **private_data;
+ int (*setup)(int nr, struct pci_sys_data *);
+ struct pci_bus *(*scan)(int nr, struct pci_sys_data *);
+ void (*preinit)(void);
+ void (*postinit)(void);
+ u8 (*swizzle)(struct pci_dev *dev, u8 *pin);
+ int (*map_irq)(const struct pci_dev *dev, u8 slot, u8 pin);
+ resource_size_t (*align_resource)(struct pci_dev *dev,
+ const struct resource *res,
+ resource_size_t start,
+ resource_size_t size,
+ resource_size_t align);
+ void (*add_bus)(struct pci_bus *bus);
+ void (*remove_bus)(struct pci_bus *bus);
+};
+
+/*
+ * Per-controller structure
+ */
+struct pci_sys_data {
+#ifdef CONFIG_PCI_DOMAINS
+ int domain;
+#endif
+ struct list_head node;
+ int busnr; /* primary bus number */
+ u64 mem_offset; /* bus->cpu memory mapping offset */
+ unsigned long io_offset; /* bus->cpu IO mapping offset */
+ struct pci_bus *bus; /* PCI bus */
+ struct list_head resources; /* root bus resources (apertures) */
+ struct resource io_res;
+ char io_res_name[12];
+ /* Bridge swizzling */
+ u8 (*swizzle)(struct pci_dev *, u8 *);
+ /* IRQ mapping */
+ int (*map_irq)(const struct pci_dev *, u8, u8);
+ /* Resource alignement requirements */
+ resource_size_t (*align_resource)(struct pci_dev *dev,
+ const struct resource *res,
+ resource_size_t start,
+ resource_size_t size,
+ resource_size_t align);
+ void (*add_bus)(struct pci_bus *bus);
+ void (*remove_bus)(struct pci_bus *bus);
+ void *private_data; /* platform controller private data */
+};
+
+/*
+ * Call this with your hw_pci struct to initialise the PCI system.
+ */
+void pci_common_init_dev(struct device *, struct hw_pci *);
+
+/*
+ * Compatibility wrapper for older platforms that do not care about
+ * passing the parent device.
+ */
+static inline void pci_common_init(struct hw_pci *hw)
+{
+ pci_common_init_dev(NULL, hw);
+}
+
+/*
+ * Setup early fixed I/O mapping.
+ */
+#if defined(CONFIG_PCI)
+extern void pci_map_io_early(unsigned long pfn);
+#else
+static inline void pci_map_io_early(unsigned long pfn) {}
+#endif
+
+#endif /* __ASM_MACH_PCI_H */
+
diff --git a/arch/arc/include/asm/pci.h b/arch/arc/include/asm/pci.h
new file mode 100644
index 0000000..085b15f
--- /dev/null
+++ b/arch/arc/include/asm/pci.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2015-2016 Synopsys, Inc. (www.synopsys.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _ASM_ARC_PCI_H
+#define _ASM_ARC_PCI_H
+
+#ifdef __KERNEL__
+#include <asm-generic/pci-dma-compat.h>
+#include <asm-generic/pci-bridge.h>
+
+#include <asm/mach/pci.h> /* for pci_sys_data */
+
+extern unsigned long pcibios_min_io;
+#define PCIBIOS_MIN_IO pcibios_min_io
+extern unsigned long pcibios_min_mem;
+#define PCIBIOS_MIN_MEM pcibios_min_mem
+
+#define pcibios_assign_all_busses() 1
+/*
+ * The PCI address space does equal the physical memory address space.
+ * The networking and block device layers use this boolean for bounce
+ * buffer decisions.
+ */
+#define PCI_DMA_BUS_IS_PHYS (1)
+
+#endif /* __KERNEL__ */
+
+#endif /* _ASM_ARC_PCI_H */
+
diff --git a/arch/arc/kernel/Makefile b/arch/arc/kernel/Makefile
index e7f3625..1bc2036 100644
--- a/arch/arc/kernel/Makefile
+++ b/arch/arc/kernel/Makefile
@@ -12,6 +12,7 @@ obj-y := arcksyms.o setup.o irq.o time.o reset.o ptrace.o process.o devtree.o
obj-y += signal.o traps.o sys.o troubleshoot.o stacktrace.o disasm.o clk.o
obj-$(CONFIG_ISA_ARCOMPACT) += entry-compact.o intc-compact.o
obj-$(CONFIG_ISA_ARCV2) += entry-arcv2.o intc-arcv2.o
+obj-$(CONFIG_PCI) += pcibios.o
obj-$(CONFIG_MODULES) += arcksyms.o module.o
obj-$(CONFIG_SMP) += smp.o
diff --git a/arch/arc/kernel/pcibios.c b/arch/arc/kernel/pcibios.c
new file mode 100644
index 0000000..a7bd76a
--- /dev/null
+++ b/arch/arc/kernel/pcibios.c
@@ -0,0 +1,360 @@
+/*
+ * Copyright (C) 2014-2015 Synopsys, Inc. (www.synopsys.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/bootmem.h>
+#include <linux/sizes.h>
+#include <linux/slab.h>
+
+#include <asm/pci.h>
+#include <asm/mach/pci.h>
+
+static int pcibios_init_resources(int busnr, struct pci_sys_data *sys)
+{
+ int ret;
+ struct pci_host_bridge_window *window;
+
+ if (list_empty(&sys->resources)) {
+ pci_add_resource_offset(&sys->resources,
+ &iomem_resource, sys->mem_offset);
+ }
+
+ list_for_each_entry(window, &sys->resources, list) {
+ if (resource_type(window->res) == IORESOURCE_IO)
+ return 0;
+ }
+
+ sys->io_res.start = (busnr * SZ_64K) ? : pcibios_min_io;
+ sys->io_res.end = (busnr + 1) * SZ_64K - 1;
+ sys->io_res.flags = IORESOURCE_IO;
+ sys->io_res.name = sys->io_res_name;
+ sprintf(sys->io_res_name, "PCI%d I/O", busnr);
+
+ ret = request_resource(&ioport_resource, &sys->io_res);
+ if (ret) {
+ pr_err("PCI: unable to allocate I/O port region (%d)\n", ret);
+ return ret;
+ }
+ pci_add_resource_offset(&sys->resources, &sys->io_res,
+ sys->io_offset);
+
+ return 0;
+}
+
+
+static void pcibios_init_hw(struct device *parent, struct hw_pci *hw,
+ struct list_head *head)
+{
+ struct pci_sys_data *sys = NULL;
+ int ret;
+ int nr, busnr;
+
+ for (nr = busnr = 0; nr < hw->nr_controllers; nr++) {
+ sys = kzalloc(sizeof(struct pci_sys_data), GFP_KERNEL);
+ if (!sys)
+ panic("PCI: unable to allocate sys data!");
+
+#ifdef CONFIG_PCI_DOMAINS
+ sys->domain = hw->domain;
+#endif
+ sys->busnr = busnr;
+ sys->swizzle = hw->swizzle;
+ sys->map_irq = hw->map_irq;
+ sys->align_resource = hw->align_resource;
+ sys->add_bus = hw->add_bus;
+ sys->remove_bus = hw->remove_bus;
+ INIT_LIST_HEAD(&sys->resources);
+
+
+ if (hw->private_data)
+ sys->private_data = hw->private_data[nr];
+
+ ret = hw->setup(nr, sys);
+
+ if (ret > 0) {
+ ret = pcibios_init_resources(nr, sys);
+ if (ret) {
+ kfree(sys);
+ break;
+ }
+
+ if (hw->scan)
+ sys->bus = hw->scan(nr, sys);
+ else
+ sys->bus = pci_scan_root_bus(parent, sys->busnr,
+ hw->ops, sys, &sys->resources);
+
+ if (!sys->bus)
+ panic("PCI: unable to scan bus!");
+
+ busnr = sys->bus->busn_res.end + 1;
+
+ list_add(&sys->node, head);
+ } else {
+ kfree(sys);
+ if (ret < 0)
+ break;
+ }
+ }
+}
+int pcibios_root_bridge_prepare(struct pci_host_bridge *bridge)
+{
+ return 0;
+}
+
+void pcibios_add_bus(struct pci_bus *bus)
+{
+ struct pci_sys_data *sys = bus->sysdata;
+
+ if (sys->add_bus)
+ sys->add_bus(bus);
+}
+
+void pcibios_remove_bus(struct pci_bus *bus)
+{
+ struct pci_sys_data *sys = bus->sysdata;
+
+ if (sys->remove_bus)
+ sys->remove_bus(bus);
+}
+
+/*
+ * Swizzle the device pin each time we cross a bridge. If a platform does
+ * not provide a swizzle function, we perform the standard PCI swizzling.
+ *
+ * The default swizzling walks up the bus tree one level at a time, applying
+ * the standard swizzle function at each step, stopping when it finds the PCI
+ * root bus. This will return the slot number of the bridge device on the
+ * root bus and the interrupt pin on that device which should correspond
+ * with the downstream device interrupt.
+ *
+ * Platforms may override this, in which case the slot and pin returned
+ * depend entirely on the platform code. However, please note that the
+ * PCI standard swizzle is implemented on plug-in cards and Cardbus based
+ * PCI extenders, so it can not be ignored.
+ */
+static u8 pcibios_swizzle(struct pci_dev *dev, u8 *pin)
+{
+ struct pci_sys_data *sys = dev->sysdata;
+ int slot, oldpin = *pin;
+
+ if (sys->swizzle)
+ slot = sys->swizzle(dev, pin);
+ else
+ slot = pci_common_swizzle(dev, pin);
+
+ return slot;
+}
+
+/*
+ * Map a slot/pin to an IRQ.
+ */
+static int pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
+{
+ struct pci_sys_data *sys = dev->sysdata;
+ int irq = -1;
+
+ if (sys->map_irq)
+ irq = sys->map_irq(dev, slot, pin);
+
+ return irq;
+}
+
+void pci_common_init_dev(struct device *parent, struct hw_pci *hw)
+{
+ struct pci_sys_data *sys;
+ LIST_HEAD(head);
+
+ pci_add_flags(PCI_REASSIGN_ALL_RSRC);
+ if (hw->preinit)
+ hw->preinit();
+ pcibios_init_hw(parent, hw, &head);
+ if (hw->postinit)
+ hw->postinit();
+
+ pci_fixup_irqs(pcibios_swizzle, pcibios_map_irq);
+
+ list_for_each_entry(sys, &head, node) {
+ struct pci_bus *bus = sys->bus;
+
+ if (!pci_has_flag(PCI_PROBE_ONLY)) {
+ /*
+ * Size the bridge windows.
+ */
+ pci_bus_size_bridges(bus);
+
+ /*
+ * Assign resources.
+ */
+ pci_bus_assign_resources(bus);
+ }
+
+ /*
+ * Tell drivers about devices found.
+ */
+ pci_bus_add_devices(bus);
+ }
+}
+
+/*
+ * We don't have to worry about legacy ISA devices, so nothing to do here
+ */
+resource_size_t pcibios_align_resource(void *data, const struct resource *res,
+ resource_size_t size, resource_size_t align)
+{
+ return res->start;
+}
+
+/**
+ * pcibios_enable_device - Enable I/O and memory.
+ * @dev: PCI device to be enabled
+ */
+int pcibios_enable_device(struct pci_dev *dev, int mask)
+{
+ u16 cmd, old_cmd;
+ int idx;
+ struct resource *r;
+
+ pci_read_config_word(dev, PCI_COMMAND, &cmd);
+ old_cmd = cmd;
+ for (idx = 0; idx < 6; idx++) {
+ /* Only set up the requested stuff */
+ if (!(mask & (1 << idx)))
+ continue;
+
+ r = dev->resource + idx;
+ if (!r->start && r->end) {
+ pr_err("PCI: Device %s not available because"/
+ " of resource collisions\n", pci_name(dev));
+ return -EINVAL;
+ }
+ if (r->flags & IORESOURCE_IO)
+ cmd |= PCI_COMMAND_IO;
+ if (r->flags & IORESOURCE_MEM)
+ cmd |= PCI_COMMAND_MEMORY;
+ }
+
+ /*
+ * Bridges (eg, cardbus bridges) need to be fully enabled
+ */
+ if ((dev->class >> 16) == PCI_BASE_CLASS_BRIDGE)
+ cmd |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY;
+
+ if (cmd != old_cmd) {
+ pr_info("PCI: enabling device %s (%04x -> %04x)\n",
+ pci_name(dev), old_cmd, cmd);
+ pci_write_config_word(dev, PCI_COMMAND, cmd);
+ }
+ return 0;
+}
+
+/*
+ * If the bus contains any of these devices, then we must not turn on
+ * parity checking of any kind. Currently this is CyberPro 20x0 only.
+ */
+static inline int pdev_bad_for_parity(struct pci_dev *dev)
+{
+ return ((dev->vendor == PCI_VENDOR_ID_INTERG &&
+ (dev->device == PCI_DEVICE_ID_INTERG_2000 ||
+ dev->device == PCI_DEVICE_ID_INTERG_2010)) ||
+ (dev->vendor == PCI_VENDOR_ID_ITE &&
+ dev->device == PCI_DEVICE_ID_ITE_8152));
+
+}
+
+/*
+ * pcibios_fixup_bus - Called after each bus is probed,
+ * but before its children are examined.
+ */
+void pcibios_fixup_bus(struct pci_bus *bus)
+{
+ struct pci_dev *dev;
+ u16 features = PCI_COMMAND_SERR | PCI_COMMAND_PARITY
+ | PCI_COMMAND_FAST_BACK;
+
+ /*
+ * Walk the devices on this bus, working out what we can
+ * and can't support.
+ */
+ list_for_each_entry(dev, &bus->devices, bus_list) {
+ u16 status;
+
+ pci_read_config_word(dev, PCI_STATUS, &status);
+
+ /*
+ * If any device on this bus does not support fast back
+ * to back transfers, then the bus as a whole is not able
+ * to support them. Having fast back to back transfers
+ * on saves us one PCI cycle per transaction.
+ */
+ if (!(status & PCI_STATUS_FAST_BACK))
+ features &= ~PCI_COMMAND_FAST_BACK;
+
+ if (pdev_bad_for_parity(dev))
+ features &= ~(PCI_COMMAND_SERR | PCI_COMMAND_PARITY);
+
+ switch (dev->class >> 8) {
+ case PCI_CLASS_BRIDGE_PCI:
+ pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &status);
+ status |= PCI_BRIDGE_CTL_PARITY
+ |PCI_BRIDGE_CTL_MASTER_ABORT;
+ status &= ~(PCI_BRIDGE_CTL_BUS_RESET
+ |PCI_BRIDGE_CTL_FAST_BACK);
+ pci_write_config_word(dev, PCI_BRIDGE_CONTROL, status);
+ break;
+
+ case PCI_CLASS_BRIDGE_CARDBUS:
+ pci_read_config_word(dev, PCI_CB_BRIDGE_CONTROL,
+ &status);
+ status |= PCI_CB_BRIDGE_CTL_PARITY
+ |PCI_CB_BRIDGE_CTL_MASTER_ABORT;
+ pci_write_config_word(dev, PCI_CB_BRIDGE_CONTROL,
+ status);
+ break;
+ }
+ }
+
+ /*
+ * Now walk the devices again, this time setting them up.
+ */
+ list_for_each_entry(dev, &bus->devices, bus_list) {
+ u16 cmd;
+
+ pci_read_config_word(dev, PCI_COMMAND, &cmd);
+ cmd |= features;
+ pci_write_config_word(dev, PCI_COMMAND, cmd);
+
+ pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE,
+ L1_CACHE_BYTES >> 2);
+ }
+
+ /*
+ * Propagate the flags to the PCI bridge.
+ */
+ if (bus->self && bus->self->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
+ if (features & PCI_COMMAND_FAST_BACK)
+ bus->bridge_ctl |= PCI_BRIDGE_CTL_FAST_BACK;
+ if (features & PCI_COMMAND_PARITY)
+ bus->bridge_ctl |= PCI_BRIDGE_CTL_PARITY;
+ }
+
+ /*
+ * Report what we did for this bus
+ */
+ pr_info("PCI: bus%d: Fast back to back transfers %sabled\n",
+ bus->number, (features & PCI_COMMAND_FAST_BACK) ? "en" : "dis");
+}
+EXPORT_SYMBOL(pcibios_fixup_bus);
+
diff --git a/arch/arc/mm/ioremap.c b/arch/arc/mm/ioremap.c
index 739e65f..583c9fe 100644
--- a/arch/arc/mm/ioremap.c
+++ b/arch/arc/mm/ioremap.c
@@ -13,6 +13,26 @@
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/cache.h>
+#include <linux/sizes.h>
+
+#ifdef CONFIG_PCI
+unsigned long pcibios_min_io = 0x100;
+EXPORT_SYMBOL(pcibios_min_io);
+
+unsigned long pcibios_min_mem = 0x100000;
+EXPORT_SYMBOL(pcibios_min_mem);
+#endif
+
+inline void __iomem *ioport_map(unsigned long port, unsigned int nr)
+{
+ return port;
+}
+EXPORT_SYMBOL(ioport_map);
+
+inline void ioport_unmap(void __iomem *addr)
+{
+}
+EXPORT_SYMBOL(ioport_unmap);
void __iomem *ioremap(unsigned long paddr, unsigned long size)
{
@@ -80,7 +100,6 @@ void __iomem *ioremap_prot(phys_addr_t paddr, unsigned long size,
}
EXPORT_SYMBOL(ioremap_prot);
-
void iounmap(const void __iomem *addr)
{
if (addr >= (void __force __iomem *)ARC_UNCACHED_ADDR_SPACE)
@@ -89,3 +108,11 @@ void iounmap(const void __iomem *addr)
vfree((void *)(PAGE_MASK & (unsigned long __force)addr));
}
EXPORT_SYMBOL(iounmap);
+
+#ifdef CONFIG_PCI
+int pci_ioremap_io(unsigned int offset, phys_addr_t phys_addr)
+{
+ return ioremap_nocache(phys_addr + offset, SZ_64K);
+}
+EXPORT_SYMBOL_GPL(pci_ioremap_io);
+#endif
diff --git a/arch/arc/plat-axs10x/Kconfig b/arch/arc/plat-axs10x/Kconfig
index d475f9d..33b5db6 100644
--- a/arch/arc/plat-axs10x/Kconfig
+++ b/arch/arc/plat-axs10x/Kconfig
@@ -8,6 +8,7 @@
menuconfig ARC_PLAT_AXS10X
bool "Synopsys ARC AXS10x Software Development Platforms"
+ select MIGHT_HAVE_PCI
select DW_APB_ICTL
select GPIO_DWAPB
select OF_GPIO
diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
index be3f631..2154092 100644
--- a/drivers/pci/Makefile
+++ b/drivers/pci/Makefile
@@ -32,6 +32,7 @@ obj-$(CONFIG_PCI_IOV) += iov.o
# Some architectures use the generic PCI setup functions
#
obj-$(CONFIG_ALPHA) += setup-irq.o
+obj-$(CONFIG_ARC) += setup-irq.o
obj-$(CONFIG_ARM) += setup-irq.o
obj-$(CONFIG_ARM64) += setup-irq.o
obj-$(CONFIG_UNICORE32) += setup-irq.o
--
1.8.1.5
^ permalink raw reply related [flat|nested] 5+ messages in thread