From 1d99534beffea9b8e54a2cf31776cff68c4fc676 Mon Sep 17 00:00:00 2001 From: Alex Marginean Date: Wed, 3 Jul 2019 12:11:41 +0300 Subject: [PATCH] drivers: net: add NXP ENETC MDIO driver 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 Acked-by: Joe Hershberger --- 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 a894a42efb..b6e40df522 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -79,4 +79,4 @@ obj-$(CONFIG_MEDIATEK_ETH) += mtk_eth.o obj-y += mscc_eswitch/ obj-$(CONFIG_HIGMACV300_ETH) += higmacv300.o obj-$(CONFIG_MDIO_SANDBOX) += mdio_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); -- 2.25.1