Merge branch '2023-02-02-assorted-networking-updates'

- DSA driver for the MV88E6xxx, assorted IPv6 fixes, TFTP fix, fsl-mc
  cleanup coding style and fsl_ls_mdio bugfix
This commit is contained in:
Tom Rini 2023-02-03 10:27:27 -05:00
commit b102bfa15e
15 changed files with 990 additions and 108 deletions

View file

@ -212,6 +212,27 @@
compatible = "marvell,mv88e6085"; compatible = "marvell,mv88e6085";
reg = <0>; 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 { ports {
#address-cells = <1>; #address-cells = <1>;
#size-cells = <0>; #size-cells = <0>;
@ -219,27 +240,40 @@
port@0 { port@0 {
reg = <0>; reg = <0>;
label = "lan4"; label = "lan4";
phy-handle = <&sw_phy0>;
phy-mode = "internal";
}; };
port@1 { port@1 {
reg = <1>; reg = <1>;
label = "lan3"; label = "lan3";
phy-handle = <&sw_phy1>;
phy-mode = "internal";
}; };
port@2 { port@2 {
reg = <2>; reg = <2>;
label = "lan2"; label = "lan2";
phy-handle = <&sw_phy2>;
phy-mode = "internal";
}; };
port@3 { port@3 {
reg = <3>; reg = <3>;
label = "lan1"; label = "lan1";
phy-handle = <&sw_phy3>;
phy-mode = "internal";
}; };
port@5 { port@5 {
reg = <5>; reg = <5>;
label = "cpu";
ethernet = <&fec>; ethernet = <&fec>;
phy-mode = "rgmii-id";
fixed-link {
speed = <1000>;
full-duplex;
};
}; };
}; };
}; };

View file

@ -83,44 +83,30 @@ int board_phy_config(struct phy_device *phydev)
break; 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) if (phydev->drv->config)
phydev->drv->config(phydev); phydev->drv->config(phydev);
return 0; 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) #if defined(CONFIG_VIDEO_IPUV3)
static void enable_hdmi(struct display_info_t const *dev) static void enable_hdmi(struct display_info_t const *dev)
{ {

View file

@ -115,13 +115,12 @@ CONFIG_SUPPORT_EMMC_BOOT=y
CONFIG_FSL_USDHC=y CONFIG_FSL_USDHC=y
CONFIG_MTD=y CONFIG_MTD=y
CONFIG_PHYLIB=y CONFIG_PHYLIB=y
CONFIG_MV88E61XX_SWITCH=y CONFIG_PHY_FIXED=y
CONFIG_MV88E61XX_CPU_PORT=5
CONFIG_MV88E61XX_PHY_PORTS=0xf
CONFIG_MV88E61XX_FIXED_PORTS=0x0
CONFIG_DM_MDIO=y CONFIG_DM_MDIO=y
CONFIG_DM_DSA=y
CONFIG_E1000=y CONFIG_E1000=y
CONFIG_FEC_MXC=y CONFIG_FEC_MXC=y
CONFIG_MV88E6XXX=y
CONFIG_MII=y CONFIG_MII=y
CONFIG_PCI=y CONFIG_PCI=y
CONFIG_PCIE_IMX=y CONFIG_PCIE_IMX=y

View file

@ -473,6 +473,13 @@ config LITEETH
help help
Driver for the LiteEth Ethernet MAC from LiteX. 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 config MVGBE
bool "Marvell Orion5x/Kirkwood network interface support" bool "Marvell Orion5x/Kirkwood network interface support"
depends on ARCH_KIRKWOOD || ARCH_ORION5X depends on ARCH_KIRKWOOD || ARCH_ORION5X

View file

@ -60,6 +60,7 @@ obj-$(CONFIG_MEDIATEK_ETH) += mtk_eth.o
obj-$(CONFIG_MPC8XX_FEC) += mpc8xx_fec.o obj-$(CONFIG_MPC8XX_FEC) += mpc8xx_fec.o
obj-$(CONFIG_MT7620_ETH) += mt7620-eth.o obj-$(CONFIG_MT7620_ETH) += mt7620-eth.o
obj-$(CONFIG_MT7628_ETH) += mt7628-eth.o obj-$(CONFIG_MT7628_ETH) += mt7628-eth.o
obj-$(CONFIG_MV88E6XXX) += mv88e6xxx.o
obj-$(CONFIG_MVGBE) += mvgbe.o obj-$(CONFIG_MVGBE) += mvgbe.o
obj-$(CONFIG_MVMDIO) += mvmdio.o obj-$(CONFIG_MVMDIO) += mvmdio.o
obj-$(CONFIG_MVNETA) += mvneta.o obj-$(CONFIG_MVNETA) += mvneta.o

View file

@ -30,6 +30,8 @@
#include <asm/arch/imx-regs.h> #include <asm/arch/imx-regs.h>
#include <asm/mach-imx/sys_proto.h> #include <asm/mach-imx/sys_proto.h>
#include <asm-generic/gpio.h> #include <asm-generic/gpio.h>
#include <dm/device_compat.h>
#include <dm/lists.h>
#include "fec_mxc.h" #include "fec_mxc.h"
#include <eth_phy.h> #include <eth_phy.h>
@ -1019,6 +1021,81 @@ struct mii_dev *fec_get_miibus(ulong base_addr, int dev_id)
return bus; 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) static int fecmxc_read_rom_hwaddr(struct udevice *dev)
{ {
struct fec_priv *priv = dev_get_priv(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) static int fec_phy_init(struct fec_priv *priv, struct udevice *dev)
{ {
struct phy_device *phydev; struct phy_device *phydev = NULL;
int addr; int addr;
addr = device_get_phy_addr(priv, dev); 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; addr = CFG_FEC_MXC_PHYADDR;
#endif #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) if (!phydev)
return -ENODEV; return -ENODEV;
@ -1221,6 +1301,12 @@ static int fecmxc_probe(struct udevice *dev)
priv->dev_id = dev_seq(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 #ifdef CONFIG_DM_ETH_PHY
bus = eth_phy_get_mdio_bus(dev); bus = eth_phy_get_mdio_bus(dev);
#endif #endif

View file

@ -356,8 +356,7 @@ static int mc_fixup_dpc_mac_addr(void *blob, int dpmac_id,
if (noff < 0) { if (noff < 0) {
err = fdt_increase_size(blob, 200); err = fdt_increase_size(blob, 200);
if (err) { if (err) {
printf("fdt_increase_size: err=%s\n", printf("fdt_increase_size: err=%s\n", fdt_strerror(err));
fdt_strerror(err));
return err; return err;
} }
@ -373,7 +372,7 @@ static int mc_fixup_dpc_mac_addr(void *blob, int dpmac_id,
"link_type", link_type_mode); "link_type", link_type_mode);
if (err) { if (err) {
printf("fdt_appendprop_string: err=%s\n", printf("fdt_appendprop_string: err=%s\n",
fdt_strerror(err)); fdt_strerror(err));
return err; return err;
} }
} }
@ -527,7 +526,6 @@ static int load_mc_dpc(u64 mc_ram_addr, size_t mc_ram_size, u64 mc_dpc_addr)
return 0; return 0;
} }
static int mc_fixup_dpl(u64 dpl_addr) static int mc_fixup_dpl(u64 dpl_addr)
{ {
void *blob = (void *)dpl_addr; void *blob = (void *)dpl_addr;
@ -699,7 +697,6 @@ static int wait_for_mc(bool booting_mc, u32 *final_reg_gsr)
printf("SUCCESS\n"); printf("SUCCESS\n");
} }
*final_reg_gsr = reg_gsr; *final_reg_gsr = reg_gsr;
return 0; return 0;
} }
@ -812,7 +809,7 @@ int mc_init(u64 mc_fw_addr, u64 mc_dpc_addr)
* Initialize the global default MC portal * Initialize the global default MC portal
* And check that the MC firmware is responding portal commands: * 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) { if (!root_mc_io) {
printf(" No memory: calloc() failed\n"); printf(" No memory: calloc() failed\n");
return -ENOMEM; return -ENOMEM;
@ -979,8 +976,7 @@ static int dpio_init(void)
int err = 0; int err = 0;
uint16_t major_ver, minor_ver; uint16_t major_ver, minor_ver;
dflt_dpio = (struct fsl_dpio_obj *)calloc( dflt_dpio = calloc(sizeof(struct fsl_dpio_obj), 1);
sizeof(struct fsl_dpio_obj), 1);
if (!dflt_dpio) { if (!dflt_dpio) {
printf("No memory: calloc() failed\n"); printf("No memory: calloc() failed\n");
err = -ENOMEM; err = -ENOMEM;
@ -1038,7 +1034,7 @@ static int dpio_init(void)
} }
#ifdef DEBUG #ifdef DEBUG
printf("Init: DPIO id=0x%d\n", dflt_dpio->dpio_id); printf("Init: DPIO.%d\n", dflt_dpio->dpio_id);
#endif #endif
err = dpio_enable(dflt_mc_io, MC_CMD_NO_FLAGS, dflt_dpio->dpio_handle); err = dpio_enable(dflt_mc_io, MC_CMD_NO_FLAGS, dflt_dpio->dpio_handle);
if (err < 0) { if (err < 0) {
@ -1107,7 +1103,7 @@ static int dpio_exit(void)
} }
#ifdef DEBUG #ifdef DEBUG
printf("Exit: DPIO id=0x%d\n", dflt_dpio->dpio_id); printf("Exit: DPIO.%d\n", dflt_dpio->dpio_id);
#endif #endif
if (dflt_dpio) if (dflt_dpio)
@ -1159,16 +1155,15 @@ static int dprc_init(void)
cfg.icid = DPRC_GET_ICID_FROM_POOL; cfg.icid = DPRC_GET_ICID_FROM_POOL;
cfg.portal_id = DPRC_GET_PORTAL_ID_FROM_POOL; cfg.portal_id = DPRC_GET_PORTAL_ID_FROM_POOL;
err = dprc_create_container(root_mc_io, MC_CMD_NO_FLAGS, err = dprc_create_container(root_mc_io, MC_CMD_NO_FLAGS,
root_dprc_handle, root_dprc_handle, &cfg,
&cfg, &child_dprc_id,
&child_dprc_id, &mc_portal_offset);
&mc_portal_offset);
if (err < 0) { if (err < 0) {
printf("dprc_create_container() failed: %d\n", err); printf("dprc_create_container() failed: %d\n", err);
goto err_create; 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) { if (!dflt_mc_io) {
err = -ENOMEM; err = -ENOMEM;
printf(" No memory: calloc() failed\n"); printf(" No memory: calloc() failed\n");
@ -1250,8 +1245,7 @@ static int dpbp_init(void)
struct dpbp_cfg dpbp_cfg; struct dpbp_cfg dpbp_cfg;
uint16_t major_ver, minor_ver; uint16_t major_ver, minor_ver;
dflt_dpbp = (struct fsl_dpbp_obj *)calloc( dflt_dpbp = calloc(sizeof(struct fsl_dpbp_obj), 1);
sizeof(struct fsl_dpbp_obj), 1);
if (!dflt_dpbp) { if (!dflt_dpbp) {
printf("No memory: calloc() failed\n"); printf("No memory: calloc() failed\n");
err = -ENOMEM; err = -ENOMEM;
@ -1312,7 +1306,7 @@ static int dpbp_init(void)
} }
#ifdef DEBUG #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 #endif
err = dpbp_close(dflt_mc_io, MC_CMD_NO_FLAGS, dflt_dpbp->dpbp_handle); err = dpbp_close(dflt_mc_io, MC_CMD_NO_FLAGS, dflt_dpbp->dpbp_handle);
@ -1351,7 +1345,7 @@ static int dpbp_exit(void)
} }
#ifdef DEBUG #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 #endif
if (dflt_dpbp) if (dflt_dpbp)
@ -1369,8 +1363,7 @@ static int dpni_init(void)
struct dpni_cfg dpni_cfg; struct dpni_cfg dpni_cfg;
uint16_t major_ver, minor_ver; uint16_t major_ver, minor_ver;
dflt_dpni = (struct fsl_dpni_obj *)calloc( dflt_dpni = calloc(sizeof(struct fsl_dpni_obj), 1);
sizeof(struct fsl_dpni_obj), 1);
if (!dflt_dpni) { if (!dflt_dpni) {
printf("No memory: calloc() failed\n"); printf("No memory: calloc() failed\n");
err = -ENOMEM; err = -ENOMEM;
@ -1422,7 +1415,7 @@ static int dpni_init(void)
} }
#ifdef DEBUG #ifdef DEBUG
printf("Init: DPNI id=0x%d\n", dflt_dpni->dpni_id); printf("Init: DPNI.%d\n", dflt_dpni->dpni_id);
#endif #endif
err = dpni_close(dflt_mc_io, MC_CMD_NO_FLAGS, dflt_dpni->dpni_handle); err = dpni_close(dflt_mc_io, MC_CMD_NO_FLAGS, dflt_dpni->dpni_handle);
if (err < 0) { if (err < 0) {
@ -1459,7 +1452,7 @@ static int dpni_exit(void)
} }
#ifdef DEBUG #ifdef DEBUG
printf("Exit: DPNI id=0x%d\n", dflt_dpni->dpni_id); printf("Exit: DPNI.%d\n", dflt_dpni->dpni_id);
#endif #endif
if (dflt_dpni) if (dflt_dpni)
@ -1796,7 +1789,6 @@ static void mc_dump_log(void)
if (size > bytes_end) { if (size > bytes_end) {
print_k_bytes(cur_ptr, &bytes_end); print_k_bytes(cur_ptr, &bytes_end);
cur_ptr = buf;
size -= bytes_end; size -= bytes_end;
} }
@ -1962,7 +1954,6 @@ static int do_fsl_mc(struct cmd_tbl *cmdtp, int flag, int argc,
default: default:
printf("Invalid option: %s\n", argv[1]); printf("Invalid option: %s\n", argv[1]);
goto usage; goto usage;
break;
} }
return err; return err;
usage: usage:

View file

@ -124,6 +124,9 @@ static int fsl_ls_mdio_probe(struct udevice *dev)
struct memac_mdio_controller *regs; struct memac_mdio_controller *regs;
priv->regs_base = dev_read_addr_ptr(dev); priv->regs_base = dev_read_addr_ptr(dev);
if (!priv->regs_base)
return -ENODEV;
regs = (struct memac_mdio_controller *)(priv->regs_base); regs = (struct memac_mdio_controller *)(priv->regs_base);
memac_setbits_32(&regs->mdio_stat, memac_setbits_32(&regs->mdio_stat,

View file

@ -62,7 +62,6 @@
struct ksz_dsa_priv { struct ksz_dsa_priv {
struct udevice *dev; struct udevice *dev;
int active_port;
}; };
static inline int ksz_read8(struct udevice *dev, u32 reg, u8 *val) 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; data8 |= SW_START;
ksz_write8(priv->dev, REG_SW_OPERATION, data8); ksz_write8(priv->dev, REG_SW_OPERATION, data8);
/* keep track of current enabled non-cpu port */
priv->active_port = port;
return 0; 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 = { static const struct dsa_ops ksz_dsa_ops = {
.port_enable = ksz_port_enable, .port_enable = ksz_port_enable,
.port_disable = ksz_port_disable, .port_disable = ksz_port_disable,
.xmit = ksz_xmit,
.rcv = ksz_recv,
}; };
static int ksz_probe_mdio(struct udevice *dev) static int ksz_probe_mdio(struct udevice *dev)

755
drivers/net/mv88e6xxx.c Normal file
View file

@ -0,0 +1,755 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2022
* Gateworks Corporation <www.gateworks.com>
* Tim Harvey <tharvey@gateworks.com>
*
* (C) Copyright 2015
* Elecsys Corporation <www.elecsyscorp.com>
* Kevin Smith <kevin.smith@elecsyscorp.com>
*
* Original driver:
* (C) Copyright 2009
* Marvell Semiconductor <www.marvell.com>
* Prafulla Wadaskar <prafulla@marvell.com>
*/
/*
* 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 <common.h>
#include <dm/device.h>
#include <dm/device_compat.h>
#include <dm/device-internal.h>
#include <dm/lists.h>
#include <dm/of_extra.h>
#include <linux/delay.h>
#include <miiphy.h>
#include <net/dsa.h>
/* 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),
};

View file

@ -24,7 +24,7 @@ struct in6_addr {
#define s6_addr in6_u.u6_addr8 #define s6_addr in6_u.u6_addr8
#define s6_addr16 in6_u.u6_addr16 #define s6_addr16 in6_u.u6_addr16
#define s6_addr32 in6_u.u6_addr32 #define s6_addr32 in6_u.u6_addr32
}; } __packed;
#define IN6ADDRSZ sizeof(struct in6_addr) #define IN6ADDRSZ sizeof(struct in6_addr)
#define INETHADDRSZ sizeof(net_ethaddr) #define INETHADDRSZ sizeof(net_ethaddr)
@ -62,7 +62,7 @@ struct ip6_hdr {
u8 hop_limit; u8 hop_limit;
struct in6_addr saddr; struct in6_addr saddr;
struct in6_addr daddr; struct in6_addr daddr;
}; } __packed;
#define IP6_HDR_SIZE (sizeof(struct ip6_hdr)) #define IP6_HDR_SIZE (sizeof(struct ip6_hdr))
/* struct udp_hdr - User Datagram Protocol header */ /* 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_managed icmp6_dataun.u_nd_ra.managed
#define icmp6_addrconf_other icmp6_dataun.u_nd_ra.other #define icmp6_addrconf_other icmp6_dataun.u_nd_ra.other
#define icmp6_rt_lifetime icmp6_dataun.u_nd_ra.rt_lifetime #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 const net_null_addr_ip6; /* NULL IPv6 address */
extern struct in6_addr net_gateway6; /* Our gateways IPv6 address */ extern struct in6_addr net_gateway6; /* Our gateways IPv6 address */

View file

@ -142,20 +142,22 @@ static int dsa_port_send(struct udevice *pdev, void *packet, int length)
struct dsa_port_pdata *port_pdata; struct dsa_port_pdata *port_pdata;
int err; int err;
if (length + head + tail > PKTSIZE_ALIGN) if (ops->xmit) {
return -EINVAL; if (length + head + tail > PKTSIZE_ALIGN)
return -EINVAL;
memset(dsa_packet_tmp, 0, head); memset(dsa_packet_tmp, 0, head);
memset(dsa_packet_tmp + head + length, 0, tail); memset(dsa_packet_tmp + head + length, 0, tail);
memcpy(dsa_packet_tmp + head, packet, length); memcpy(dsa_packet_tmp + head, packet, length);
length += head + tail; length += head + tail;
/* copy back to preserve original buffer alignment */ /* copy back to preserve original buffer alignment */
memcpy(packet, dsa_packet_tmp, length); memcpy(packet, dsa_packet_tmp, length);
port_pdata = dev_get_parent_plat(pdev); port_pdata = dev_get_parent_plat(pdev);
err = ops->xmit(dev, port_pdata->index, packet, length); err = ops->xmit(dev, port_pdata->index, packet, length);
if (err) if (err)
return err; return err;
}
return eth_get_ops(master)->send(master, packet, length); 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; int length, port_index, err;
length = eth_get_ops(master)->recv(master, flags, packetp); length = eth_get_ops(master)->recv(master, flags, packetp);
if (length <= 0) if (length <= 0 || !ops->rcv)
return length; return length;
/* /*
@ -342,6 +344,19 @@ U_BOOT_DRIVER(dsa_port) = {
.plat_auto = sizeof(struct eth_pdata), .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 * This function mostly deals with pulling information out of the device tree
* into the pdata structure. * into the pdata structure.
@ -358,6 +373,10 @@ static int dsa_post_bind(struct udevice *dev)
if (!ofnode_valid(node)) if (!ofnode_valid(node))
return -ENODEV; return -ENODEV;
err = dsa_sanitize_ops(dev);
if (err)
return err;
pdata->master_node = ofnode_null(); pdata->master_node = ofnode_null();
node = ofnode_find_subnode(node, "ports"); node = ofnode_find_subnode(node, "ports");
@ -466,7 +485,6 @@ static int dsa_pre_probe(struct udevice *dev)
{ {
struct dsa_pdata *pdata = dev_get_uclass_plat(dev); struct dsa_pdata *pdata = dev_get_uclass_plat(dev);
struct dsa_priv *priv = dev_get_uclass_priv(dev); struct dsa_priv *priv = dev_get_uclass_priv(dev);
struct dsa_ops *ops = dsa_get_ops(dev);
int err; int err;
priv->num_ports = pdata->num_ports; priv->num_ports = pdata->num_ports;
@ -482,6 +500,15 @@ static int dsa_pre_probe(struct udevice *dev)
if (err) if (err)
return 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 */ /* Simulate a probing event for the CPU port */
if (ops->port_probe) { if (ops->port_probe) {
err = ops->port_probe(dev, priv->cpu_port, err = ops->port_probe(dev, priv->cpu_port,
@ -498,6 +525,7 @@ UCLASS_DRIVER(dsa) = {
.name = "dsa", .name = "dsa",
.post_bind = dsa_post_bind, .post_bind = dsa_post_bind,
.pre_probe = dsa_pre_probe, .pre_probe = dsa_pre_probe,
.post_probe = dsa_post_probe,
.per_device_auto = sizeof(struct dsa_priv), .per_device_auto = sizeof(struct dsa_priv),
.per_device_plat_auto = sizeof(struct dsa_pdata), .per_device_plat_auto = sizeof(struct dsa_pdata),
.per_child_plat_auto = sizeof(struct dsa_port_pdata), .per_child_plat_auto = sizeof(struct dsa_port_pdata),

View file

@ -49,7 +49,11 @@ static int dm_mdio_post_bind(struct udevice *dev)
return -EINVAL; return -EINVAL;
} }
#if CONFIG_IS_ENABLED(OF_REAL)
return dm_scan_fdt_dev(dev);
#else
return 0; return 0;
#endif
} }
int dm_mdio_read(struct udevice *mdio_dev, int addr, int devad, int reg) int dm_mdio_read(struct udevice *mdio_dev, int addr, int devad, int reg)

View file

@ -47,10 +47,13 @@ static int on_ip6addr(const char *name, const char *value, enum env_op op,
} }
mask = strchr(value, '/'); mask = strchr(value, '/');
len = strlen(value);
if (mask) if (mask) {
net_prefix_length = simple_strtoul(value + len, NULL, 10); net_prefix_length = simple_strtoul(mask + 1, NULL, 10);
len = mask - value;
} else {
len = strlen(value);
}
return string_to_ip6(value, len, &net_ip6); return string_to_ip6(value, len, &net_ip6);
} }

View file

@ -592,6 +592,14 @@ static void tftp_handler(uchar *pkt, unsigned dest, struct in_addr sip,
debug("Received unexpected block: %d, expected: %d\n", debug("Received unexpected block: %d, expected: %d\n",
ntohs(*(__be16 *)pkt), ntohs(*(__be16 *)pkt),
(ushort)(tftp_cur_block + 1)); (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 * If one packet is dropped most likely
* all other buffers in the window * all other buffers in the window
@ -837,7 +845,7 @@ void tftp_start(enum proto_t protocol)
e = strchr(net_boot_file_name, ']'); e = strchr(net_boot_file_name, ']');
len = e - s; len = e - s;
if (s && e) { 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); strlcpy(tftp_filename, e + 2, MAX_LEN);
} else { } else {
strlcpy(tftp_filename, net_boot_file_name, MAX_LEN); strlcpy(tftp_filename, net_boot_file_name, MAX_LEN);