From c3452b50c3aaa0db2bb0bc68039fed4d40bedbc0 Mon Sep 17 00:00:00 2001 From: Alex Marginean Date: Mon, 3 Jun 2019 19:10:30 +0300 Subject: [PATCH] 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 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), +}; -- 2.25.1