From patchwork Thu Feb 23 17:24:38 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thierry Reding X-Patchwork-Id: 731636 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3vTh4j2Gnyz9s7C for ; Fri, 24 Feb 2017 04:27:17 +1100 (AEDT) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="UrvEI9Rb"; dkim-atps=neutral Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751726AbdBWR0F (ORCPT ); Thu, 23 Feb 2017 12:26:05 -0500 Received: from mail-wm0-f68.google.com ([74.125.82.68]:34425 "EHLO mail-wm0-f68.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751220AbdBWRZA (ORCPT ); Thu, 23 Feb 2017 12:25:00 -0500 Received: by mail-wm0-f68.google.com with SMTP id m70so1005107wma.1; Thu, 23 Feb 2017 09:24:54 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=tGpwIhrKdUfnqAtUmL0sqac8yrXAc8b8w9fR7Pf/+KM=; b=UrvEI9RbQcbjmVCHdAXamg/asiohMabsmcfexBwNPOtIInq4gVyAKNbucPbw18ItMs 6haKBqELyuIdWIi3+uJ85TLvqmUQHor+Tke7/0fIjFvkNpknYvxvQx+S3pyMbl3etB5U Rk3zmBOTbvRElHVlWsRMIzSkZ65LZFBZ3u4+aOwZC03K8XtuQahEoICjXFNZr41CXZzU 5IFE3DnbmDvnkVsvqi4WlluvrTKPxa55wCRt4o9fj8V5ltQY4TCwp+G2q7jyqMNcZH+4 7f09SiRLmujgmsj4RJdQJNEyImgDERcCGFuFzE6A9PZTAyUIIVl3yC/O53I/yb+JnsEu hl0g== 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:in-reply-to :references; bh=tGpwIhrKdUfnqAtUmL0sqac8yrXAc8b8w9fR7Pf/+KM=; b=r8kY0WnUWNLAH+qM5V7GTD8UgLRJfRNaEPSr5Wsjwck6krITKT+v/2sJaZLMZHqjBe +QNkangz60rn0LYmvS1fZgwfXNYvES53iyMfHDCJu/0FcS/vKV8vZopcd6kKqOs+FtN2 SSLVunAVZEtNfKUFHTpPTysFrInhqybLI9/8ofKputfMX+wgKsMDkl2d2hh/Y4dVso01 k9UHNlc7yqVl4ArbUqQWWiM7rsFatC/8ytSpKwRkaaut5l/ravTr4nRtToCBQeNM8yav F8tumRVyyL+uQoGvDv+ZbbeKDe1XgsVOb06lxGVN/2ZcTkfOKo0AUspINCnPR3Gsiods WcXQ== X-Gm-Message-State: AMke39lFBG5Jt5ReLmDD3c9DDgvGOAQFfBqW66HNxz3vPBi8lICGvNaZ5X3RhMA70obirA== X-Received: by 10.28.48.78 with SMTP id w75mr6137139wmw.55.1487870693062; Thu, 23 Feb 2017 09:24:53 -0800 (PST) Received: from localhost (port-435.pppoe.wtnet.de. [84.46.1.180]) by smtp.gmail.com with ESMTPSA id b10sm7626937wmi.34.2017.02.23.09.24.51 (version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256); Thu, 23 Feb 2017 09:24:52 -0800 (PST) From: Thierry Reding To: "David S . Miller" Cc: Giuseppe Cavallaro , Alexandre Torgue , Rob Herring , Mark Rutland , Joao Pinto , Alexandre Courbot , Jon Hunter , netdev@vger.kernel.org, linux-tegra@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH 7/7] net: stmmac: dwc-qos: Add Tegra186 support Date: Thu, 23 Feb 2017 18:24:38 +0100 Message-Id: <20170223172438.14770-8-thierry.reding@gmail.com> X-Mailer: git-send-email 2.11.1 In-Reply-To: <20170223172438.14770-1-thierry.reding@gmail.com> References: <20170223172438.14770-1-thierry.reding@gmail.com> Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org From: Thierry Reding The NVIDIA Tegra186 SoC contains an instance of the Synopsys DWC ethernet QOS IP core. The binding that it uses is slightly different from existing ones because of the integration (clocks, resets, ...). Signed-off-by: Thierry Reding --- .../ethernet/stmicro/stmmac/dwmac-dwc-qos-eth.c | 252 +++++++++++++++++++++ 1 file changed, 252 insertions(+) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-dwc-qos-eth.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-dwc-qos-eth.c index 5071d3c15adc..54dfbdc48f6d 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-dwc-qos-eth.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-dwc-qos-eth.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -22,10 +23,24 @@ #include #include #include +#include #include #include "stmmac_platform.h" +struct tegra_eqos { + struct device *dev; + void __iomem *regs; + + struct reset_control *rst; + struct clk *clk_master; + struct clk *clk_slave; + struct clk *clk_tx; + struct clk *clk_rx; + + struct gpio_desc *reset; +}; + static int dwc_eth_dwmac_config_dt(struct platform_device *pdev, struct plat_stmmacenet_data *plat_dat) { @@ -148,6 +163,237 @@ static int dwc_qos_remove(struct platform_device *pdev) return 0; } +#define SDMEMCOMPPADCTRL 0x8800 +#define SDMEMCOMPPADCTRL_PAD_E_INPUT_OR_E_PWRD BIT(31) + +#define AUTO_CAL_CONFIG 0x8804 +#define AUTO_CAL_CONFIG_START BIT(31) +#define AUTO_CAL_CONFIG_ENABLE BIT(29) + +#define AUTO_CAL_STATUS 0x880c +#define AUTO_CAL_STATUS_ACTIVE BIT(31) + +static void tegra_eqos_fix_speed(void *priv, unsigned int speed) +{ + struct tegra_eqos *eqos = priv; + unsigned long rate = 125000000; + bool needs_calibration = false; + unsigned int i; + u32 value; + + switch (speed) { + case SPEED_1000: + needs_calibration = true; + rate = 125000000; + break; + + case SPEED_100: + needs_calibration = true; + rate = 25000000; + break; + + case SPEED_10: + rate = 2500000; + break; + + default: + dev_err(eqos->dev, "invalid speed %u\n", speed); + break; + } + + if (needs_calibration) { + /* calibrate */ + value = readl(eqos->regs + SDMEMCOMPPADCTRL); + value |= SDMEMCOMPPADCTRL_PAD_E_INPUT_OR_E_PWRD; + writel(value, eqos->regs + SDMEMCOMPPADCTRL); + + udelay(1); + + value = readl(eqos->regs + AUTO_CAL_CONFIG); + value |= AUTO_CAL_CONFIG_START | AUTO_CAL_CONFIG_ENABLE; + writel(value, eqos->regs + AUTO_CAL_CONFIG); + + for (i = 0; i <= 10; i++) { + value = readl(eqos->regs + AUTO_CAL_STATUS); + if (value & AUTO_CAL_STATUS_ACTIVE) + break; + + udelay(1); + } + + if ((value & AUTO_CAL_STATUS_ACTIVE) == 0) { + dev_err(eqos->dev, "calibration did not start\n"); + goto failed; + } + + for (i = 0; i <= 10; i++) { + value = readl(eqos->regs + AUTO_CAL_STATUS); + if ((value & AUTO_CAL_STATUS_ACTIVE) == 0) + break; + + udelay(20); + } + + if (value & AUTO_CAL_STATUS_ACTIVE) { + dev_err(eqos->dev, "calibration didn't finish\n"); + goto failed; + } + + failed: + value = readl(eqos->regs + SDMEMCOMPPADCTRL); + value &= ~SDMEMCOMPPADCTRL_PAD_E_INPUT_OR_E_PWRD; + writel(value, eqos->regs + SDMEMCOMPPADCTRL); + } else { + value = readl(eqos->regs + AUTO_CAL_CONFIG); + value &= ~AUTO_CAL_CONFIG_ENABLE; + writel(value, eqos->regs + AUTO_CAL_CONFIG); + } + + clk_set_rate(eqos->clk_tx, rate); +} + +static int tegra_eqos_init(struct platform_device *pdev, void *priv) +{ + struct tegra_eqos *eqos = priv; + unsigned long rate; + u32 value; + + rate = clk_get_rate(eqos->clk_slave); + + value = readl(eqos->regs + 0xdc); + value = (rate / 1000000) - 1; + writel(value, eqos->regs + 0xdc); + + return 0; +} + +static void *tegra_eqos_probe(struct platform_device *pdev, + struct plat_stmmacenet_data *data, + struct stmmac_resources *res) +{ + struct tegra_eqos *eqos; + int err; + + eqos = devm_kzalloc(&pdev->dev, sizeof(*eqos), GFP_KERNEL); + if (!eqos) { + err = -ENOMEM; + goto error; + } + + eqos->dev = &pdev->dev; + eqos->regs = res->addr; + + eqos->clk_master = devm_clk_get(&pdev->dev, "master_bus"); + if (IS_ERR(eqos->clk_master)) { + err = PTR_ERR(eqos->clk_master); + goto error; + } + + err = clk_prepare_enable(eqos->clk_master); + if (err < 0) + goto error; + + eqos->clk_slave = devm_clk_get(&pdev->dev, "slave_bus"); + if (IS_ERR(eqos->clk_slave)) { + err = PTR_ERR(eqos->clk_slave); + goto disable_master; + } + + data->stmmac_clk = eqos->clk_slave; + + err = clk_prepare_enable(eqos->clk_slave); + if (err < 0) + goto disable_master; + + eqos->clk_rx = devm_clk_get(&pdev->dev, "rx"); + if (IS_ERR(eqos->clk_rx)) { + err = PTR_ERR(eqos->clk_rx); + goto disable_slave; + } + + err = clk_prepare_enable(eqos->clk_rx); + if (err < 0) + goto disable_slave; + + eqos->clk_tx = devm_clk_get(&pdev->dev, "tx"); + if (IS_ERR(eqos->clk_tx)) { + err = PTR_ERR(eqos->clk_tx); + goto disable_rx; + } + + err = clk_prepare_enable(eqos->clk_tx); + if (err < 0) + goto disable_rx; + + eqos->reset = devm_gpiod_get(&pdev->dev, "phy-reset", GPIOD_OUT_HIGH); + if (IS_ERR(eqos->reset)) { + err = PTR_ERR(eqos->reset); + goto disable_tx; + } + + usleep_range(2000, 4000); + gpiod_set_value(eqos->reset, 0); + + eqos->rst = devm_reset_control_get(&pdev->dev, "eqos"); + if (IS_ERR(eqos->rst)) { + err = PTR_ERR(eqos->rst); + goto reset_phy; + } + + err = reset_control_assert(eqos->rst); + if (err < 0) + goto reset_phy; + + usleep_range(2000, 4000); + + err = reset_control_deassert(eqos->rst); + if (err < 0) + goto reset_phy; + + usleep_range(2000, 4000); + + data->fix_mac_speed = tegra_eqos_fix_speed; + data->init = tegra_eqos_init; + data->bsp_priv = eqos; + + err = tegra_eqos_init(pdev, eqos); + if (err < 0) + goto reset; + +out: + return eqos; + +reset: + reset_control_assert(eqos->rst); +reset_phy: + gpiod_set_value(eqos->reset, 1); +disable_tx: + clk_disable_unprepare(eqos->clk_tx); +disable_rx: + clk_disable_unprepare(eqos->clk_rx); +disable_slave: + clk_disable_unprepare(eqos->clk_slave); +disable_master: + clk_disable_unprepare(eqos->clk_master); +error: + eqos = ERR_PTR(err); + goto out; +} + +static int tegra_eqos_remove(struct platform_device *pdev) +{ + struct tegra_eqos *eqos = get_stmmac_bsp_priv(&pdev->dev); + + reset_control_assert(eqos->rst); + gpiod_set_value(eqos->reset, 1); + clk_disable_unprepare(eqos->clk_tx); + clk_disable_unprepare(eqos->clk_rx); + clk_disable_unprepare(eqos->clk_slave); + clk_disable_unprepare(eqos->clk_master); + + return 0; +} + struct dwc_eth_dwmac_data { void *(*probe)(struct platform_device *pdev, struct plat_stmmacenet_data *data, @@ -160,6 +406,11 @@ static const struct dwc_eth_dwmac_data dwc_qos_data = { .remove = dwc_qos_remove, }; +static const struct dwc_eth_dwmac_data tegra_eqos_data = { + .probe = tegra_eqos_probe, + .remove = tegra_eqos_remove, +}; + static int dwc_eth_dwmac_probe(struct platform_device *pdev) { const struct dwc_eth_dwmac_data *data; @@ -245,6 +496,7 @@ static int dwc_eth_dwmac_remove(struct platform_device *pdev) static const struct of_device_id dwc_eth_dwmac_match[] = { { .compatible = "snps,dwc-qos-ethernet-4.10", .data = &dwc_qos_data }, + { .compatible = "nvidia,tegra186-eqos", .data = &tegra_eqos_data}, { } }; MODULE_DEVICE_TABLE(of, dwc_eth_dwmac_match);