From patchwork Fri Feb 19 18:01:00 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thierry Reding X-Patchwork-Id: 8363281 X-Patchwork-Delegate: bhelgaas@google.com Return-Path: X-Original-To: patchwork-linux-pci@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork2.web.kernel.org (Postfix) with ESMTP id 99393C0553 for ; Fri, 19 Feb 2016 18:01:10 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id BF1FA20546 for ; Fri, 19 Feb 2016 18:01:08 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id AA6872053D for ; Fri, 19 Feb 2016 18:01:07 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753350AbcBSSBF (ORCPT ); Fri, 19 Feb 2016 13:01:05 -0500 Received: from mail-wm0-f41.google.com ([74.125.82.41]:38510 "EHLO mail-wm0-f41.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751021AbcBSSBD (ORCPT ); Fri, 19 Feb 2016 13:01:03 -0500 Received: by mail-wm0-f41.google.com with SMTP id a4so81816950wme.1; Fri, 19 Feb 2016 10:01:02 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=2Mvsg/8xS5dtgIpBhBB6TB/M3xVu2mLwyxBOMXhypNc=; b=Q+yJ6O5WBypPENQV4ovnsGYqUnVTjkDbCQbsi+sO6waGxfCNxThda/MerFHafLf7r7 ELF3zsIpDJTTU0uVasaxPjI2AxIDhp7w9Fd/+EX/EWjfgkvDRnNr9MoE9pMODK7t2qFd O/z+sPYjNCwR3WEl+hdtmRJ37XWpQpIQe6ki76dDr+XdNGtrn545jwWXE8glXhCm+Yse IOx6vEY2fkg5ga2vY+V0dJ7jLhEvc3cIL8RS1zvNmN1v4LpB+pXSZzknAHSft0n76m+r 6IaU3jhuRaBwuVL9KwushoSSjMXaq20kV2796yeBecSS9VeNm6C9kBaf58lyFt0aE3/e U2fg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=2Mvsg/8xS5dtgIpBhBB6TB/M3xVu2mLwyxBOMXhypNc=; b=Bb0D9iiOMNxW7vVomDWWoeUmNhajuf2aRs903d9DyHWC+/uRJhcj8/maE8Pj7bsUma 0n1AXsDPYUfTfr4dN8uuNTebSzUmj/EPJw+AKe4uUi3bHTaquvUhUtVIMjam/yut0mfg I69zfQnbFdCQ+3E4OY1Lf1MJX3+4hjixNz6UhAMTnscp/hXaffklDx1xUk12L89Z8bqy BrAWsmeExXnQrgif9jMJkwTthoj1djNn87+vrvMYsQZ4ezRCU8HuLEhkHcKyoI4qb7US zIEJMcjMhA87nrZurJ0eT/XBxx4PX7nUteCF2/Tz3njrIJgOBAcsQk+rLT9Qk+bC/C+h UZmA== X-Gm-Message-State: AG10YORcX7HZZ+ArXQpDA8Rh7cLDQXga0MOb7ecDKD5cK41zN2Ap2FqjPAjxPgMDjyUq0A== X-Received: by 10.28.32.19 with SMTP id g19mr10754985wmg.98.1455904861817; Fri, 19 Feb 2016 10:01:01 -0800 (PST) Received: from localhost (port-5003.pppoe.wtnet.de. [84.46.19.158]) by smtp.gmail.com with ESMTPSA id s2sm12213263wjs.39.2016.02.19.10.01.01 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Fri, 19 Feb 2016 10:01:01 -0800 (PST) From: Thierry Reding To: Bjorn Helgaas Cc: Stephen Warren , Alexandre Courbot , linux-pci@vger.kernel.org, linux-tegra@vger.kernel.org Subject: [PATCH v2] PCI: tegra: Support per-lane PHYs Date: Fri, 19 Feb 2016 19:01:00 +0100 Message-Id: <1455904860-29090-1-git-send-email-thierry.reding@gmail.com> X-Mailer: git-send-email 2.7.1 In-Reply-To: <1455029465-9211-1-git-send-email-thierry.reding@gmail.com> References: <1455029465-9211-1-git-send-email-thierry.reding@gmail.com> Sender: linux-pci-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pci@vger.kernel.org X-Spam-Status: No, score=-6.8 required=5.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, FREEMAIL_FROM, RCVD_IN_DNSWL_HI, RP_MATCHES_RCVD, T_DKIM_INVALID, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Thierry Reding The current XUSB pad controller bindings are insufficient to describe PHY devices attached to USB controllers. New bindings have been created to overcome these restrictions. As a side-effect each root port now is assigned a set of PHY devices, one for each lane associated with the root port. This has the benefit of allowing fine-grained control of the power management for each lane. Signed-off-by: Thierry Reding --- Changes in v2: - rework commit message to more accurately describe this change drivers/pci/host/pci-tegra.c | 148 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 132 insertions(+), 16 deletions(-) diff --git a/drivers/pci/host/pci-tegra.c b/drivers/pci/host/pci-tegra.c index 75c55265ca73..4cb8157a4678 100644 --- a/drivers/pci/host/pci-tegra.c +++ b/drivers/pci/host/pci-tegra.c @@ -307,11 +307,14 @@ struct tegra_pcie { struct tegra_pcie_port { struct tegra_pcie *pcie; + struct device_node *np; struct list_head list; struct resource regs; void __iomem *base; unsigned int index; unsigned int lanes; + + struct phy **phys; }; struct tegra_pcie_bus { @@ -844,6 +847,24 @@ static int tegra_pcie_phy_enable(struct tegra_pcie *pcie) return 0; } +static int tegra_pcie_port_phy_power_on(struct tegra_pcie_port *port) +{ + struct device *dev = port->pcie->dev; + unsigned int i; + int err; + + for (i = 0; i < port->lanes; i++) { + err = phy_power_on(port->phys[i]); + if (err < 0) { + dev_err(dev, "failed to power on PHY#%u: %d\n", i, + err); + return err; + } + } + + return 0; +} + static int tegra_pcie_enable_controller(struct tegra_pcie *pcie) { const struct tegra_pcie_soc_data *soc = pcie->soc_data; @@ -883,14 +904,24 @@ static int tegra_pcie_enable_controller(struct tegra_pcie *pcie) afi_writel(pcie, value, AFI_FUSE); } - if (!pcie->phy) - err = tegra_pcie_phy_enable(pcie); - else - err = phy_power_on(pcie->phy); + if (of_get_property(pcie->dev->of_node, "phys", NULL) == NULL) { + list_for_each_entry(port, &pcie->ports, list) { + err = tegra_pcie_port_phy_power_on(port); + if (err < 0) { + dev_err(pcie->dev, + "failed to power on PCIe port: %d\n", + err); + return err; + } + } + } else { + if (!pcie->phy) + err = tegra_pcie_phy_enable(pcie); + else + err = phy_power_on(pcie->phy); - if (err < 0) { - dev_err(pcie->dev, "failed to power on PHY: %d\n", err); - return err; + if (err < 0) + dev_err(pcie->dev, "failed to power on PHY: %d\n", err); } /* take the PCIe interface module out of reset */ @@ -1033,6 +1064,97 @@ static int tegra_pcie_resets_get(struct tegra_pcie *pcie) return 0; } +static int tegra_pcie_phys_get_legacy(struct tegra_pcie *pcie) +{ + int err; + + pcie->phy = devm_phy_optional_get(pcie->dev, "pcie"); + if (IS_ERR(pcie->phy)) { + err = PTR_ERR(pcie->phy); + dev_err(pcie->dev, "failed to get PHY: %d\n", err); + return err; + } + + err = phy_init(pcie->phy); + if (err < 0) { + dev_err(pcie->dev, "failed to initialize PHY: %d\n", err); + return err; + } + + return 0; +} + +static struct phy *devm_of_phy_optional_get_index(struct device *dev, + struct device_node *np, + const char *consumer, + unsigned int index) +{ + struct phy *phy; + char *name; + + name = kasprintf(GFP_KERNEL, "%s-%u", consumer, index); + if (!name) + return ERR_PTR(-ENOMEM); + + phy = devm_of_phy_get(dev, np, name); + kfree(name); + + if (IS_ERR(phy) && PTR_ERR(phy) == -ENODEV) + phy = NULL; + + return phy; +} + +static int tegra_pcie_port_get_phys(struct tegra_pcie_port *port) +{ + struct device *dev = port->pcie->dev; + struct phy *phy; + unsigned int i; + int err; + + port->phys = devm_kcalloc(dev, sizeof(phy), port->lanes, GFP_KERNEL); + if (!port->phys) + return -ENOMEM; + + for (i = 0; i < port->lanes; i++) { + phy = devm_of_phy_optional_get_index(dev, port->np, "pcie", i); + if (IS_ERR(phy)) { + dev_err(dev, "failed to get PHY#%u: %ld\n", i, + PTR_ERR(phy)); + return PTR_ERR(phy); + } + + err = phy_init(phy); + if (err < 0) { + dev_err(dev, "failed to initialize PHY#%u: %d\n", i, + err); + return err; + } + + port->phys[i] = phy; + } + + return 0; +} + +static int tegra_pcie_phys_get(struct tegra_pcie *pcie) +{ + struct tegra_pcie_port *port; + int err; + + if (of_get_property(pcie->dev->of_node, "phys", NULL) != NULL) + return tegra_pcie_phys_get_legacy(pcie); + + list_for_each_entry(port, &pcie->ports, list) { + err = tegra_pcie_port_get_phys(port); + if (err < 0) { + return err; + } + } + + return 0; +} + static int tegra_pcie_get_resources(struct tegra_pcie *pcie) { struct platform_device *pdev = to_platform_device(pcie->dev); @@ -1051,16 +1173,9 @@ static int tegra_pcie_get_resources(struct tegra_pcie *pcie) return err; } - pcie->phy = devm_phy_optional_get(pcie->dev, "pcie"); - if (IS_ERR(pcie->phy)) { - err = PTR_ERR(pcie->phy); - dev_err(&pdev->dev, "failed to get PHY: %d\n", err); - return err; - } - - err = phy_init(pcie->phy); + err = tegra_pcie_phys_get(pcie); if (err < 0) { - dev_err(&pdev->dev, "failed to initialize PHY: %d\n", err); + dev_err(&pdev->dev, "failed to get PHYs: %d\n", err); return err; } @@ -1725,6 +1840,7 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie) rp->index = index; rp->lanes = value; rp->pcie = pcie; + rp->np = port; rp->base = devm_ioremap_resource(pcie->dev, &rp->regs); if (IS_ERR(rp->base))