[U-Boot] [PATCH v3] NS2: Add PCI support
Jon Mason
jdmason at kudzu.us
Wed Mar 15 17:38:00 UTC 2017
From: Jon Mason <jonmason at broadcom.com>
Write a new driver to add PCI support to iProc devices. Currently, this
will only work for NS2 (due to some hard coding done in the driver), but
should be extensible in the future. Some hacks had to be made due to
time constraints, but are documented in the code.
PCI was verified by tftpbooting over an e1000 PCI NIC.
Signed-off-by: Jon Mason <jon.mason at broadcom.com>
---
configs/bcm958712k_defconfig | 2 +
drivers/pci/Kconfig | 6 +
drivers/pci/Makefile | 1 +
drivers/pci/pcie_iproc.c | 285 +++++++++++++++++++++++++++++++++++++++
include/configs/bcm_northstar2.h | 3 +
5 files changed, 297 insertions(+)
create mode 100644 drivers/pci/pcie_iproc.c
diff --git a/configs/bcm958712k_defconfig b/configs/bcm958712k_defconfig
index 96e4bce..0c4e566 100644
--- a/configs/bcm958712k_defconfig
+++ b/configs/bcm958712k_defconfig
@@ -6,5 +6,7 @@ CONFIG_BOOTDELAY=5
# CONFIG_DISPLAY_CPUINFO is not set
CONFIG_SYS_PROMPT="u-boot> "
# CONFIG_CMD_IMLS is not set
+CONFIG_PCI=y
+CONFIG_PCIE_IPROC=y
CONFIG_SYS_NS16550=y
CONFIG_OF_LIBFDT=y
diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig
index 692a398..001ddc1 100644
--- a/drivers/pci/Kconfig
+++ b/drivers/pci/Kconfig
@@ -79,4 +79,10 @@ config PCIE_LAYERSCAPE
PCIe controllers. The PCIe may works in RC or EP mode according to
RCW[HOST_AGT_PEX] setting.
+config PCIE_IPROC
+ bool "iProc PCI support"
+ depends on TARGET_BCMNS2
+ help
+ PCIe Support on iProc based SoCs
+
endif
diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
index ad44e83..ddfde4c 100644
--- a/drivers/pci/Makefile
+++ b/drivers/pci/Makefile
@@ -22,6 +22,7 @@ obj-$(CONFIG_PCI_INDIRECT_BRIDGE) += pci_indirect.o
obj-$(CONFIG_PCI_GT64120) += pci_gt64120.o
obj-$(CONFIG_PCI_MSC01) += pci_msc01.o
obj-$(CONFIG_PCIE_IMX) += pcie_imx.o
+obj-$(CONFIG_PCIE_IPROC) += pcie_iproc.o
obj-$(CONFIG_FTPCI100) += pci_ftpci100.o
obj-$(CONFIG_PCI_MVEBU) += pci_mvebu.o
obj-$(CONFIG_SH4_PCI) += pci_sh4.o
diff --git a/drivers/pci/pcie_iproc.c b/drivers/pci/pcie_iproc.c
new file mode 100644
index 0000000..66f5cb5
--- /dev/null
+++ b/drivers/pci/pcie_iproc.c
@@ -0,0 +1,285 @@
+/*
+ * Broadcom iProc PCI Express Root-Complex driver
+ *
+ * Copyright (C) 2016 Broadcom
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+#include <common.h>
+#include <pci.h>
+#include <asm/io.h>
+#include <linux/sizes.h>
+#include <linux/compat.h>
+//#include <asm/arch/bcm_mdio.h>
+//#include <asm/arch-bcm_ns2/socregs.h>
+
+#ifdef DEBUG
+#define pr_debug printf
+#else
+#define pr_debug(...) do {} while (0)
+#endif
+
+enum iproc_pcie_type {
+ IPROC_PCI_GEN,
+ IPROC_PCI_NITRO,
+};
+
+struct iproc_pcie {
+ void __iomem *reg;
+
+ struct pci_controller hose;
+ enum iproc_pcie_type type;
+};
+
+#define GEN_CFG_IND_ADDR_OFFSET 0x120
+#define GEN_CFG_IND_DATA_OFFSET 0x124
+
+#define NITRO_CFG_IND_ADDR_OFFSET 0x1f0
+#define NITRO_CFG_IND_DATA_OFFSET 0x1f4
+
+#define CLK_CONTROL_OFFSET 0x000
+#define EP_PERST_SOURCE_SELECT_SHIFT 2
+#define EP_PERST_SOURCE_SELECT (1 << EP_PERST_SOURCE_SELECT_SHIFT)
+#define EP_MODE_SURVIVE_PERST_SHIFT 1
+#define EP_MODE_SURVIVE_PERST (1 << EP_MODE_SURVIVE_PERST_SHIFT)
+#define RC_PCIE_RST_OUTPUT_SHIFT 0
+#define RC_PCIE_RST_OUTPUT (1 << RC_PCIE_RST_OUTPUT_SHIFT)
+
+#define CFG_ADDR_OFFSET 0x1F8
+#define CFG_ADDR_BUS_NUM_SHIFT 20
+#define CFG_ADDR_BUS_NUM_MASK 0x0FF00000
+#define CFG_ADDR_DEV_NUM_SHIFT 15
+#define CFG_ADDR_DEV_NUM_MASK 0x000F8000
+#define CFG_ADDR_FUNC_NUM_SHIFT 12
+#define CFG_ADDR_FUNC_NUM_MASK 0x00007000
+#define CFG_ADDR_REG_NUM_SHIFT 2
+#define CFG_ADDR_REG_NUM_MASK 0x00000FFC
+#define CFG_ADDR_CFG_TYPE_SHIFT 0
+#define CFG_ADDR_CFG_TYPE_MASK 0x00000003
+
+#define CFG_DATA_OFFSET 0x1FC
+#define CFG_IND_ADDR_MASK 0x00001FFC
+
+#define PCIE_LINK_STATUS_OFFSET 0xF0C
+#define PCIE_PHYLINKUP_SHITF 3
+#define PCIE_PHYLINKUP (1 << PCIE_PHYLINKUP_SHITF)
+#define PCIE_DL_ACTIVE_SHIFT 2
+#define PCIE_DL_ACTIVE (1 << PCIE_DL_ACTIVE_SHIFT)
+
+#define INVALID_ACCESS_OFFSET 0xFFFFFFFF
+
+#define PAXC_ROOT 0x60c00000
+#define PAXB_0_CLK_CONTROL 0x20020000
+
+
+
+static u32 iproc_pcie_conf_access(struct pci_controller *hose, pci_dev_t d,
+ int where)
+{
+ struct iproc_pcie *pcie = hose->priv_data;
+ int bus, dev, func;
+ u32 val;
+
+ if (!pcie || !pcie->reg)
+ return INVALID_ACCESS_OFFSET;
+
+ /* root complex access */
+ if (PCI_DEV(d) == 0) {
+ if (pcie->type == IPROC_PCI_NITRO) {
+ writel(where & CFG_IND_ADDR_MASK,
+ pcie->reg + NITRO_CFG_IND_ADDR_OFFSET);
+ return NITRO_CFG_IND_DATA_OFFSET;
+ } else {
+ writel(where & CFG_IND_ADDR_MASK,
+ pcie->reg + GEN_CFG_IND_ADDR_OFFSET);
+ return GEN_CFG_IND_DATA_OFFSET;
+ }
+ }
+
+ pr_debug("%s:%d - Bus %x, Dev %x, Fun %x, where %x, val %x\n",
+ __func__, __LINE__, PCI_BUS(d), PCI_DEV(d), PCI_FUNC(d), where,
+ *val);
+
+ /* Note, u-boot starts off the dev at 1, but the func at 0. Linux
+ * starts at 0 for dev. Our code assumes starting at 0, so we need to
+ * subtact 1.
+ */
+ dev = PCI_DEV(d) - 1;
+
+ /* FIXME - Nitro only wants to respond if the Bus is 1, but the regular
+ * PCI only wants to respond if the bus is 0. Investigation needs to
+ * be done to determine why
+ */
+ if (pcie->type == IPROC_PCI_NITRO)
+ bus = 1;
+ else
+ bus = PCI_BUS(d) - pcie->hose.first_busno;
+ func = PCI_FUNC(d);
+
+ /* FIXME - Quick and dirty hack to fix a problem only found on e1000
+ * adapters. For those adapters, a device is found for every "dev"
+ * queried, even though only one is physically present. To work
+ * around that, stop looking after the first device
+ */
+ if (dev > 0)
+ return INVALID_ACCESS_OFFSET;
+
+ /* access of EP deivce */
+ val = (bus << CFG_ADDR_BUS_NUM_SHIFT) |
+ (dev << CFG_ADDR_DEV_NUM_SHIFT) |
+ (func << CFG_ADDR_FUNC_NUM_SHIFT) |
+ (where & CFG_ADDR_REG_NUM_MASK) |
+ (1 & CFG_ADDR_CFG_TYPE_MASK);
+ writel(val, pcie->reg + CFG_ADDR_OFFSET);
+
+ return CFG_DATA_OFFSET;
+}
+
+static int iproc_pcie_read_config(struct pci_controller *hose, pci_dev_t d,
+ int where, u32 *val)
+{
+ struct iproc_pcie *pcie = hose->priv_data;
+ u32 offset;
+
+
+ offset = iproc_pcie_conf_access(hose, d, where);
+ if (offset == INVALID_ACCESS_OFFSET)
+ return -EINVAL;
+
+ *val = readl(pcie->reg + offset);
+
+
+ return 0;
+}
+
+static int iproc_pcie_write_config(struct pci_controller *hose, pci_dev_t d,
+ int where, u32 val)
+{
+ struct iproc_pcie *pcie = hose->priv_data;
+ u32 offset;
+
+ offset = iproc_pcie_conf_access(hose, d, where);
+ if (offset == INVALID_ACCESS_OFFSET)
+ return -EINVAL;
+
+ writel(val, pcie->reg + offset);
+
+ return 0;
+}
+
+static int iproc_pcie_init_hose(struct iproc_pcie *pcie)
+{
+ u32 val32;
+ u16 val;
+
+ /* Check for a device to be present. Only proceed if one is present.
+ * If not, it will cause a spurious interrupt (which is found when
+ * booting Linux after the fact)
+ */
+ val32 = readl(pcie->reg + PCIE_LINK_STATUS_OFFSET);
+ if (pcie->type != IPROC_PCI_NITRO &&
+ (!(val32 & PCIE_PHYLINKUP) || !(val32 & PCIE_DL_ACTIVE)))
+ return -ENODEV;
+
+ pcie->hose.priv_data = pcie;
+
+ /* FIXME - Major hack below. This should be more limited than saying
+ * all of memory
+ */
+ pci_set_region(&pcie->hose.regions[0], 0, 0, SZ_2G * 2 - 1,
+ PCI_REGION_MEM);
+ pcie->hose.region_count = 1;
+
+ pci_set_ops(&pcie->hose,
+ pci_hose_read_config_byte_via_dword,
+ pci_hose_read_config_word_via_dword,
+ iproc_pcie_read_config,
+ pci_hose_write_config_byte_via_dword,
+ pci_hose_write_config_word_via_dword,
+ iproc_pcie_write_config);
+
+ /* Before we register, we need to fix up the bridge's incorrect device
+ * class
+ */
+ pci_hose_write_config_word(&pcie->hose, PCI_BDF(0, 0, 0),
+ PCI_CLASS_DEVICE, PCI_CLASS_BRIDGE_PCI);
+
+ pci_hose_read_config_word(&pcie->hose, PCI_BDF(0, 0, 0),
+ PCI_CLASS_DEVICE, &val);
+ pr_debug("%s:%d - Dev Class = %x\n", __func__, __LINE__, val);
+
+ pci_register_hose(&pcie->hose);
+ pciauto_config_init(&pcie->hose);
+ pcie->hose.last_busno = pci_hose_scan(&pcie->hose);
+
+ return 0;
+}
+
+static void iproc_pcie_reset(struct iproc_pcie *pcie)
+{
+ if (pcie->type == IPROC_PCI_NITRO) {
+ writel(0x0000007F, pcie->reg + CLK_CONTROL_OFFSET);
+ } else {
+ u32 val;
+
+ /*
+ * Select perst_b signal as reset source, and put the device in
+ * reset
+ */
+ val = readl(pcie->reg + CLK_CONTROL_OFFSET);
+ val &= ~EP_PERST_SOURCE_SELECT & ~EP_MODE_SURVIVE_PERST &
+ ~RC_PCIE_RST_OUTPUT;
+ writel(val, pcie->reg + CLK_CONTROL_OFFSET);
+ udelay(250);
+
+ /* now bring it out of reset*/
+ val |= RC_PCIE_RST_OUTPUT;
+ writel(val, pcie->reg + CLK_CONTROL_OFFSET);
+ mdelay(250);
+ }
+}
+
+void pci_init_board(void)
+{
+ struct iproc_pcie *pcie;
+ int i, rc;
+
+ /* Do PCI slots first */
+ for (i = 0; i < 2; i++) {
+ pcie = kzalloc(sizeof(*pcie), GFP_KERNEL);
+ if (!pcie)
+ return;
+
+ pcie->type = IPROC_PCI_GEN;
+ pcie->hose.first_busno = i;
+
+ /* map registers */
+ pcie->reg = (void *)PAXB_0_CLK_CONTROL + (0x30000000 * i);
+
+#if 0
+ /* turn on the phys. NOTE: Only for NS2. */
+ bcm_mdio_write(INTERNAL, CLAUS22, i * 7, 0, 0x1F, 0x2100);
+ bcm_mdio_write(INTERNAL, CLAUS22, i * 7, 0, 0x3, 0x2b18);
+#endif
+ iproc_pcie_reset(pcie);
+
+ rc = iproc_pcie_init_hose(pcie);
+ if (rc)
+ kfree(pcie);
+ }
+
+ /* Now do Nitro */
+ pcie = kzalloc(sizeof(*pcie), GFP_KERNEL);
+ if (!pcie)
+ return;
+
+ pcie->type = IPROC_PCI_NITRO;
+ pcie->hose.first_busno = i;
+ pcie->reg = (void *)PAXC_ROOT;
+
+ iproc_pcie_reset(pcie);
+
+ rc = iproc_pcie_init_hose(pcie);
+ if (rc)
+ kfree(pcie);
+}
diff --git a/include/configs/bcm_northstar2.h b/include/configs/bcm_northstar2.h
index ec2ce3f..7a0d26e 100644
--- a/include/configs/bcm_northstar2.h
+++ b/include/configs/bcm_northstar2.h
@@ -52,4 +52,7 @@
#define CONFIG_COMMAND_HISTORY
#define CONFIG_SYS_LONGHELP
+/* PCI/PCIE */
+#define CONFIG_CMD_PCI
+
#endif /* __BCM_NORTHSTAR2_H */
--
2.9.3
More information about the U-Boot
mailing list