From 4ef3231b6379cff18b6939619784d1ad60bfc14b Mon Sep 17 00:00:00 2001 From: Trent Piepho Date: Thu, 9 May 2019 19:23:39 +0000 Subject: cmd: mii: Refactor some of the MII reg dump code Share the code that prints out a register field with the function that prints out the "special" fields. There were two arrays the register dump list, one with reg number and name, another with a pointer to the field table and the table size. These two arrays had have each entry match what register is referred to. Combine them into just one table. Now they can't not match and there is just one table. Add some missing consts to pointers to string literals. The dump code was ignoring the regno field in the description table and assuming register 0 was at index 0, etc. Have it use the field. Change reg > max+1 into reg >= max, which doesn't fail if max+1 could overflow, besides just making more sense. Signed-off-by: Trent Piepho Acked-by: Joe Hershberger --- cmd/mii.c | 155 ++++++++++++++++++++++++++++---------------------------------- 1 file changed, 70 insertions(+), 85 deletions(-) (limited to 'cmd') diff --git a/cmd/mii.c b/cmd/mii.c index c0c42a851f..4dc4dee0e0 100644 --- a/cmd/mii.c +++ b/cmd/mii.c @@ -12,25 +12,11 @@ #include #include -typedef struct _MII_reg_desc_t { - ushort regno; - char * name; -} MII_reg_desc_t; - -static const MII_reg_desc_t reg_0_5_desc_tbl[] = { - { MII_BMCR, "PHY control register" }, - { MII_BMSR, "PHY status register" }, - { MII_PHYSID1, "PHY ID 1 register" }, - { MII_PHYSID2, "PHY ID 2 register" }, - { MII_ADVERTISE, "Autonegotiation advertisement register" }, - { MII_LPA, "Autonegotiation partner abilities register" }, -}; - typedef struct _MII_field_desc_t { ushort hi; ushort lo; ushort mask; - char * name; + const char *name; } MII_field_desc_t; static const MII_field_desc_t reg_0_desc_tbl[] = { @@ -87,7 +73,7 @@ static const MII_field_desc_t reg_4_desc_tbl[] = { { 7, 7, 0x01, "100BASE-TX able" }, { 6, 6, 0x01, "10BASE-T full duplex able" }, { 5, 5, 0x01, "10BASE-T able" }, - { 4, 0, 0x1f, "xxx to do" }, + { 4, 0, 0x1f, "selector" }, }; static const MII_field_desc_t reg_5_desc_tbl[] = { @@ -102,50 +88,65 @@ static const MII_field_desc_t reg_5_desc_tbl[] = { { 7, 7, 0x01, "100BASE-TX able" }, { 6, 6, 0x01, "10BASE-T full duplex able" }, { 5, 5, 0x01, "10BASE-T able" }, - { 4, 0, 0x1f, "xxx to do" }, + { 4, 0, 0x1f, "partner selector" }, }; -typedef struct _MII_field_desc_and_len_t { + +typedef struct _MII_reg_desc_t { + ushort regno; const MII_field_desc_t *pdesc; ushort len; -} MII_field_desc_and_len_t; - -static const MII_field_desc_and_len_t desc_and_len_tbl[] = { - { reg_0_desc_tbl, ARRAY_SIZE(reg_0_desc_tbl) }, - { reg_1_desc_tbl, ARRAY_SIZE(reg_1_desc_tbl) }, - { reg_2_desc_tbl, ARRAY_SIZE(reg_2_desc_tbl) }, - { reg_3_desc_tbl, ARRAY_SIZE(reg_3_desc_tbl) }, - { reg_4_desc_tbl, ARRAY_SIZE(reg_4_desc_tbl) }, - { reg_5_desc_tbl, ARRAY_SIZE(reg_5_desc_tbl) }, + const char *name; +} MII_reg_desc_t; + +static const MII_reg_desc_t mii_reg_desc_tbl[] = { + { MII_BMCR, reg_0_desc_tbl, ARRAY_SIZE(reg_0_desc_tbl), + "PHY control register" }, + { MII_BMSR, reg_1_desc_tbl, ARRAY_SIZE(reg_1_desc_tbl), + "PHY status register" }, + { MII_PHYSID1, reg_2_desc_tbl, ARRAY_SIZE(reg_2_desc_tbl), + "PHY ID 1 register" }, + { MII_PHYSID2, reg_3_desc_tbl, ARRAY_SIZE(reg_3_desc_tbl), + "PHY ID 2 register" }, + { MII_ADVERTISE, reg_4_desc_tbl, ARRAY_SIZE(reg_4_desc_tbl), + "Autonegotiation advertisement register" }, + { MII_LPA, reg_5_desc_tbl, ARRAY_SIZE(reg_5_desc_tbl), + "Autonegotiation partner abilities register" }, }; static void dump_reg( ushort regval, - const MII_reg_desc_t *prd, - const MII_field_desc_and_len_t *pdl); - -static int special_field( - ushort regno, - const MII_field_desc_t *pdesc, - ushort regval); - -static void MII_dump_0_to_5( - ushort regvals[6], - uchar reglo, - uchar reghi) + const MII_reg_desc_t *prd); + +static bool special_field(ushort regno, const MII_field_desc_t *pdesc, + ushort regval); + +static void MII_dump(const ushort *regvals, uchar reglo, uchar reghi) { ulong i; - for (i = 0; i < 6; i++) { - if ((reglo <= i) && (i <= reghi)) - dump_reg(regvals[i], ®_0_5_desc_tbl[i], - &desc_and_len_tbl[i]); + for (i = 0; i < ARRAY_SIZE(mii_reg_desc_tbl); i++) { + const uchar reg = mii_reg_desc_tbl[i].regno; + + if (reg >= reglo && reg <= reghi) + dump_reg(regvals[reg - reglo], &mii_reg_desc_tbl[i]); } } +/* Print out field position, value, name */ +static void dump_field(const MII_field_desc_t *pdesc, ushort regval) +{ + if (pdesc->hi == pdesc->lo) + printf("%2u ", pdesc->lo); + else + printf("%2u-%2u", pdesc->hi, pdesc->lo); + + printf(" = %5u %s", (regval >> pdesc->lo) & pdesc->mask, + pdesc->name); +} + static void dump_reg( ushort regval, - const MII_reg_desc_t *prd, - const MII_field_desc_and_len_t *pdl) + const MII_reg_desc_t *prd) { ulong i; ushort mask_in_place; @@ -154,8 +155,8 @@ static void dump_reg( printf("%u. (%04hx) -- %s --\n", prd->regno, regval, prd->name); - for (i = 0; i < pdl->len; i++) { - pdesc = &pdl->pdesc[i]; + for (i = 0; i < prd->len; i++) { + pdesc = &prd->pdesc[i]; mask_in_place = pdesc->mask << pdesc->lo; @@ -164,17 +165,8 @@ static void dump_reg( regval & mask_in_place, prd->regno); - if (special_field(prd->regno, pdesc, regval)) { - } - else { - if (pdesc->hi == pdesc->lo) - printf("%2u ", pdesc->lo); - else - printf("%2u-%2u", pdesc->hi, pdesc->lo); - printf(" = %5u %s", - (regval & mask_in_place) >> pdesc->lo, - pdesc->name); - } + if (!special_field(prd->regno, pdesc, regval)) + dump_field(pdesc, regval); printf("\n"); } @@ -190,11 +182,11 @@ static void dump_reg( ** 5.4-0 */ -static int special_field( - ushort regno, - const MII_field_desc_t *pdesc, - ushort regval) +static bool special_field(ushort regno, const MII_field_desc_t *pdesc, + ushort regval) { + const ushort sel_bits = (regval >> pdesc->lo) & pdesc->mask; + if ((regno == MII_BMCR) && (pdesc->lo == 6)) { ushort speed_bits = regval & (BMCR_SPEED1000 | BMCR_SPEED100); printf("%2u,%2u = b%u%u speed selection = %s Mbps", @@ -208,34 +200,26 @@ static int special_field( } else if ((regno == MII_BMCR) && (pdesc->lo == 8)) { - printf("%2u = %5u duplex = %s", - pdesc->lo, - (regval >> pdesc->lo) & 1, - ((regval >> pdesc->lo) & 1) ? "full" : "half"); + dump_field(pdesc, regval); + printf(" = %s", ((regval >> pdesc->lo) & 1) ? "full" : "half"); return 1; } else if ((regno == MII_ADVERTISE) && (pdesc->lo == 0)) { - ushort sel_bits = (regval >> pdesc->lo) & pdesc->mask; - printf("%2u-%2u = %5u selector = %s", - pdesc->hi, pdesc->lo, sel_bits, - sel_bits == PHY_ANLPAR_PSB_802_3 ? - "IEEE 802.3" : - sel_bits == PHY_ANLPAR_PSB_802_9 ? - "IEEE 802.9 ISLAN-16T" : - "???"); + dump_field(pdesc, regval); + printf(" = %s", + sel_bits == PHY_ANLPAR_PSB_802_3 ? "IEEE 802.3 CSMA/CD" : + sel_bits == PHY_ANLPAR_PSB_802_9 ? + "IEEE 802.9 ISLAN-16T" : "???"); return 1; } else if ((regno == MII_LPA) && (pdesc->lo == 0)) { - ushort sel_bits = (regval >> pdesc->lo) & pdesc->mask; - printf("%2u-%2u = %u selector = %s", - pdesc->hi, pdesc->lo, sel_bits, - sel_bits == PHY_ANLPAR_PSB_802_3 ? - "IEEE 802.3" : - sel_bits == PHY_ANLPAR_PSB_802_9 ? - "IEEE 802.9 ISLAN-16T" : - "???"); + dump_field(pdesc, regval); + printf(" = %s", + sel_bits == PHY_ANLPAR_PSB_802_3 ? "IEEE 802.3 CSMA/CD" : + sel_bits == PHY_ANLPAR_PSB_802_9 ? + "IEEE 802.9 ISLAN-16T" : "???"); return 1; } @@ -415,8 +399,9 @@ static int do_mii(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) return 1; } for (addr = addrlo; addr <= addrhi; addr++) { - for (reg = reglo; reg < reghi + 1; reg++) { - if (miiphy_read(devname, addr, reg, ®s[reg]) != 0) { + for (reg = reglo; reg <= reghi; reg++) { + if (miiphy_read(devname, addr, reg, + ®s[reg - reglo]) != 0) { ok = 0; printf( "Error reading from the PHY addr=%02x reg=%02x\n", @@ -425,7 +410,7 @@ static int do_mii(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) } } if (ok) - MII_dump_0_to_5(regs, reglo, reghi); + MII_dump(regs, reglo, reghi); printf("\n"); } } else if (strncmp(op, "de", 2) == 0) { -- cgit v1.2.3 From 956378683146578d0d069dace8c91c56dc09d743 Mon Sep 17 00:00:00 2001 From: Trent Piepho Date: Thu, 9 May 2019 19:23:47 +0000 Subject: cmd: mii: Add the standard 1000BASE-T registers These are standard across gigabit phys. These mostly extend the auto-negotiation information with gigabit fields. Signed-off-by: Trent Piepho Acked-by: Joe Hershberger --- cmd/mii.c | 34 +++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) (limited to 'cmd') diff --git a/cmd/mii.c b/cmd/mii.c index 4dc4dee0e0..23ee1e6cfa 100644 --- a/cmd/mii.c +++ b/cmd/mii.c @@ -91,6 +91,28 @@ static const MII_field_desc_t reg_5_desc_tbl[] = { { 4, 0, 0x1f, "partner selector" }, }; +static const MII_field_desc_t reg_9_desc_tbl[] = { + { 15, 13, 0x07, "test mode" }, + { 12, 12, 0x01, "manual master/slave enable" }, + { 11, 11, 0x01, "manual master/slave value" }, + { 10, 10, 0x01, "multi/single port" }, + { 9, 9, 0x01, "1000BASE-T full duplex able" }, + { 8, 8, 0x01, "1000BASE-T half duplex able" }, + { 7, 7, 0x01, "automatic TDR on link down" }, + { 6, 6, 0x7f, "(reserved)" }, +}; + +static const MII_field_desc_t reg_10_desc_tbl[] = { + { 15, 15, 0x01, "master/slave config fault" }, + { 14, 14, 0x01, "master/slave config result" }, + { 13, 13, 0x01, "local receiver status OK" }, + { 12, 12, 0x01, "remote receiver status OK" }, + { 11, 11, 0x01, "1000BASE-T full duplex able" }, + { 10, 10, 0x01, "1000BASE-T half duplex able" }, + { 9, 8, 0x03, "(reserved)" }, + { 7, 0, 0xff, "1000BASE-T idle error counter"}, +}; + typedef struct _MII_reg_desc_t { ushort regno; const MII_field_desc_t *pdesc; @@ -111,6 +133,10 @@ static const MII_reg_desc_t mii_reg_desc_tbl[] = { "Autonegotiation advertisement register" }, { MII_LPA, reg_5_desc_tbl, ARRAY_SIZE(reg_5_desc_tbl), "Autonegotiation partner abilities register" }, + { MII_CTRL1000, reg_9_desc_tbl, ARRAY_SIZE(reg_9_desc_tbl), + "1000BASE-T control register" }, + { MII_STAT1000, reg_10_desc_tbl, ARRAY_SIZE(reg_10_desc_tbl), + "1000BASE-T status register" }, }; static void dump_reg( @@ -390,12 +416,10 @@ static int do_mii(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) } } } else if (strncmp(op, "du", 2) == 0) { - ushort regs[6]; + ushort regs[MII_STAT1000 + 1]; /* Last reg is 0x0a */ int ok = 1; - if ((reglo > 5) || (reghi > 5)) { - printf( - "The MII dump command only formats the " - "standard MII registers, 0-5.\n"); + if (reglo > MII_STAT1000 || reghi > MII_STAT1000) { + printf("The MII dump command only formats the standard MII registers, 0-5, 9-a.\n"); return 1; } for (addr = addrlo; addr <= addrhi; addr++) { -- cgit v1.2.3 From c3452b50c3aaa0db2bb0bc68039fed4d40bedbc0 Mon Sep 17 00:00:00 2001 From: Alex Marginean Date: Mon, 3 Jun 2019 19:10:30 +0300 Subject: net: introduce MDIO DM class for MDIO devices Adds UCLASS_MDIO DM class supporting MDIO buses that are probed as stand-alone devices. Useful in particular for systems that support DM_ETH and have a stand-alone MDIO hardware block shared by multiple Ethernet interfaces. Signed-off-by: Alex Marginean Reviewed-by: Bin Meng Acked-by: Joe Hershberger --- cmd/mdio.c | 5 +++ drivers/net/Kconfig | 13 ++++++ include/dm/uclass-id.h | 1 + include/miiphy.h | 49 +++++++++++++++++++++ net/Makefile | 1 + net/mdio-uclass.c | 115 +++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 184 insertions(+) create mode 100644 net/mdio-uclass.c (limited to 'cmd') diff --git a/cmd/mdio.c b/cmd/mdio.c index 5e219f699d..a6fa9266d0 100644 --- a/cmd/mdio.c +++ b/cmd/mdio.c @@ -203,6 +203,11 @@ static int do_mdio(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) if (argc < 2) return CMD_RET_USAGE; +#ifdef CONFIG_DM_MDIO + /* probe DM MII device before any operation so they are all accesible */ + dm_mdio_probe_devices(); +#endif + /* * We use the last specified parameters, unless new ones are * entered. diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index e6a4fdf30e..6fba5a84dd 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -11,6 +11,19 @@ config DM_ETH This is currently implemented in net/eth-uclass.c Look in include/net.h for details. +config DM_MDIO + bool "Enable Driver Model for MDIO devices" + depends on DM_ETH && PHYLIB + help + Enable driver model for MDIO devices + + Adds UCLASS_MDIO DM class supporting MDIO buses that are probed as + stand-alone devices. Useful in particular for systems that support + DM_ETH and have a stand-alone MDIO hardware block shared by multiple + Ethernet interfaces. + This is currently implemented in net/mdio-uclass.c + Look in include/miiphy.h for details. + menuconfig NETDEVICES bool "Network device support" depends on NET diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index 5056a084d2..f9300a64ce 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -58,6 +58,7 @@ enum uclass_id { UCLASS_LPC, /* x86 'low pin count' interface */ UCLASS_MAILBOX, /* Mailbox controller */ UCLASS_MASS_STORAGE, /* Mass storage device */ + UCLASS_MDIO, /* MDIO bus */ UCLASS_MISC, /* Miscellaneous device */ UCLASS_MMC, /* SD / MMC card or chip */ UCLASS_MOD_EXP, /* RSA Mod Exp device */ diff --git a/include/miiphy.h b/include/miiphy.h index f11763affd..e6dd441983 100644 --- a/include/miiphy.h +++ b/include/miiphy.h @@ -118,4 +118,53 @@ int bb_miiphy_write(struct mii_dev *miidev, int addr, int devad, int reg, #define ESTATUS_1000XF 0x8000 #define ESTATUS_1000XH 0x4000 +#ifdef CONFIG_DM_MDIO + +/** + * struct mdio_perdev_priv - Per-device class data for MDIO DM + * + * @mii_bus: Supporting MII legacy bus + */ +struct mdio_perdev_priv { + struct mii_dev *mii_bus; +}; + +/** + * struct mdio_ops - MDIO bus operations + * + * @read: Read from a PHY register + * @write: Write to a PHY register + * @reset: Reset the MDIO bus, NULL if not supported + */ +struct mdio_ops { + int (*read)(struct udevice *mdio_dev, int addr, int devad, int reg); + int (*write)(struct udevice *mdio_dev, int addr, int devad, int reg, + u16 val); + int (*reset)(struct udevice *mdio_dev); +}; + +#define mdio_get_ops(dev) ((struct mdio_ops *)(dev)->driver->ops) + +/** + * dm_mdio_probe_devices - Call probe on all MII devices, currently used for + * MDIO console commands. + */ +void dm_mdio_probe_devices(void); + +/** + * dm_mdio_phy_connect - Wrapper over phy_connect for DM MDIO + * + * @dev: mdio dev + * @addr: PHY address on MDIO bus + * @ethdev: ethernet device to connect to the PHY + * @interface: MAC-PHY protocol + * + * @return pointer to phy_device, or 0 on error + */ +struct phy_device *dm_mdio_phy_connect(struct udevice *dev, int addr, + struct udevice *ethdev, + phy_interface_t interface); + +#endif + #endif diff --git a/net/Makefile b/net/Makefile index ce36362168..6251ff3991 100644 --- a/net/Makefile +++ b/net/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_NET) += eth-uclass.o else obj-$(CONFIG_NET) += eth_legacy.o endif +obj-$(CONFIG_DM_MDIO) += mdio-uclass.o obj-$(CONFIG_NET) += eth_common.o obj-$(CONFIG_CMD_LINK_LOCAL) += link_local.o obj-$(CONFIG_NET) += net.o diff --git a/net/mdio-uclass.c b/net/mdio-uclass.c new file mode 100644 index 0000000000..36a404ff44 --- /dev/null +++ b/net/mdio-uclass.c @@ -0,0 +1,115 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2019 + * Alex Marginean, NXP + */ + +#include +#include +#include +#include +#include + +void dm_mdio_probe_devices(void) +{ + struct udevice *it; + struct uclass *uc; + + uclass_get(UCLASS_MDIO, &uc); + uclass_foreach_dev(it, uc) { + device_probe(it); + } +} + +static int dm_mdio_post_bind(struct udevice *dev) +{ + /* + * MDIO command doesn't like spaces in names, don't allow them to keep + * it happy + */ + if (strchr(dev->name, ' ')) { + debug("\nError: MDIO device name \"%s\" has a space!\n", + dev->name); + return -EINVAL; + } + + return 0; +} + +/* + * Following read/write/reset functions are registered with legacy MII code. + * These are called for PHY operations by upper layers and we further call the + * DM MDIO driver functions. + */ +static int mdio_read(struct mii_dev *mii_bus, int addr, int devad, int reg) +{ + struct udevice *dev = mii_bus->priv; + + return mdio_get_ops(dev)->read(dev, addr, devad, reg); +} + +static int mdio_write(struct mii_dev *mii_bus, int addr, int devad, int reg, + u16 val) +{ + struct udevice *dev = mii_bus->priv; + + return mdio_get_ops(dev)->write(dev, addr, devad, reg, val); +} + +static int mdio_reset(struct mii_dev *mii_bus) +{ + struct udevice *dev = mii_bus->priv; + + if (mdio_get_ops(dev)->reset) + return mdio_get_ops(dev)->reset(dev); + else + return 0; +} + +static int dm_mdio_post_probe(struct udevice *dev) +{ + struct mdio_perdev_priv *pdata = dev_get_uclass_priv(dev); + + pdata->mii_bus = mdio_alloc(); + pdata->mii_bus->read = mdio_read; + pdata->mii_bus->write = mdio_write; + pdata->mii_bus->reset = mdio_reset; + pdata->mii_bus->priv = dev; + strncpy(pdata->mii_bus->name, dev->name, MDIO_NAME_LEN); + + return mdio_register(pdata->mii_bus); +} + +static int dm_mdio_pre_remove(struct udevice *dev) +{ + struct mdio_perdev_priv *pdata = dev_get_uclass_priv(dev); + struct mdio_ops *ops = mdio_get_ops(dev); + + if (ops->reset) + ops->reset(dev); + mdio_unregister(pdata->mii_bus); + mdio_free(pdata->mii_bus); + + return 0; +} + +struct phy_device *dm_mdio_phy_connect(struct udevice *dev, int addr, + struct udevice *ethdev, + phy_interface_t interface) +{ + struct mdio_perdev_priv *pdata = dev_get_uclass_priv(dev); + + if (device_probe(dev)) + return 0; + + return phy_connect(pdata->mii_bus, addr, ethdev, interface); +} + +UCLASS_DRIVER(mdio) = { + .id = UCLASS_MDIO, + .name = "mdio", + .post_bind = dm_mdio_post_bind, + .post_probe = dm_mdio_post_probe, + .pre_remove = dm_mdio_pre_remove, + .per_device_auto_alloc_size = sizeof(struct mdio_perdev_priv), +}; -- cgit v1.2.3