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 @@ -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 + +/**************************************************************************** + * 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 +#include + +#include +#include +#include + +#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 +#include + +/**************************************************************************** + * 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 +#include +#include +#include +#include + +/**************************************************************************** + * 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 +#include +#include + +/**************************************************************************** + * 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 */