[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