mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-12 16:07:30 +00:00
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:
commit
b102bfa15e
15 changed files with 990 additions and 108 deletions
|
@ -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;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
@ -83,43 +83,29 @@ int board_phy_config(struct phy_device *phydev)
|
|||
break;
|
||||
}
|
||||
|
||||
if (phydev->drv->config)
|
||||
phydev->drv->config(phydev);
|
||||
/* 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");
|
||||
|
||||
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;
|
||||
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;
|
||||
}
|
||||
#endif // CONFIG_MV88E61XX_SWITCH
|
||||
|
||||
#if defined(CONFIG_VIDEO_IPUV3)
|
||||
static void enable_hdmi(struct display_info_t const *dev)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -30,6 +30,8 @@
|
|||
#include <asm/arch/imx-regs.h>
|
||||
#include <asm/mach-imx/sys_proto.h>
|
||||
#include <asm-generic/gpio.h>
|
||||
#include <dm/device_compat.h>
|
||||
#include <dm/lists.h>
|
||||
|
||||
#include "fec_mxc.h"
|
||||
#include <eth_phy.h>
|
||||
|
@ -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,6 +1167,9 @@ static int fec_phy_init(struct fec_priv *priv, struct udevice *dev)
|
|||
addr = CFG_FEC_MXC_PHYADDR;
|
||||
#endif
|
||||
|
||||
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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -527,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;
|
||||
|
@ -699,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;
|
||||
}
|
||||
|
@ -812,7 +809,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 +976,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;
|
||||
|
@ -1038,7 +1034,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 +1103,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)
|
||||
|
@ -1159,8 +1155,7 @@ 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,
|
||||
root_dprc_handle, &cfg,
|
||||
&child_dprc_id,
|
||||
&mc_portal_offset);
|
||||
if (err < 0) {
|
||||
|
@ -1168,7 +1163,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 +1245,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;
|
||||
|
@ -1312,7 +1306,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 +1345,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)
|
||||
|
@ -1369,8 +1363,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;
|
||||
|
@ -1422,7 +1415,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 +1452,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)
|
||||
|
@ -1796,7 +1789,6 @@ static void mc_dump_log(void)
|
|||
if (size > bytes_end) {
|
||||
print_k_bytes(cur_ptr, &bytes_end);
|
||||
|
||||
cur_ptr = buf;
|
||||
size -= bytes_end;
|
||||
}
|
||||
|
||||
|
@ -1962,7 +1954,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:
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
|
755
drivers/net/mv88e6xxx.c
Normal file
755
drivers/net/mv88e6xxx.c
Normal 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),
|
||||
};
|
|
@ -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 */
|
||||
|
|
|
@ -142,6 +142,7 @@ static int dsa_port_send(struct udevice *pdev, void *packet, int length)
|
|||
struct dsa_port_pdata *port_pdata;
|
||||
int err;
|
||||
|
||||
if (ops->xmit) {
|
||||
if (length + head + tail > PKTSIZE_ALIGN)
|
||||
return -EINVAL;
|
||||
|
||||
|
@ -156,6 +157,7 @@ static int dsa_port_send(struct udevice *pdev, void *packet, int length)
|
|||
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;
|
||||
|
||||
/*
|
||||
|
@ -342,6 +344,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 +373,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");
|
||||
|
@ -466,7 +485,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 +500,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 +525,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),
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
10
net/tftp.c
10
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
|
||||
|
@ -837,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);
|
||||
|
|
Loading…
Reference in a new issue