Message ID | 20220707074818.1481776-10-thierry.reding@gmail.com (mailing list archive) |
---|---|
State | Changes Requested |
Delegated to: | Netdev Maintainers |
Headers | show |
Series | tegra: Add support for MGBE controller | expand |
On Thu, Jul 07, 2022 at 09:48:18AM +0200, Thierry Reding wrote: > From: Bhadram Varka <vbhadram@nvidia.com> > > Add support for the Multi-Gigabit Ethernet (MGBE/XPCS) IP found on > NVIDIA Tegra234 SoCs. > > Signed-off-by: Bhadram Varka <vbhadram@nvidia.com> > Signed-off-by: Thierry Reding <treding@nvidia.com> > --- > Note that this doesn't have any dependencies on any of the patches > earlier in the series, so this can be applied independently. > > drivers/net/ethernet/stmicro/stmmac/Kconfig | 6 + > drivers/net/ethernet/stmicro/stmmac/Makefile | 1 + > .../net/ethernet/stmicro/stmmac/dwmac-tegra.c | 290 ++++++++++++++++++ > 3 files changed, 297 insertions(+) > create mode 100644 drivers/net/ethernet/stmicro/stmmac/dwmac-tegra.c Patches 1-8 of this have already been applied to the Tegra tree. Are there any more comments on this or can this be merged as well? From a Tegra point of view this looks good, so: Acked-by: Thierry Reding <treding@nvidia.com> > > diff --git a/drivers/net/ethernet/stmicro/stmmac/Kconfig b/drivers/net/ethernet/stmicro/stmmac/Kconfig > index 929cfc22cd0c..47af5a59ce88 100644 > --- a/drivers/net/ethernet/stmicro/stmmac/Kconfig > +++ b/drivers/net/ethernet/stmicro/stmmac/Kconfig > @@ -232,6 +232,12 @@ config DWMAC_INTEL_PLAT > the stmmac device driver. This driver is used for the Intel Keem Bay > SoC. > > +config DWMAC_TEGRA > + tristate "NVIDIA Tegra MGBE support" > + depends on ARCH_TEGRA || COMPILE_TEST > + help > + Support for the MGBE controller found on Tegra SoCs. > + > config DWMAC_VISCONTI > tristate "Toshiba Visconti DWMAC support" > default ARCH_VISCONTI > diff --git a/drivers/net/ethernet/stmicro/stmmac/Makefile b/drivers/net/ethernet/stmicro/stmmac/Makefile > index d4e12e9ace4f..057e4bab5c08 100644 > --- a/drivers/net/ethernet/stmicro/stmmac/Makefile > +++ b/drivers/net/ethernet/stmicro/stmmac/Makefile > @@ -31,6 +31,7 @@ obj-$(CONFIG_DWMAC_DWC_QOS_ETH) += dwmac-dwc-qos-eth.o > obj-$(CONFIG_DWMAC_INTEL_PLAT) += dwmac-intel-plat.o > obj-$(CONFIG_DWMAC_GENERIC) += dwmac-generic.o > obj-$(CONFIG_DWMAC_IMX8) += dwmac-imx.o > +obj-$(CONFIG_DWMAC_TEGRA) += dwmac-tegra.o > obj-$(CONFIG_DWMAC_VISCONTI) += dwmac-visconti.o > stmmac-platform-objs:= stmmac_platform.o > dwmac-altr-socfpga-objs := altr_tse_pcs.o dwmac-socfpga.o > diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-tegra.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-tegra.c > new file mode 100644 > index 000000000000..bb4b540820fa > --- /dev/null > +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-tegra.c > @@ -0,0 +1,290 @@ > +// SPDX-License-Identifier: GPL-2.0-only > +#include <linux/platform_device.h> > +#include <linux/of_device.h> > +#include <linux/module.h> > +#include <linux/stmmac.h> > +#include <linux/clk.h> > + > +#include "stmmac_platform.h" > + > +static const char *const mgbe_clks[] = { > + "rx-pcs", "tx", "tx-pcs", "mac-divider", "mac", "mgbe", "ptp-ref", "mac" > +}; > + > +struct tegra_mgbe { > + struct device *dev; > + > + struct clk_bulk_data *clks; > + > + struct reset_control *rst_mac; > + struct reset_control *rst_pcs; > + > + void __iomem *hv; > + void __iomem *regs; > + void __iomem *xpcs; > + > + struct mii_bus *mii; > +}; > + > +#define XPCS_WRAP_UPHY_RX_CONTROL 0x801c > +#define XPCS_WRAP_UPHY_RX_CONTROL_RX_SW_OVRD BIT(31) > +#define XPCS_WRAP_UPHY_RX_CONTROL_RX_PCS_PHY_RDY BIT(10) > +#define XPCS_WRAP_UPHY_RX_CONTROL_RX_CDR_RESET BIT(9) > +#define XPCS_WRAP_UPHY_RX_CONTROL_RX_CAL_EN BIT(8) > +#define XPCS_WRAP_UPHY_RX_CONTROL_RX_SLEEP (BIT(7) | BIT(6)) > +#define XPCS_WRAP_UPHY_RX_CONTROL_AUX_RX_IDDQ BIT(5) > +#define XPCS_WRAP_UPHY_RX_CONTROL_RX_IDDQ BIT(4) > +#define XPCS_WRAP_UPHY_RX_CONTROL_RX_DATA_EN BIT(0) > +#define XPCS_WRAP_UPHY_HW_INIT_CTRL 0x8020 > +#define XPCS_WRAP_UPHY_HW_INIT_CTRL_TX_EN BIT(0) > +#define XPCS_WRAP_UPHY_HW_INIT_CTRL_RX_EN BIT(2) > +#define XPCS_WRAP_UPHY_STATUS 0x8044 > +#define XPCS_WRAP_UPHY_STATUS_TX_P_UP BIT(0) > +#define XPCS_WRAP_IRQ_STATUS 0x8050 > +#define XPCS_WRAP_IRQ_STATUS_PCS_LINK_STS BIT(6) > + > +#define XPCS_REG_ADDR_SHIFT 10 > +#define XPCS_REG_ADDR_MASK 0x1fff > +#define XPCS_ADDR 0x3fc > + > +#define MGBE_WRAP_COMMON_INTR_ENABLE 0x8704 > +#define MAC_SBD_INTR BIT(2) > +#define MGBE_WRAP_AXI_ASID0_CTRL 0x8400 > +#define MGBE_SID 0x6 > + > +static void mgbe_uphy_lane_bringup(struct tegra_mgbe *mgbe) > +{ > + unsigned int retry = 300; > + u32 value; > + int err; > + > + value = readl(mgbe->xpcs + XPCS_WRAP_UPHY_STATUS); > + if ((value & XPCS_WRAP_UPHY_STATUS_TX_P_UP) == 0) { > + value = readl(mgbe->xpcs + XPCS_WRAP_UPHY_HW_INIT_CTRL); > + value |= XPCS_WRAP_UPHY_HW_INIT_CTRL_TX_EN; > + writel(value, mgbe->xpcs + XPCS_WRAP_UPHY_HW_INIT_CTRL); > + } > + > + err = readl_poll_timeout(mgbe->xpcs + XPCS_WRAP_UPHY_HW_INIT_CTRL, value, > + (value & XPCS_WRAP_UPHY_HW_INIT_CTRL_TX_EN) == 0, > + 500, 500 * 2000); > + if (err < 0) > + dev_err(mgbe->dev, "timeout waiting for TX lane to become enabled\n"); > + > + usleep_range(10000, 20000); > + > + value = readl(mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL); > + value |= XPCS_WRAP_UPHY_RX_CONTROL_RX_SW_OVRD; > + writel(value, mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL); > + > + value = readl(mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL); > + value &= ~XPCS_WRAP_UPHY_RX_CONTROL_RX_IDDQ; > + writel(value, mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL); > + > + value = readl(mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL); > + value &= ~XPCS_WRAP_UPHY_RX_CONTROL_AUX_RX_IDDQ; > + writel(value, mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL); > + > + value = readl(mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL); > + value &= ~XPCS_WRAP_UPHY_RX_CONTROL_RX_SLEEP; > + writel(value, mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL); > + > + value = readl(mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL); > + value |= XPCS_WRAP_UPHY_RX_CONTROL_RX_CAL_EN; > + writel(value, mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL); > + > + err = readl_poll_timeout(mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL, value, > + (value & XPCS_WRAP_UPHY_RX_CONTROL_RX_CAL_EN) == 0, > + 1000, 1000 * 2000); > + if (err < 0) > + dev_err(mgbe->dev, "timeout waiting for RX calibration to become enabled\n"); > + > + value = readl(mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL); > + value |= XPCS_WRAP_UPHY_RX_CONTROL_RX_DATA_EN; > + writel(value, mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL); > + > + value = readl(mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL); > + value |= XPCS_WRAP_UPHY_RX_CONTROL_RX_CDR_RESET; > + writel(value, mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL); > + > + value = readl(mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL); > + value &= ~XPCS_WRAP_UPHY_RX_CONTROL_RX_CDR_RESET; > + writel(value, mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL); > + > + value = readl(mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL); > + value |= XPCS_WRAP_UPHY_RX_CONTROL_RX_PCS_PHY_RDY; > + writel(value, mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL); > + > + while (--retry) { > + err = readl_poll_timeout(mgbe->xpcs + XPCS_WRAP_IRQ_STATUS, value, > + value & XPCS_WRAP_IRQ_STATUS_PCS_LINK_STS, > + 500, 500 * 2000); > + if (err < 0) { > + dev_err(mgbe->dev, "timeout waiting for link to become ready\n"); > + usleep_range(10000, 20000); > + continue; > + } > + break; > + } > + > + /* clear status */ > + writel(value, mgbe->xpcs + XPCS_WRAP_IRQ_STATUS); > +} > + > +static int tegra_mgbe_probe(struct platform_device *pdev) > +{ > + struct plat_stmmacenet_data *plat; > + struct stmmac_resources res; > + struct tegra_mgbe *mgbe; > + int irq, err, i; > + > + mgbe = devm_kzalloc(&pdev->dev, sizeof(*mgbe), GFP_KERNEL); > + if (!mgbe) > + return -ENOMEM; > + > + mgbe->dev = &pdev->dev; > + > + memset(&res, 0, sizeof(res)); > + > + irq = platform_get_irq(pdev, 0); > + if (irq < 0) > + return irq; > + > + mgbe->hv = devm_platform_ioremap_resource_byname(pdev, "hypervisor"); > + if (IS_ERR(mgbe->hv)) > + return PTR_ERR(mgbe->hv); > + > + mgbe->regs = devm_platform_ioremap_resource_byname(pdev, "mac"); > + if (IS_ERR(mgbe->regs)) > + return PTR_ERR(mgbe->regs); > + > + mgbe->xpcs = devm_platform_ioremap_resource_byname(pdev, "xpcs"); > + if (IS_ERR(mgbe->xpcs)) > + return PTR_ERR(mgbe->xpcs); > + > + res.addr = mgbe->regs; > + res.irq = irq; > + > + mgbe->clks = devm_kzalloc(&pdev->dev, sizeof(*mgbe->clks), GFP_KERNEL); > + if (!mgbe->clks) > + return -ENOMEM; > + > + for (i = 0; i < ARRAY_SIZE(mgbe_clks); i++) > + mgbe->clks[i].id = mgbe_clks[i]; > + > + err = devm_clk_bulk_get(mgbe->dev, ARRAY_SIZE(mgbe_clks), mgbe->clks); > + if (err < 0) > + return err; > + > + err = clk_bulk_prepare_enable(ARRAY_SIZE(mgbe_clks), mgbe->clks); > + if (err < 0) > + return err; > + > + /* Perform MAC reset */ > + mgbe->rst_mac = devm_reset_control_get(&pdev->dev, "mac"); > + if (IS_ERR(mgbe->rst_mac)) > + return PTR_ERR(mgbe->rst_mac); > + > + err = reset_control_assert(mgbe->rst_mac); > + if (err < 0) > + return err; > + > + usleep_range(2000, 4000); > + > + err = reset_control_deassert(mgbe->rst_mac); > + if (err < 0) > + return err; > + > + /* Perform PCS reset */ > + mgbe->rst_pcs = devm_reset_control_get(&pdev->dev, "pcs"); > + if (IS_ERR(mgbe->rst_pcs)) > + return PTR_ERR(mgbe->rst_pcs); > + > + err = reset_control_assert(mgbe->rst_pcs); > + if (err < 0) > + return err; > + > + usleep_range(2000, 4000); > + > + err = reset_control_deassert(mgbe->rst_pcs); > + if (err < 0) > + return err; > + > + plat = stmmac_probe_config_dt(pdev, res.mac); > + if (IS_ERR(plat)) > + return PTR_ERR(plat); > + > + plat->has_xgmac = 1; > + plat->tso_en = 1; > + plat->pmt = 1; > + plat->bsp_priv = mgbe; > + > + if (!plat->mdio_node) > + plat->mdio_node = of_get_child_by_name(pdev->dev.of_node, "mdio"); > + > + if (!plat->mdio_bus_data) { > + plat->mdio_bus_data = devm_kzalloc(&pdev->dev, sizeof(*plat->mdio_bus_data), > + GFP_KERNEL); > + if (!plat->mdio_bus_data) { > + err = -ENOMEM; > + goto remove; > + } > + } > + > + plat->mdio_bus_data->needs_reset = true; > + > + mgbe_uphy_lane_bringup(mgbe); > + > + /* Tx FIFO Size - 128KB */ > + plat->tx_fifo_size = 131072; > + /* Rx FIFO Size - 192KB */ > + plat->rx_fifo_size = 196608; > + > + /* Enable common interrupt at wrapper level */ > + writel(MAC_SBD_INTR, mgbe->regs + MGBE_WRAP_COMMON_INTR_ENABLE); > + > + /* Program SID */ > + writel(MGBE_SID, mgbe->hv + MGBE_WRAP_AXI_ASID0_CTRL); > + > + err = stmmac_dvr_probe(&pdev->dev, plat, &res); > + if (err < 0) > + goto remove; > + > + return 0; > + > +remove: > + stmmac_remove_config_dt(pdev, plat); > + return err; > +} > + > +static int tegra_mgbe_remove(struct platform_device *pdev) > +{ > + struct tegra_mgbe *mgbe = get_stmmac_bsp_priv(&pdev->dev); > + > + clk_bulk_disable_unprepare(ARRAY_SIZE(mgbe_clks), mgbe->clks); > + > + stmmac_pltfr_remove(pdev); > + > + return 0; > +} > + > +static const struct of_device_id tegra_mgbe_match[] = { > + { .compatible = "nvidia,tegra234-mgbe", }, > + { } > +}; > +MODULE_DEVICE_TABLE(of, tegra_mgbe_match); > + > +static struct platform_driver tegra_mgbe_driver = { > + .probe = tegra_mgbe_probe, > + .remove = tegra_mgbe_remove, > + .driver = { > + .name = "tegra-mgbe", > + .pm = &stmmac_pltfr_pm_ops, > + .of_match_table = tegra_mgbe_match, > + }, > +}; > +module_platform_driver(tegra_mgbe_driver); > + > +MODULE_AUTHOR("Thierry Reding <treding@nvidia.com>"); > +MODULE_DESCRIPTION("NVIDIA Tegra MGBE driver"); > +MODULE_LICENSE("GPL"); > -- > 2.36.1 >
Hi David, Jakub, On 07/09/2022 17:36, Thierry Reding wrote: > On Thu, Jul 07, 2022 at 09:48:18AM +0200, Thierry Reding wrote: >> From: Bhadram Varka <vbhadram@nvidia.com> >> >> Add support for the Multi-Gigabit Ethernet (MGBE/XPCS) IP found on >> NVIDIA Tegra234 SoCs. >> >> Signed-off-by: Bhadram Varka <vbhadram@nvidia.com> >> Signed-off-by: Thierry Reding <treding@nvidia.com> >> --- >> Note that this doesn't have any dependencies on any of the patches >> earlier in the series, so this can be applied independently. >> >> drivers/net/ethernet/stmicro/stmmac/Kconfig | 6 + >> drivers/net/ethernet/stmicro/stmmac/Makefile | 1 + >> .../net/ethernet/stmicro/stmmac/dwmac-tegra.c | 290 ++++++++++++++++++ >> 3 files changed, 297 insertions(+) >> create mode 100644 drivers/net/ethernet/stmicro/stmmac/dwmac-tegra.c > > Patches 1-8 of this have already been applied to the Tegra tree. Are > there any more comments on this or can this be merged as well? > > From a Tegra point of view this looks good, so: > > Acked-by: Thierry Reding <treding@nvidia.com> Acked-by: Jon Hunter <jonathanh@nvidia.com> Please can we queue this for v6.1? I have added the stmmac maintainers to the email, but not sure if you can pick this up? Thanks! Jon
On Thu, 22 Sep 2022 16:05:22 +0100 Jon Hunter wrote: > On 07/09/2022 17:36, Thierry Reding wrote: > > On Thu, Jul 07, 2022 at 09:48:18AM +0200, Thierry Reding wrote: > >> From: Bhadram Varka <vbhadram@nvidia.com> > >> > >> Add support for the Multi-Gigabit Ethernet (MGBE/XPCS) IP found on > >> NVIDIA Tegra234 SoCs. > >> > >> Signed-off-by: Bhadram Varka <vbhadram@nvidia.com> > >> Signed-off-by: Thierry Reding <treding@nvidia.com> > >> --- > >> Note that this doesn't have any dependencies on any of the patches > >> earlier in the series, so this can be applied independently. > > > Patches 1-8 of this have already been applied to the Tegra tree. Are > > there any more comments on this or can this be merged as well? > > > > From a Tegra point of view this looks good, so: > > > > Acked-by: Thierry Reding <treding@nvidia.com> > > Acked-by: Jon Hunter <jonathanh@nvidia.com> > > Please can we queue this for v6.1? I have added the stmmac maintainers > to the email, but not sure if you can pick this up? Could you repost it independently of the series so that it can go thru the net auto-checkers? It should be able to make 6.1 pretty comfortably.
On Thu, Sep 22, 2022 at 08:34:54AM -0700, Jakub Kicinski wrote: > On Thu, 22 Sep 2022 16:05:22 +0100 Jon Hunter wrote: > > On 07/09/2022 17:36, Thierry Reding wrote: > > > On Thu, Jul 07, 2022 at 09:48:18AM +0200, Thierry Reding wrote: > > >> From: Bhadram Varka <vbhadram@nvidia.com> > > >> > > >> Add support for the Multi-Gigabit Ethernet (MGBE/XPCS) IP found on > > >> NVIDIA Tegra234 SoCs. > > >> > > >> Signed-off-by: Bhadram Varka <vbhadram@nvidia.com> > > >> Signed-off-by: Thierry Reding <treding@nvidia.com> > > >> --- > > >> Note that this doesn't have any dependencies on any of the patches > > >> earlier in the series, so this can be applied independently. > > > > > Patches 1-8 of this have already been applied to the Tegra tree. Are > > > there any more comments on this or can this be merged as well? > > > > > > From a Tegra point of view this looks good, so: > > > > > > Acked-by: Thierry Reding <treding@nvidia.com> > > > > Acked-by: Jon Hunter <jonathanh@nvidia.com> > > > > Please can we queue this for v6.1? I have added the stmmac maintainers > > to the email, but not sure if you can pick this up? > > Could you repost it independently of the series so that it can go thru > the net auto-checkers? It should be able to make 6.1 pretty comfortably. Done. Let me know if there's anything in the patch that needs more work. For the auto-checkers, do they send out notifications if they find anything or can I monitor them manually somewhere? Are all of those reported in the patchwork checks? Thierry
On Fri, 23 Sep 2022 14:39:49 +0200 Thierry Reding wrote: > > Could you repost it independently of the series so that it can go thru > > the net auto-checkers? It should be able to make 6.1 pretty comfortably. > > Done. Let me know if there's anything in the patch that needs more work. > For the auto-checkers, do they send out notifications if they find > anything or can I monitor them manually somewhere? Are all of those > reported in the patchwork checks? They are reported to patchwork, but they sometimes produce false positives so we don't expect folks to look at the themselves. The maintainers will reply to the submission and quote the failed checks as needed. Also note that we have a "at most one version per day" policy, if, say, there is a build issue discovered please hold off posting the new version for 24h. Frequent posting makes reviewing harder for folks who read the list top to bottom and increases ML traffic.
diff --git a/drivers/net/ethernet/stmicro/stmmac/Kconfig b/drivers/net/ethernet/stmicro/stmmac/Kconfig index 929cfc22cd0c..47af5a59ce88 100644 --- a/drivers/net/ethernet/stmicro/stmmac/Kconfig +++ b/drivers/net/ethernet/stmicro/stmmac/Kconfig @@ -232,6 +232,12 @@ config DWMAC_INTEL_PLAT the stmmac device driver. This driver is used for the Intel Keem Bay SoC. +config DWMAC_TEGRA + tristate "NVIDIA Tegra MGBE support" + depends on ARCH_TEGRA || COMPILE_TEST + help + Support for the MGBE controller found on Tegra SoCs. + config DWMAC_VISCONTI tristate "Toshiba Visconti DWMAC support" default ARCH_VISCONTI diff --git a/drivers/net/ethernet/stmicro/stmmac/Makefile b/drivers/net/ethernet/stmicro/stmmac/Makefile index d4e12e9ace4f..057e4bab5c08 100644 --- a/drivers/net/ethernet/stmicro/stmmac/Makefile +++ b/drivers/net/ethernet/stmicro/stmmac/Makefile @@ -31,6 +31,7 @@ obj-$(CONFIG_DWMAC_DWC_QOS_ETH) += dwmac-dwc-qos-eth.o obj-$(CONFIG_DWMAC_INTEL_PLAT) += dwmac-intel-plat.o obj-$(CONFIG_DWMAC_GENERIC) += dwmac-generic.o obj-$(CONFIG_DWMAC_IMX8) += dwmac-imx.o +obj-$(CONFIG_DWMAC_TEGRA) += dwmac-tegra.o obj-$(CONFIG_DWMAC_VISCONTI) += dwmac-visconti.o stmmac-platform-objs:= stmmac_platform.o dwmac-altr-socfpga-objs := altr_tse_pcs.o dwmac-socfpga.o diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-tegra.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-tegra.c new file mode 100644 index 000000000000..bb4b540820fa --- /dev/null +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-tegra.c @@ -0,0 +1,290 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include <linux/platform_device.h> +#include <linux/of_device.h> +#include <linux/module.h> +#include <linux/stmmac.h> +#include <linux/clk.h> + +#include "stmmac_platform.h" + +static const char *const mgbe_clks[] = { + "rx-pcs", "tx", "tx-pcs", "mac-divider", "mac", "mgbe", "ptp-ref", "mac" +}; + +struct tegra_mgbe { + struct device *dev; + + struct clk_bulk_data *clks; + + struct reset_control *rst_mac; + struct reset_control *rst_pcs; + + void __iomem *hv; + void __iomem *regs; + void __iomem *xpcs; + + struct mii_bus *mii; +}; + +#define XPCS_WRAP_UPHY_RX_CONTROL 0x801c +#define XPCS_WRAP_UPHY_RX_CONTROL_RX_SW_OVRD BIT(31) +#define XPCS_WRAP_UPHY_RX_CONTROL_RX_PCS_PHY_RDY BIT(10) +#define XPCS_WRAP_UPHY_RX_CONTROL_RX_CDR_RESET BIT(9) +#define XPCS_WRAP_UPHY_RX_CONTROL_RX_CAL_EN BIT(8) +#define XPCS_WRAP_UPHY_RX_CONTROL_RX_SLEEP (BIT(7) | BIT(6)) +#define XPCS_WRAP_UPHY_RX_CONTROL_AUX_RX_IDDQ BIT(5) +#define XPCS_WRAP_UPHY_RX_CONTROL_RX_IDDQ BIT(4) +#define XPCS_WRAP_UPHY_RX_CONTROL_RX_DATA_EN BIT(0) +#define XPCS_WRAP_UPHY_HW_INIT_CTRL 0x8020 +#define XPCS_WRAP_UPHY_HW_INIT_CTRL_TX_EN BIT(0) +#define XPCS_WRAP_UPHY_HW_INIT_CTRL_RX_EN BIT(2) +#define XPCS_WRAP_UPHY_STATUS 0x8044 +#define XPCS_WRAP_UPHY_STATUS_TX_P_UP BIT(0) +#define XPCS_WRAP_IRQ_STATUS 0x8050 +#define XPCS_WRAP_IRQ_STATUS_PCS_LINK_STS BIT(6) + +#define XPCS_REG_ADDR_SHIFT 10 +#define XPCS_REG_ADDR_MASK 0x1fff +#define XPCS_ADDR 0x3fc + +#define MGBE_WRAP_COMMON_INTR_ENABLE 0x8704 +#define MAC_SBD_INTR BIT(2) +#define MGBE_WRAP_AXI_ASID0_CTRL 0x8400 +#define MGBE_SID 0x6 + +static void mgbe_uphy_lane_bringup(struct tegra_mgbe *mgbe) +{ + unsigned int retry = 300; + u32 value; + int err; + + value = readl(mgbe->xpcs + XPCS_WRAP_UPHY_STATUS); + if ((value & XPCS_WRAP_UPHY_STATUS_TX_P_UP) == 0) { + value = readl(mgbe->xpcs + XPCS_WRAP_UPHY_HW_INIT_CTRL); + value |= XPCS_WRAP_UPHY_HW_INIT_CTRL_TX_EN; + writel(value, mgbe->xpcs + XPCS_WRAP_UPHY_HW_INIT_CTRL); + } + + err = readl_poll_timeout(mgbe->xpcs + XPCS_WRAP_UPHY_HW_INIT_CTRL, value, + (value & XPCS_WRAP_UPHY_HW_INIT_CTRL_TX_EN) == 0, + 500, 500 * 2000); + if (err < 0) + dev_err(mgbe->dev, "timeout waiting for TX lane to become enabled\n"); + + usleep_range(10000, 20000); + + value = readl(mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL); + value |= XPCS_WRAP_UPHY_RX_CONTROL_RX_SW_OVRD; + writel(value, mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL); + + value = readl(mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL); + value &= ~XPCS_WRAP_UPHY_RX_CONTROL_RX_IDDQ; + writel(value, mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL); + + value = readl(mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL); + value &= ~XPCS_WRAP_UPHY_RX_CONTROL_AUX_RX_IDDQ; + writel(value, mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL); + + value = readl(mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL); + value &= ~XPCS_WRAP_UPHY_RX_CONTROL_RX_SLEEP; + writel(value, mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL); + + value = readl(mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL); + value |= XPCS_WRAP_UPHY_RX_CONTROL_RX_CAL_EN; + writel(value, mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL); + + err = readl_poll_timeout(mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL, value, + (value & XPCS_WRAP_UPHY_RX_CONTROL_RX_CAL_EN) == 0, + 1000, 1000 * 2000); + if (err < 0) + dev_err(mgbe->dev, "timeout waiting for RX calibration to become enabled\n"); + + value = readl(mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL); + value |= XPCS_WRAP_UPHY_RX_CONTROL_RX_DATA_EN; + writel(value, mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL); + + value = readl(mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL); + value |= XPCS_WRAP_UPHY_RX_CONTROL_RX_CDR_RESET; + writel(value, mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL); + + value = readl(mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL); + value &= ~XPCS_WRAP_UPHY_RX_CONTROL_RX_CDR_RESET; + writel(value, mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL); + + value = readl(mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL); + value |= XPCS_WRAP_UPHY_RX_CONTROL_RX_PCS_PHY_RDY; + writel(value, mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL); + + while (--retry) { + err = readl_poll_timeout(mgbe->xpcs + XPCS_WRAP_IRQ_STATUS, value, + value & XPCS_WRAP_IRQ_STATUS_PCS_LINK_STS, + 500, 500 * 2000); + if (err < 0) { + dev_err(mgbe->dev, "timeout waiting for link to become ready\n"); + usleep_range(10000, 20000); + continue; + } + break; + } + + /* clear status */ + writel(value, mgbe->xpcs + XPCS_WRAP_IRQ_STATUS); +} + +static int tegra_mgbe_probe(struct platform_device *pdev) +{ + struct plat_stmmacenet_data *plat; + struct stmmac_resources res; + struct tegra_mgbe *mgbe; + int irq, err, i; + + mgbe = devm_kzalloc(&pdev->dev, sizeof(*mgbe), GFP_KERNEL); + if (!mgbe) + return -ENOMEM; + + mgbe->dev = &pdev->dev; + + memset(&res, 0, sizeof(res)); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + mgbe->hv = devm_platform_ioremap_resource_byname(pdev, "hypervisor"); + if (IS_ERR(mgbe->hv)) + return PTR_ERR(mgbe->hv); + + mgbe->regs = devm_platform_ioremap_resource_byname(pdev, "mac"); + if (IS_ERR(mgbe->regs)) + return PTR_ERR(mgbe->regs); + + mgbe->xpcs = devm_platform_ioremap_resource_byname(pdev, "xpcs"); + if (IS_ERR(mgbe->xpcs)) + return PTR_ERR(mgbe->xpcs); + + res.addr = mgbe->regs; + res.irq = irq; + + mgbe->clks = devm_kzalloc(&pdev->dev, sizeof(*mgbe->clks), GFP_KERNEL); + if (!mgbe->clks) + return -ENOMEM; + + for (i = 0; i < ARRAY_SIZE(mgbe_clks); i++) + mgbe->clks[i].id = mgbe_clks[i]; + + err = devm_clk_bulk_get(mgbe->dev, ARRAY_SIZE(mgbe_clks), mgbe->clks); + if (err < 0) + return err; + + err = clk_bulk_prepare_enable(ARRAY_SIZE(mgbe_clks), mgbe->clks); + if (err < 0) + return err; + + /* Perform MAC reset */ + mgbe->rst_mac = devm_reset_control_get(&pdev->dev, "mac"); + if (IS_ERR(mgbe->rst_mac)) + return PTR_ERR(mgbe->rst_mac); + + err = reset_control_assert(mgbe->rst_mac); + if (err < 0) + return err; + + usleep_range(2000, 4000); + + err = reset_control_deassert(mgbe->rst_mac); + if (err < 0) + return err; + + /* Perform PCS reset */ + mgbe->rst_pcs = devm_reset_control_get(&pdev->dev, "pcs"); + if (IS_ERR(mgbe->rst_pcs)) + return PTR_ERR(mgbe->rst_pcs); + + err = reset_control_assert(mgbe->rst_pcs); + if (err < 0) + return err; + + usleep_range(2000, 4000); + + err = reset_control_deassert(mgbe->rst_pcs); + if (err < 0) + return err; + + plat = stmmac_probe_config_dt(pdev, res.mac); + if (IS_ERR(plat)) + return PTR_ERR(plat); + + plat->has_xgmac = 1; + plat->tso_en = 1; + plat->pmt = 1; + plat->bsp_priv = mgbe; + + if (!plat->mdio_node) + plat->mdio_node = of_get_child_by_name(pdev->dev.of_node, "mdio"); + + if (!plat->mdio_bus_data) { + plat->mdio_bus_data = devm_kzalloc(&pdev->dev, sizeof(*plat->mdio_bus_data), + GFP_KERNEL); + if (!plat->mdio_bus_data) { + err = -ENOMEM; + goto remove; + } + } + + plat->mdio_bus_data->needs_reset = true; + + mgbe_uphy_lane_bringup(mgbe); + + /* Tx FIFO Size - 128KB */ + plat->tx_fifo_size = 131072; + /* Rx FIFO Size - 192KB */ + plat->rx_fifo_size = 196608; + + /* Enable common interrupt at wrapper level */ + writel(MAC_SBD_INTR, mgbe->regs + MGBE_WRAP_COMMON_INTR_ENABLE); + + /* Program SID */ + writel(MGBE_SID, mgbe->hv + MGBE_WRAP_AXI_ASID0_CTRL); + + err = stmmac_dvr_probe(&pdev->dev, plat, &res); + if (err < 0) + goto remove; + + return 0; + +remove: + stmmac_remove_config_dt(pdev, plat); + return err; +} + +static int tegra_mgbe_remove(struct platform_device *pdev) +{ + struct tegra_mgbe *mgbe = get_stmmac_bsp_priv(&pdev->dev); + + clk_bulk_disable_unprepare(ARRAY_SIZE(mgbe_clks), mgbe->clks); + + stmmac_pltfr_remove(pdev); + + return 0; +} + +static const struct of_device_id tegra_mgbe_match[] = { + { .compatible = "nvidia,tegra234-mgbe", }, + { } +}; +MODULE_DEVICE_TABLE(of, tegra_mgbe_match); + +static struct platform_driver tegra_mgbe_driver = { + .probe = tegra_mgbe_probe, + .remove = tegra_mgbe_remove, + .driver = { + .name = "tegra-mgbe", + .pm = &stmmac_pltfr_pm_ops, + .of_match_table = tegra_mgbe_match, + }, +}; +module_platform_driver(tegra_mgbe_driver); + +MODULE_AUTHOR("Thierry Reding <treding@nvidia.com>"); +MODULE_DESCRIPTION("NVIDIA Tegra MGBE driver"); +MODULE_LICENSE("GPL");