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 <luchiann.mihai@gmail.com>
This commit is contained in:
LuchianMihai
2025-09-06 23:38:30 +03:00
committed by Xiang Xiao
parent c8b7950bd4
commit 95efa6f7cf
12 changed files with 881 additions and 165 deletions
+1
View File
@@ -12,6 +12,7 @@ Network Support
nat.rst
netdev.rst
netdriver.rst
mdio.rst
netguardsize.rst
netlink.rst
slip.rst
+73
View File
@@ -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``.
+4
View File
@@ -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()
+4
View File
@@ -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
+42 -165
View File
@@ -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))
+232
View File
@@ -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);
}
+58
View File
@@ -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 */
+4
View File
@@ -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()
+14
View File
@@ -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
+4
View File
@@ -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
+253
View File
@@ -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;
}
+192
View File
@@ -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 */