From patchwork Wed Mar 15 17:38:00 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jon Mason X-Patchwork-Id: 95364 Delivered-To: patch@linaro.org Received: by 10.140.89.134 with SMTP id v6csp434614qgd; Wed, 15 Mar 2017 10:38:15 -0700 (PDT) X-Received: by 10.28.169.199 with SMTP id s190mr20387731wme.2.1489599495240; Wed, 15 Mar 2017 10:38:15 -0700 (PDT) Return-Path: Received: from lists.denx.de (dione.denx.de. [81.169.180.215]) by mx.google.com with ESMTP id b49si3380009wra.308.2017.03.15.10.38.14; Wed, 15 Mar 2017 10:38:15 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of u-boot-bounces@lists.denx.de designates 81.169.180.215 as permitted sender) client-ip=81.169.180.215; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@kudzu-us.20150623.gappssmtp.com; spf=pass (google.com: best guess record for domain of u-boot-bounces@lists.denx.de designates 81.169.180.215 as permitted sender) smtp.mailfrom=u-boot-bounces@lists.denx.de Received: by lists.denx.de (Postfix, from userid 105) id B0EE1C21C62; Wed, 15 Mar 2017 17:38:14 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on lists.denx.de X-Spam-Level: X-Spam-Status: No, score=-0.0 required=5.0 tests=RCVD_IN_DNSWL_BLOCKED, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, T_DKIM_INVALID autolearn=unavailable autolearn_force=no version=3.4.0 Received: from lists.denx.de (localhost [IPv6:::1]) by lists.denx.de (Postfix) with ESMTP id 03FFEC21C27; Wed, 15 Mar 2017 17:38:11 +0000 (UTC) Received: by lists.denx.de (Postfix, from userid 105) id 70E6CC21C27; Wed, 15 Mar 2017 17:38:10 +0000 (UTC) Received: from mail-qt0-f194.google.com (mail-qt0-f194.google.com [209.85.216.194]) by lists.denx.de (Postfix) with ESMTPS id C3D52C21C26 for ; Wed, 15 Mar 2017 17:38:09 +0000 (UTC) Received: by mail-qt0-f194.google.com with SMTP id x35so2762050qtc.1 for ; Wed, 15 Mar 2017 10:38:09 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kudzu-us.20150623.gappssmtp.com; s=20150623; h=from:to:cc:subject:date:message-id; bh=r6a44m4X/kPhCvCmDsPnWTcecCy6VtOCyk7PoZA7zRQ=; b=tW24Eq0buy6XxWuvhSXT3rO/OZmWiVM5PcPrILfcUSy0ynM5OvUS0Ljj/Me8SSchlv qHKEM5uV0e8xfAV2WYzlVnoYtwdUNPYd9HOjWqLbHlOBH7uoTvD+j5U4wcVs6AC+qxT3 TbGdMs+vedr6HFwrkRrIa3RPSk6UccPuuw5Ck8k7olAkA2JSqRmyfBzWr0H6VLOY8ddb l5WhwTABpAiLcpWE6bduK2gjKspYcsBtjtV9xodde8neFExUv7Uz0rjVVTKTfivTukBk abfV4xlzlB57Ev1RtEwlfRDpdC3KZQmjyNBVqpHRJqGQnRjnGLsUa8e3fJDngNT3PcUa JZqQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id; bh=r6a44m4X/kPhCvCmDsPnWTcecCy6VtOCyk7PoZA7zRQ=; b=LiaAz06xj4glwfqbXeH9tQNPMQb9q3vQOCBgfD21LSw5N9AeJeIif1sOR/4bH2h1xq ldthStrttEnvqXGpZxUpVGl2nIPxeUj3o6ELUWwFD08dIM1B0IiVpv4GFoj6Frzhdo8T tH6ZESe9IIVurI/JSV1JljmU0C9pDxivKCTfnJy/pkAtRs5vjeWKRylYe2Al7S4cAar9 pdZR1M2QVVdISbIymxn8IVDs89kDkIUDGg7AsL26oqU3qzJbgK1yB4jQ9rlVi17ciOEK 2AfGHzsfkcbj7mWBksVHWmg0JLMnjifbyN+9R/x4RtK4hBkbdwDVmBQCH9awWzUaZb/J ZnTg== X-Gm-Message-State: AFeK/H1nlSfkzSSsN0D4kABmBQm35k393KD/zi8Yl5BRlWZ5DW6HBxOO7gRQ5KCuHSF/Gg== X-Received: by 10.200.37.208 with SMTP id f16mr3905076qtf.118.1489599488098; Wed, 15 Mar 2017 10:38:08 -0700 (PDT) Received: from graymalkin ([98.122.141.161]) by smtp.gmail.com with ESMTPSA id o130sm1752704qke.15.2017.03.15.10.38.06 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Wed, 15 Mar 2017 10:38:07 -0700 (PDT) Received: by graymalkin (sSMTP sendmail emulation); Wed, 15 Mar 2017 13:38:00 -0400 From: Jon Mason To: Albert Aribaud Date: Wed, 15 Mar 2017 13:38:00 -0400 Message-Id: <20170315173800.14841-1-jdmason@kudzu.us> X-Mailer: git-send-email 2.9.3 Cc: Jon Mason , u-boot@lists.denx.de Subject: [U-Boot] [PATCH v3] NS2: Add PCI support X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.18 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" From: Jon Mason 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 --- 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 +#include +#include +#include +#include +//#include +//#include + +#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 */