From patchwork Thu Mar 14 14:01:34 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jerome Brunet X-Patchwork-Id: 160333 Delivered-To: patch@linaro.org Received: by 2002:a02:5cc1:0:0:0:0:0 with SMTP id w62csp15547404jad; Thu, 14 Mar 2019 07:02:11 -0700 (PDT) X-Google-Smtp-Source: APXvYqx/oQSNQ1i9m5AfG/xad6aM+p7lEYOKIVnVYWf7ovoPvPAxuPMkjDukhi6ScNfK21KP+i99 X-Received: by 2002:a63:1a12:: with SMTP id a18mr24897238pga.200.1552572131095; Thu, 14 Mar 2019 07:02:11 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1552572131; cv=none; d=google.com; s=arc-20160816; b=CkoWs5UMDiq6r9KqrgLen/+5gpND1oX1BbidppTGrxvJcpv5MpECxxofMT8knYL+GN hsK4WKaWiNIOqnoniaYFhhdHsSGpH0TVMKmu3QFAJpAH4KBeqdAXFon2OId07WsLh6YI Dbty6EIjfEzriReooPb/0JSUUrLNayaO3FZuzE3RFzru8CEZfY6u3YL1SLv9o6S2GGSb U5C2JrtoSKFe41N1eza3YWEjFQvWmbX8wkGIz8HrNm94s2gTEZmadFTfL16X3rhBBGVc dpsNtKG0zh4vtAAZayLOb8xfbHxJMhVcRINdLzc107XUh16BKkNOrrCMVkWh4NMWYjJQ pViA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature; bh=o8hkwStb4arPJdoRuGRS4yit7i2bvsVJxinFa9oo8k4=; b=E+KgJpBLhWS0KSzL2CZe2sMd4SQFKOuePbPIiNLn2f7MxxZQMil9ykQNZpVfmm72X2 WI/rxBgDAanAQy9fJMyNaKl4CfQnzsc3ZmdiOjxWT6CNqCx1oj1wSXrYjnAzeeE0Rljj +7lKa2RyupHus7NHpbRAo4sGFwRvOdeT3S6AND5Xh8LkmJNsYFogYs1OIV1qtkJq7p8/ TIgvbfG6W3j2+qpTW2ypRT7xSk1A+P1LKSQwRb7dKIquuXOH9nf3H81wpdJaNRlzgpMs xsFfK1Bh7cpSIi34mdiDCwwaWeuTVbC03HqRCeUnJc1axWjQCC4g+32J3iN5Fyadlucn 7chw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@baylibre-com.20150623.gappssmtp.com header.s=20150623 header.b=UE6Fs1bM; spf=pass (google.com: best guess record for domain of netdev-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=netdev-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 f21si11906914pgv.28.2019.03.14.07.02.10; Thu, 14 Mar 2019 07:02:11 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of netdev-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@baylibre-com.20150623.gappssmtp.com header.s=20150623 header.b=UE6Fs1bM; spf=pass (google.com: best guess record for domain of netdev-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=netdev-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727715AbfCNOCB (ORCPT + 10 others); Thu, 14 Mar 2019 10:02:01 -0400 Received: from mail-wm1-f68.google.com ([209.85.128.68]:33159 "EHLO mail-wm1-f68.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727705AbfCNOB7 (ORCPT ); Thu, 14 Mar 2019 10:01:59 -0400 Received: by mail-wm1-f68.google.com with SMTP id c13so6928891wmb.0 for ; Thu, 14 Mar 2019 07:01:57 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=baylibre-com.20150623.gappssmtp.com; s=20150623; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=o8hkwStb4arPJdoRuGRS4yit7i2bvsVJxinFa9oo8k4=; b=UE6Fs1bMujjiKWH/OsWrchpDUI5PVjkSqQ9EzCprdG+dP6l8W+qyRzYu2xbbymlKmT BSAed8ieNayIy8wOVss3wR6YYb211Dvbtp4Q7AnadVFqlc0qMGR4A6BATUoDlvzteSPE /kcP+I9z4CV/0+aVkHaKWPZgvuIhCpu9QQNHUIApUCPXdhklBY8TPKsHy+WlAVF1SvW0 Scl8Y+rJtZv0ajs9IHrE4t10OzUsDr+sy/bi28jdm8R1slk7MpsFVfRBr4TAEgqRD6Lx EhRWrS+f8SToaXTkJcdAry1JK8RLXE6MAPRrzzTqI7q+xkoVrKaGupOkUc7IChgAVxfG cZ3A== 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:mime-version:content-transfer-encoding; bh=o8hkwStb4arPJdoRuGRS4yit7i2bvsVJxinFa9oo8k4=; b=E5b/4f9P/4at/BmFN3NDoBL1+TlQSd9qJuERD+lbaTGkFKk9KqyprqKcnahHkWZOM7 BH3g91VGvtM418B41d1gEtpAqVj4VjxSFFEoJoUqNa9V/nFW9g/Obs7HC++cd53ppESu hJQUT5ST2AGKh/tHDZ6xrD5oNnUp1gEwhGVtZ/eZUNQqpnXB1Sk/IApToVktXLD08gcK TCAnJXwyG4AVJYfI28gXE0peYWemAuvvEu/pZhFAAsprKcrBa0nx34jKeXLCqDy04bHJ 570/uYdTGG7ahi9ZxcK86YgBZZk2izqtm5YwTT1U0mb7uGFkR2y5c1oOBK6qAgTUu7wv 6UFA== X-Gm-Message-State: APjAAAXKpuN2XOidUSKKk+kgfvnNfrLMe09gd64WfBP+63FZiT3+fRRp 4yX3w3rtp+kDWrepxevI4q2BdA== X-Received: by 2002:a1c:c60e:: with SMTP id w14mr2670501wmf.146.1552572116925; Thu, 14 Mar 2019 07:01:56 -0700 (PDT) Received: from boomer.local ([2a01:e34:eeb6:4690:106b:bae3:31ed:7561]) by smtp.googlemail.com with ESMTPSA id y7sm2661051wmi.34.2019.03.14.07.01.54 (version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256); Thu, 14 Mar 2019 07:01:56 -0700 (PDT) From: Jerome Brunet To: Andrew Lunn , Florian Fainelli , Heiner Kallweit , "David S. Miller" , Kevin Hilman Cc: Jerome Brunet , netdev@vger.kernel.org, linux-amlogic@lists.infradead.org, linux-kernel@vger.kernel.org, devicetree@vger.kernel.org Subject: [PATCH net-next 2/3] net: phy: add amlogic g12a mdio mux support Date: Thu, 14 Mar 2019 15:01:34 +0100 Message-Id: <20190314140135.19184-3-jbrunet@baylibre.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20190314140135.19184-1-jbrunet@baylibre.com> References: <20190314140135.19184-1-jbrunet@baylibre.com> MIME-Version: 1.0 Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Add support for the mdio mux and internal phy glue of the g12a SoC family Signed-off-by: Jerome Brunet --- drivers/net/phy/Kconfig | 10 + drivers/net/phy/Makefile | 1 + drivers/net/phy/mdio-mux-meson-g12a.c | 371 ++++++++++++++++++++++++++ 3 files changed, 382 insertions(+) create mode 100644 drivers/net/phy/mdio-mux-meson-g12a.c -- 2.20.1 diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index 071869db44cf..831aa350b1cb 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -74,6 +74,16 @@ config MDIO_BUS_MUX_GPIO several child MDIO busses to a parent bus. Child bus selection is under the control of GPIO lines. +config MDIO_BUS_MUX_MESON_G12A + tristate "Amlogic G12a based MDIO bus multiplexer" + depends on ARCH_MESON || COMPILE_TEST + depends on OF_MDIO && HAS_IOMEM + select MDIO_BUS_MUX + help + This module provides a driver for the MDIO multiplexer/glue of + the amlogic g12a SoC. The multiplexers connects either the external + or the internal MDIO bus to the parent bus. + config MDIO_BUS_MUX_MMIOREG tristate "MMIO device-controlled MDIO bus multiplexers" depends on OF_MDIO && HAS_IOMEM diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index ece5dae67174..27d7f9f3b0de 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -28,6 +28,7 @@ obj-$(CONFIG_MDIO_BITBANG) += mdio-bitbang.o obj-$(CONFIG_MDIO_BUS_MUX) += mdio-mux.o obj-$(CONFIG_MDIO_BUS_MUX_BCM_IPROC) += mdio-mux-bcm-iproc.o obj-$(CONFIG_MDIO_BUS_MUX_GPIO) += mdio-mux-gpio.o +obj-$(CONFIG_MDIO_BUS_MUX_MESON_G12A) += mdio-mux-meson-g12a.o obj-$(CONFIG_MDIO_BUS_MUX_MMIOREG) += mdio-mux-mmioreg.o obj-$(CONFIG_MDIO_BUS_MUX_MULTIPLEXER) += mdio-mux-multiplexer.o obj-$(CONFIG_MDIO_CAVIUM) += mdio-cavium.o diff --git a/drivers/net/phy/mdio-mux-meson-g12a.c b/drivers/net/phy/mdio-mux-meson-g12a.c new file mode 100644 index 000000000000..9da681cce7cd --- /dev/null +++ b/drivers/net/phy/mdio-mux-meson-g12a.c @@ -0,0 +1,371 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2019 Baylibre, SAS. + * Author: Jerome Brunet + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ETH_PLL_STS 0x40 +#define ETH_PLL_CTL0 0x44 +#define PLL_CTL0_LOCK_DIG BIT(30) +#define PLL_CTL0_RST BIT(29) +#define PLL_CTL0_EN BIT(28) +#define PLL_CTL0_SEL BIT(23) +#define PLL_CTL0_N GENMASK(14, 10) +#define PLL_CTL0_M GENMASK(8, 0) +#define PLL_LOCK_TIMEOUT 1000000 +#define PLL_MUX_NUM_PARENT 2 +#define ETH_PLL_CTL1 0x48 +#define ETH_PLL_CTL2 0x4c +#define ETH_PLL_CTL3 0x50 +#define ETH_PLL_CTL4 0x54 +#define ETH_PLL_CTL5 0x58 +#define ETH_PLL_CTL6 0x5c +#define ETH_PLL_CTL7 0x60 + +#define ETH_PHY_CNTL0 0x80 +#define EPHY_G12A_ID 0x33000180 +#define ETH_PHY_CNTL1 0x84 +#define PHY_CNTL1_ST_MODE GENMASK(2, 0) +#define PHY_CNTL1_ST_PHYADD GENMASK(7, 3) +#define EPHY_DFLT_ADD 8 +#define PHY_CNTL1_MII_MODE GENMASK(15, 14) +#define EPHY_MODE_RMII 0x1 +#define PHY_CNTL1_CLK_EN BIT(16) +#define PHY_CNTL1_CLKFREQ BIT(17) +#define PHY_CNTL1_PHY_ENB BIT(18) +#define ETH_PHY_CNTL2 0x88 +#define PHY_CNTL2_USE_INTERNAL BIT(5) +#define PHY_CNTL2_SMI_SRC_MAC BIT(6) +#define PHY_CNTL2_RX_CLK_EPHY BIT(9) + +#define MESON_G12A_MDIO_EXTERNAL_ID 0 +#define MESON_G12A_MDIO_INTERNAL_ID 1 + +struct g12a_mdio_mux { + void __iomem *regs; + struct clk *pclk; + struct clk *pll; + bool pll_is_enabled; + void *mux_handle; +}; + +struct g12a_ephy_pll { + struct clk_hw hw; + void __iomem *base; +}; + +#define g12a_ephy_pll_to_dev(_hw) \ + container_of(_hw, struct g12a_ephy_pll, hw) \ + +static unsigned long g12a_ephy_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct g12a_ephy_pll *pll = g12a_ephy_pll_to_dev(hw); + u32 val, m, n; + + val = readl(pll->base + ETH_PLL_CTL0); + m = FIELD_GET(PLL_CTL0_M, val); + n = FIELD_GET(PLL_CTL0_N, val); + + return parent_rate * m / n; +} + +static int g12a_ephy_pll_enable(struct clk_hw *hw) +{ + struct g12a_ephy_pll *pll = g12a_ephy_pll_to_dev(hw); + u32 val = readl(pll->base + ETH_PLL_CTL0); + + /* Apply both enable an reset */ + val |= PLL_CTL0_RST | PLL_CTL0_EN; + writel(val, pll->base + ETH_PLL_CTL0); + + /* Clear the reset to let PLL lock */ + val &= ~PLL_CTL0_RST; + writel(val, pll->base + ETH_PLL_CTL0); + + /* Poll on the digital lock instead of the usual analog lock + * This is done because bit 31 is unreliable on some SoC. Bit + * 31 may indicate that the PLL is not lock eventhough the clock + * is actually running + */ + return readl_poll_timeout(pll->base + ETH_PLL_CTL0, val, + val & PLL_CTL0_LOCK_DIG, 0, PLL_LOCK_TIMEOUT); +} + +static void g12a_ephy_pll_disable(struct clk_hw *hw) +{ + struct g12a_ephy_pll *pll = g12a_ephy_pll_to_dev(hw); + u32 val; + + val = readl(pll->base + ETH_PLL_CTL0); + val &= ~PLL_CTL0_EN; + val |= PLL_CTL0_RST; + writel(val, pll->base + ETH_PLL_CTL0); +} + +static int g12a_ephy_pll_is_enabled(struct clk_hw *hw) +{ + struct g12a_ephy_pll *pll = g12a_ephy_pll_to_dev(hw); + unsigned int val; + + val = readl(pll->base + ETH_PLL_CTL0); + + return (val & PLL_CTL0_LOCK_DIG) ? 1 : 0; +} + +static void g12a_ephy_pll_init(struct clk_hw *hw) +{ + struct g12a_ephy_pll *pll = g12a_ephy_pll_to_dev(hw); + + /* Apply PLL HW settings */ + writel(0x29c0040a, pll->base + ETH_PLL_CTL0); + writel(0x927e0000, pll->base + ETH_PLL_CTL1); + writel(0xac5f49e5, pll->base + ETH_PLL_CTL2); + writel(0x00000000, pll->base + ETH_PLL_CTL3); + writel(0x00000000, pll->base + ETH_PLL_CTL4); + writel(0x20200000, pll->base + ETH_PLL_CTL5); + writel(0x0000c002, pll->base + ETH_PLL_CTL6); + writel(0x00000023, pll->base + ETH_PLL_CTL7); +} + +static const struct clk_ops g12a_ephy_pll_ops = { + .recalc_rate = g12a_ephy_pll_recalc_rate, + .is_enabled = g12a_ephy_pll_is_enabled, + .enable = g12a_ephy_pll_enable, + .disable = g12a_ephy_pll_disable, + .init = g12a_ephy_pll_init, +}; + +static int _g12a_enable_internal_mdio(struct g12a_mdio_mux *priv) +{ + int ret; + + /* Enable the phy clock */ + if (!priv->pll_is_enabled) { + ret = clk_prepare_enable(priv->pll); + if (ret) + return ret; + } + + priv->pll_is_enabled = true; + + /* Initialize ephy control */ + writel(EPHY_G12A_ID, priv->regs + ETH_PHY_CNTL0); + writel(FIELD_PREP(PHY_CNTL1_ST_MODE, 3) | + FIELD_PREP(PHY_CNTL1_ST_PHYADD, EPHY_DFLT_ADD) | + FIELD_PREP(PHY_CNTL1_MII_MODE, EPHY_MODE_RMII) | + PHY_CNTL1_CLK_EN | + PHY_CNTL1_CLKFREQ | + PHY_CNTL1_PHY_ENB, + priv->regs + ETH_PHY_CNTL1); + writel(PHY_CNTL2_USE_INTERNAL | + PHY_CNTL2_SMI_SRC_MAC | + PHY_CNTL2_RX_CLK_EPHY, + priv->regs + ETH_PHY_CNTL2); + + return 0; +} + +static int _g12a_enable_external_mdio(struct g12a_mdio_mux *priv) +{ + /* Reset the mdio bus mux */ + writel_relaxed(0x0, priv->regs + ETH_PHY_CNTL2); + + /* Disable the phy clock if enabled */ + if (priv->pll_is_enabled) { + clk_disable_unprepare(priv->pll); + priv->pll_is_enabled = false; + } + + return 0; +} + +static int g12a_mdio_switch_fn(int current_child, int desired_child, + void *data) +{ + struct device *dev = data; + struct g12a_mdio_mux *priv = dev_get_drvdata(dev); + + if (current_child == desired_child) + return 0; + + switch (desired_child) { + case MESON_G12A_MDIO_EXTERNAL_ID: + return _g12a_enable_external_mdio(priv); + case MESON_G12A_MDIO_INTERNAL_ID: + return _g12a_enable_internal_mdio(priv); + default: + return -EINVAL; + } +} + +static const struct of_device_id g12a_mdio_mux_match[] = { + { .compatible = "amlogic,g12a-mdio-mux", }, + {}, +}; +MODULE_DEVICE_TABLE(of, g12a_mdio_mux_match); + +static int g12a_ephy_glue_clk_register(struct device *dev) +{ + struct g12a_mdio_mux *priv = dev_get_drvdata(dev); + const char *parent_names[PLL_MUX_NUM_PARENT]; + struct clk_init_data init; + struct clk_mux *mux; + struct g12a_ephy_pll *pll; + struct clk *clk; + char *name; + int i; + + /* get the mux parents */ + for (i = 0; i < PLL_MUX_NUM_PARENT; i++) { + char in_name[8]; + + snprintf(in_name, sizeof(in_name), "clkin%d", i); + clk = devm_clk_get(dev, in_name); + if (IS_ERR(clk)) { + if (PTR_ERR(clk) != -EPROBE_DEFER) + dev_err(dev, "Missing clock %s\n", in_name); + return PTR_ERR(clk); + } + + parent_names[i] = __clk_get_name(clk); + } + + /* create the input mux */ + mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL); + if (!mux) + return -ENOMEM; + + name = kasprintf(GFP_KERNEL, "%s#mux", dev_name(dev)); + if (!name) + return -ENOMEM; + + init.name = name; + init.ops = &clk_mux_ro_ops; + init.flags = 0; + init.parent_names = parent_names; + init.num_parents = PLL_MUX_NUM_PARENT; + + mux->reg = priv->regs + ETH_PLL_CTL0; + mux->shift = __ffs(PLL_CTL0_SEL); + mux->mask = PLL_CTL0_SEL >> mux->shift; + mux->hw.init = &init; + + clk = devm_clk_register(dev, &mux->hw); + kfree(name); + if (IS_ERR(clk)) { + dev_err(dev, "failed to register input mux\n"); + return PTR_ERR(clk); + } + + /* create the pll */ + pll = devm_kzalloc(dev, sizeof(*pll), GFP_KERNEL); + if (!pll) + return -ENOMEM; + + name = kasprintf(GFP_KERNEL, "%s#pll", dev_name(dev)); + if (!name) + return -ENOMEM; + + init.name = name; + init.ops = &g12a_ephy_pll_ops; + init.flags = 0; + parent_names[0] = __clk_get_name(clk); + init.parent_names = parent_names; + init.num_parents = 1; + + pll->base = priv->regs; + pll->hw.init = &init; + + clk = devm_clk_register(dev, &pll->hw); + kfree(name); + if (IS_ERR(clk)) { + dev_err(dev, "failed to register input mux\n"); + return PTR_ERR(clk); + } + + priv->pll = clk; + + return 0; +} + +static int g12a_mdio_mux_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct resource *res; + struct g12a_mdio_mux *priv; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + platform_set_drvdata(pdev, priv); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->regs = devm_ioremap_resource(dev, res); + if (IS_ERR(priv->regs)) + return PTR_ERR(priv->regs); + + priv->pclk = devm_clk_get(dev, "pclk"); + if (IS_ERR(priv->pclk)) { + ret = PTR_ERR(priv->pclk); + if (ret != -EPROBE_DEFER) + dev_err(dev, "failed to get peripheral clock\n"); + return ret; + } + + /* Make sure the device registers are clocked */ + ret = clk_prepare_enable(priv->pclk); + if (ret) { + dev_err(dev, "failed to enable peripheral clock"); + return ret; + } + + /* Register PLL in CCF */ + ret = g12a_ephy_glue_clk_register(dev); + if (ret) + return ret; + + return mdio_mux_init(dev, dev->of_node, g12a_mdio_switch_fn, + &priv->mux_handle, dev, NULL); +} + +static int g12a_mdio_mux_remove(struct platform_device *pdev) +{ + struct g12a_mdio_mux *priv = platform_get_drvdata(pdev); + + mdio_mux_uninit(priv->mux_handle); + + if (priv->pll_is_enabled) + clk_disable_unprepare(priv->pll); + + clk_disable_unprepare(priv->pclk); + + return 0; +} + +static struct platform_driver g12a_mdio_mux_driver = { + .probe = g12a_mdio_mux_probe, + .remove = g12a_mdio_mux_remove, + .driver = { + .name = "g12a-mdio_mux", + .of_match_table = g12a_mdio_mux_match, + }, +}; +module_platform_driver(g12a_mdio_mux_driver); + +MODULE_DESCRIPTION("Amlogic G12a MDIO multiplexer driver"); +MODULE_AUTHOR("Jerome Brunet "); +MODULE_LICENSE("GPL v2");