From patchwork Thu Jun 20 14:48:25 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alexandru Marginean X-Patchwork-Id: 1119496 X-Patchwork-Delegate: prabhakar@freescale.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.denx.de (client-ip=81.169.180.215; helo=lists.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=nxp.com Received: from lists.denx.de (dione.denx.de [81.169.180.215]) by ozlabs.org (Postfix) with ESMTP id 45V4VL4bg2z9s7h for ; Fri, 21 Jun 2019 00:50:02 +1000 (AEST) Received: by lists.denx.de (Postfix, from userid 105) id 2444EC21DD9; Thu, 20 Jun 2019 14:49:12 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on lists.denx.de X-Spam-Level: X-Spam-Status: No, score=0.0 required=5.0 tests=none autolearn=unavailable autolearn_force=no version=3.4.0 Received: from lists.denx.de (localhost [IPv6:::1]) by lists.denx.de (Postfix) with ESMTP id 60718C21DB6; Thu, 20 Jun 2019 14:48:44 +0000 (UTC) Received: by lists.denx.de (Postfix, from userid 105) id 51CE7C21BE5; Thu, 20 Jun 2019 14:48:42 +0000 (UTC) Received: from inva021.nxp.com (inva021.nxp.com [92.121.34.21]) by lists.denx.de (Postfix) with ESMTPS id 1CF85C21C38 for ; Thu, 20 Jun 2019 14:48:42 +0000 (UTC) Received: from inva021.nxp.com (localhost [127.0.0.1]) by inva021.eu-rdc02.nxp.com (Postfix) with ESMTP id E44D22001DD; Thu, 20 Jun 2019 16:48:41 +0200 (CEST) Received: from inva024.eu-rdc02.nxp.com (inva024.eu-rdc02.nxp.com [134.27.226.22]) by inva021.eu-rdc02.nxp.com (Postfix) with ESMTP id D6CB6200086; Thu, 20 Jun 2019 16:48:41 +0200 (CEST) Received: from fsr-ub1864-115.ea.freescale.net (fsr-ub1864-115.ea.freescale.net [10.171.82.26]) by inva024.eu-rdc02.nxp.com (Postfix) with ESMTP id A6AA120604; Thu, 20 Jun 2019 16:48:41 +0200 (CEST) From: Alex Marginean To: u-boot@lists.denx.de Date: Thu, 20 Jun 2019 17:48:25 +0300 Message-Id: <20190620144828.11899-4-alexandru.marginean@nxp.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20190620144828.11899-1-alexandru.marginean@nxp.com> References: <20190620144828.11899-1-alexandru.marginean@nxp.com> X-Virus-Scanned: ClamAV using ClamSMTP Cc: Joe Hershberger Subject: [U-Boot] [PATCH 3/6 v3] drivers: net: add NXP ENETC MDIO driver X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.18 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" Adds a driver for the MDIO interface currently integrated in LS1028A SoC. This MDIO interface is shared by multiple ethernet interfaces and is presented as a stand-alone PCI function on the SoC ECAM. Ethernet has a functional dependency on MDIO, for simplicity there is a single config option for both. Signed-off-by: Alex Marginean Reviewed-by: Bin Meng --- drivers/net/Kconfig | 2 +- drivers/net/Makefile | 2 +- drivers/net/fsl_enetc.c | 57 ++++++++++++++ drivers/net/fsl_enetc.h | 21 +++++ drivers/net/fsl_enetc_mdio.c | 149 +++++++++++++++++++++++++++++++++++ 5 files changed, 229 insertions(+), 2 deletions(-) create mode 100644 drivers/net/fsl_enetc_mdio.c diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 802eadf99d..4d85fb1716 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -590,7 +590,7 @@ config HIGMACV300_ETH config FSL_ENETC bool "NXP ENETC Ethernet controller" - depends on DM_PCI && DM_ETH + depends on DM_PCI && DM_ETH && DM_MDIO help This driver supports the NXP ENETC Ethernet controller found on some of the NXP SoCs. diff --git a/drivers/net/Makefile b/drivers/net/Makefile index b9a6bd6297..282d6d220f 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -79,4 +79,4 @@ obj-y += mscc_eswitch/ obj-$(CONFIG_HIGMACV300_ETH) += higmacv300.o obj-$(CONFIG_MDIO_SANDBOX) += mdio_sandbox.o obj-$(CONFIG_MDIO_MUX_SANDBOX) += mdio_mux_sandbox.o -obj-$(CONFIG_FSL_ENETC) += fsl_enetc.o +obj-$(CONFIG_FSL_ENETC) += fsl_enetc.o fsl_enetc_mdio.o diff --git a/drivers/net/fsl_enetc.c b/drivers/net/fsl_enetc.c index 58ba63ceb1..f14b484b26 100644 --- a/drivers/net/fsl_enetc.c +++ b/drivers/net/fsl_enetc.c @@ -10,6 +10,7 @@ #include #include #include +#include #include "fsl_enetc.h" @@ -38,6 +39,59 @@ static int enetc_bind(struct udevice *dev) return 0; } +/* Configure the actual/external ethernet PHY, if one is found */ +static void enetc_start_phy(struct udevice *dev) +{ + struct enetc_priv *priv = dev_get_priv(dev); + struct udevice *miidev; + struct phy_device *phy; + u32 phandle, phy_id; + ofnode phy_node; + int supported; + + if (!ofnode_valid(dev->node)) { + enetc_dbg(dev, "no enetc ofnode found, skipping PHY set-up\n"); + return; + } + + if (ofnode_read_u32(dev->node, "phy-handle", &phandle)) { + enetc_dbg(dev, "phy-handle not found, skipping PHY set-up\n"); + return; + } + + phy_node = ofnode_get_by_phandle(phandle); + if (!ofnode_valid(phy_node)) { + enetc_dbg(dev, "invalid phy node, skipping PHY set-up\n"); + return; + } + enetc_dbg(dev, "phy node: %s\n", ofnode_get_name(phy_node)); + + if (ofnode_read_u32(phy_node, "reg", &phy_id)) { + enetc_dbg(dev, + "missing reg in PHY node, skipping PHY set-up\n"); + return; + } + + if (uclass_get_device_by_ofnode(UCLASS_MDIO, + ofnode_get_parent(phy_node), + &miidev)) { + enetc_dbg(dev, "can't find MDIO bus for node %s\n", + ofnode_get_name(ofnode_get_parent(phy_node))); + return; + } + + phy = dm_mdio_phy_connect(miidev, phy_id, dev, priv->if_type); + if (!phy) { + enetc_dbg(dev, "dm_mdio_phy_connect returned null\n"); + return; + } + + supported = GENMASK(6, 0); /* speeds up to 1G & AN */ + phy->advertising = phy->supported & supported; + phy_config(phy); + phy_startup(phy); +} + /* * Probe ENETC driver: * - initialize port and station interface BARs @@ -249,6 +303,9 @@ static int enetc_start(struct udevice *dev) enetc_setup_tx_bdr(dev); enetc_setup_rx_bdr(dev); + priv->if_type = PHY_INTERFACE_MODE_NONE; + enetc_start_phy(dev); + return 0; } diff --git a/drivers/net/fsl_enetc.h b/drivers/net/fsl_enetc.h index 155ecc895b..fbb9dfa15e 100644 --- a/drivers/net/fsl_enetc.h +++ b/drivers/net/fsl_enetc.h @@ -11,6 +11,7 @@ /* PCI function IDs */ #define PCI_DEVICE_ID_ENETC_ETH 0xE100 +#define PCI_DEVICE_ID_ENETC_MDIO 0xEE01 /* ENETC Ethernet controller registers */ /* Station interface register offsets */ @@ -143,6 +144,8 @@ struct enetc_priv { /* Rx/Tx buffer descriptor rings info */ struct bd_ring tx_bdr; struct bd_ring rx_bdr; + + int if_type; }; /* register accessors */ @@ -165,4 +168,22 @@ struct enetc_priv { #define enetc_bdr_write(priv, t, n, off, val) \ enetc_write(priv, ENETC_BDR(t, n, off), val) +/* ENETC external MDIO registers */ +#define ENETC_MDIO_BASE 0x1c00 +#define ENETC_MDIO_CFG 0x00 +#define ENETC_EMDIO_CFG_C22 0x00809508 +#define ENETC_EMDIO_CFG_C45 0x00809548 +#define ENETC_EMDIO_CFG_RD_ER BIT(1) +#define ENETC_EMDIO_CFG_BSY BIT(0) +#define ENETC_MDIO_CTL 0x04 +#define ENETC_MDIO_CTL_READ BIT(15) +#define ENETC_MDIO_DATA 0x08 +#define ENETC_MDIO_STAT 0x0c + +#define ENETC_MDIO_READ_ERR 0xffff + +struct enetc_mdio_priv { + void *regs_base; +}; + #endif /* _ENETC_H */ diff --git a/drivers/net/fsl_enetc_mdio.c b/drivers/net/fsl_enetc_mdio.c new file mode 100644 index 0000000000..46afac06d7 --- /dev/null +++ b/drivers/net/fsl_enetc_mdio.c @@ -0,0 +1,149 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * ENETC ethernet controller driver + * Copyright 2019 NXP + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fsl_enetc.h" + +static void enetc_mdio_wait_bsy(struct enetc_mdio_priv *priv) +{ + while (enetc_read(priv, ENETC_MDIO_CFG) & ENETC_EMDIO_CFG_BSY) + cpu_relax(); +} + +static int enetc_mdio_read_priv(struct enetc_mdio_priv *priv, int addr, + int devad, int reg) +{ + if (devad == MDIO_DEVAD_NONE) + enetc_write(priv, ENETC_MDIO_CFG, ENETC_EMDIO_CFG_C22); + else + enetc_write(priv, ENETC_MDIO_CFG, ENETC_EMDIO_CFG_C45); + enetc_mdio_wait_bsy(priv); + + if (devad == MDIO_DEVAD_NONE) { + enetc_write(priv, ENETC_MDIO_CTL, ENETC_MDIO_CTL_READ | + (addr << 5) | reg); + } else { + enetc_write(priv, ENETC_MDIO_CTL, (addr << 5) + devad); + enetc_mdio_wait_bsy(priv); + + enetc_write(priv, ENETC_MDIO_STAT, reg); + enetc_mdio_wait_bsy(priv); + + enetc_write(priv, ENETC_MDIO_CTL, ENETC_MDIO_CTL_READ | + (addr << 5) | devad); + } + + enetc_mdio_wait_bsy(priv); + if (enetc_read(priv, ENETC_MDIO_CFG) & ENETC_EMDIO_CFG_RD_ER) + return ENETC_MDIO_READ_ERR; + + return enetc_read(priv, ENETC_MDIO_DATA); +} + +static int enetc_mdio_write_priv(struct enetc_mdio_priv *priv, int addr, + int devad, int reg, u16 val) +{ + if (devad == MDIO_DEVAD_NONE) + enetc_write(priv, ENETC_MDIO_CFG, ENETC_EMDIO_CFG_C22); + else + enetc_write(priv, ENETC_MDIO_CFG, ENETC_EMDIO_CFG_C45); + enetc_mdio_wait_bsy(priv); + + if (devad != MDIO_DEVAD_NONE) { + enetc_write(priv, ENETC_MDIO_CTL, (addr << 5) + devad); + enetc_write(priv, ENETC_MDIO_STAT, reg); + } else { + enetc_write(priv, ENETC_MDIO_CTL, (addr << 5) + reg); + } + enetc_mdio_wait_bsy(priv); + + enetc_write(priv, ENETC_MDIO_DATA, val); + enetc_mdio_wait_bsy(priv); + + return 0; +} + +/* DM wrappers */ +static int dm_enetc_mdio_read(struct udevice *dev, int addr, int devad, int reg) +{ + struct enetc_mdio_priv *priv = dev_get_priv(dev); + + return enetc_mdio_read_priv(priv, addr, devad, reg); +} + +static int dm_enetc_mdio_write(struct udevice *dev, int addr, int devad, + int reg, u16 val) +{ + struct enetc_mdio_priv *priv = dev_get_priv(dev); + + return enetc_mdio_write_priv(priv, addr, devad, reg, val); +} + +static const struct mdio_ops enetc_mdio_ops = { + .read = dm_enetc_mdio_read, + .write = dm_enetc_mdio_write, +}; + +static int enetc_mdio_bind(struct udevice *dev) +{ + char name[16]; + static int eth_num_devices; + + /* + * prefer using PCI function numbers to number interfaces, but these + * are only available if dts nodes are present. For PCI they are + * optional, handle that case too. Just in case some nodes are present + * and some are not, use different naming scheme - enetc-N based on + * PCI function # and enetc#N based on interface count + */ + if (ofnode_valid(dev->node)) + sprintf(name, "emdio-%u", PCI_FUNC(pci_get_devfn(dev))); + else + sprintf(name, "emdio#%u", eth_num_devices++); + device_set_name(dev, name); + + return 0; +} + +static int enetc_mdio_probe(struct udevice *dev) +{ + struct enetc_mdio_priv *priv = dev_get_priv(dev); + + priv->regs_base = dm_pci_map_bar(dev, PCI_BASE_ADDRESS_0, 0); + if (!priv->regs_base) { + enetc_dbg(dev, "failed to map BAR0\n"); + return -EINVAL; + } + + priv->regs_base += ENETC_MDIO_BASE; + + dm_pci_clrset_config16(dev, PCI_COMMAND, 0, PCI_COMMAND_MEMORY); + + return 0; +} + +U_BOOT_DRIVER(enetc_mdio) = { + .name = "enetc_mdio", + .id = UCLASS_MDIO, + .bind = enetc_mdio_bind, + .probe = enetc_mdio_probe, + .ops = &enetc_mdio_ops, + .priv_auto_alloc_size = sizeof(struct enetc_mdio_priv), +}; + +static struct pci_device_id enetc_mdio_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, PCI_DEVICE_ID_ENETC_MDIO) }, +}; + +U_BOOT_PCI_DEVICE(enetc_mdio, enetc_mdio_ids);