From patchwork Wed Sep 5 02:32:11 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kunihiko Hayashi X-Patchwork-Id: 145971 Delivered-To: patch@linaro.org Received: by 2002:a2e:1648:0:0:0:0:0 with SMTP id 8-v6csp4387169ljw; Tue, 4 Sep 2018 19:32:29 -0700 (PDT) X-Google-Smtp-Source: ANB0VdYyFkdZM7rq5/WYScfveaie6obM5N1R17RdPtHyK11JyDWjDgx7x5QaPdPovi2AcTijmKm5 X-Received: by 2002:a17:902:18a:: with SMTP id b10-v6mr36615439plb.62.1536114749120; Tue, 04 Sep 2018 19:32:29 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1536114749; cv=none; d=google.com; s=arc-20160816; b=BnoGAlo2NytTA1SLn+MS8j10ogjMnf99u0YkJB36H5f0WDXkLxNg0zpMHyYwtDHnYt 0JE6wsKlv5LN5Bpb7xsxnrKqrH2p64fvEyJh2r7ZChU+wh3fkL+6xq3vIrqlfWrIe6fq gNlMyAXn1hzpnaqE6qO2Rp5gg7/MwVohFrBnHfaFzm9QMhSWNKwCBNXB+YsoVjUpC/+D WTrBcqNaDG8syXBhZYi8686U/RVtkTeTt5xa3T7InYQ4KglgTJUQpCk/OQ5TBaUzxW8V zpl9qZqI44xBkOLxyg1kocmlQEYToZdarfkA3zxmCNYCUTHtJ8MibK1PkabrkDVKNXb2 mPVg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from; bh=VPYaRe/OEAopOxkwIYjIAe1EciMjvWSHrnLXF7gSs04=; b=RVj3xfkdJ2A4dje2u4RdRIqteaI9dEb/KagMLYIZDGuZ2FOPl0meiJUreYPTHSg+Mu HLJEsGgVE6+En2tcc0h66Phgpnn2QSiZsik/OdjqbklxAmaNOpipf+SMsicNxsfpFJez ryN1nF4sTOzfDJ4FtW0GQ8WTKtB+7L3ethP/aXuad7aDNChLE4CZlpo+3zRVVWNhHTYY ABfVeTj1h4qjYjnTOEbrUrVvDjRMgToTk4ttKilWimaQYuJItR1A+rJLp3ldwe+hewMe UrFuk43SZaXzuXGxp5513IBsJ3b2lq9esGty1fPl0H7AG4loyyYXJO3xFUQSTKCJ4ulb 3dvw== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id z187-v6si601470pgd.2.2018.09.04.19.32.28; Tue, 04 Sep 2018 19:32:29 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727201AbeIEHAS (ORCPT + 32 others); Wed, 5 Sep 2018 03:00:18 -0400 Received: from mx.socionext.com ([202.248.49.38]:48617 "EHLO mx.socionext.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725853AbeIEHAR (ORCPT ); Wed, 5 Sep 2018 03:00:17 -0400 Received: from unknown (HELO iyokan-ex.css.socionext.com) ([172.31.9.54]) by mx.socionext.com with ESMTP; 05 Sep 2018 11:32:23 +0900 Received: from mail.mfilter.local (m-filter-1 [10.213.24.61]) by iyokan-ex.css.socionext.com (Postfix) with ESMTP id 24D74600AA; Wed, 5 Sep 2018 11:32:23 +0900 (JST) Received: from 172.31.9.51 (172.31.9.51) by m-FILTER with ESMTP; Wed, 5 Sep 2018 11:32:23 +0900 Received: from plum.e01.socionext.com (unknown [10.213.132.32]) by kinkan.css.socionext.com (Postfix) with ESMTP id 5CE1B1A04D8; Wed, 5 Sep 2018 11:32:22 +0900 (JST) From: Kunihiko Hayashi To: Lorenzo Pieralisi , Bjorn Helgaas , Rob Herring , Mark Rutland , Masahiro Yamada Cc: linux-pci@vger.kernel.org, devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, Masami Hiramatsu , Jassi Brar , Kunihiko Hayashi Subject: [PATCH 2/2] pci: dwc: add UniPhier PCIe host controller support Date: Wed, 5 Sep 2018 11:32:11 +0900 Message-Id: <1536114731-28630-3-git-send-email-hayashi.kunihiko@socionext.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1536114731-28630-1-git-send-email-hayashi.kunihiko@socionext.com> References: <1536114731-28630-1-git-send-email-hayashi.kunihiko@socionext.com> Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org This introduces specific glue layer for UniPhier platform to support PCIe host controller that is based on the Designware PCIe Core, and this driver supports Root Complex (host) mode. Signed-off-by: Kunihiko Hayashi --- drivers/pci/controller/dwc/Kconfig | 9 + drivers/pci/controller/dwc/Makefile | 1 + drivers/pci/controller/dwc/pcie-uniphier.c | 464 +++++++++++++++++++++++++++++ 3 files changed, 474 insertions(+) create mode 100644 drivers/pci/controller/dwc/pcie-uniphier.c -- 2.7.4 diff --git a/drivers/pci/controller/dwc/Kconfig b/drivers/pci/controller/dwc/Kconfig index 91b0194..d8fdb02 100644 --- a/drivers/pci/controller/dwc/Kconfig +++ b/drivers/pci/controller/dwc/Kconfig @@ -193,4 +193,13 @@ config PCIE_HISI_STB help Say Y here if you want PCIe controller support on HiSilicon STB SoCs +config PCIE_UNIPHIER + bool "Socionext UniPhier PCIe controllers" + depends on OF && (ARCH_UNIPHIER || COMPILE_TEST) + depends on PCI_MSI_IRQ_DOMAIN + select PCIE_DW_HOST + help + Say Y here if you want PCIe controller support on UniPhier SoCs. + This driver supports LD20 and PXs3 SoCs. + endmenu diff --git a/drivers/pci/controller/dwc/Makefile b/drivers/pci/controller/dwc/Makefile index 5d2ce72..cbde733 100644 --- a/drivers/pci/controller/dwc/Makefile +++ b/drivers/pci/controller/dwc/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_PCIE_ARMADA_8K) += pcie-armada8k.o obj-$(CONFIG_PCIE_ARTPEC6) += pcie-artpec6.o obj-$(CONFIG_PCIE_KIRIN) += pcie-kirin.o obj-$(CONFIG_PCIE_HISI_STB) += pcie-histb.o +obj-$(CONFIG_PCIE_UNIPHIER) += pcie-uniphier.o # The following drivers are for devices that use the generic ACPI # pci_root.c driver but don't support standard ECAM config access. diff --git a/drivers/pci/controller/dwc/pcie-uniphier.c b/drivers/pci/controller/dwc/pcie-uniphier.c new file mode 100644 index 0000000..13ce02e --- /dev/null +++ b/drivers/pci/controller/dwc/pcie-uniphier.c @@ -0,0 +1,464 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// PCI-express host controller driver for UniPhier SoCs +// Copyright 2018 Socionext Inc. +// Author: Kunihiko Hayashi + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pcie-designware.h" + +#define PCL_PINCTRL0 0x002c +#define PCL_PERST_PLDN_REGEN BIT(12) +#define PCL_PERST_NOE_REGEN BIT(11) +#define PCL_PERST_OUT_REGEN BIT(8) +#define PCL_PERST_PLDN_REGVAL BIT(4) +#define PCL_PERST_NOE_REGVAL BIT(3) +#define PCL_PERST_OUT_REGVAL BIT(0) + +#define PCL_PIPEMON 0x0044 +#define PCL_PCLK_ALIVE BIT(15) + +#define PCL_APP_READY_CTRL 0x8008 +#define PCL_APP_LTSSM_ENABLE BIT(0) + +#define PCL_APP_PM0 0x8078 +#define PCL_SYS_AUX_PWR_DET BIT(8) + +#define PCL_RCV_INT 0x8108 +#define PCL_CFG_BW_MGT_ENABLE BIT(20) +#define PCL_CFG_LINK_AUTO_BW_ENABLE BIT(19) +#define PCL_CFG_AER_RC_ERR_MSI_ENABLE BIT(18) +#define PCL_CFG_PME_MSI_ENABLE BIT(17) +#define PCL_CFG_BW_MGT_STATUS BIT(4) +#define PCL_CFG_LINK_AUTO_BW_STATUS BIT(3) +#define PCL_CFG_AER_RC_ERR_MSI_STATUS BIT(2) +#define PCL_CFG_PME_MSI_STATUS BIT(1) +#define PCL_RCV_INT_ALL_ENABLE \ + (PCL_CFG_BW_MGT_ENABLE | PCL_CFG_LINK_AUTO_BW_ENABLE \ + | PCL_CFG_AER_RC_ERR_MSI_ENABLE | PCL_CFG_PME_MSI_ENABLE) + +#define PCL_RCV_INTX 0x810c +#define PCL_RADM_INTD_ENABLE BIT(19) +#define PCL_RADM_INTC_ENABLE BIT(18) +#define PCL_RADM_INTB_ENABLE BIT(17) +#define PCL_RADM_INTA_ENABLE BIT(16) +#define PCL_RADM_INTD_STATUS BIT(3) +#define PCL_RADM_INTC_STATUS BIT(2) +#define PCL_RADM_INTB_STATUS BIT(1) +#define PCL_RADM_INTA_STATUS BIT(0) +#define PCL_RCV_INTX_ALL_ENABLE \ + (PCL_RADM_INTD_ENABLE | PCL_RADM_INTC_ENABLE \ + | PCL_RADM_INTB_ENABLE | PCL_RADM_INTA_ENABLE) + +#define PCL_STATUS_LINK 0x8140 +#define PCL_RDLH_LINK_UP BIT(1) +#define PCL_XMLH_LINK_UP BIT(0) + +struct uniphier_pcie_priv { + void __iomem *base; + struct dw_pcie pci; + struct clk *clk; + struct reset_control *rst; + struct phy *phy; + struct irq_domain *irq_domain; +}; + +#define to_uniphier_pcie(x) dev_get_drvdata((x)->dev) + +static void uniphier_pcie_ltssm_enable(struct uniphier_pcie_priv *priv) +{ + u32 val; + + val = readl(priv->base + PCL_APP_READY_CTRL); + val |= PCL_APP_LTSSM_ENABLE; + writel(val, priv->base + PCL_APP_READY_CTRL); +} + +static void uniphier_pcie_ltssm_disable(struct uniphier_pcie_priv *priv) +{ + u32 val; + + val = readl(priv->base + PCL_APP_READY_CTRL); + val &= ~PCL_APP_LTSSM_ENABLE; + writel(val, priv->base + PCL_APP_READY_CTRL); +} + +static void uniphier_pcie_init_rc(struct uniphier_pcie_priv *priv) +{ + u32 val; + + /* use auxiliary power detection */ + val = readl(priv->base + PCL_APP_PM0); + val |= PCL_SYS_AUX_PWR_DET; + writel(val, priv->base + PCL_APP_PM0); + + /* assert PERST# */ + val = readl(priv->base + PCL_PINCTRL0); + val &= ~(PCL_PERST_NOE_REGVAL | PCL_PERST_OUT_REGVAL + | PCL_PERST_PLDN_REGVAL); + val |= PCL_PERST_NOE_REGEN | PCL_PERST_OUT_REGEN + | PCL_PERST_PLDN_REGEN; + writel(val, priv->base + PCL_PINCTRL0); + + uniphier_pcie_ltssm_disable(priv); + + usleep_range(100000, 200000); + + /* deassert PERST# */ + val = readl(priv->base + PCL_PINCTRL0); + val |= PCL_PERST_OUT_REGVAL | PCL_PERST_OUT_REGEN; + writel(val, priv->base + PCL_PINCTRL0); +} + +static int uniphier_pcie_wait_rc(struct uniphier_pcie_priv *priv) +{ + u32 status; + int ret; + + /* wait PIPE clock */ + ret = readl_poll_timeout(priv->base + PCL_PIPEMON, status, + status & PCL_PCLK_ALIVE, 100000, 1000000); + if (ret) { + dev_err(priv->pci.dev, + "Failed to initialize controller in RC mode\n"); + return ret; + } + + return 0; +} + +static int uniphier_pcie_link_up(struct dw_pcie *pci) +{ + struct uniphier_pcie_priv *priv = to_uniphier_pcie(pci); + u32 val, mask; + + val = readl(priv->base + PCL_STATUS_LINK); + mask = PCL_RDLH_LINK_UP | PCL_XMLH_LINK_UP; + + return (val & mask) == mask; +} + +static int uniphier_pcie_establish_link(struct dw_pcie *pci) +{ + struct uniphier_pcie_priv *priv = to_uniphier_pcie(pci); + int ret; + + if (dw_pcie_link_up(pci)) + return 0; + + uniphier_pcie_ltssm_enable(priv); + + ret = dw_pcie_wait_for_link(pci); + if (ret == -ETIMEDOUT) { + dev_warn(pci->dev, "Link not up\n"); + ret = 0; + } + + return ret; +} + +static void uniphier_pcie_stop_link(struct dw_pcie *pci) +{ + struct uniphier_pcie_priv *priv = to_uniphier_pcie(pci); + + uniphier_pcie_ltssm_disable(priv); +} + +static int uniphier_pcie_intx_map(struct irq_domain *domain, unsigned int irq, + irq_hw_number_t hwirq) +{ + irq_set_chip_and_handler(irq, &dummy_irq_chip, handle_simple_irq); + irq_set_chip_data(irq, domain->host_data); + + return 0; +} + +static const struct irq_domain_ops uniphier_intx_domain_ops = { + .map = uniphier_pcie_intx_map, +}; + +static int uniphier_pcie_init_irq_domain(struct pcie_port *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct uniphier_pcie_priv *priv = to_uniphier_pcie(pci); + struct device_node *np = pci->dev->of_node; + struct device_node *np_intc = of_get_next_child(np, NULL); + + if (!np_intc) { + dev_err(pci->dev, "Failed to get child node\n"); + return -ENODEV; + } + + priv->irq_domain = irq_domain_add_linear(np_intc, PCI_NUM_INTX, + &uniphier_intx_domain_ops, + pp); + if (!priv->irq_domain) { + dev_err(pci->dev, "Failed to get INTx domain\n"); + return -ENODEV; + } + + return 0; +} + +static void uniphier_pcie_irq_enable(struct uniphier_pcie_priv *priv) +{ + writel(PCL_RCV_INT_ALL_ENABLE, priv->base + PCL_RCV_INT); + writel(PCL_RCV_INTX_ALL_ENABLE, priv->base + PCL_RCV_INTX); +} + +static void uniphier_pcie_irq_disable(struct uniphier_pcie_priv *priv) +{ + writel(0, priv->base + PCL_RCV_INT); + writel(0, priv->base + PCL_RCV_INTX); +} + +static irqreturn_t uniphier_pcie_irq_handler(int irq, void *arg) +{ + struct uniphier_pcie_priv *priv = arg; + struct dw_pcie *pci = &priv->pci; + u32 val; + + /* INT for debug */ + val = readl(priv->base + PCL_RCV_INT); + + if (val & PCL_CFG_BW_MGT_STATUS) + dev_dbg(pci->dev, "Link Bandwidth Management Event\n"); + if (val & PCL_CFG_LINK_AUTO_BW_STATUS) + dev_dbg(pci->dev, "Link Autonomous Bandwidth Event\n"); + if (val & PCL_CFG_AER_RC_ERR_MSI_STATUS) + dev_dbg(pci->dev, "Root Error\n"); + if (val & PCL_CFG_PME_MSI_STATUS) + dev_dbg(pci->dev, "PME Interrupt\n"); + + writel(val, priv->base + PCL_RCV_INT); + + /* INTx */ + val = readl(priv->base + PCL_RCV_INTX); + + if (val & PCL_RADM_INTA_STATUS) + generic_handle_irq(irq_find_mapping(priv->irq_domain, 0)); + if (val & PCL_RADM_INTB_STATUS) + generic_handle_irq(irq_find_mapping(priv->irq_domain, 1)); + if (val & PCL_RADM_INTC_STATUS) + generic_handle_irq(irq_find_mapping(priv->irq_domain, 2)); + if (val & PCL_RADM_INTD_STATUS) + generic_handle_irq(irq_find_mapping(priv->irq_domain, 3)); + + writel(val, priv->base + PCL_RCV_INTX); + + return IRQ_HANDLED; +} + +static irqreturn_t uniphier_pcie_msi_irq_handler(int irq, void *arg) +{ + struct pcie_port *pp = arg; + + return dw_handle_msi_irq(pp); +} + +static int uniphier_pcie_host_init(struct pcie_port *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + int ret; + + dw_pcie_setup_rc(pp); + ret = uniphier_pcie_establish_link(pci); + if (ret) + return ret; + + if (IS_ENABLED(CONFIG_PCI_MSI)) + dw_pcie_msi_init(pp); + + return 0; +} + +static const struct dw_pcie_host_ops uniphier_pcie_host_ops = { + .host_init = uniphier_pcie_host_init, +}; + +static int uniphier_add_pcie_port(struct uniphier_pcie_priv *priv, + struct platform_device *pdev) +{ + struct dw_pcie *pci = &priv->pci; + struct pcie_port *pp = &pci->pp; + struct device *dev = &pdev->dev; + int ret; + + pp->root_bus_nr = -1; + pp->ops = &uniphier_pcie_host_ops; + + pp->irq = platform_get_irq_byname(pdev, "intx"); + if (pp->irq < 0) { + dev_err(dev, "Failed to get intx irq\n"); + return pp->irq; + } + + ret = devm_request_irq(dev, pp->irq, uniphier_pcie_irq_handler, + IRQF_SHARED, "pcie", priv); + if (ret) { + dev_err(dev, "Failed to request irq %d\n", pp->irq); + return ret; + } + + ret = uniphier_pcie_init_irq_domain(pp); + if (ret) + return ret; + + if (IS_ENABLED(CONFIG_PCI_MSI)) { + pp->msi_irq = platform_get_irq_byname(pdev, "msi"); + if (pp->msi_irq < 0) + return pp->msi_irq; + + ret = devm_request_irq(dev, pp->msi_irq, + uniphier_pcie_msi_irq_handler, + IRQF_SHARED, "pcie-msi", pp); + if (ret) { + dev_err(dev, "failed to request msi_irq %d\n", + pp->msi_irq); + return ret; + } + } + + ret = dw_pcie_host_init(pp); + if (ret) { + dev_err(dev, "Failed to initialize host (%d)\n", ret); + return ret; + } + + return 0; +} + +static int uniphier_pcie_host_enable(struct uniphier_pcie_priv *priv) +{ + int ret; + + ret = clk_prepare_enable(priv->clk); + if (ret) + return ret; + + ret = reset_control_deassert(priv->rst); + if (ret) + goto out_clk_disable; + + uniphier_pcie_init_rc(priv); + + ret = phy_init(priv->phy); + if (ret) + goto out_rst_assert; + + ret = uniphier_pcie_wait_rc(priv); + if (ret) + goto out_phy_exit; + + uniphier_pcie_irq_enable(priv); + + return 0; + +out_phy_exit: + phy_exit(priv->phy); +out_rst_assert: + reset_control_assert(priv->rst); +out_clk_disable: + clk_disable_unprepare(priv->clk); + + return ret; +} + +static void uniphier_pcie_host_disable(struct uniphier_pcie_priv *priv) +{ + uniphier_pcie_irq_disable(priv); + phy_exit(priv->phy); + reset_control_assert(priv->rst); + clk_disable_unprepare(priv->clk); +} + +static const struct dw_pcie_ops dw_pcie_ops = { + .start_link = uniphier_pcie_establish_link, + .stop_link = uniphier_pcie_stop_link, + .link_up = uniphier_pcie_link_up, +}; + +static int uniphier_pcie_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct uniphier_pcie_priv *priv; + struct resource *res; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->pci.dev = dev; + priv->pci.ops = &dw_pcie_ops; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi"); + priv->pci.dbi_base = devm_pci_remap_cfg_resource(dev, res); + if (IS_ERR(priv->pci.dbi_base)) + return PTR_ERR(priv->pci.dbi_base); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "link"); + priv->base = devm_ioremap_resource(dev, res); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + priv->clk = devm_clk_get(dev, NULL); + if (IS_ERR(priv->clk)) + return PTR_ERR(priv->clk); + + priv->rst = devm_reset_control_get_shared(dev, NULL); + if (IS_ERR(priv->rst)) + return PTR_ERR(priv->rst); + + priv->phy = devm_phy_optional_get(dev, "pcie-phy"); + if (IS_ERR(priv->phy)) + return PTR_ERR(priv->phy); + + platform_set_drvdata(pdev, priv); + + ret = uniphier_pcie_host_enable(priv); + if (ret) + return ret; + + return uniphier_add_pcie_port(priv, pdev); +} + +static int uniphier_pcie_remove(struct platform_device *pdev) +{ + struct uniphier_pcie_priv *priv = platform_get_drvdata(pdev); + + uniphier_pcie_host_disable(priv); + + return 0; +} + +static const struct of_device_id uniphier_pcie_match[] = { + { .compatible = "socionext,uniphier-pcie", }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, uniphier_pcie_match); + +static struct platform_driver uniphier_pcie_driver = { + .probe = uniphier_pcie_probe, + .remove = uniphier_pcie_remove, + .driver = { + .name = "uniphier-pcie", + .of_match_table = uniphier_pcie_match, + }, +}; +builtin_platform_driver(uniphier_pcie_driver); + +MODULE_AUTHOR("Kunihiko Hayashi "); +MODULE_DESCRIPTION("UniPhier PCIe host controller driver"); +MODULE_LICENSE("GPL v2");