
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.
Signed-off-by: Alex Marginean alexm.osslist@gmail.com ---
Changes in v2: - fix priv structure used (was using the eth driver structure) - simplified naming code in _bind - ENETC_DBG -> enetc_dbg - several styling and cosmetic updates to the header file
configs/ls1028aqds_tfa_defconfig | 1 + configs/ls1028ardb_tfa_defconfig | 1 + drivers/net/fsl_enetc.c | 169 +++++++++++++++++++++++++++++++ drivers/net/fsl_enetc.h | 18 ++++ include/pci_ids.h | 1 + 5 files changed, 190 insertions(+)
diff --git a/configs/ls1028aqds_tfa_defconfig b/configs/ls1028aqds_tfa_defconfig index 11fe344b04..84a1bf90bf 100644 --- a/configs/ls1028aqds_tfa_defconfig +++ b/configs/ls1028aqds_tfa_defconfig @@ -43,6 +43,7 @@ CONFIG_SPI_FLASH_STMICRO=y CONFIG_PHYLIB=y CONFIG_PHY_ATHEROS=y CONFIG_DM_ETH=y +CONFIG_DM_MDIO=y CONFIG_PHY_GIGE=y CONFIG_E1000=y CONFIG_FSL_ENETC=y diff --git a/configs/ls1028ardb_tfa_defconfig b/configs/ls1028ardb_tfa_defconfig index ab6f2a850c..3f5bc2e139 100644 --- a/configs/ls1028ardb_tfa_defconfig +++ b/configs/ls1028ardb_tfa_defconfig @@ -43,6 +43,7 @@ CONFIG_SPI_FLASH_STMICRO=y CONFIG_PHYLIB=y CONFIG_PHY_ATHEROS=y CONFIG_DM_ETH=y +CONFIG_DM_MDIO=y CONFIG_PHY_GIGE=y CONFIG_E1000=y CONFIG_FSL_ENETC=y diff --git a/drivers/net/fsl_enetc.c b/drivers/net/fsl_enetc.c index 325e032746..8fe84949b8 100644 --- a/drivers/net/fsl_enetc.c +++ b/drivers/net/fsl_enetc.c @@ -12,6 +12,7 @@ #include <asm/io.h> #include <asm/processor.h> #include <pci.h> +#include <miiphy.h>
static int enetc_bind(struct udevice *dev) { @@ -23,6 +24,61 @@ static int enetc_bind(struct udevice *dev) return 0; }
+static void enetc_start_phy(struct udevice *dev) +{ +#ifdef CONFIG_DM_MDIO + int supported, if_type = PHY_INTERFACE_MODE_NONE; + struct udevice *miidev; + struct phy_device *phy; + u32 phandle, phy_id; + const char *if_str; + ofnode phy_node; + + 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_str = ofnode_read_string(phy_node, "phy-mode"); + if (if_str) + if_type = phy_get_interface_by_name(if_str); + if (if_type < 0) + if_type = PHY_INTERFACE_MODE_NONE; + + if (uclass_get_device_by_ofnode(UCLASS_MDIO, + ofnode_get_parent(phy_node), + &miidev)) + return; + + phy = dm_mdio_phy_connect(miidev, phy_id, dev, if_type); + if (!phy) + return; + + supported = GENMASK(6, 0); /* speeds up to 1G & AN */ + phy->advertising = phy->supported & supported; + phy_config(phy); + phy_startup(phy); +#endif +} + /* * Probe ENETC driver: * - initialize port and station interface BARs @@ -223,6 +279,8 @@ static int enetc_start(struct udevice *dev) enetc_setup_tx_bdr(hw); enetc_setup_rx_bdr(dev, hw);
+ enetc_start_phy(dev); + return 0; }
@@ -342,3 +400,114 @@ static struct pci_device_id enetc_ids[] = { };
U_BOOT_PCI_DEVICE(eth_enetc, enetc_ids); + +#ifdef CONFIG_DM_MDIO + +static void enetc_mdio_wait_bsy(struct enetc_mdio_devfn *hw) +{ + while (enetc_read(hw, ENETC_MDIO_CFG) & ENETC_EMDIO_CFG_BSY) + cpu_relax(); +} + +static int enetc_mdio_read(struct udevice *dev, int addr, int devad, int reg) +{ + struct enetc_mdio_devfn *hw = dev_get_priv(dev); + + if (devad == MDIO_DEVAD_NONE) + enetc_write(hw, ENETC_MDIO_CFG, ENETC_EMDIO_CFG_C22); + else + enetc_write(hw, ENETC_MDIO_CFG, ENETC_EMDIO_CFG_C45); + enetc_mdio_wait_bsy(hw); + + if (devad == MDIO_DEVAD_NONE) { + enetc_write(hw, ENETC_MDIO_CTL, ENETC_MDIO_CTL_READ | + (addr << 5) | reg); + } else { + enetc_write(hw, ENETC_MDIO_CTL, (addr << 5) + devad); + enetc_mdio_wait_bsy(hw); + + enetc_write(hw, ENETC_MDIO_STAT, reg); + enetc_mdio_wait_bsy(hw); + + enetc_write(hw, ENETC_MDIO_CTL, ENETC_MDIO_CTL_READ | + (addr << 5) | devad); + } + + enetc_mdio_wait_bsy(hw); + if (enetc_read(hw, ENETC_MDIO_CFG) & ENETC_EMDIO_CFG_RD_ER) + return ENETC_MDIO_READ_ERR; + + return enetc_read(hw, ENETC_MDIO_DATA); +} + +static int enetc_mdio_write(struct udevice *dev, int addr, int devad, int reg, + u16 val) +{ + struct enetc_mdio_devfn *hw = dev_get_priv(dev); + + if (devad == MDIO_DEVAD_NONE) + enetc_write(hw, ENETC_MDIO_CFG, ENETC_EMDIO_CFG_C22); + else + enetc_write(hw, ENETC_MDIO_CFG, ENETC_EMDIO_CFG_C45); + enetc_mdio_wait_bsy(hw); + + if (devad != MDIO_DEVAD_NONE) { + enetc_write(hw, ENETC_MDIO_CTL, (addr << 5) + devad); + enetc_write(hw, ENETC_MDIO_STAT, reg); + } else { + enetc_write(hw, ENETC_MDIO_CTL, (addr << 5) + reg); + } + enetc_mdio_wait_bsy(hw); + + enetc_write(hw, ENETC_MDIO_DATA, val); + enetc_mdio_wait_bsy(hw); + + return 0; +} + +static const struct mdio_ops enetc_mdio_ops = { + .read = enetc_mdio_read, + .write = enetc_mdio_write, +}; + +static int enetc_mdio_bind(struct udevice *dev) +{ + char name[16]; + + sprintf(name, "emdio#%u", PCI_FUNC(dm_pci_get_bdf(dev))); + device_set_name(dev, name); + + return 0; +} + +static int enetc_mdio_probe(struct udevice *dev) +{ + struct enetc_devfn *hw = dev_get_priv(dev); + + hw->regs_base = dm_pci_map_bar(dev, PCI_BASE_ADDRESS_0, 0); + if (!hw->regs_base) { + enetc_dbg(dev, "failed to map BAR0\n"); + return -EINVAL; + } + + 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_devfn), +}; + +static struct pci_device_id enetc_mdio_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, PCI_DEVICE_ID_MDIO) }, +}; + +U_BOOT_PCI_DEVICE(enetc_mdio, enetc_mdio_ids); + +#endif /* CONFIG_DM_MDIO */ diff --git a/drivers/net/fsl_enetc.h b/drivers/net/fsl_enetc.h index 94f836760a..fb9d31bf79 100644 --- a/drivers/net/fsl_enetc.h +++ b/drivers/net/fsl_enetc.h @@ -169,4 +169,22 @@ struct enetc_devfn { #define enetc_bdr_write(hw, t, n, off, val) \ enetc_write(hw, ENETC_BDR(t, n, off), val)
+/* ENETC external MDIO registers */ + +#define ENETC_MDIO_CFG 0x1c00 +#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 0x1c04 +#define ENETC_MDIO_CTL_READ BIT(15) +#define ENETC_MDIO_DATA 0x1c08 +#define ENETC_MDIO_STAT 0x1c0c + +#define ENETC_MDIO_READ_ERR 0xffff + +struct enetc_mdio_devfn { + void *regs_base; +}; + #endif /* _ENETC_H */ diff --git a/include/pci_ids.h b/include/pci_ids.h index 16cf0641c7..855136e89a 100644 --- a/include/pci_ids.h +++ b/include/pci_ids.h @@ -2484,6 +2484,7 @@ #define PCI_DEVICE_ID_MPC8641D 0x7011 #define PCI_DEVICE_ID_MPC8610 0x7018 #define PCI_DEVICE_ID_ENETC_PF 0xE100 +#define PCI_DEVICE_ID_MDIO 0xEE01
#define PCI_VENDOR_ID_PASEMI 0x1959