From patchwork Tue Mar 8 15:48:14 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thierry Reding X-Patchwork-Id: 8535461 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 149CBC0553 for ; Tue, 8 Mar 2016 15:48:29 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 0B6BD20131 for ; Tue, 8 Mar 2016 15:48:28 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id E6631201E4 for ; Tue, 8 Mar 2016 15:48:26 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932385AbcCHPsZ (ORCPT ); Tue, 8 Mar 2016 10:48:25 -0500 Received: from mail-pf0-f180.google.com ([209.85.192.180]:32951 "EHLO mail-pf0-f180.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S932361AbcCHPsY (ORCPT ); Tue, 8 Mar 2016 10:48:24 -0500 Received: by mail-pf0-f180.google.com with SMTP id 124so15586556pfg.0; Tue, 08 Mar 2016 07:48:24 -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=iGDv4uNHFaVC6asq3HozaSVF8f7SwyZSrUWRoCSaJeE=; b=ZQH2svWGynsbBm3k8ZiZ9iOfmk3DN0tppAqfmZIhQhVw3e9qzJIHTTmdgMdOeto3vi IbkKtKKI5ws0i45x3qTVBd1tKrBgACuTbw+OxaCBvZi4YwqXAQhpfio8rofrGT+DbqVn mxH/0Q1SJoeq5OiOsuO37YLSdrT1Hd6GLzEExVYgaeeDirBVVpaCIyt2wDhBQbF4rPm/ W20N0bAhlncc9OtIi5o9BuTM3RV0k7PrTaG05kQu1/QTzkkz544PGZZUG2r/BISKn6+m rkPI1LS7xZv9OVtIDZUtrOjSmqsjU6T/t6Vk/DQumFj5zuv3ZWIqwXdCm8bdpj3yqFeF 4iYA== 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=iGDv4uNHFaVC6asq3HozaSVF8f7SwyZSrUWRoCSaJeE=; b=V81CsE50lW6ATRYmXWzNQeLdubEXdv+njGCWuyhAH7dld8ZdAkFX7ezvoZh2/t1mtt YEB7rTXysvlnUV5VdGSLUlLxvW+pOyJ7n5hAcBjPY2BHt/f8P4lp9U4ipo3I0eFclncH XlX4rlNYJmoi4IyyRWhdwnMmHyXKFNLzMgmOZntXC9zRKQBh3xOm0Ngj/dTQ7Rdbqbnw bxGgr05symeSQ6vQE7Qd0+nPvZ7PvpFb3qwczjOuuSyUy7zntGfv8YkI+ZwJ2/+492aT ZD8EX3b4wG4R3rrqI1jZbj6v8MMDsKGdPe9HN5UgqsFw2BH/tJ5c255Rw/1iY0uQ9NRB tF3g== X-Gm-Message-State: AD7BkJIWK+xoX372rPQ09N+59BI4FDNOushzVTGzdiHodfLUMNMN8+U8J3zWjCgtca6jbw== X-Received: by 10.98.14.149 with SMTP id 21mr42551839pfo.79.1457452103776; Tue, 08 Mar 2016 07:48:23 -0800 (PST) Received: from localhost (port-3654.pppoe.wtnet.de. [84.46.14.84]) by smtp.gmail.com with ESMTPSA id u64sm5883995pfa.86.2016.03.08.07.48.21 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 08 Mar 2016 07:48:22 -0800 (PST) From: Thierry Reding To: Bjorn Helgaas Cc: Rob Herring , Pawel Moll , Mark Rutland , Ian Campbell , Kumar Gala , Stephen Warren , Alexandre Courbot , linux-pci@vger.kernel.org, devicetree@vger.kernel.org, linux-tegra@vger.kernel.org Subject: [PATCH v3 2/2] PCI: tegra: Support per-lane PHYs Date: Tue, 8 Mar 2016 16:48:14 +0100 Message-Id: <1457452094-5409-2-git-send-email-thierry.reding@gmail.com> X-Mailer: git-send-email 2.7.1 In-Reply-To: <1457452094-5409-1-git-send-email-thierry.reding@gmail.com> References: <1457452094-5409-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 v3: - cache result of check for new PHY bindings usage (Stephen Warren) Changes in v2: - rework commit message to more accurately describe this change drivers/pci/host/pci-tegra.c | 151 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 135 insertions(+), 16 deletions(-) diff --git a/drivers/pci/host/pci-tegra.c b/drivers/pci/host/pci-tegra.c index 75c55265ca73..3b59e3162dfa 100644 --- a/drivers/pci/host/pci-tegra.c +++ b/drivers/pci/host/pci-tegra.c @@ -290,6 +290,7 @@ struct tegra_pcie { struct reset_control *afi_rst; struct reset_control *pcie_xrst; + bool legacy_phy; struct phy *phy; struct tegra_msi msi; @@ -307,11 +308,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 +848,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 +905,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 (!pcie->legacy_phy) { + 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 +1065,99 @@ 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; + } + + pcie->legacy_phy = true; + + 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 +1176,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 +1843,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))