[U-Boot] [PATCH v3] NS2: Add PCI support
Jon Mason
jon.mason at broadcom.com
Fri Mar 17 16:11:13 UTC 2017
Please disregard this patch. I meant to send out v3 of the core NS2
support, and not a half-baked PCI driver.
Thanks,
Jon
On Wed, Mar 15, 2017 at 1:38 PM, Jon Mason <jdmason at kudzu.us> wrote:
> 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