This is an automated email from the ASF dual-hosted git repository.
xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git
The following commit(s) were added to refs/heads/master by this push:
new 95efa6f7cf8 net/mdio: add mdio bus
95efa6f7cf8 is described below
commit 95efa6f7cf8162139b9c5dac21f20d2f299bf305
Author: LuchianMihai <[email protected]>
AuthorDate: Sat Sep 6 23:38:30 2025 +0300
net/mdio: add mdio bus
Currently the mdio communication is part of the monolithic 'full netdevs'.
This commit serves as an way to add modularity for the netdevs drivers.
A new upperhalf/lowerhalf mdio device comes with this commit that manages
the data transfer ofer mdio interface.
Signed-off-by: Luchian Mihai <[email protected]>
---
Documentation/components/net/index.rst | 1 +
Documentation/components/net/mdio.rst | 73 ++++++++++
arch/arm/src/stm32h7/CMakeLists.txt | 4 +
arch/arm/src/stm32h7/Make.defs | 4 +
arch/arm/src/stm32h7/stm32_ethernet.c | 207 ++++++---------------------
arch/arm/src/stm32h7/stm32_mdio.c | 232 ++++++++++++++++++++++++++++++
arch/arm/src/stm32h7/stm32_mdio.h | 58 ++++++++
drivers/net/CMakeLists.txt | 4 +
drivers/net/Kconfig | 14 ++
drivers/net/Make.defs | 4 +
drivers/net/mdio.c | 253 +++++++++++++++++++++++++++++++++
include/nuttx/net/mdio.h | 192 +++++++++++++++++++++++++
12 files changed, 881 insertions(+), 165 deletions(-)
diff --git a/Documentation/components/net/index.rst
b/Documentation/components/net/index.rst
index 025966919b6..148880d8982 100644
--- a/Documentation/components/net/index.rst
+++ b/Documentation/components/net/index.rst
@@ -12,6 +12,7 @@ Network Support
nat.rst
netdev.rst
netdriver.rst
+ mdio.rst
netguardsize.rst
netlink.rst
slip.rst
diff --git a/Documentation/components/net/mdio.rst
b/Documentation/components/net/mdio.rst
new file mode 100644
index 00000000000..2c6fa15347e
--- /dev/null
+++ b/Documentation/components/net/mdio.rst
@@ -0,0 +1,73 @@
+.. _mdio_bus:
+
+.. include:: /substitutions.rst
+
+=================
+MDIO Bus Driver
+=================
+
+The NuttX MDIO bus driver provides a standardized interface for communicating
with Ethernet PHY (Physical Layer) transceivers.
+It employs a classic upper-half/lower-half architecture to abstract
hardware-specific logic from the generic MDIO protocol,
+which is currently compliant with Clause 22 of the IEEE 802.3 standard.
+The primary implementation of the upper-half can be found in
``drivers/net/mdio.c``.
+
+Driver Architecture
+===================
+
+The MDIO driver framework serves as an intermediary layer between a network
device driver and the physical bus.
+The intended operational model is ``netdev -> phydev -> mdio``, where the
network device communicates with a dedicated PHY driver,
+which in turn uses the MDIO bus driver for low-level hardware access.
+Direct interaction between the network device and the MDIO bus is discouraged.
+
+Upper-Half Implementation
+-------------------------
+
+The upper-half driver contains the core logic for the MDIO bus, including bus
locking mechanisms to ensure safe transactions.
+It exposes a generic API for managing the bus lifecycle and is capable of
handling multiple, independent MDIO bus instances concurrently.
+This abstracts implementation details from both the PHY driver and the
underlying hardware-specific code.
+
+Lower-Half Implementation
+-------------------------
+
+A lower-half MDIO driver serves as a thin layer that maps the generic
operations defined by the upper-half to hardware-specific register
manipulations.
+It is not intended to contain complex logic, but rather to provide a direct
translation for bus operations.
+
+Implementing a Lower-Half Driver
+================================
+
+Integrating MDIO support for new hardware requires the implementation of a
lower-half driver.
+The contract between the upper and lower halves is defined in
``include/nuttx/net/mdio.h`` and is centered around two key structures.
+
+Key Data Structures
+-------------------
+
+1. ``struct mdio_ops_s``: A structure containing function pointers that the
lower-half driver must implement to perform hardware-level operations.
+ * ``read``: Performs a Clause 22 MDIO read operation.
+ * ``write``: Performs a Clause 22 MDIO write operation.
+ * ``reset``: An optional function to execute a hardware-specific PHY
reset.
+
+2. ``struct mdio_lowerhalf_s``: The container for the lower-half instance,
which holds a pointer to the ``mdio_ops_s`` vtable
+ and an optional private data pointer for the driver's internal state.
+
+Registration and Unregistration
+-------------------------------
+
+The board-level initialization logic is responsible for instantiating the
lower-half driver and registering it with the upper-half via the
``mdio_register()`` function.
+Each call to this function with a distinct lower-half driver creates a new,
unique bus handle, allowing the system to manage several MDIO buses
concurrently.
+
+.. code-block:: c
+
+ FAR struct mdio_dev_s *mdio_register(FAR struct mdio_lowerhalf_s *lower);
+
+This function accepts the lower-half instance and returns an opaque handle
(``FAR struct mdio_dev_s *``),
+which is subsequently used by the PHY driver to interact with the bus.
+
+When a bus instance is no longer required, it should be deallocated by calling
the ``mdio_unregister()`` function to ensure proper cleanup of resources.
+
+.. code-block:: c
+
+ int mdio_unregister(FAR struct mdio_dev_s *dev);
+
+This function takes the handle returned by ``mdio_register()`` and releases
the associated bus instance.
+
+A (mostly) complete reference implementation for a lower-half driver is
available in ``arch/arm/src/stm32h7/stm32_mdio.c``.
diff --git a/arch/arm/src/stm32h7/CMakeLists.txt
b/arch/arm/src/stm32h7/CMakeLists.txt
index 517a72af7c9..85c278d67d5 100644
--- a/arch/arm/src/stm32h7/CMakeLists.txt
+++ b/arch/arm/src/stm32h7/CMakeLists.txt
@@ -185,6 +185,10 @@ if(CONFIG_STM32H7_ETHMAC)
list(APPEND SRCS stm32_ethernet.c)
endif()
+if(CONFIG_MDIO_BUS)
+ list(APPEND SRCS stm32_mdio.c)
+endif()
+
if(CONFIG_SENSORS_QENCODER)
list(APPEND SRCS stm32_qencoder.c)
endif()
diff --git a/arch/arm/src/stm32h7/Make.defs b/arch/arm/src/stm32h7/Make.defs
index f83a080c942..008dfc475ad 100644
--- a/arch/arm/src/stm32h7/Make.defs
+++ b/arch/arm/src/stm32h7/Make.defs
@@ -187,6 +187,10 @@ ifeq ($(CONFIG_STM32H7_ETHMAC),y)
CHIP_CSRCS += stm32_ethernet.c
endif
+ifeq ($(CONFIG_MDIO_BUS),y)
+CHIP_CSRCS += stm32_mdio.c
+endif
+
ifeq ($(CONFIG_SENSORS_QENCODER),y)
CHIP_CSRCS += stm32_qencoder.c
endif
diff --git a/arch/arm/src/stm32h7/stm32_ethernet.c
b/arch/arm/src/stm32h7/stm32_ethernet.c
index 088a88638d6..01f5e1f6f32 100644
--- a/arch/arm/src/stm32h7/stm32_ethernet.c
+++ b/arch/arm/src/stm32h7/stm32_ethernet.c
@@ -64,6 +64,7 @@
#include "stm32_rcc.h"
#include "stm32_ethernet.h"
#include "stm32_uid.h"
+#include "stm32_mdio.h"
#include <arch/board/board.h>
@@ -690,6 +691,8 @@ struct stm32_ethmac_s
uint16_t segments; /* RX segment count */
uint16_t inflight; /* Number of TX transfers "in_flight" */
sq_queue_t freeb; /* The free buffer list */
+
+ struct mdio_bus_s *mdio;
};
/****************************************************************************
@@ -808,16 +811,13 @@ static void stm32_rxdescinit(struct stm32_ethmac_s *priv,
#if defined(CONFIG_NETDEV_PHY_IOCTL) && defined(CONFIG_ARCH_PHY_INTERRUPT)
static int stm32_phyintenable(struct stm32_ethmac_s *priv);
#endif
-static int stm32_phyread(uint16_t phydevaddr, uint16_t phyregaddr,
- uint16_t *value);
-static int stm32_phywrite(uint16_t phydevaddr, uint16_t phyregaddr,
- uint16_t value, uint16_t mask);
+
#ifdef CONFIG_ETH0_PHY_DM9161
static inline int stm32_dm9161(struct stm32_ethmac_s *priv);
#endif
static int stm32_phyinit(struct stm32_ethmac_s *priv);
#ifdef CONFIG_STM32H7_ETHMAC_REGDEBUG
-static void stm32_phyregdump(void);
+static void stm32_phyregdump(struct stm32_ethmac_s *priv);
#endif
#endif
@@ -3038,7 +3038,8 @@ static int stm32_ioctl(struct net_driver_s *dev, int cmd,
unsigned long arg)
{
struct mii_ioctl_data_s *req =
(struct mii_ioctl_data_s *)((uintptr_t)arg);
- ret = stm32_phyread(req->phy_id, req->reg_num, &req->val_out);
+ ret = mdio_read(priv->mdio,
+ req->phy_id, req->reg_num, &req->val_out);
}
break;
@@ -3046,8 +3047,8 @@ static int stm32_ioctl(struct net_driver_s *dev, int cmd,
unsigned long arg)
{
struct mii_ioctl_data_s *req =
(struct mii_ioctl_data_s *)((uintptr_t)arg);
- ret = stm32_phywrite(req->phy_id, req->reg_num, req->val_in,
- 0xffff);
+ ret = mdio_write(priv->mdio,
+ req->phy_id, req->reg_num, req->val_in);
}
break;
@@ -3091,148 +3092,6 @@ static int stm32_phyintenable(struct stm32_ethmac_s
*priv)
}
#endif
-/****************************************************************************
- * Function: stm32_phyread
- *
- * Description:
- * Read a PHY register.
- *
- * Parameters:
- * phydevaddr - The PHY device address
- * phyregaddr - The PHY register address
- * value - The location to return the 16-bit PHY register value.
- *
- * Returned Value:
- * OK on success; Negated errno on failure.
- *
- * Assumptions:
- *
- ****************************************************************************/
-
-static int stm32_phyread(uint16_t phydevaddr, uint16_t phyregaddr,
- uint16_t *value)
-{
- volatile uint32_t timeout;
- uint32_t regval;
-
- /* Configure the MACMDIOAR register, preserving CSR Clock Range CR[3:0]
- * bits
- */
-
- regval = stm32_getreg(STM32_ETH_MACMDIOAR);
- regval &= ETH_MACMDIOAR_CR_MASK;
-
- /* Set the PHY device address, PHY register address, and set the buy bit.
- * the ETH_MACMDIOAR_GOC == 3, indicating a read operation.
- */
-
- regval |= (((uint32_t)phydevaddr << ETH_MACMDIOAR_PA_SHIFT) &
- ETH_MACMDIOAR_PA_MASK);
- regval |= (((uint32_t)phyregaddr << ETH_MACMDIOAR_RDA_SHIFT) &
- ETH_MACMDIOAR_RDA_MASK);
- regval |= ETH_MACMDIOAR_MB | ETH_MACMDIOAR_GOC_READ;
-
- stm32_putreg(regval, STM32_ETH_MACMDIOAR);
-
- /* Wait for the transfer to complete */
-
- for (timeout = 0; timeout < PHY_READ_TIMEOUT; timeout++)
- {
- if ((stm32_getreg(STM32_ETH_MACMDIOAR) & ETH_MACMDIOAR_MB) == 0)
- {
- *value = (uint16_t)stm32_getreg(STM32_ETH_MACMDIODR);
- return OK;
- }
- }
-
- ninfo("MII transfer timed out: phydevaddr: %04x phyregaddr: %04x\n",
- phydevaddr, phyregaddr);
-
- return -ETIMEDOUT;
-}
-
-/****************************************************************************
- * Function: stm32_phywrite
- *
- * Description:
- * Write to a PHY register.
- *
- * Parameters:
- * phydevaddr - The PHY device address
- * phyregaddr - The PHY register address
- * value - The 16-bit value to write to the PHY register value.
- *
- * Returned Value:
- * OK on success; Negated errno on failure.
- *
- * Assumptions:
- *
- ****************************************************************************/
-
-static int stm32_phywrite(uint16_t phydevaddr, uint16_t phyregaddr,
- uint16_t set, uint16_t clear)
-{
- volatile uint32_t timeout;
- uint32_t regval;
- uint16_t value;
-
- /* Configure the MACMDIOAR register, preserving CSR Clock Range CR[3:0]
- * bits
- */
-
- regval = stm32_getreg(STM32_ETH_MACMDIOAR);
- regval &= ETH_MACMDIOAR_CR_MASK;
-
- /* Read the existing register value, if clear mask is given */
-
- if (clear != 0xffff)
- {
- if (stm32_phyread(phydevaddr, phyregaddr, &value) != OK)
- {
- return -ETIMEDOUT;
- }
-
- value &= ~clear;
- value |= set;
- }
- else
- {
- value = set;
- }
-
- /* Set the PHY device address, PHY register address, and set the busy bit.
- * the ETH_MACMDIOAR_GOC == 1, indicating a write operation.
- */
-
- regval |= (((uint32_t)phydevaddr << ETH_MACMDIOAR_PA_SHIFT) &
- ETH_MACMDIOAR_PA_MASK);
- regval |= (((uint32_t)phyregaddr << ETH_MACMDIOAR_RDA_SHIFT) &
- ETH_MACMDIOAR_RDA_MASK);
- regval |= (ETH_MACMDIOAR_MB | ETH_MACMDIOAR_GOC_WRITE);
-
- /* Write the value into the MACMDIODR register before setting the new
- * MACMDIOAR register value.
- */
-
- stm32_putreg(value, STM32_ETH_MACMDIODR);
- stm32_putreg(regval, STM32_ETH_MACMDIOAR);
-
- /* Wait for the transfer to complete */
-
- for (timeout = 0; timeout < PHY_WRITE_TIMEOUT; timeout++)
- {
- if ((stm32_getreg(STM32_ETH_MACMDIOAR) & ETH_MACMDIOAR_MB) == 0)
- {
- return OK;
- }
- }
-
- ninfo("MII transfer timed out: phydevaddr: %04x phyregaddr: %04x value: "
- "%04x\n", phydevaddr, phyregaddr, value);
-
- return -ETIMEDOUT;
-}
-
/****************************************************************************
* Function: stm32_dm9161
*
@@ -3261,7 +3120,8 @@ static inline int stm32_dm9161(struct stm32_ethmac_s
*priv)
* indication that check if the DM9161 PHY CHIP is not ready.
*/
- ret = stm32_phyread(CONFIG_STM32H7_PHYADDR, MII_PHYID1, &phyval);
+ ret = mdio_read(priv->mdio,
+ CONFIG_STM32H7_PHYADDR, MII_PHYID1, &phyval);
if (ret < 0)
{
nerr("ERROR: Failed to read the PHY ID1: %d\n", ret);
@@ -3283,7 +3143,8 @@ static inline int stm32_dm9161(struct stm32_ethmac_s
*priv)
* Register 16
*/
- ret = stm32_phyread(CONFIG_STM32H7_PHYADDR, 16, &phyval);
+ ret = mdio_read(priv->mdio,
+ CONFIG_STM32H7_PHYADDR, 16, &phyval);
if (ret < 0)
{
nerr("ERROR: Failed to read the PHY Register 0x10: %d\n", ret);
@@ -3318,7 +3179,7 @@ static inline int stm32_dm9161(struct stm32_ethmac_s
*priv)
****************************************************************************/
#ifdef CONFIG_STM32H7_ETHMAC_REGDEBUG
-static void stm32_phyregdump()
+static void stm32_phyregdump(struct stm32_ethmac_s *priv)
{
uint16_t phyval;
int ret;
@@ -3326,7 +3187,8 @@ static void stm32_phyregdump()
for (i = 0; i < 0x20; i++)
{
- ret = stm32_phyread(CONFIG_STM32H7_PHYADDR, i, &phyval);
+ ret = mdio_read(priv->mdio,
+ CONFIG_STM32H7_PHYADDR, i, &phyval);
if (ret < 0)
{
nerr("ERROR: Failed to read reg: 0%2x\n", i);
@@ -3379,8 +3241,8 @@ static int stm32_phyinit(struct stm32_ethmac_s *priv)
/* Put the PHY in reset mode */
- ret = stm32_phywrite(CONFIG_STM32H7_PHYADDR, MII_MCR, MII_MCR_RESET,
- MII_MCR_RESET);
+ ret = mdio_write(priv->mdio,
+ CONFIG_STM32H7_PHYADDR, MII_MCR, MII_MCR_RESET);
if (ret < 0)
{
nerr("ERROR: Failed to reset the PHY: %d\n", ret);
@@ -3393,7 +3255,8 @@ static int stm32_phyinit(struct stm32_ethmac_s *priv)
up_mdelay(10);
to -= 10;
phyval = 0xffff;
- ret = stm32_phyread(CONFIG_STM32H7_PHYADDR, MII_MCR, &phyval);
+ ret = mdio_read(priv->mdio,
+ CONFIG_STM32H7_PHYADDR, MII_MCR, &phyval);
ninfo("MII_MCR: phyval: %u ret: %d\n", phyval, ret);
}
@@ -3409,7 +3272,8 @@ static int stm32_phyinit(struct stm32_ethmac_s *priv)
ninfo("Phy reset in %d ms\n", PHY_RESET_DELAY - to);
}
- ret = stm32_phyread(CONFIG_STM32H7_PHYADDR, MII_PHYID1, &phyval);
+ ret = mdio_read(priv->mdio,
+ CONFIG_STM32H7_PHYADDR, MII_PHYID1, &phyval);
if (ret < 0)
{
@@ -3426,7 +3290,8 @@ static int stm32_phyinit(struct stm32_ethmac_s *priv)
ninfo("MII_PHYID1: phyval: %u ret: %d\n", phyval, ret);
- ret = stm32_phyread(CONFIG_STM32H7_PHYADDR, MII_PHYID2, &phyval);
+ ret = mdio_read(priv->mdio,
+ CONFIG_STM32H7_PHYADDR, MII_PHYID2, &phyval);
if (ret < 0)
{
@@ -3444,7 +3309,7 @@ static int stm32_phyinit(struct stm32_ethmac_s *priv)
ninfo("MII_PHYID2: phyval: %u ret: %d\n", phyval, ret);
#ifdef CONFIG_STM32H7_ETHMAC_REGDEBUG
- stm32_phyregdump();
+ stm32_phyregdump(priv);
#endif
/* Special workaround for the Davicom DM9161 PHY is required. */
@@ -3464,7 +3329,8 @@ static int stm32_phyinit(struct stm32_ethmac_s *priv)
for (timeout = 0; timeout < PHY_RETRY_TIMEOUT; timeout++)
{
- ret = stm32_phyread(CONFIG_STM32H7_PHYADDR, MII_MSR, &phyval);
+ ret = mdio_read(priv->mdio,
+ CONFIG_STM32H7_PHYADDR, MII_MSR, &phyval);
if (ret < 0)
{
nerr("ERROR: Failed to read the PHY MSR: %d\n", ret);
@@ -3487,8 +3353,8 @@ static int stm32_phyinit(struct stm32_ethmac_s *priv)
/* Enable auto-negotiation */
- ret = stm32_phywrite(CONFIG_STM32H7_PHYADDR, MII_MCR, MII_MCR_ANENABLE,
- MII_MCR_ANENABLE);
+ ret = mdio_write(priv->mdio,
+ CONFIG_STM32H7_PHYADDR, MII_MCR, MII_MCR_ANENABLE);
if (ret < 0)
{
nerr("ERROR: Failed to enable auto-negotiation: %d\n", ret);
@@ -3499,7 +3365,8 @@ static int stm32_phyinit(struct stm32_ethmac_s *priv)
for (timeout = 0; timeout < PHY_RETRY_TIMEOUT; timeout++)
{
- ret = stm32_phyread(CONFIG_STM32H7_PHYADDR, MII_MSR, &phyval);
+ ret = mdio_read(priv->mdio,
+ CONFIG_STM32H7_PHYADDR, MII_MSR, &phyval);
if (ret < 0)
{
nerr("ERROR: Failed to read the PHY MSR: %d\n", ret);
@@ -3521,7 +3388,8 @@ static int stm32_phyinit(struct stm32_ethmac_s *priv)
/* Read the result of the auto-negotiation from the PHY-specific register */
- ret = stm32_phyread(CONFIG_STM32H7_PHYADDR, CONFIG_STM32H7_PHYSR, &phyval);
+ ret = mdio_read(priv->mdio,
+ CONFIG_STM32H7_PHYADDR, CONFIG_STM32H7_PHYSR, &phyval);
if (ret < 0)
{
nerr("ERROR: Failed to read PHY status register\n");
@@ -4306,6 +4174,15 @@ static inline int stm32_ethinitialize(int intf)
stm32_ethgpioconfig(priv);
+ /* Initialize the MDIO device */
+
+ priv->mdio = stm32_mdio_bus_initialize();
+ if (!priv->mdio)
+ {
+ nerr("ERROR: Failed to initialize MDIO bus\n");
+ return -ENOMEM;
+ }
+
/* Attach the IRQ to the driver */
if (irq_attach(STM32_IRQ_ETH, stm32_interrupt, NULL))
diff --git a/arch/arm/src/stm32h7/stm32_mdio.c
b/arch/arm/src/stm32h7/stm32_mdio.c
new file mode 100644
index 00000000000..dec85291f9e
--- /dev/null
+++ b/arch/arm/src/stm32h7/stm32_mdio.c
@@ -0,0 +1,232 @@
+/****************************************************************************
+ * arch/arm/src/stm32h7/stm32_mdio.c
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <arm_internal.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32H7_ETHMAC_REGDEBUG
+static uint32_t stm32_getreg(uint32_t addr);
+static void stm32_putreg(uint32_t val, uint32_t addr);
+static void stm32_checksetup(void);
+#else
+# define stm32_getreg(addr) getreg32(addr)
+# define stm32_putreg(val,addr) putreg32(val,addr)
+# define stm32_checksetup()
+#endif
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/mutex.h>
+#include <nuttx/config.h>
+
+#include <debug.h>
+#include <errno.h>
+#include <inttypes.h>
+
+#include "stm32_mdio.h"
+#include "hardware/stm32_ethernet.h"
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct stm32_mdio_bus_s
+{
+ struct mdio_lowerhalf_s *lower;
+
+ /* MDIO bus timeout in milliseconds */
+
+ int timeout;
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int stm32_c22_read(struct mdio_lowerhalf_s *dev, uint8_t phydev,
+ uint8_t regaddr, uint16_t *value);
+
+static int stm32_c22_write(struct mdio_lowerhalf_s *dev, uint8_t phydev,
+ uint8_t regaddr, uint16_t value);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+const struct mdio_ops_s g_stm32_mdio_ops =
+{
+ .read = stm32_c22_read,
+ .write = stm32_c22_write,
+ .reset = NULL,
+};
+
+struct mdio_lowerhalf_s g_stm32_mdio_lowerhalf =
+{
+ .ops = &g_stm32_mdio_ops
+};
+
+struct stm32_mdio_bus_s g_stm32_mdio_bus =
+{
+ .lower = &g_stm32_mdio_lowerhalf,
+ .timeout = 10
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+static int stm32_c22_read(struct mdio_lowerhalf_s *dev, uint8_t phydev,
+ uint8_t regaddr, uint16_t *value)
+{
+ int to;
+ uint32_t regval;
+
+ int retval = -ETIMEDOUT;
+ struct stm32_mdio_bus_s *priv = (struct stm32_mdio_bus_s *)dev;
+
+ /* Configure the MACMDIOAR register, preserving CSR Clock Range CR[3:0]
+ * bits
+ */
+
+ regval = stm32_getreg(STM32_ETH_MACMDIOAR);
+ regval &= ETH_MACMDIOAR_CR_MASK;
+
+ /* Set the PHY device address, PHY register address, and set the buy bit.
+ * the ETH_MACMDIOAR_GOC == 3, indicating a read operation.
+ */
+
+ regval |= (((uint32_t)phydev << ETH_MACMDIOAR_PA_SHIFT) &
+ ETH_MACMDIOAR_PA_MASK);
+ regval |= (((uint32_t)regaddr << ETH_MACMDIOAR_RDA_SHIFT) &
+ ETH_MACMDIOAR_RDA_MASK);
+ regval |= ETH_MACMDIOAR_MB | ETH_MACMDIOAR_GOC_READ;
+
+ stm32_putreg(regval, STM32_ETH_MACMDIOAR);
+
+ /* Wait for the transfer to complete */
+
+ for (to = priv->timeout; to >= 0; to--)
+ {
+ if ((stm32_getreg(STM32_ETH_MACMDIOAR) & ETH_MACMDIOAR_MB) == 0)
+ {
+ *value = (uint16_t)stm32_getreg(STM32_ETH_MACMDIODR);
+ retval = OK;
+ break;
+ }
+
+ up_mdelay(5);
+ }
+
+ if (to <= 0)
+ {
+ ninfo("MII transfer timed out: phydev: %04x regaddr: %04x\n",
+ phydev, regaddr);
+ }
+
+ return retval;
+}
+
+static int stm32_c22_write(struct mdio_lowerhalf_s *dev, uint8_t phydev,
+ uint8_t regaddr, uint16_t value)
+{
+ int to;
+ uint32_t regval;
+
+ int retval = -ETIMEDOUT;
+ struct stm32_mdio_bus_s *priv = (struct stm32_mdio_bus_s *)dev;
+
+ /* Configure the MACMDIOAR register, preserving CSR Clock Range CR[3:0]
+ * bits
+ */
+
+ regval = stm32_getreg(STM32_ETH_MACMDIOAR);
+ regval &= ETH_MACMDIOAR_CR_MASK;
+
+ /* Read the existing register value, if clear mask is given */
+
+ /* Set the PHY device address, PHY register address, and set the busy bit.
+ * the ETH_MACMDIOAR_GOC == 1, indicating a write operation.
+ */
+
+ regval |= (((uint32_t)phydev << ETH_MACMDIOAR_PA_SHIFT) &
+ ETH_MACMDIOAR_PA_MASK);
+ regval |= (((uint32_t)phydev << ETH_MACMDIOAR_RDA_SHIFT) &
+ ETH_MACMDIOAR_RDA_MASK);
+ regval |= (ETH_MACMDIOAR_MB | ETH_MACMDIOAR_GOC_WRITE);
+
+ /* Write the value into the MACMDIODR register before setting the new
+ * MACMDIOAR register value.
+ */
+
+ stm32_putreg(value, STM32_ETH_MACMDIODR);
+ stm32_putreg(regval, STM32_ETH_MACMDIOAR);
+
+ /* Wait for the transfer to complete */
+
+ for (to = priv->timeout; to >= 0; to--)
+ {
+ if ((stm32_getreg(STM32_ETH_MACMDIOAR) & ETH_MACMDIOAR_MB) == 0)
+ {
+ retval = OK;
+ break;
+ }
+
+ up_mdelay(5);
+ }
+
+ if (to <= 0)
+ {
+ ninfo("MII transfer timed out: phydevaddr: %04x phyregaddr: %04x"
+ "value: %04x\n", phydev, regaddr, value);
+ }
+
+ return retval;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32_mdio_bus_initialize
+ *
+ * Description:
+ * Initialize the MDIO bus
+ *
+ * Returned Value:
+ * Initialized MDIO bus structure or NULL on failure
+ *
+ ****************************************************************************/
+
+struct mdio_bus_s *stm32_mdio_bus_initialize(void)
+{
+ return mdio_register(&g_stm32_mdio_lowerhalf);
+}
diff --git a/arch/arm/src/stm32h7/stm32_mdio.h
b/arch/arm/src/stm32h7/stm32_mdio.h
new file mode 100644
index 00000000000..127feaa1efd
--- /dev/null
+++ b/arch/arm/src/stm32h7/stm32_mdio.h
@@ -0,0 +1,58 @@
+/****************************************************************************
+ * arch/arm/src/stm32h7/stm32_mdio.h
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_ARM_SRC_STM32H7_STM32_MDIO_H
+#define __ARCH_ARM_SRC_STM32H7_STM32_MDIO_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/net/mdio.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32_mdio_bus_initialize
+ *
+ * Description:
+ * Initialize the MDIO bus
+ *
+ * Returned Value:
+ * Initialized MDIO bus structure or NULL on failure
+ *
+ ****************************************************************************/
+
+struct mdio_bus_s *stm32_mdio_bus_initialize(void);
+
+#endif /* __ARCH_ARM_SRC_STM32H7_STM32_MDIO_H */
\ No newline at end of file
diff --git a/drivers/net/CMakeLists.txt b/drivers/net/CMakeLists.txt
index 0f8f8731a1d..5120c29d4d1 100644
--- a/drivers/net/CMakeLists.txt
+++ b/drivers/net/CMakeLists.txt
@@ -29,6 +29,10 @@ if(CONFIG_NET)
list(APPEND SRCS netdev_upperhalf.c)
endif()
+ if(CONFIG_MDIO_BUS)
+ list(APPEND SRCS mdio.c)
+ endif()
+
if(CONFIG_NET_LOOPBACK)
list(APPEND SRCS loopback.c)
endif()
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index c27a4693148..420ac115c68 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -68,6 +68,20 @@ config NETDEV_RSS
When the hardware supports RSS/aRFS function, provide the
hash value and CPU ID to the hardware driver.
+menuconfig MDIO_BUS
+ bool "Upper-half MDIO Bus Driver Options"
+ default y
+
+if MDIO_BUS
+
+comment "IEEE 802.3 Ethernet MDIO Clauses Support"
+
+config MDIO_CLAUSE_45
+ bool "MDIO bus supports Clause 45"
+ default n
+
+endif
+
comment "General Ethernet MAC Driver Options"
config NET_RPMSG_DRV
diff --git a/drivers/net/Make.defs b/drivers/net/Make.defs
index 8a311bd255f..a5f18c71276 100644
--- a/drivers/net/Make.defs
+++ b/drivers/net/Make.defs
@@ -30,6 +30,10 @@ ifeq ($(CONFIG_MM_IOB),y)
CSRCS += netdev_upperhalf.c
endif
+ifeq ($(CONFIG_MDIO_BUS),y)
+ CSRCS += mdio.c
+endif
+
ifeq ($(CONFIG_NET_LOOPBACK),y)
CSRCS += loopback.c
endif
diff --git a/drivers/net/mdio.c b/drivers/net/mdio.c
new file mode 100644
index 00000000000..62a826778ed
--- /dev/null
+++ b/drivers/net/mdio.c
@@ -0,0 +1,253 @@
+/****************************************************************************
+ * drivers/net/mdio.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/mutex.h>
+#include <nuttx/net/mdio.h>
+#include <debug.h>
+
+/****************************************************************************
+ * Private Defines
+ ****************************************************************************/
+
+#define MDIO_READ(d,a,r,v) d->lower->ops->read(d->lower, a, r, v);
+
+#define MDIO_WRITE(d,a,r,v) d->lower->ops->write(d->lower, a, r, v);
+
+#define MDIO_RESET(d,a) d->lower->ops->reset(d->lower, a);
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* This is the opaque handle used by application code to access the
+ * MDIO bus.
+ */
+
+struct mdio_bus_s
+{
+ /* Pointer to the lower-half driver's state */
+
+ FAR struct mdio_lowerhalf_s *lower;
+
+ /* For exclusive access to the bus */
+
+ mutex_t lock;
+};
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mdio_register
+ *
+ * Description:
+ * Register a new MDIO bus instance.
+ *
+ * Input Parameters:
+ * lower - An instance of the lower-half MDIO driver, with the ops vtable
+ * as the first member.
+ *
+ * Returned Value:
+ * A non-NULL handle on success; NULL on failure.
+ *
+ ****************************************************************************/
+
+FAR struct mdio_bus_s *mdio_register(FAR struct mdio_lowerhalf_s *lower)
+{
+ FAR struct mdio_bus_s *dev;
+
+ /* Allocate the upper-half MDIO driver state structure */
+
+ dev = (FAR struct mdio_bus_s *)kmm_zalloc(sizeof(struct mdio_bus_s));
+ if (dev != NULL)
+ {
+ /* Initialize the upper-half driver state */
+
+ nxmutex_init(&dev->lock);
+ dev->lower = lower;
+ }
+ else
+ {
+ nerr("ERROR: Failed to allocate MDIO device structure\n");
+ }
+
+ return dev;
+}
+
+/****************************************************************************
+ * Name: mdio_unregister
+ *
+ * Description:
+ * Unregister an MDIO bus instance.
+ *
+ * Input Parameters:
+ * dev - The MDIO bus handle returned by mdio_register.
+ *
+ * Returned Value:
+ * Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+int mdio_unregister(FAR struct mdio_bus_s *dev)
+{
+ DEBUGASSERT(dev != NULL);
+
+ nxmutex_destroy(&dev->lock);
+ kmm_free(dev);
+ return 0;
+}
+
+/****************************************************************************
+ * Name: mdio_read
+ *
+ * Description:
+ * Read a 16-bit value from a PHY register on the MDIO bus.
+ *
+ * Input Parameters:
+ * dev - The MDIO bus handle.
+ * phyaddr - The PHY address (0-31).
+ * regaddr - The PHY register address (0-31).
+ * value - A pointer to the location to store the read value.
+ *
+ * Returned Value:
+ * Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+int mdio_read(FAR struct mdio_bus_s *dev, uint8_t phyaddr, uint8_t regaddr,
+ FAR uint16_t *value)
+{
+ int ret;
+
+ DEBUGASSERT(dev != NULL && dev->lower != NULL);
+ DEBUGASSERT(dev->lower->ops->read != NULL);
+
+ /* Take the mutex */
+
+ ret = nxmutex_lock(&dev->lock);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ /* Call the lowerhalf driver's read method */
+
+ ret = MDIO_READ(dev, phyaddr, regaddr, value);
+
+ /* Release the mutex */
+
+ nxmutex_unlock(&dev->lock);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: mdio_write
+ *
+ * Description:
+ * Write a 16-bit value to a PHY register on the MDIO bus.
+ *
+ * Input Parameters:
+ * dev - The MDIO bus handle.
+ * phyaddr - The PHY address (0-31).
+ * regaddr - The PHY register address (0-31).
+ * value - The value to write.
+ *
+ * Returned Value:
+ * Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+int mdio_write(FAR struct mdio_bus_s *dev, uint8_t phyaddr, uint8_t regaddr,
+ uint16_t value)
+{
+ int ret;
+
+ DEBUGASSERT(dev != NULL && dev->lower != NULL);
+ DEBUGASSERT(dev->lower->ops->write != NULL);
+
+ /* Take the mutex */
+
+ ret = nxmutex_lock(&dev->lock);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ /* Call the lowerhalf driver's write method */
+
+ ret = MDIO_WRITE(dev, phyaddr, regaddr, value);
+
+ /* Release the mutex */
+
+ nxmutex_unlock(&dev->lock);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: mdio_reset
+ *
+ * Description:
+ * Reset a PHY on the MDIO bus.
+ *
+ * Input Parameters:
+ * dev - The MDIO bus handle.
+ * phyaddr - The PHY address (0-31) to reset.
+ *
+ * Returned Value:
+ * Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+int mdio_reset(FAR struct mdio_bus_s *dev, uint8_t phyaddr)
+{
+ int ret = -ENOSYS;
+
+ DEBUGASSERT(dev != NULL && dev->lower != NULL);
+
+ /* Check if the reset method is provided by the lower-half */
+
+ if (dev->lower->ops->reset)
+ {
+ /* Take the mutex */
+
+ ret = nxmutex_lock(&dev->lock);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ /* Call the lowerhalf driver's reset method */
+
+ ret = MDIO_RESET(dev, phyaddr);
+
+ /* Release the mutex */
+
+ nxmutex_unlock(&dev->lock);
+ }
+
+ return ret;
+}
diff --git a/include/nuttx/net/mdio.h b/include/nuttx/net/mdio.h
new file mode 100644
index 00000000000..7be30d7d925
--- /dev/null
+++ b/include/nuttx/net/mdio.h
@@ -0,0 +1,192 @@
+/****************************************************************************
+ * include/nuttx/net/mdio.h
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __INCLUDE_NUTTX_NET_MDIO_H
+#define __INCLUDE_NUTTX_NET_MDIO_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/compiler.h>
+#include <stdint.h>
+#include <nuttx/mutex.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/* Forward references */
+
+struct mdio_bus_s;
+struct mdio_lowerhalf_s;
+
+/* This structure defines the interface for the MDIO lower-half driver.
+ * These methods are called by the upper-half MDIO driver.
+ */
+
+struct mdio_ops_s
+{
+ /* Clause 22 MDIO Read. The first argument is a reference to the
+ * lower-half driver's private state.
+ */
+
+ int (*read)(FAR struct mdio_lowerhalf_s *lower, uint8_t phyaddr,
+ uint8_t regaddr, FAR uint16_t *value);
+
+ /* Clause 22 MDIO Write */
+
+ int (*write)(FAR struct mdio_lowerhalf_s *lower, uint8_t phyaddr,
+ uint8_t regaddr, uint16_t value);
+
+ #ifdef CONFIG_MDIO_CLAUSE_45
+ #endif
+
+ /* PHY Reset. Optional. */
+
+ int (*reset)(FAR struct mdio_lowerhalf_s *lower, uint8_t phyaddr);
+};
+
+/* This structure defines the state of the MDIO lower-half driver.
+ * The chip-specific MDIO driver must allocate and initialize one instance
+ * of this structure.
+ */
+
+struct mdio_lowerhalf_s
+{
+ /* The vtable of MDIO lower-half operations.
+ * This must be the first field.
+ */
+
+ FAR const struct mdio_ops_s *ops;
+};
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+#ifdef __cplusplus
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+/****************************************************************************
+ * Name: mdio_register
+ *
+ * Description:
+ * Register a new MDIO bus instance.
+ *
+ * Input Parameters:
+ * lower - An instance of the lower-half MDIO driver, with the ops vtable
+ * as the first member.
+ *
+ * Returned Value:
+ * A non-NULL handle on success; NULL on failure.
+ *
+ ****************************************************************************/
+
+FAR struct mdio_bus_s *mdio_register(FAR struct mdio_lowerhalf_s *lower);
+
+/****************************************************************************
+ * Name: mdio_unregister
+ *
+ * Description:
+ * Unregister an MDIO bus instance.
+ *
+ * Input Parameters:
+ * dev - The MDIO bus handle returned by mdio_register.
+ *
+ * Returned Value:
+ * Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+int mdio_unregister(FAR struct mdio_bus_s *dev);
+
+/****************************************************************************
+ * Name: mdio_read
+ *
+ * Description:
+ * Read a 16-bit value from a PHY register on the MDIO bus.
+ *
+ * Input Parameters:
+ * dev - The MDIO bus handle.
+ * phyaddr - The PHY address (0-31).
+ * regaddr - The PHY register address (0-31).
+ * value - A pointer to the location to store the read value.
+ *
+ * Returned Value:
+ * Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+int mdio_read(FAR struct mdio_bus_s *dev, uint8_t phyaddr, uint8_t regaddr,
+ FAR uint16_t *value);
+
+/****************************************************************************
+ * Name: mdio_write
+ *
+ * Description:
+ * Write a 16-bit value to a PHY register on the MDIO bus.
+ *
+ * Input Parameters:
+ * dev - The MDIO bus handle.
+ * phyaddr - The PHY address (0-31).
+ * regaddr - The PHY register address (0-31).
+ * value - The value to write.
+ *
+ * Returned Value:
+ * Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+int mdio_write(FAR struct mdio_bus_s *dev, uint8_t phyaddr, uint8_t regaddr,
+ uint16_t value);
+
+/****************************************************************************
+ * Name: mdio_reset
+ *
+ * Description:
+ * Reset a PHY on the MDIO bus.
+ *
+ * Input Parameters:
+ * dev - The MDIO bus handle.
+ * phyaddr - The PHY address (0-31) to reset.
+ *
+ * Returned Value:
+ * Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+int mdio_reset(FAR struct mdio_bus_s *dev, uint8_t phyaddr);
+
+#undef EXTERN
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __INCLUDE_NUTTX_NET_MDIO_H */