From f54002d774d1cd215a0f12da7a3c06f539ae92ea Mon Sep 17 00:00:00 2001 From: Tim Harvey Date: Wed, 30 Nov 2022 09:42:44 -0800 Subject: [PATCH 01/19] net: mdio-uclass: scan for dm mdio children on post-bind If a DM_MDIO driver is used we need to scan the subnodes as well. Signed-off-by: Tim Harvey Signed-off-by: Vladimir Oltean Reviewed-by: Ramon Fried Reviewed-by: Fabio Estevam --- net/mdio-uclass.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/net/mdio-uclass.c b/net/mdio-uclass.c index 4401492ca01..d80037d0ac7 100644 --- a/net/mdio-uclass.c +++ b/net/mdio-uclass.c @@ -49,7 +49,11 @@ static int dm_mdio_post_bind(struct udevice *dev) return -EINVAL; } +#if CONFIG_IS_ENABLED(OF_REAL) + return dm_scan_fdt_dev(dev); +#else return 0; +#endif } int dm_mdio_read(struct udevice *mdio_dev, int addr, int devad, int reg) From c5868fcd25c670f5c4e37afca2c5eaddadd218b4 Mon Sep 17 00:00:00 2001 From: Tim Harvey Date: Wed, 30 Nov 2022 09:42:45 -0800 Subject: [PATCH 02/19] net: dsa: move cpu port probe to dsa_post_probe In order to ensure that a DSA driver probe gets called before dsa_ops->port_probe move the port_probe of the cpu_port to a post-probe function. Signed-off-by: Tim Harvey Reviewed-by: Ramon Fried Reviewed-by: Vladimir Oltean Reviewed-by: Fabio Estevam --- net/dsa-uclass.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/net/dsa-uclass.c b/net/dsa-uclass.c index 5b7046432ff..5759cedcbec 100644 --- a/net/dsa-uclass.c +++ b/net/dsa-uclass.c @@ -466,7 +466,6 @@ static int dsa_pre_probe(struct udevice *dev) { struct dsa_pdata *pdata = dev_get_uclass_plat(dev); struct dsa_priv *priv = dev_get_uclass_priv(dev); - struct dsa_ops *ops = dsa_get_ops(dev); int err; priv->num_ports = pdata->num_ports; @@ -482,6 +481,15 @@ static int dsa_pre_probe(struct udevice *dev) if (err) return err; + return 0; +} + +static int dsa_post_probe(struct udevice *dev) +{ + struct dsa_priv *priv = dev_get_uclass_priv(dev); + struct dsa_ops *ops = dsa_get_ops(dev); + int err; + /* Simulate a probing event for the CPU port */ if (ops->port_probe) { err = ops->port_probe(dev, priv->cpu_port, @@ -498,6 +506,7 @@ UCLASS_DRIVER(dsa) = { .name = "dsa", .post_bind = dsa_post_bind, .pre_probe = dsa_pre_probe, + .post_probe = dsa_post_probe, .per_device_auto = sizeof(struct dsa_priv), .per_device_plat_auto = sizeof(struct dsa_pdata), .per_child_plat_auto = sizeof(struct dsa_port_pdata), From 43c2a00a14a7e4567b156a2345870d008485da3c Mon Sep 17 00:00:00 2001 From: Tim Harvey Date: Wed, 30 Nov 2022 09:42:46 -0800 Subject: [PATCH 03/19] net: dsa: ensure dsa driver has proper ops Add a function to sanity check a dsa driver having proper ops. Suggested-by: Vladimir Oltean Signed-off-by: Tim Harvey Reviewed-by: Vladimir Oltean Reviewed-by: Fabio Estevam --- net/dsa-uclass.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/net/dsa-uclass.c b/net/dsa-uclass.c index 5759cedcbec..211a991cdd0 100644 --- a/net/dsa-uclass.c +++ b/net/dsa-uclass.c @@ -342,6 +342,19 @@ U_BOOT_DRIVER(dsa_port) = { .plat_auto = sizeof(struct eth_pdata), }; +static int dsa_sanitize_ops(struct udevice *dev) +{ + struct dsa_ops *ops = dsa_get_ops(dev); + + if ((!ops->xmit || !ops->rcv) && + (!ops->port_enable && !ops->port_disable)) { + dev_err(dev, "Packets cannot be steered to ports\n"); + return -EINVAL; + } + + return 0; +} + /* * This function mostly deals with pulling information out of the device tree * into the pdata structure. @@ -358,6 +371,10 @@ static int dsa_post_bind(struct udevice *dev) if (!ofnode_valid(node)) return -ENODEV; + err = dsa_sanitize_ops(dev); + if (err) + return err; + pdata->master_node = ofnode_null(); node = ofnode_find_subnode(node, "ports"); From 54d11e2019b73758070632d695cf47f3d5fbf108 Mon Sep 17 00:00:00 2001 From: Tim Harvey Date: Wed, 30 Nov 2022 09:42:47 -0800 Subject: [PATCH 04/19] net: dsa: allow rcv() and xmit() to be optional Allow rcv() and xmit() dsa driver ops to be optional in case a driver does not care to mangle a packet as in U-Boot only one network port is enabled at a time and thus no packet mangling is necessary. Suggested-by: Vladimir Oltean Signed-off-by: Tim Harvey Reviewed-by: Vladimir Oltean Reviewed-by: Fabio Estevam Signed-off-by: Tim Harvey --- net/dsa-uclass.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/net/dsa-uclass.c b/net/dsa-uclass.c index 211a991cdd0..dd78e5744d5 100644 --- a/net/dsa-uclass.c +++ b/net/dsa-uclass.c @@ -142,20 +142,22 @@ static int dsa_port_send(struct udevice *pdev, void *packet, int length) struct dsa_port_pdata *port_pdata; int err; - if (length + head + tail > PKTSIZE_ALIGN) - return -EINVAL; + if (ops->xmit) { + if (length + head + tail > PKTSIZE_ALIGN) + return -EINVAL; - memset(dsa_packet_tmp, 0, head); - memset(dsa_packet_tmp + head + length, 0, tail); - memcpy(dsa_packet_tmp + head, packet, length); - length += head + tail; - /* copy back to preserve original buffer alignment */ - memcpy(packet, dsa_packet_tmp, length); + memset(dsa_packet_tmp, 0, head); + memset(dsa_packet_tmp + head + length, 0, tail); + memcpy(dsa_packet_tmp + head, packet, length); + length += head + tail; + /* copy back to preserve original buffer alignment */ + memcpy(packet, dsa_packet_tmp, length); - port_pdata = dev_get_parent_plat(pdev); - err = ops->xmit(dev, port_pdata->index, packet, length); - if (err) - return err; + port_pdata = dev_get_parent_plat(pdev); + err = ops->xmit(dev, port_pdata->index, packet, length); + if (err) + return err; + } return eth_get_ops(master)->send(master, packet, length); } @@ -172,7 +174,7 @@ static int dsa_port_recv(struct udevice *pdev, int flags, uchar **packetp) int length, port_index, err; length = eth_get_ops(master)->recv(master, flags, packetp); - if (length <= 0) + if (length <= 0 || !ops->rcv) return length; /* From 8abb9f6b17c8f76301f7359450813b6f6f52540e Mon Sep 17 00:00:00 2001 From: Tim Harvey Date: Wed, 30 Nov 2022 09:42:48 -0800 Subject: [PATCH 05/19] net: ksz9477: remove unnecessary xmit and recv functions Remove the unnecessary xmit and recv functions. Signed-off-by: Tim Harvey Reviewed-by: Vladimir Oltean Reviewed-by: Fabio Estevam --- drivers/net/ksz9477.c | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/drivers/net/ksz9477.c b/drivers/net/ksz9477.c index ed8f1895cb1..fb5c76c600b 100644 --- a/drivers/net/ksz9477.c +++ b/drivers/net/ksz9477.c @@ -62,7 +62,6 @@ struct ksz_dsa_priv { struct udevice *dev; - int active_port; }; static inline int ksz_read8(struct udevice *dev, u32 reg, u8 *val) @@ -382,9 +381,6 @@ static int ksz_port_enable(struct udevice *dev, int port, struct phy_device *phy data8 |= SW_START; ksz_write8(priv->dev, REG_SW_OPERATION, data8); - /* keep track of current enabled non-cpu port */ - priv->active_port = port; - return 0; } @@ -413,28 +409,9 @@ static void ksz_port_disable(struct udevice *dev, int port, struct phy_device *p */ } -static int ksz_xmit(struct udevice *dev, int port, void *packet, int length) -{ - dev_dbg(dev, "%s P%d %d\n", __func__, port + 1, length); - - return 0; -} - -static int ksz_recv(struct udevice *dev, int *port, void *packet, int length) -{ - struct ksz_dsa_priv *priv = dev_get_priv(dev); - - dev_dbg(dev, "%s P%d %d\n", __func__, priv->active_port + 1, length); - *port = priv->active_port; - - return 0; -}; - static const struct dsa_ops ksz_dsa_ops = { .port_enable = ksz_port_enable, .port_disable = ksz_port_disable, - .xmit = ksz_xmit, - .rcv = ksz_recv, }; static int ksz_probe_mdio(struct udevice *dev) From 3b8f99a3e7628c17763b18a14f880bf1725c3828 Mon Sep 17 00:00:00 2001 From: Tim Harvey Date: Wed, 30 Nov 2022 09:42:49 -0800 Subject: [PATCH 06/19] net: fec: add support for DM_MDIO Add support for DM_MDIO by registering a UCLASS_MDIO driver and attempting to use it. This is necessary if wanting to use a DSA driver for example hanging off of the FEC MAC. Care is taken to fallback to non DM_MDIO mii bus as several boards define DM_MDIO without having the proper device-tree configuration necessary such as an mdio subnode, a phy-mode prop, and either a valid phy-handle prop or fixed-phy subnode which will cause dm_eth_phy_connect() to fail. Signed-off-by: Tim Harvey Reviewed-by: Fabio Estevam --- drivers/net/fec_mxc.c | 90 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 88 insertions(+), 2 deletions(-) diff --git a/drivers/net/fec_mxc.c b/drivers/net/fec_mxc.c index 006d27051eb..8abfdbd5d91 100644 --- a/drivers/net/fec_mxc.c +++ b/drivers/net/fec_mxc.c @@ -30,6 +30,8 @@ #include #include #include +#include +#include #include "fec_mxc.h" #include @@ -1019,6 +1021,81 @@ struct mii_dev *fec_get_miibus(ulong base_addr, int dev_id) return bus; } +#ifdef CONFIG_DM_MDIO +struct dm_fec_mdio_priv { + struct ethernet_regs *regs; +}; + +static int dm_fec_mdio_read(struct udevice *dev, int addr, int devad, int reg) +{ + struct dm_fec_mdio_priv *priv = dev_get_priv(dev); + + return fec_mdio_read(priv->regs, addr, reg); +} + +static int dm_fec_mdio_write(struct udevice *dev, int addr, int devad, int reg, u16 data) +{ + struct dm_fec_mdio_priv *priv = dev_get_priv(dev); + + return fec_mdio_write(priv->regs, addr, reg, data); +} + +static const struct mdio_ops dm_fec_mdio_ops = { + .read = dm_fec_mdio_read, + .write = dm_fec_mdio_write, +}; + +static int dm_fec_mdio_probe(struct udevice *dev) +{ + struct dm_fec_mdio_priv *priv = dev_get_priv(dev); + + priv->regs = (struct ethernet_regs *)ofnode_get_addr(dev_ofnode(dev->parent)); + + return 0; +} + +U_BOOT_DRIVER(fec_mdio) = { + .name = "fec_mdio", + .id = UCLASS_MDIO, + .probe = dm_fec_mdio_probe, + .ops = &dm_fec_mdio_ops, + .priv_auto = sizeof(struct dm_fec_mdio_priv), +}; + +static int dm_fec_bind_mdio(struct udevice *dev) +{ + struct udevice *mdiodev; + const char *name; + ofnode mdio; + int ret = -ENODEV; + + /* for a UCLASS_MDIO driver we need to bind and probe manually + * for an internal MDIO bus that has no dt compatible of its own + */ + ofnode_for_each_subnode(mdio, dev_ofnode(dev)) { + name = ofnode_get_name(mdio); + + if (strcmp(name, "mdio")) + continue; + + ret = device_bind_driver_to_node(dev, "fec_mdio", + name, mdio, &mdiodev); + if (ret) { + printf("%s bind %s failed: %d\n", __func__, name, ret); + break; + } + + /* need to probe it as there is no compatible to do so */ + ret = uclass_get_device_by_ofnode(UCLASS_MDIO, mdio, &mdiodev); + if (!ret) + return 0; + printf("%s probe %s failed: %d\n", __func__, name, ret); + } + + return ret; +} +#endif + static int fecmxc_read_rom_hwaddr(struct udevice *dev) { struct fec_priv *priv = dev_get_priv(dev); @@ -1082,7 +1159,7 @@ static int device_get_phy_addr(struct fec_priv *priv, struct udevice *dev) static int fec_phy_init(struct fec_priv *priv, struct udevice *dev) { - struct phy_device *phydev; + struct phy_device *phydev = NULL; int addr; addr = device_get_phy_addr(priv, dev); @@ -1090,7 +1167,10 @@ static int fec_phy_init(struct fec_priv *priv, struct udevice *dev) addr = CFG_FEC_MXC_PHYADDR; #endif - phydev = phy_connect(priv->bus, addr, dev, priv->interface); + if (IS_ENABLED(CONFIG_DM_MDIO)) + phydev = dm_eth_phy_connect(dev); + if (!phydev) + phydev = phy_connect(priv->bus, addr, dev, priv->interface); if (!phydev) return -ENODEV; @@ -1221,6 +1301,12 @@ static int fecmxc_probe(struct udevice *dev) priv->dev_id = dev_seq(dev); +#ifdef CONFIG_DM_MDIO + ret = dm_fec_bind_mdio(dev); + if (ret && ret != -ENODEV) + return ret; +#endif + #ifdef CONFIG_DM_ETH_PHY bus = eth_phy_get_mdio_bus(dev); #endif From 01e7dd050fc90358c64d990e146c9b0f86d6d563 Mon Sep 17 00:00:00 2001 From: Tim Harvey Date: Wed, 30 Nov 2022 09:42:50 -0800 Subject: [PATCH 07/19] net: add MV88E6xxx DSA driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a DSA driver for the MV88E6xxx compatible Ethernet switches. Cc: Marek BehĂșn Cc: Vladimir Oltean Signed-off-by: Tim Harvey Reviewed-by: Vladimir Oltean Reviewed-by: Fabio Estevam --- drivers/net/Kconfig | 7 + drivers/net/Makefile | 1 + drivers/net/mv88e6xxx.c | 755 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 763 insertions(+) create mode 100644 drivers/net/mv88e6xxx.c diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 7873538cc2d..62d2c03849a 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -473,6 +473,13 @@ config LITEETH help Driver for the LiteEth Ethernet MAC from LiteX. +config MV88E6XXX + bool "Marvell MV88E6xxx Ethernet switch DSA driver" + depends on DM_DSA && DM_MDIO + help + This driver implements a DSA switch driver for the MV88E6xxx family + of Ethernet switches using the MDIO interface + config MVGBE bool "Marvell Orion5x/Kirkwood network interface support" depends on ARCH_KIRKWOOD || ARCH_ORION5X diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 5b4e60eea3e..90fbb02ab02 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -60,6 +60,7 @@ obj-$(CONFIG_MEDIATEK_ETH) += mtk_eth.o obj-$(CONFIG_MPC8XX_FEC) += mpc8xx_fec.o obj-$(CONFIG_MT7620_ETH) += mt7620-eth.o obj-$(CONFIG_MT7628_ETH) += mt7628-eth.o +obj-$(CONFIG_MV88E6XXX) += mv88e6xxx.o obj-$(CONFIG_MVGBE) += mvgbe.o obj-$(CONFIG_MVMDIO) += mvmdio.o obj-$(CONFIG_MVNETA) += mvneta.o diff --git a/drivers/net/mv88e6xxx.c b/drivers/net/mv88e6xxx.c new file mode 100644 index 00000000000..64e860e324d --- /dev/null +++ b/drivers/net/mv88e6xxx.c @@ -0,0 +1,755 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2022 + * Gateworks Corporation + * Tim Harvey + * + * (C) Copyright 2015 + * Elecsys Corporation + * Kevin Smith + * + * Original driver: + * (C) Copyright 2009 + * Marvell Semiconductor + * Prafulla Wadaskar + */ + +/* + * DSA driver for mv88e6xxx ethernet switches. + * + * This driver configures the mv88e6xxx for basic use as a DSA switch. + * + * This driver was adapted from drivers/net/phy/mv88e61xx and tested + * on the mv88e6176 via an SGMII interface. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Device addresses */ +#define DEVADDR_PHY(p) (p) +#define DEVADDR_SERDES 0x0F + +/* SMI indirection registers for multichip addressing mode */ +#define SMI_CMD_REG 0x00 +#define SMI_DATA_REG 0x01 + +/* Global registers */ +#define GLOBAL1_STATUS 0x00 +#define GLOBAL1_CTRL 0x04 + +/* Global 2 registers */ +#define GLOBAL2_REG_PHY_CMD 0x18 +#define GLOBAL2_REG_PHY_DATA 0x19 +#define GLOBAL2_REG_SCRATCH 0x1A + +/* Port registers */ +#define PORT_REG_STATUS 0x00 +#define PORT_REG_PHYS_CTRL 0x01 +#define PORT_REG_SWITCH_ID 0x03 +#define PORT_REG_CTRL 0x04 + +/* Phy registers */ +#define PHY_REG_PAGE 0x16 + +/* Phy page numbers */ +#define PHY_PAGE_COPPER 0 +#define PHY_PAGE_SERDES 1 + +/* Register fields */ +#define GLOBAL1_CTRL_SWRESET BIT(15) + +#define PORT_REG_STATUS_SPEED_SHIFT 8 +#define PORT_REG_STATUS_SPEED_10 0 +#define PORT_REG_STATUS_SPEED_100 1 +#define PORT_REG_STATUS_SPEED_1000 2 + +#define PORT_REG_STATUS_CMODE_MASK 0xF +#define PORT_REG_STATUS_CMODE_SGMII 0xa +#define PORT_REG_STATUS_CMODE_1000BASE_X 0x9 +#define PORT_REG_STATUS_CMODE_100BASE_X 0x8 +#define PORT_REG_STATUS_CMODE_RGMII 0x7 +#define PORT_REG_STATUS_CMODE_RMII 0x5 +#define PORT_REG_STATUS_CMODE_RMII_PHY 0x4 +#define PORT_REG_STATUS_CMODE_GMII 0x3 +#define PORT_REG_STATUS_CMODE_MII 0x2 +#define PORT_REG_STATUS_CMODE_MIIPHY 0x1 + +#define PORT_REG_PHYS_CTRL_RGMII_DELAY_RXCLK BIT(15) +#define PORT_REG_PHYS_CTRL_RGMII_DELAY_TXCLK BIT(14) +#define PORT_REG_PHYS_CTRL_PCS_AN_EN BIT(10) +#define PORT_REG_PHYS_CTRL_PCS_AN_RST BIT(9) +#define PORT_REG_PHYS_CTRL_FC_VALUE BIT(7) +#define PORT_REG_PHYS_CTRL_FC_FORCE BIT(6) +#define PORT_REG_PHYS_CTRL_LINK_VALUE BIT(5) +#define PORT_REG_PHYS_CTRL_LINK_FORCE BIT(4) +#define PORT_REG_PHYS_CTRL_DUPLEX_VALUE BIT(3) +#define PORT_REG_PHYS_CTRL_DUPLEX_FORCE BIT(2) +#define PORT_REG_PHYS_CTRL_SPD1000 BIT(1) +#define PORT_REG_PHYS_CTRL_SPD100 BIT(0) +#define PORT_REG_PHYS_CTRL_SPD_MASK (BIT(1) | BIT(0)) + +#define PORT_REG_CTRL_PSTATE_SHIFT 0 +#define PORT_REG_CTRL_PSTATE_MASK 3 + +/* Field values */ +#define PORT_REG_CTRL_PSTATE_DISABLED 0 +#define PORT_REG_CTRL_PSTATE_FORWARD 3 + +/* + * Macros for building commands for indirect addressing modes. These are valid + * for both the indirect multichip addressing mode and the PHY indirection + * required for the writes to any PHY register. + */ +#define SMI_BUSY BIT(15) +#define SMI_CMD_CLAUSE_22 BIT(12) +#define SMI_CMD_CLAUSE_22_OP_READ (2 << 10) +#define SMI_CMD_CLAUSE_22_OP_WRITE (1 << 10) +#define SMI_CMD_ADDR_SHIFT 5 +#define SMI_CMD_ADDR_MASK 0x1f +#define SMI_CMD_REG_SHIFT 0 +#define SMI_CMD_REG_MASK 0x1f +#define SMI_CMD_READ(addr, reg) \ + (SMI_BUSY | SMI_CMD_CLAUSE_22 | SMI_CMD_CLAUSE_22_OP_READ) | \ + (((addr) & SMI_CMD_ADDR_MASK) << SMI_CMD_ADDR_SHIFT) | \ + (((reg) & SMI_CMD_REG_MASK) << SMI_CMD_REG_SHIFT) +#define SMI_CMD_WRITE(addr, reg) \ + (SMI_BUSY | SMI_CMD_CLAUSE_22 | SMI_CMD_CLAUSE_22_OP_WRITE) | \ + (((addr) & SMI_CMD_ADDR_MASK) << SMI_CMD_ADDR_SHIFT) | \ + (((reg) & SMI_CMD_REG_MASK) << SMI_CMD_REG_SHIFT) + +/* ID register values for different switch models */ +#define PORT_SWITCH_ID_6020 0x0200 +#define PORT_SWITCH_ID_6070 0x0700 +#define PORT_SWITCH_ID_6071 0x0710 +#define PORT_SWITCH_ID_6096 0x0980 +#define PORT_SWITCH_ID_6097 0x0990 +#define PORT_SWITCH_ID_6172 0x1720 +#define PORT_SWITCH_ID_6176 0x1760 +#define PORT_SWITCH_ID_6220 0x2200 +#define PORT_SWITCH_ID_6240 0x2400 +#define PORT_SWITCH_ID_6250 0x2500 +#define PORT_SWITCH_ID_6320 0x1150 +#define PORT_SWITCH_ID_6352 0x3520 + +struct mv88e6xxx_priv { + int smi_addr; + int id; + int port_count; /* Number of switch ports */ + int port_reg_base; /* Base of the switch port registers */ + u8 global1; /* Offset of Switch Global 1 registers */ + u8 global2; /* Offset of Switch Global 2 registers */ +}; + +/* Wait for the current SMI indirect command to complete */ +static int mv88e6xxx_smi_wait(struct udevice *dev, int smi_addr) +{ + int val; + u32 timeout = 100; + + do { + val = dm_mdio_read(dev->parent, smi_addr, MDIO_DEVAD_NONE, SMI_CMD_REG); + if (val >= 0 && (val & SMI_BUSY) == 0) + return 0; + + mdelay(1); + } while (--timeout); + + dev_err(dev, "SMI busy timeout\n"); + return -ETIMEDOUT; +} + +/* + * The mv88e6xxx has three types of addresses: the smi bus address, the device + * address, and the register address. The smi bus address distinguishes it on + * the smi bus from other PHYs or switches. The device address determines + * which on-chip register set you are reading/writing (the various PHYs, their + * associated ports, or global configuration registers). The register address + * is the offset of the register you are reading/writing. + * + * When the mv88e6xxx is hardware configured to have address zero, it behaves in + * single-chip addressing mode, where it responds to all SMI addresses, using + * the smi address as its device address. This obviously only works when this + * is the only chip on the SMI bus. This allows the driver to access device + * registers without using indirection. When the chip is configured to a + * non-zero address, it only responds to that SMI address and requires indirect + * writes to access the different device addresses. + */ +static int mv88e6xxx_reg_read(struct udevice *dev, int addr, int reg) +{ + struct mv88e6xxx_priv *priv = dev_get_priv(dev); + int smi_addr = priv->smi_addr; + int res; + + /* In single-chip mode, the device can be addressed directly */ + if (smi_addr == 0) + return dm_mdio_read(dev->parent, addr, MDIO_DEVAD_NONE, reg); + + /* Wait for the bus to become free */ + res = mv88e6xxx_smi_wait(dev, smi_addr); + if (res < 0) + return res; + + /* Issue the read command */ + res = dm_mdio_write(dev->parent, smi_addr, MDIO_DEVAD_NONE, SMI_CMD_REG, + SMI_CMD_READ(addr, reg)); + if (res < 0) + return res; + + /* Wait for the read command to complete */ + res = mv88e6xxx_smi_wait(dev, smi_addr); + if (res < 0) + return res; + + /* Read the data */ + res = dm_mdio_read(dev->parent, smi_addr, MDIO_DEVAD_NONE, SMI_DATA_REG); + if (res < 0) + return res; + + return res & 0xffff; +} + +/* See the comment above mv88e6xxx_reg_read */ +static int mv88e6xxx_reg_write(struct udevice *dev, int addr, int reg, u16 val) +{ + struct mv88e6xxx_priv *priv = dev_get_priv(dev); + int smi_addr = priv->smi_addr; + int res; + + /* In single-chip mode, the device can be addressed directly */ + if (smi_addr == 0) + return dm_mdio_write(dev->parent, addr, MDIO_DEVAD_NONE, reg, val); + + /* Wait for the bus to become free */ + res = mv88e6xxx_smi_wait(dev, smi_addr); + if (res < 0) + return res; + + /* Set the data to write */ + res = dm_mdio_write(dev->parent, smi_addr, MDIO_DEVAD_NONE, + SMI_DATA_REG, val); + if (res < 0) + return res; + + /* Issue the write command */ + res = dm_mdio_write(dev->parent, smi_addr, MDIO_DEVAD_NONE, SMI_CMD_REG, + SMI_CMD_WRITE(addr, reg)); + if (res < 0) + return res; + + /* Wait for the write command to complete */ + res = mv88e6xxx_smi_wait(dev, smi_addr); + if (res < 0) + return res; + + return 0; +} + +static int mv88e6xxx_phy_wait(struct udevice *dev) +{ + struct mv88e6xxx_priv *priv = dev_get_priv(dev); + int val; + u32 timeout = 100; + + do { + val = mv88e6xxx_reg_read(dev, priv->global2, GLOBAL2_REG_PHY_CMD); + if (val >= 0 && (val & SMI_BUSY) == 0) + return 0; + + mdelay(1); + } while (--timeout); + + return -ETIMEDOUT; +} + +static int mv88e6xxx_phy_read_indirect(struct udevice *dev, int phyad, int devad, int reg) +{ + struct mv88e6xxx_priv *priv = dev_get_priv(dev); + int res; + + /* Issue command to read */ + res = mv88e6xxx_reg_write(dev, priv->global2, + GLOBAL2_REG_PHY_CMD, + SMI_CMD_READ(phyad, reg)); + + /* Wait for data to be read */ + res = mv88e6xxx_phy_wait(dev); + if (res < 0) + return res; + + /* Read retrieved data */ + return mv88e6xxx_reg_read(dev, priv->global2, + GLOBAL2_REG_PHY_DATA); +} + +static int mv88e6xxx_phy_write_indirect(struct udevice *dev, int phyad, + int devad, int reg, u16 data) +{ + struct mv88e6xxx_priv *priv = dev_get_priv(dev); + int res; + + /* Set the data to write */ + res = mv88e6xxx_reg_write(dev, priv->global2, + GLOBAL2_REG_PHY_DATA, data); + if (res < 0) + return res; + /* Issue the write command */ + res = mv88e6xxx_reg_write(dev, priv->global2, + GLOBAL2_REG_PHY_CMD, + SMI_CMD_WRITE(phyad, reg)); + if (res < 0) + return res; + + /* Wait for command to complete */ + return mv88e6xxx_phy_wait(dev); +} + +/* Wrapper function to make calls to phy_read_indirect simpler */ +static int mv88e6xxx_phy_read(struct udevice *dev, int phy, int reg) +{ + return mv88e6xxx_phy_read_indirect(dev, DEVADDR_PHY(phy), + MDIO_DEVAD_NONE, reg); +} + +/* Wrapper function to make calls to phy_write_indirect simpler */ +static int mv88e6xxx_phy_write(struct udevice *dev, int phy, int reg, u16 val) +{ + return mv88e6xxx_phy_write_indirect(dev, DEVADDR_PHY(phy), + MDIO_DEVAD_NONE, reg, val); +} + +static int mv88e6xxx_port_read(struct udevice *dev, u8 port, u8 reg) +{ + struct mv88e6xxx_priv *priv = dev_get_priv(dev); + + return mv88e6xxx_reg_read(dev, priv->port_reg_base + port, reg); +} + +static int mv88e6xxx_port_write(struct udevice *dev, u8 port, u8 reg, u16 val) +{ + struct mv88e6xxx_priv *priv = dev_get_priv(dev); + + return mv88e6xxx_reg_write(dev, priv->port_reg_base + port, reg, val); +} + +static int mv88e6xxx_set_page(struct udevice *dev, u8 phy, u8 page) +{ + return mv88e6xxx_phy_write(dev, phy, PHY_REG_PAGE, page); +} + +static int mv88e6xxx_get_switch_id(struct udevice *dev) +{ + int res; + + res = mv88e6xxx_port_read(dev, 0, PORT_REG_SWITCH_ID); + if (res < 0) { + dev_err(dev, "Failed to read switch ID: %d\n", res); + return res; + } + return res & 0xfff0; +} + +static bool mv88e6xxx_6352_family(struct udevice *dev) +{ + struct mv88e6xxx_priv *priv = dev_get_priv(dev); + + switch (priv->id) { + case PORT_SWITCH_ID_6172: + case PORT_SWITCH_ID_6176: + case PORT_SWITCH_ID_6240: + case PORT_SWITCH_ID_6352: + return true; + } + return false; +} + +static int mv88e6xxx_get_cmode(struct udevice *dev, u8 port) +{ + int res; + + res = mv88e6xxx_port_read(dev, port, PORT_REG_STATUS); + if (res < 0) + return res; + return res & PORT_REG_STATUS_CMODE_MASK; +} + +static int mv88e6xxx_switch_reset(struct udevice *dev) +{ + struct mv88e6xxx_priv *priv = dev_get_priv(dev); + int time_ms; + int val; + u8 port; + + /* Disable all ports */ + for (port = 0; port < priv->port_count; port++) { + val = mv88e6xxx_port_read(dev, port, PORT_REG_CTRL); + if (val < 0) + return val; + val &= ~(PORT_REG_CTRL_PSTATE_MASK << PORT_REG_CTRL_PSTATE_SHIFT); + val |= (PORT_REG_CTRL_PSTATE_DISABLED << PORT_REG_CTRL_PSTATE_SHIFT); + val = mv88e6xxx_port_write(dev, port, PORT_REG_CTRL, val); + if (val < 0) + return val; + } + + /* Wait 2 ms for queues to drain */ + udelay(2000); + + /* Reset switch */ + val = mv88e6xxx_reg_read(dev, priv->global1, GLOBAL1_CTRL); + if (val < 0) + return val; + val |= GLOBAL1_CTRL_SWRESET; + val = mv88e6xxx_reg_write(dev, priv->global1, GLOBAL1_CTRL, val); + if (val < 0) + return val; + + /* Wait up to 1 second for switch to reset complete */ + for (time_ms = 1000; time_ms; time_ms--) { + val = mv88e6xxx_reg_read(dev, priv->global1, GLOBAL1_CTRL); + if (val >= 0 && ((val & GLOBAL1_CTRL_SWRESET) == 0)) + break; + udelay(1000); + } + if (!time_ms) + return -ETIMEDOUT; + + return 0; +} + +static int mv88e6xxx_serdes_init(struct udevice *dev) +{ + int val; + + val = mv88e6xxx_set_page(dev, DEVADDR_SERDES, PHY_PAGE_SERDES); + if (val < 0) + return val; + + /* Power up serdes module */ + val = mv88e6xxx_phy_read(dev, DEVADDR_SERDES, MII_BMCR); + if (val < 0) + return val; + val &= ~(BMCR_PDOWN); + val = mv88e6xxx_phy_write(dev, DEVADDR_SERDES, MII_BMCR, val); + if (val < 0) + return val; + + return 0; +} + +/* + * This function is used to pre-configure the required register + * offsets, so that the indirect register access to the PHY registers + * is possible. This is necessary to be able to read the PHY ID + * while driver probing or in get_phy_id(). The globalN register + * offsets must be initialized correctly for a detected switch, + * otherwise detection of the PHY ID won't work! + */ +static int mv88e6xxx_priv_reg_offs_pre_init(struct udevice *dev) +{ + struct mv88e6xxx_priv *priv = dev_get_priv(dev); + + /* + * Initial 'port_reg_base' value must be an offset of existing + * port register, then reading the ID should succeed. First, try + * to read via port registers with device address 0x10 (88E6096 + * and compatible switches). + */ + priv->port_reg_base = 0x10; + priv->id = mv88e6xxx_get_switch_id(dev); + if (priv->id != 0xfff0) { + priv->global1 = 0x1B; + priv->global2 = 0x1C; + return 0; + } + + /* + * Now try via port registers with device address 0x08 + * (88E6020 and compatible switches). + */ + priv->port_reg_base = 0x08; + priv->id = mv88e6xxx_get_switch_id(dev); + if (priv->id != 0xfff0) { + priv->global1 = 0x0F; + priv->global2 = 0x07; + return 0; + } + + dev_warn(dev, "%s Unknown ID 0x%x\n", __func__, priv->id); + + return -ENODEV; +} + +static int mv88e6xxx_mdio_read(struct udevice *dev, int addr, int devad, int reg) +{ + return mv88e6xxx_phy_read_indirect(dev->parent, DEVADDR_PHY(addr), + MDIO_DEVAD_NONE, reg); +} + +static int mv88e6xxx_mdio_write(struct udevice *dev, int addr, int devad, + int reg, u16 val) +{ + return mv88e6xxx_phy_write_indirect(dev->parent, DEVADDR_PHY(addr), + MDIO_DEVAD_NONE, reg, val); +} + +static const struct mdio_ops mv88e6xxx_mdio_ops = { + .read = mv88e6xxx_mdio_read, + .write = mv88e6xxx_mdio_write, +}; + +static int mv88e6xxx_mdio_bind(struct udevice *dev) +{ + char name[32]; + static int num_devices; + + sprintf(name, "mv88e6xxx-mdio-%d", num_devices++); + device_set_name(dev, name); + + return 0; +} + +U_BOOT_DRIVER(mv88e6xxx_mdio) = { + .name = "mv88e6xxx_mdio", + .id = UCLASS_MDIO, + .ops = &mv88e6xxx_mdio_ops, + .bind = mv88e6xxx_mdio_bind, + .plat_auto = sizeof(struct mdio_perdev_priv), +}; + +static int mv88e6xxx_port_probe(struct udevice *dev, int port, struct phy_device *phy) +{ + struct mv88e6xxx_priv *priv = dev_get_priv(dev); + int supported; + + switch (priv->id) { + case PORT_SWITCH_ID_6020: + case PORT_SWITCH_ID_6070: + case PORT_SWITCH_ID_6071: + supported = PHY_BASIC_FEATURES | SUPPORTED_MII; + break; + default: + supported = PHY_GBIT_FEATURES; + break; + } + + phy->supported &= supported; + phy->advertising &= supported; + + return phy_config(phy); +} + +static int mv88e6xxx_port_enable(struct udevice *dev, int port, struct phy_device *phy) +{ + int val, ret; + + dev_dbg(dev, "%s P%d phy:0x%08x %s\n", __func__, port, + phy->phy_id, phy_string_for_interface(phy->interface)); + + if (phy->phy_id == PHY_FIXED_ID) { + /* Physical Control register: Table 62 */ + val = mv88e6xxx_port_read(dev, port, PORT_REG_PHYS_CTRL); + + /* configure RGMII delays for fixed link */ + switch (phy->interface) { + case PHY_INTERFACE_MODE_RGMII: + case PHY_INTERFACE_MODE_RGMII_ID: + case PHY_INTERFACE_MODE_RGMII_RXID: + case PHY_INTERFACE_MODE_RGMII_TXID: + dev_dbg(dev, "configure internal RGMII delays\n"); + + /* RGMII delays */ + val &= ~(PORT_REG_PHYS_CTRL_RGMII_DELAY_RXCLK || + PORT_REG_PHYS_CTRL_RGMII_DELAY_TXCLK); + if (phy->interface == PHY_INTERFACE_MODE_RGMII_ID || + phy->interface == PHY_INTERFACE_MODE_RGMII_RXID) + val |= PORT_REG_PHYS_CTRL_RGMII_DELAY_RXCLK; + if (phy->interface == PHY_INTERFACE_MODE_RGMII_ID || + phy->interface == PHY_INTERFACE_MODE_RGMII_TXID) + val |= PORT_REG_PHYS_CTRL_RGMII_DELAY_TXCLK; + break; + default: + break; + } + + /* Force Link */ + val |= PORT_REG_PHYS_CTRL_LINK_VALUE | + PORT_REG_PHYS_CTRL_LINK_FORCE; + + ret = mv88e6xxx_port_write(dev, port, PORT_REG_PHYS_CTRL, val); + if (ret < 0) + return ret; + + if (mv88e6xxx_6352_family(dev)) { + /* validate interface type */ + dev_dbg(dev, "validate interface type\n"); + val = mv88e6xxx_get_cmode(dev, port); + if (val < 0) + return val; + switch (phy->interface) { + case PHY_INTERFACE_MODE_RGMII: + case PHY_INTERFACE_MODE_RGMII_RXID: + case PHY_INTERFACE_MODE_RGMII_TXID: + case PHY_INTERFACE_MODE_RGMII_ID: + if (val != PORT_REG_STATUS_CMODE_RGMII) + goto mismatch; + break; + case PHY_INTERFACE_MODE_1000BASEX: + if (val != PORT_REG_STATUS_CMODE_1000BASE_X) + goto mismatch; + break; +mismatch: + default: + dev_err(dev, "Mismatched PHY mode %s on port %d!\n", + phy_string_for_interface(phy->interface), port); + break; + } + } + } + + /* enable port */ + val = mv88e6xxx_port_read(dev, port, PORT_REG_CTRL); + if (val < 0) + return val; + val &= ~(PORT_REG_CTRL_PSTATE_MASK << PORT_REG_CTRL_PSTATE_SHIFT); + val |= (PORT_REG_CTRL_PSTATE_FORWARD << PORT_REG_CTRL_PSTATE_SHIFT); + val = mv88e6xxx_port_write(dev, port, PORT_REG_CTRL, val); + if (val < 0) + return val; + + return phy_startup(phy); +} + +static void mv88e6xxx_port_disable(struct udevice *dev, int port, struct phy_device *phy) +{ + int val; + + dev_dbg(dev, "%s P%d phy:0x%08x %s\n", __func__, port, + phy->phy_id, phy_string_for_interface(phy->interface)); + + val = mv88e6xxx_port_read(dev, port, PORT_REG_CTRL); + val &= ~(PORT_REG_CTRL_PSTATE_MASK << PORT_REG_CTRL_PSTATE_SHIFT); + val |= (PORT_REG_CTRL_PSTATE_DISABLED << PORT_REG_CTRL_PSTATE_SHIFT); + mv88e6xxx_port_write(dev, port, PORT_REG_CTRL, val); +} + +static const struct dsa_ops mv88e6xxx_dsa_ops = { + .port_probe = mv88e6xxx_port_probe, + .port_enable = mv88e6xxx_port_enable, + .port_disable = mv88e6xxx_port_disable, +}; + +/* bind and probe the switch mdios */ +static int mv88e6xxx_probe_mdio(struct udevice *dev) +{ + struct udevice *mdev; + const char *name; + ofnode node; + int ret; + + /* bind phy ports of mdio child node to mv88e6xxx_mdio device */ + node = dev_read_subnode(dev, "mdio"); + if (!ofnode_valid(node)) + return 0; + + name = ofnode_get_name(node); + ret = device_bind_driver_to_node(dev, + "mv88e6xxx_mdio", + name, node, NULL); + if (ret) { + dev_err(dev, "failed to bind %s: %d\n", name, ret); + } else { + /* need to probe it as there is no compatible to do so */ + ret = uclass_get_device_by_ofnode(UCLASS_MDIO, node, &mdev); + if (ret) + dev_err(dev, "failed to probe %s: %d\n", name, ret); + } + + return ret; +} + +static int mv88e6xxx_probe(struct udevice *dev) +{ + struct dsa_pdata *dsa_pdata = dev_get_uclass_plat(dev); + struct mv88e6xxx_priv *priv = dev_get_priv(dev); + int val, ret; + + if (ofnode_valid(dev_ofnode(dev)) && + !ofnode_is_enabled(dev_ofnode(dev))) { + dev_dbg(dev, "switch disabled\n"); + return -ENODEV; + } + + /* probe internal mdio bus */ + ret = mv88e6xxx_probe_mdio(dev); + if (ret) + return ret; + + ret = mv88e6xxx_priv_reg_offs_pre_init(dev); + if (ret) + return ret; + + dev_dbg(dev, "ID=0x%x PORT_BASE=0x%02x GLOBAL1=0x%02x GLOBAL2=0x%02x\n", + priv->id, priv->port_reg_base, priv->global1, priv->global2); + switch (priv->id) { + case PORT_SWITCH_ID_6096: + case PORT_SWITCH_ID_6097: + case PORT_SWITCH_ID_6172: + case PORT_SWITCH_ID_6176: + case PORT_SWITCH_ID_6240: + case PORT_SWITCH_ID_6352: + priv->port_count = 11; + break; + case PORT_SWITCH_ID_6020: + case PORT_SWITCH_ID_6070: + case PORT_SWITCH_ID_6071: + case PORT_SWITCH_ID_6220: + case PORT_SWITCH_ID_6250: + case PORT_SWITCH_ID_6320: + priv->port_count = 7; + break; + default: + return -ENODEV; + } + + ret = mv88e6xxx_switch_reset(dev); + if (ret < 0) + return ret; + + if (mv88e6xxx_6352_family(dev)) { + val = mv88e6xxx_get_cmode(dev, dsa_pdata->cpu_port); + if (val < 0) + return val; + /* initialize serdes */ + if (val == PORT_REG_STATUS_CMODE_100BASE_X || + val == PORT_REG_STATUS_CMODE_1000BASE_X || + val == PORT_REG_STATUS_CMODE_SGMII) { + ret = mv88e6xxx_serdes_init(dev); + if (ret < 0) + return ret; + } + } + + return 0; +} + +static const struct udevice_id mv88e6xxx_ids[] = { + { .compatible = "marvell,mv88e6085" }, + { } +}; + +U_BOOT_DRIVER(mv88e6xxx) = { + .name = "mv88e6xxx", + .id = UCLASS_DSA, + .of_match = mv88e6xxx_ids, + .probe = mv88e6xxx_probe, + .ops = &mv88e6xxx_dsa_ops, + .priv_auto = sizeof(struct mv88e6xxx_priv), +}; From c3d9736d54e7504656577020e3608f01c8fa76d5 Mon Sep 17 00:00:00 2001 From: Tim Harvey Date: Wed, 30 Nov 2022 09:42:51 -0800 Subject: [PATCH 08/19] board: gw_ventana: enable MV88E61XX DSA support Add MV88E61XX DSA support: - update dt to provide internal MDIO bus and port handles. U-Boot requires a more restrictive subset of the dt bindings required by Linux for the sake of simplifying code - update defconfig to remove old driver and enable new one - replace mv88e61xx_hw_reset weak override with board_phy_config support for register configuration that is outside the scope of the DSA driver Signed-off-by: Tim Harvey Reviewed-by: Fabio Estevam Reviewed-by: Vladimir Oltean --- arch/arm/dts/imx6qdl-gw5904.dtsi | 36 +++++++++++++++++- board/gateworks/gw_ventana/gw_ventana.c | 50 +++++++++---------------- configs/gwventana_gw5904_defconfig | 7 ++-- 3 files changed, 56 insertions(+), 37 deletions(-) diff --git a/arch/arm/dts/imx6qdl-gw5904.dtsi b/arch/arm/dts/imx6qdl-gw5904.dtsi index 612b6e068e2..ea54922f15f 100644 --- a/arch/arm/dts/imx6qdl-gw5904.dtsi +++ b/arch/arm/dts/imx6qdl-gw5904.dtsi @@ -212,6 +212,27 @@ compatible = "marvell,mv88e6085"; reg = <0>; + mdio { + #address-cells = <1>; + #size-cells = <0>; + + sw_phy0: ethernet-phy@0 { + reg = <0x0>; + }; + + sw_phy1: ethernet-phy@1 { + reg = <0x1>; + }; + + sw_phy2: ethernet-phy@2 { + reg = <0x2>; + }; + + sw_phy3: ethernet-phy@3 { + reg = <0x3>; + }; + }; + ports { #address-cells = <1>; #size-cells = <0>; @@ -219,27 +240,40 @@ port@0 { reg = <0>; label = "lan4"; + phy-handle = <&sw_phy0>; + phy-mode = "internal"; }; port@1 { reg = <1>; label = "lan3"; + phy-handle = <&sw_phy1>; + phy-mode = "internal"; }; port@2 { reg = <2>; label = "lan2"; + phy-handle = <&sw_phy2>; + phy-mode = "internal"; }; port@3 { reg = <3>; label = "lan1"; + phy-handle = <&sw_phy3>; + phy-mode = "internal"; }; port@5 { reg = <5>; - label = "cpu"; ethernet = <&fec>; + phy-mode = "rgmii-id"; + + fixed-link { + speed = <1000>; + full-duplex; + }; }; }; }; diff --git a/board/gateworks/gw_ventana/gw_ventana.c b/board/gateworks/gw_ventana/gw_ventana.c index 0ecfd98c226..683def7e9f7 100644 --- a/board/gateworks/gw_ventana/gw_ventana.c +++ b/board/gateworks/gw_ventana/gw_ventana.c @@ -83,44 +83,30 @@ int board_phy_config(struct phy_device *phydev) break; } + /* Fixed PHY: for GW5904/GW5909 this is Marvell 88E6176 GbE Switch */ + if (phydev->phy_id == PHY_FIXED_ID && + (board_type == GW5904 || board_type == GW5909)) { + struct mii_dev *bus = miiphy_get_dev_by_name("mdio"); + + puts("MV88E61XX "); + /* GPIO[0] output CLK125 for RGMII_REFCLK */ + bus->write(bus, 0x1c, 0, 0x1a, (1 << 15) | (0x62 << 8) | 0xfe); + bus->write(bus, 0x1c, 0, 0x1a, (1 << 15) | (0x68 << 8) | 7); + + /* Port 0-3 LED configuration: Table 80/82 */ + /* LED configuration: 7:4-green (8=Activity) 3:0 amber (8=Link) */ + bus->write(bus, 0x10, 0, 0x16, 0x8088); + bus->write(bus, 0x11, 0, 0x16, 0x8088); + bus->write(bus, 0x12, 0, 0x16, 0x8088); + bus->write(bus, 0x13, 0, 0x16, 0x8088); + } + if (phydev->drv->config) phydev->drv->config(phydev); return 0; } -#ifdef CONFIG_MV88E61XX_SWITCH -int mv88e61xx_hw_reset(struct phy_device *phydev) -{ - struct mii_dev *bus = phydev->bus; - - /* GPIO[0] output, CLK125 */ - debug("enabling RGMII_REFCLK\n"); - bus->write(bus, 0x1c /*MV_GLOBAL2*/, 0, - 0x1a /*MV_SCRATCH_MISC*/, - (1 << 15) | (0x62 /*MV_GPIO_DIR*/ << 8) | 0xfe); - bus->write(bus, 0x1c /*MV_GLOBAL2*/, 0, - 0x1a /*MV_SCRATCH_MISC*/, - (1 << 15) | (0x68 /*MV_GPIO01_CNTL*/ << 8) | 7); - - /* RGMII delay - Physical Control register bit[15:14] */ - debug("setting port%d RGMII rx/tx delay\n", CONFIG_MV88E61XX_CPU_PORT); - /* forced 1000mbps full-duplex link */ - bus->write(bus, 0x10 + CONFIG_MV88E61XX_CPU_PORT, 0, 1, 0xc0fe); - phydev->autoneg = AUTONEG_DISABLE; - phydev->speed = SPEED_1000; - phydev->duplex = DUPLEX_FULL; - - /* LED configuration: 7:4-green (8=Activity) 3:0 amber (8=Link) */ - bus->write(bus, 0x10, 0, 0x16, 0x8088); - bus->write(bus, 0x11, 0, 0x16, 0x8088); - bus->write(bus, 0x12, 0, 0x16, 0x8088); - bus->write(bus, 0x13, 0, 0x16, 0x8088); - - return 0; -} -#endif // CONFIG_MV88E61XX_SWITCH - #if defined(CONFIG_VIDEO_IPUV3) static void enable_hdmi(struct display_info_t const *dev) { diff --git a/configs/gwventana_gw5904_defconfig b/configs/gwventana_gw5904_defconfig index 37fc1e51430..8387d0fa79e 100644 --- a/configs/gwventana_gw5904_defconfig +++ b/configs/gwventana_gw5904_defconfig @@ -115,13 +115,12 @@ CONFIG_SUPPORT_EMMC_BOOT=y CONFIG_FSL_USDHC=y CONFIG_MTD=y CONFIG_PHYLIB=y -CONFIG_MV88E61XX_SWITCH=y -CONFIG_MV88E61XX_CPU_PORT=5 -CONFIG_MV88E61XX_PHY_PORTS=0xf -CONFIG_MV88E61XX_FIXED_PORTS=0x0 +CONFIG_PHY_FIXED=y CONFIG_DM_MDIO=y +CONFIG_DM_DSA=y CONFIG_E1000=y CONFIG_FEC_MXC=y +CONFIG_MV88E6XXX=y CONFIG_MII=y CONFIG_PCI=y CONFIG_PCIE_IMX=y From 21a265c0d1ef93ed40e1990f33be0bc427e956f0 Mon Sep 17 00:00:00 2001 From: Sean Edmond Date: Wed, 4 Jan 2023 18:16:26 -0800 Subject: [PATCH 09/19] net: tftp: Fix for DATA ACK for block count out of order In rfc7440, if an ACK is not received by the server or if the last data block in a window is dropped, the server will timeout and retransmit the window. In this case, the block count received will be less than the internal block count. In this case, the client should not ACK. ACK should only be sent if the received block count is greater than the expected block count. Signed-off-by: Sean Edmond Reviewed-by: Ramon Fried --- net/tftp.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/net/tftp.c b/net/tftp.c index c780c33f379..51e062bddf7 100644 --- a/net/tftp.c +++ b/net/tftp.c @@ -592,6 +592,14 @@ static void tftp_handler(uchar *pkt, unsigned dest, struct in_addr sip, debug("Received unexpected block: %d, expected: %d\n", ntohs(*(__be16 *)pkt), (ushort)(tftp_cur_block + 1)); + /* + * Only ACK if the block count received is greater than + * the expected block count, otherwise skip ACK. + * (required to properly handle the server retransmitting + * the window) + */ + if ((ushort)(tftp_cur_block + 1) - (short)(ntohs(*(__be16 *)pkt)) > 0) + break; /* * If one packet is dropped most likely * all other buffers in the window From 32221ee236b933c14e1cd1c6393c255086f519c6 Mon Sep 17 00:00:00 2001 From: Ioana Ciornei Date: Thu, 5 Jan 2023 17:03:15 +0200 Subject: [PATCH 10/19] drivers: net: fsl-mc: remove useless assignment of variable The cur_ptr variable is set to the start of the log buffer but then it's not used. Just remove the assignment altogether. Signed-off-by: Ioana Ciornei Reviewed-by: Ramon Fried --- drivers/net/fsl-mc/mc.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/fsl-mc/mc.c b/drivers/net/fsl-mc/mc.c index 6b36860187c..66fcb48ebd5 100644 --- a/drivers/net/fsl-mc/mc.c +++ b/drivers/net/fsl-mc/mc.c @@ -1796,7 +1796,6 @@ static void mc_dump_log(void) if (size > bytes_end) { print_k_bytes(cur_ptr, &bytes_end); - cur_ptr = buf; size -= bytes_end; } From 3ad24e4d361e6e37f782a492c14f43d928c4c6f6 Mon Sep 17 00:00:00 2001 From: Ioana Ciornei Date: Thu, 5 Jan 2023 17:03:16 +0200 Subject: [PATCH 11/19] drivers: net: fsl-mc: remove an useless break statement The break statement is just after a goto statement, thus it will not get executed. Just remove it. Signed-off-by: Ioana Ciornei Reviewed-by: Ramon Fried --- drivers/net/fsl-mc/mc.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/fsl-mc/mc.c b/drivers/net/fsl-mc/mc.c index 66fcb48ebd5..180e57e4226 100644 --- a/drivers/net/fsl-mc/mc.c +++ b/drivers/net/fsl-mc/mc.c @@ -1961,7 +1961,6 @@ static int do_fsl_mc(struct cmd_tbl *cmdtp, int flag, int argc, default: printf("Invalid option: %s\n", argv[1]); goto usage; - break; } return err; usage: From 8ecf1ca093bbe27d11af86212c1b974a5c9918c2 Mon Sep 17 00:00:00 2001 From: Ioana Ciornei Date: Thu, 5 Jan 2023 17:03:17 +0200 Subject: [PATCH 12/19] drivers: net: fsl-mc: do not prefix decimal values with 0x The fsl-mc driver printed debug information which used the 0x prefix for decimal values. This only confuses anyone looking through the log. Because of this, just remove the prefix and use the "DPXY." notation which is the standard one for the DPAA2 objects. Signed-off-by: Ioana Ciornei Reviewed-by: Ramon Fried --- drivers/net/fsl-mc/mc.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/net/fsl-mc/mc.c b/drivers/net/fsl-mc/mc.c index 180e57e4226..440273a1756 100644 --- a/drivers/net/fsl-mc/mc.c +++ b/drivers/net/fsl-mc/mc.c @@ -1038,7 +1038,7 @@ static int dpio_init(void) } #ifdef DEBUG - printf("Init: DPIO id=0x%d\n", dflt_dpio->dpio_id); + printf("Init: DPIO.%d\n", dflt_dpio->dpio_id); #endif err = dpio_enable(dflt_mc_io, MC_CMD_NO_FLAGS, dflt_dpio->dpio_handle); if (err < 0) { @@ -1107,7 +1107,7 @@ static int dpio_exit(void) } #ifdef DEBUG - printf("Exit: DPIO id=0x%d\n", dflt_dpio->dpio_id); + printf("Exit: DPIO.%d\n", dflt_dpio->dpio_id); #endif if (dflt_dpio) @@ -1312,7 +1312,7 @@ static int dpbp_init(void) } #ifdef DEBUG - printf("Init: DPBP id=0x%x\n", dflt_dpbp->dpbp_attr.id); + printf("Init: DPBP.%d\n", dflt_dpbp->dpbp_attr.id); #endif err = dpbp_close(dflt_mc_io, MC_CMD_NO_FLAGS, dflt_dpbp->dpbp_handle); @@ -1351,7 +1351,7 @@ static int dpbp_exit(void) } #ifdef DEBUG - printf("Exit: DPBP id=0x%d\n", dflt_dpbp->dpbp_attr.id); + printf("Exit: DPBP.%d\n", dflt_dpbp->dpbp_attr.id); #endif if (dflt_dpbp) @@ -1422,7 +1422,7 @@ static int dpni_init(void) } #ifdef DEBUG - printf("Init: DPNI id=0x%d\n", dflt_dpni->dpni_id); + printf("Init: DPNI.%d\n", dflt_dpni->dpni_id); #endif err = dpni_close(dflt_mc_io, MC_CMD_NO_FLAGS, dflt_dpni->dpni_handle); if (err < 0) { @@ -1459,7 +1459,7 @@ static int dpni_exit(void) } #ifdef DEBUG - printf("Exit: DPNI id=0x%d\n", dflt_dpni->dpni_id); + printf("Exit: DPNI.%d\n", dflt_dpni->dpni_id); #endif if (dflt_dpni) From 6dcf5e44774347f0079bb351d44539b5c8a0b0ff Mon Sep 17 00:00:00 2001 From: Ioana Ciornei Date: Thu, 5 Jan 2023 17:03:19 +0200 Subject: [PATCH 13/19] drivers: net: fsl-mc: remove explicit cast Remove all the explicit casts from the void* returned by calloc. With this we also improve a bit the length of those lines and there is no need to split the assignment. Signed-off-by: Ioana Ciornei Reviewed-by: Ramon Fried --- drivers/net/fsl-mc/mc.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/drivers/net/fsl-mc/mc.c b/drivers/net/fsl-mc/mc.c index 440273a1756..3c618c7d3da 100644 --- a/drivers/net/fsl-mc/mc.c +++ b/drivers/net/fsl-mc/mc.c @@ -812,7 +812,7 @@ int mc_init(u64 mc_fw_addr, u64 mc_dpc_addr) * Initialize the global default MC portal * And check that the MC firmware is responding portal commands: */ - root_mc_io = (struct fsl_mc_io *)calloc(sizeof(struct fsl_mc_io), 1); + root_mc_io = calloc(sizeof(struct fsl_mc_io), 1); if (!root_mc_io) { printf(" No memory: calloc() failed\n"); return -ENOMEM; @@ -979,8 +979,7 @@ static int dpio_init(void) int err = 0; uint16_t major_ver, minor_ver; - dflt_dpio = (struct fsl_dpio_obj *)calloc( - sizeof(struct fsl_dpio_obj), 1); + dflt_dpio = calloc(sizeof(struct fsl_dpio_obj), 1); if (!dflt_dpio) { printf("No memory: calloc() failed\n"); err = -ENOMEM; @@ -1168,7 +1167,7 @@ static int dprc_init(void) goto err_create; } - dflt_mc_io = (struct fsl_mc_io *)calloc(sizeof(struct fsl_mc_io), 1); + dflt_mc_io = calloc(sizeof(struct fsl_mc_io), 1); if (!dflt_mc_io) { err = -ENOMEM; printf(" No memory: calloc() failed\n"); @@ -1250,8 +1249,7 @@ static int dpbp_init(void) struct dpbp_cfg dpbp_cfg; uint16_t major_ver, minor_ver; - dflt_dpbp = (struct fsl_dpbp_obj *)calloc( - sizeof(struct fsl_dpbp_obj), 1); + dflt_dpbp = calloc(sizeof(struct fsl_dpbp_obj), 1); if (!dflt_dpbp) { printf("No memory: calloc() failed\n"); err = -ENOMEM; @@ -1369,8 +1367,7 @@ static int dpni_init(void) struct dpni_cfg dpni_cfg; uint16_t major_ver, minor_ver; - dflt_dpni = (struct fsl_dpni_obj *)calloc( - sizeof(struct fsl_dpni_obj), 1); + dflt_dpni = calloc(sizeof(struct fsl_dpni_obj), 1); if (!dflt_dpni) { printf("No memory: calloc() failed\n"); err = -ENOMEM; From f45ed0b5ec18c6a2df4d04c53c65d3c016ca085f Mon Sep 17 00:00:00 2001 From: Ioana Ciornei Date: Thu, 5 Jan 2023 17:03:20 +0200 Subject: [PATCH 14/19] drivers: net: fsl-mc: align parameters to the open paranthesis There were some cases in which the function parameters were not aligned to the open paranthesis. Fix those instances. Signed-off-by: Ioana Ciornei Reviewed-by: Ramon Fried --- drivers/net/fsl-mc/mc.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/drivers/net/fsl-mc/mc.c b/drivers/net/fsl-mc/mc.c index 3c618c7d3da..d33a39dca9b 100644 --- a/drivers/net/fsl-mc/mc.c +++ b/drivers/net/fsl-mc/mc.c @@ -356,8 +356,7 @@ static int mc_fixup_dpc_mac_addr(void *blob, int dpmac_id, if (noff < 0) { err = fdt_increase_size(blob, 200); if (err) { - printf("fdt_increase_size: err=%s\n", - fdt_strerror(err)); + printf("fdt_increase_size: err=%s\n", fdt_strerror(err)); return err; } @@ -373,7 +372,7 @@ static int mc_fixup_dpc_mac_addr(void *blob, int dpmac_id, "link_type", link_type_mode); if (err) { printf("fdt_appendprop_string: err=%s\n", - fdt_strerror(err)); + fdt_strerror(err)); return err; } } @@ -1158,10 +1157,9 @@ static int dprc_init(void) cfg.icid = DPRC_GET_ICID_FROM_POOL; cfg.portal_id = DPRC_GET_PORTAL_ID_FROM_POOL; err = dprc_create_container(root_mc_io, MC_CMD_NO_FLAGS, - root_dprc_handle, - &cfg, - &child_dprc_id, - &mc_portal_offset); + root_dprc_handle, &cfg, + &child_dprc_id, + &mc_portal_offset); if (err < 0) { printf("dprc_create_container() failed: %d\n", err); goto err_create; From cfbd388ae756aac9151f21c9eb09d53f79360e6e Mon Sep 17 00:00:00 2001 From: Ioana Ciornei Date: Thu, 5 Jan 2023 17:03:21 +0200 Subject: [PATCH 15/19] drivers: net: fsl-mc: do not use multiple blank lines Remove the instances in which we have multiple blank lines. Signed-off-by: Ioana Ciornei Reviewed-by: Ramon Fried --- drivers/net/fsl-mc/mc.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/fsl-mc/mc.c b/drivers/net/fsl-mc/mc.c index d33a39dca9b..4f84403d956 100644 --- a/drivers/net/fsl-mc/mc.c +++ b/drivers/net/fsl-mc/mc.c @@ -526,7 +526,6 @@ static int load_mc_dpc(u64 mc_ram_addr, size_t mc_ram_size, u64 mc_dpc_addr) return 0; } - static int mc_fixup_dpl(u64 dpl_addr) { void *blob = (void *)dpl_addr; @@ -698,7 +697,6 @@ static int wait_for_mc(bool booting_mc, u32 *final_reg_gsr) printf("SUCCESS\n"); } - *final_reg_gsr = reg_gsr; return 0; } From 878a20aa15880e8b0cc2009331e7b207b7c668c0 Mon Sep 17 00:00:00 2001 From: Ioana Ciornei Date: Thu, 5 Jan 2023 17:09:48 +0200 Subject: [PATCH 16/19] drivers: net: fsl_ls_mdio: prevent a NULL pointer dereference Prevent a NULL pointer dereference in the probe path by checking the return valud of dev_read_addr_ptr() against NULL. Signed-off-by: Ioana Ciornei Reviewed-by: Ramon Fried --- drivers/net/fsl_ls_mdio.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/fsl_ls_mdio.c b/drivers/net/fsl_ls_mdio.c index f213e0dd859..fce73937502 100644 --- a/drivers/net/fsl_ls_mdio.c +++ b/drivers/net/fsl_ls_mdio.c @@ -124,6 +124,9 @@ static int fsl_ls_mdio_probe(struct udevice *dev) struct memac_mdio_controller *regs; priv->regs_base = dev_read_addr_ptr(dev); + if (!priv->regs_base) + return -ENODEV; + regs = (struct memac_mdio_controller *)(priv->regs_base); memac_setbits_32(®s->mdio_stat, From 796e549822bdb49128578d8ae82d28a5249b4816 Mon Sep 17 00:00:00 2001 From: Sean Edmond Date: Fri, 6 Jan 2023 14:22:55 -0800 Subject: [PATCH 17/19] net: ipv6: Fix IPv6 netmask parsing It should be possible to specify a netmask when setting a static IPv6 address. For example: setenv ip6addr 2001:cafe:cafe:cafe::100/64 The net_prefix_length and net_ip6 should be updated properly. Signed-off-by: Sean Edmond Reviewed-by: Viacheslav Mitrofanov Reviewed-by: Ramon Fried --- net/net6.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/net/net6.c b/net/net6.c index fdea0787885..75577bcea17 100644 --- a/net/net6.c +++ b/net/net6.c @@ -47,10 +47,13 @@ static int on_ip6addr(const char *name, const char *value, enum env_op op, } mask = strchr(value, '/'); - len = strlen(value); - if (mask) - net_prefix_length = simple_strtoul(value + len, NULL, 10); + if (mask) { + net_prefix_length = simple_strtoul(mask + 1, NULL, 10); + len = mask - value; + } else { + len = strlen(value); + } return string_to_ip6(value, len, &net_ip6); } From 7db25d99b21ae7b2a04153a93afe7692c86992ac Mon Sep 17 00:00:00 2001 From: Ehsan Mohandesi Date: Fri, 13 Jan 2023 09:27:41 -0800 Subject: [PATCH 18/19] net: ipv6: Fixed IPv6 string to address conversion off-by-one error One extra character was being checked in the IPv6 string which caused the last character of the address to be neither '\0' nor ':'. This raises an error condition and causes the function to always return an error. This issue was resolved by this fix. Signed-off-by: Ehsan Mohandesi Reviewed-by: Viacheslav Mitrofanov --- net/tftp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/tftp.c b/net/tftp.c index 51e062bddf7..88e71e67de3 100644 --- a/net/tftp.c +++ b/net/tftp.c @@ -845,7 +845,7 @@ void tftp_start(enum proto_t protocol) e = strchr(net_boot_file_name, ']'); len = e - s; if (s && e) { - string_to_ip6(s + 1, len, &tftp_remote_ip6); + string_to_ip6(s + 1, len - 1, &tftp_remote_ip6); strlcpy(tftp_filename, e + 2, MAX_LEN); } else { strlcpy(tftp_filename, net_boot_file_name, MAX_LEN); From 9bc80c0bc028a1658502eea02af2c1f26456c6b2 Mon Sep 17 00:00:00 2001 From: Sergei Antonov Date: Wed, 18 Jan 2023 20:52:18 +0300 Subject: [PATCH 19/19] net: ipv6: fix alignment errors on ARM Commands "ping6" and "tftpboot ... -ipv6" did not work on ARM because machine code expects 4-byte alignment and some structures from net6.h are not aligned in memory. Fix by adding __packed, since it is already used in this file. Signed-off-by: Sergei Antonov Reviewed-by: Viacheslav Mitrofanov --- include/net6.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/net6.h b/include/net6.h index 9b3de028e6d..2d7c5a09604 100644 --- a/include/net6.h +++ b/include/net6.h @@ -24,7 +24,7 @@ struct in6_addr { #define s6_addr in6_u.u6_addr8 #define s6_addr16 in6_u.u6_addr16 #define s6_addr32 in6_u.u6_addr32 -}; +} __packed; #define IN6ADDRSZ sizeof(struct in6_addr) #define INETHADDRSZ sizeof(net_ethaddr) @@ -62,7 +62,7 @@ struct ip6_hdr { u8 hop_limit; struct in6_addr saddr; struct in6_addr daddr; -}; +} __packed; #define IP6_HDR_SIZE (sizeof(struct ip6_hdr)) /* struct udp_hdr - User Datagram Protocol header */ @@ -164,7 +164,7 @@ struct icmp6hdr { #define icmp6_addrconf_managed icmp6_dataun.u_nd_ra.managed #define icmp6_addrconf_other icmp6_dataun.u_nd_ra.other #define icmp6_rt_lifetime icmp6_dataun.u_nd_ra.rt_lifetime -}; +} __packed; extern struct in6_addr const net_null_addr_ip6; /* NULL IPv6 address */ extern struct in6_addr net_gateway6; /* Our gateways IPv6 address */