From c51c0c5ae9afd74d8ec3a5210e86475ae2b37c78 Mon Sep 17 00:00:00 2001 From: Immo Birnbaum Date: Thu, 20 Feb 2025 22:09:42 +0100 Subject: [PATCH 1/9] drivers: ethernet: xlnx_gem: remove legacy MDIO/PHY code Remove the legacy MDIO and PHY support for the GEM MAC which dates back to 2021 and doesn't use the MDIO/PHY frameworks provided by Zephyr nowadays, as those didn't exist back then. Signed-off-by: Immo Birnbaum --- drivers/ethernet/CMakeLists.txt | 6 +- drivers/ethernet/phy_xlnx_gem.c | 980 -------------------------------- drivers/ethernet/phy_xlnx_gem.h | 155 ----- 3 files changed, 1 insertion(+), 1140 deletions(-) delete mode 100644 drivers/ethernet/phy_xlnx_gem.c delete mode 100644 drivers/ethernet/phy_xlnx_gem.h diff --git a/drivers/ethernet/CMakeLists.txt b/drivers/ethernet/CMakeLists.txt index f9cef24042b5e..a5e4f0b06934d 100644 --- a/drivers/ethernet/CMakeLists.txt +++ b/drivers/ethernet/CMakeLists.txt @@ -13,11 +13,7 @@ zephyr_library_sources_ifdef(CONFIG_ETH_GECKO phy_gecko.c ) -zephyr_library_sources_ifdef(CONFIG_ETH_XLNX_GEM - eth_xlnx_gem.c - phy_xlnx_gem.c - ) - +zephyr_library_sources_ifdef(CONFIG_ETH_XLNX_GEM eth_xlnx_gem.c) zephyr_library_sources_ifdef(CONFIG_ETH_DWMAC eth_dwmac.c) zephyr_library_sources_ifdef(CONFIG_ETH_DWMAC_STM32H7X eth_dwmac_stm32h7x.c) zephyr_library_sources_ifdef(CONFIG_ETH_DWMAC_MMU eth_dwmac_mmu.c) diff --git a/drivers/ethernet/phy_xlnx_gem.c b/drivers/ethernet/phy_xlnx_gem.c deleted file mode 100644 index 126626a0f25ac..0000000000000 --- a/drivers/ethernet/phy_xlnx_gem.c +++ /dev/null @@ -1,980 +0,0 @@ -/* - * Xilinx Processor System Gigabit Ethernet controller (GEM) driver - * - * PHY management interface implementation - * Models currently supported: - * - Marvell Alaska 88E1111 (QEMU simulated PHY) - * - Marvell Alaska 88E1510/88E1518/88E1512/88E1514 (Zedboard) - * - Texas Instruments TLK105 - * - Texas Instruments DP83822 - * - * Copyright (c) 2021, Weidmueller Interface GmbH & Co. KG - * SPDX-License-Identifier: Apache-2.0 - */ - -#include -#include - -#include "eth_xlnx_gem_priv.h" - -#define LOG_MODULE_NAME phy_xlnx_gem -#define LOG_LEVEL CONFIG_ETHERNET_LOG_LEVEL -#include -LOG_MODULE_REGISTER(LOG_MODULE_NAME); - -/* Basic MDIO read / write functions for PHY access */ - -/** - * @brief Read PHY data via the MDIO interface - * Reads data from a PHY attached to the respective GEM's MDIO interface - * - * @param base_addr Base address of the GEM's register space - * @param phy_addr MDIO address of the PHY to be accessed - * @param reg_addr Index of the PHY register to be read - * @return 16-bit data word received from the PHY - */ -static uint16_t phy_xlnx_gem_mdio_read( - uint32_t base_addr, uint8_t phy_addr, - uint8_t reg_addr) -{ - uint32_t reg_val; - uint32_t poll_cnt = 0; - - /* - * MDIO read operation as described in Zynq-7000 TRM, - * chapter 16.3.4, p. 517. - */ - - /* - * Wait until gem.net_status[phy_mgmt_idle] == 1 before issuing the - * current command. - */ - do { - if (poll_cnt++ > 0) { - k_busy_wait(100); - } - reg_val = sys_read32(base_addr + ETH_XLNX_GEM_NWSR_OFFSET); - } while ((reg_val & ETH_XLNX_GEM_MDIO_IDLE_BIT) == 0 && poll_cnt < 10); - if (poll_cnt == 10) { - LOG_ERR("GEM@0x%08X read from PHY address %hhu, " - "register address %hhu timed out", - base_addr, phy_addr, reg_addr); - return 0; - } - - /* Assemble & write the read command to the gem.phy_maint register */ - - /* Set the bits constant for any operation */ - reg_val = ETH_XLNX_GEM_PHY_MAINT_CONST_BITS; - /* Indicate a read operation */ - reg_val |= ETH_XLNX_GEM_PHY_MAINT_READ_OP_BIT; - /* PHY address */ - reg_val |= (((uint32_t)phy_addr & ETH_XLNX_GEM_PHY_MAINT_PHY_ADDRESS_MASK) << - ETH_XLNX_GEM_PHY_MAINT_PHY_ADDRESS_SHIFT); - /* Register address */ - reg_val |= (((uint32_t)reg_addr & ETH_XLNX_GEM_PHY_MAINT_REGISTER_ID_MASK) << - ETH_XLNX_GEM_PHY_MAINT_REGISTER_ID_SHIFT); - - sys_write32(reg_val, base_addr + ETH_XLNX_GEM_PHY_MAINTENANCE_OFFSET); - - /* - * Wait until gem.net_status[phy_mgmt_idle] == 1 -> current command - * completed. - */ - poll_cnt = 0; - do { - if (poll_cnt++ > 0) { - k_busy_wait(100); - } - reg_val = sys_read32(base_addr + ETH_XLNX_GEM_NWSR_OFFSET); - } while ((reg_val & ETH_XLNX_GEM_MDIO_IDLE_BIT) == 0 && poll_cnt < 10); - if (poll_cnt == 10) { - LOG_ERR("GEM@0x%08X read from PHY address %hhu, " - "register address %hhu timed out", - base_addr, phy_addr, reg_addr); - return 0; - } - - /* - * Read the data returned by the PHY -> lower 16 bits of the PHY main- - * tenance register - */ - reg_val = sys_read32(base_addr + ETH_XLNX_GEM_PHY_MAINTENANCE_OFFSET); - return (uint16_t)reg_val; -} - -/** - * @brief Writes PHY data via the MDIO interface - * Writes data to a PHY attached to the respective GEM's MDIO interface - * - * @param base_addr Base address of the GEM's register space - * @param phy_addr MDIO address of the PHY to be accessed - * @param reg_addr Index of the PHY register to be written to - * @param value 16-bit data word to be written to the target register - */ -static void phy_xlnx_gem_mdio_write( - uint32_t base_addr, uint8_t phy_addr, - uint8_t reg_addr, uint16_t value) -{ - uint32_t reg_val; - uint32_t poll_cnt = 0; - - /* - * MDIO write operation as described in Zynq-7000 TRM, - * chapter 16.3.4, p. 517. - */ - - /* - * Wait until gem.net_status[phy_mgmt_idle] == 1 before issuing the - * current command. - */ - do { - if (poll_cnt++ > 0) { - k_busy_wait(100); - } - reg_val = sys_read32(base_addr + ETH_XLNX_GEM_NWSR_OFFSET); - } while ((reg_val & ETH_XLNX_GEM_MDIO_IDLE_BIT) == 0 && poll_cnt < 10); - if (poll_cnt == 10) { - LOG_ERR("GEM@0x%08X write to PHY address %hhu, " - "register address %hhu timed out", - base_addr, phy_addr, reg_addr); - return; - } - - /* Assemble & write the read command to the gem.phy_maint register */ - - /* Set the bits constant for any operation */ - reg_val = ETH_XLNX_GEM_PHY_MAINT_CONST_BITS; - /* Indicate a read operation */ - reg_val |= ETH_XLNX_GEM_PHY_MAINT_WRITE_OP_BIT; - /* PHY address */ - reg_val |= (((uint32_t)phy_addr & ETH_XLNX_GEM_PHY_MAINT_PHY_ADDRESS_MASK) << - ETH_XLNX_GEM_PHY_MAINT_PHY_ADDRESS_SHIFT); - /* Register address */ - reg_val |= (((uint32_t)reg_addr & ETH_XLNX_GEM_PHY_MAINT_REGISTER_ID_MASK) << - ETH_XLNX_GEM_PHY_MAINT_REGISTER_ID_SHIFT); - /* 16 bits of data for the destination register */ - reg_val |= ((uint32_t)value & ETH_XLNX_GEM_PHY_MAINT_DATA_MASK); - - sys_write32(reg_val, base_addr + ETH_XLNX_GEM_PHY_MAINTENANCE_OFFSET); - - /* - * Wait until gem.net_status[phy_mgmt_idle] == 1 -> current command - * completed. - */ - poll_cnt = 0; - do { - if (poll_cnt++ > 0) { - k_busy_wait(100); - } - reg_val = sys_read32(base_addr + ETH_XLNX_GEM_NWSR_OFFSET); - } while ((reg_val & ETH_XLNX_GEM_MDIO_IDLE_BIT) == 0 && poll_cnt < 10); - if (poll_cnt == 10) { - LOG_ERR("GEM@0x%08X write to PHY address %hhu, " - "register address %hhu timed out", - base_addr, phy_addr, reg_addr); - } -} - -/* - * Vendor-specific PHY management functions for: - * Marvell Alaska 88E1111 (QEMU simulated PHY) - * Marvell Alaska 88E1510/88E1518/88E1512/88E1514 (Zedboard) - * Register IDs & procedures are based on the corresponding datasheets: - * https://www.marvell.com/content/dam/marvell/en/public-collateral/transceivers/marvell-phys-transceivers-alaska-88e1111-datasheet.pdf - * https://www.marvell.com/content/dam/marvell/en/public-collateral/transceivers/marvell-phys-transceivers-alaska-88e151x-datasheet.pdf - * - * NOTICE: Unless indicated otherwise, page/table source references refer to - * the 88E151x datasheet. - */ - -/** - * @brief Marvell Alaska PHY reset function - * Reset function for the Marvell Alaska PHY series - * - * @param dev Pointer to the device data - */ -static void phy_xlnx_gem_marvell_alaska_reset(const struct device *dev) -{ - const struct eth_xlnx_gem_dev_cfg *dev_conf = dev->config; - struct eth_xlnx_gem_dev_data *dev_data = dev->data; - uint16_t phy_data; - uint32_t retries = 0; - - /* - * Page 0, register address 0 = Copper control register, - * bit [15] = PHY reset. Register 0/0 access is R/M/W. Comp. - * datasheet chapter 2.6 and table 64 "Copper Control Register". - */ - phy_data = phy_xlnx_gem_mdio_read(dev_conf->base_addr, dev_data->phy_addr, - PHY_MRVL_COPPER_CONTROL_REGISTER); - phy_data |= PHY_MRVL_COPPER_CONTROL_RESET_BIT; - phy_xlnx_gem_mdio_write(dev_conf->base_addr, dev_data->phy_addr, - PHY_MRVL_COPPER_CONTROL_REGISTER, phy_data); - - /* Bit [15] reverts to 0 once the reset is complete. */ - while (((phy_data & PHY_MRVL_COPPER_CONTROL_RESET_BIT) != 0) && (retries++ < 10)) { - phy_data = phy_xlnx_gem_mdio_read(dev_conf->base_addr, dev_data->phy_addr, - PHY_MRVL_COPPER_CONTROL_REGISTER); - } - if (retries == 10) { - LOG_ERR("%s reset PHY address %hhu (Marvell Alaska) timed out", - dev->name, dev_data->phy_addr); - } -} - -/** - * @brief Marvell Alaska PHY configuration function - * Configuration function for the Marvell Alaska PHY series - * - * @param dev Pointer to the device data - */ -static void phy_xlnx_gem_marvell_alaska_cfg(const struct device *dev) -{ - const struct eth_xlnx_gem_dev_cfg *dev_conf = dev->config; - struct eth_xlnx_gem_dev_data *dev_data = dev->data; - uint16_t phy_data; - uint16_t phy_data_gbit; - uint32_t retries = 0; - - /* - * Page 0, register address 0 = Copper control register, - * bit [12] = auto-negotiation enable bit is to be cleared - * for now, afterwards, trigger a PHY reset. - * Register 0/0 access is R/M/W. Comp. datasheet chapter 2.6 - * and table 64 "Copper Control Register". - */ - phy_data = phy_xlnx_gem_mdio_read(dev_conf->base_addr, dev_data->phy_addr, - PHY_MRVL_COPPER_CONTROL_REGISTER); - phy_data &= ~PHY_MRVL_COPPER_CONTROL_AUTONEG_ENABLE_BIT; - phy_xlnx_gem_mdio_write(dev_conf->base_addr, dev_data->phy_addr, - PHY_MRVL_COPPER_CONTROL_REGISTER, phy_data); - phy_xlnx_gem_marvell_alaska_reset(dev); - - if ((dev_data->phy_id & PHY_MRVL_PHY_ID_MODEL_MASK) == - PHY_MRVL_PHY_ID_MODEL_88E151X) { - /* - * 88E151x only: configure the system interface and media type - * (i.e. "RGMII to Copper", 0x0). On the 88E1111, this setting - * is configured using I/O pins on the device. - * TODO: Make this value configurable via KConfig or DT? - * Page 18, register address 20 = General Control Register 1, - * bits [2..0] = mode configuration - * Comp. datasheet table 129 "General Control Register 1" - * NOTICE: a change of this value requires a subsequent software - * reset command via the same register's bit [15]. - */ - phy_xlnx_gem_mdio_write(dev_conf->base_addr, dev_data->phy_addr, - PHY_MRVL_COPPER_PAGE_SWITCH_REGISTER, - PHY_MRVL_GENERAL_CONTROL_1_PAGE); - - phy_data = phy_xlnx_gem_mdio_read(dev_conf->base_addr, dev_data->phy_addr, - PHY_MRVL_GENERAL_CONTROL_1_REGISTER); - phy_data &= ~(PHY_MRVL_MODE_CONFIG_MASK << PHY_MRVL_MODE_CONFIG_SHIFT); - phy_xlnx_gem_mdio_write(dev_conf->base_addr, dev_data->phy_addr, - PHY_MRVL_GENERAL_CONTROL_1_REGISTER, phy_data); - - /* - * [15] Mode Software Reset bit, affecting pages 6 and 18 - * Reset is performed immediately, bit [15] is self-clearing. - * This reset bit is not to be confused with the actual PHY - * reset in register 0/0! - */ - phy_data |= PHY_MRVL_GENERAL_CONTROL_1_RESET_BIT; - phy_xlnx_gem_mdio_write(dev_conf->base_addr, dev_data->phy_addr, - PHY_MRVL_GENERAL_CONTROL_1_REGISTER, phy_data); - - /* Bit [15] reverts to 0 once the reset is complete. */ - while (((phy_data & PHY_MRVL_GENERAL_CONTROL_1_RESET_BIT) != 0) && - (retries++ < 10)) { - phy_data = phy_xlnx_gem_mdio_read(dev_conf->base_addr, - dev_data->phy_addr, - PHY_MRVL_GENERAL_CONTROL_1_REGISTER); - } - if (retries == 10) { - LOG_ERR("%s configure PHY address %hhu (Marvell Alaska) timed out", - dev->name, dev_data->phy_addr); - return; - } - - /* Revert to register page 0 */ - phy_xlnx_gem_mdio_write(dev_conf->base_addr, dev_data->phy_addr, - PHY_MRVL_COPPER_PAGE_SWITCH_REGISTER, - PHY_MRVL_BASE_REGISTERS_PAGE); - } - - /* - * Configure MDIX - * TODO: Make this value configurable via KConfig or DT? - * 88E151x: Page 0, register address 16 = Copper specific control register 1, - * 88E1111: Page any, register address 16 = PHY specific control register, - * bits [6..5] = MDIO crossover mode. Comp. datasheet table 76. - * NOTICE: a change of this value requires a subsequent software - * reset command via Copper Control Register's bit [15]. - */ - - /* [6..5] 11 = Enable auto cross over detection */ - phy_data = phy_xlnx_gem_mdio_read(dev_conf->base_addr, dev_data->phy_addr, - PHY_MRVL_COPPER_CONTROL_1_REGISTER); - phy_data &= ~(PHY_MRVL_MDIX_CONFIG_MASK << PHY_MRVL_MDIX_CONFIG_SHIFT); - phy_data |= (PHY_MRVL_MDIX_AUTO_CROSSOVER_ENABLE << PHY_MRVL_MDIX_CONFIG_SHIFT); - phy_xlnx_gem_mdio_write(dev_conf->base_addr, dev_data->phy_addr, - PHY_MRVL_COPPER_CONTROL_1_REGISTER, phy_data); - - /* - * Configure the Copper Specific Interrupt Enable Register - * (88E151x) / Interrupt Enable Register (88E1111). - * The interrupt status register provides a convenient way to - * detect relevant state changes, also, PHY management could - * eventually be changed from polling to interrupt-driven. - * There's just one big catch: at least on the Zedboard, the - * PHY interrupt line isn't wired up, therefore, the GEM can - * never trigger a PHY interrupt. Still, the PHY interrupts - * are configured & enabled in order to obtain all relevant - * status data from a single source. - * - * -> all bits contained herein will be retained during the - * upcoming software reset operation. - * Page 0, register address 18 = (Copper Specific) Interrupt - * Enable Register, - * bit [14] = Speed changed interrupt enable, - * bit [13] = Duplex changed interrupt enable, - * bit [11] = Auto-negotiation completed interrupt enable, - * bit [10] = Link status changed interrupt enable. - * Comp. datasheet table 78 - */ - phy_data = PHY_MRVL_COPPER_SPEED_CHANGED_INT_BIT | - PHY_MRVL_COPPER_DUPLEX_CHANGED_INT_BIT | - PHY_MRVL_COPPER_AUTONEG_COMPLETED_INT_BIT | - PHY_MRVL_COPPER_LINK_STATUS_CHANGED_INT_BIT; - phy_xlnx_gem_mdio_write(dev_conf->base_addr, dev_data->phy_addr, - PHY_MRVL_COPPER_INT_ENABLE_REGISTER, phy_data); - - /* Trigger a PHY Reset, affecting pages 0, 2, 3, 5, 7. */ - phy_xlnx_gem_marvell_alaska_reset(dev); - - /* - * Clear the interrupt status register before advertising the - * supported link speed(s). - */ - phy_data = phy_xlnx_gem_mdio_read(dev_conf->base_addr, dev_data->phy_addr, - PHY_MRVL_COPPER_INT_STATUS_REGISTER); - - /* - * Set which link speeds and duplex modes shall be advertised during - * auto-negotiation, then re-enable auto-negotiation. PHY link speed - * advertisement configuration as described in Zynq-7000 TRM, chapter - * 16.3.4, p. 517. - */ - - /* - * Advertise the link speed from the device configuration & perform - * auto-negotiation. This process involves: - * - * Page 0, register address 4 = - * Copper Auto-Negotiation Advertisement Register, - * Page 0, register address 0 = - * Copper Control Register, bit [15] = Reset -> apply all changes - * made regarding advertisement, - * Page 0, register address 9 = - * 1000BASE-T Control Register (if link speed = 1GBit/s), - * Page 0, register address 1 = - * Copper Status Register, bit [5] = Copper Auto-Negotiation - * Complete. - * - * Comp. datasheet tables 68 & 73. - */ - - /* - * 88E151x only: - * Register 4, bits [4..0] = Selector field, 00001 = 802.3. Those bits - * are reserved in other Marvell PHYs. - */ - if ((dev_data->phy_id & PHY_MRVL_PHY_ID_MODEL_MASK) == - PHY_MRVL_PHY_ID_MODEL_88E151X) { - phy_data = PHY_MRVL_ADV_SELECTOR_802_3; - } else { - phy_data = 0x0000; - } - - /* - * Clear the 1 GBit/s FDX/HDX advertisement bits from reg. 9's current - * contents in case we're going to advertise anything below 1 GBit/s - * as maximum / nominal link speed. - */ - phy_data_gbit = phy_xlnx_gem_mdio_read(dev_conf->base_addr, dev_data->phy_addr, - PHY_MRVL_1000BASET_CONTROL_REGISTER); - phy_data_gbit &= ~PHY_MRVL_ADV_1000BASET_FDX_BIT; - phy_data_gbit &= ~PHY_MRVL_ADV_1000BASET_HDX_BIT; - - if (dev_conf->enable_fdx) { - if (dev_conf->max_link_speed == LINK_1GBIT) { - /* Advertise 1 GBit/s, full duplex */ - phy_data_gbit |= PHY_MRVL_ADV_1000BASET_FDX_BIT; - if (dev_conf->phy_advertise_lower) { - /* + 100 MBit/s, full duplex */ - phy_data |= PHY_MRVL_ADV_100BASET_FDX_BIT; - /* + 10 MBit/s, full duplex */ - phy_data |= PHY_MRVL_ADV_10BASET_FDX_BIT; - } - } else if (dev_conf->max_link_speed == LINK_100MBIT) { - /* Advertise 100 MBit/s, full duplex */ - phy_data |= PHY_MRVL_ADV_100BASET_FDX_BIT; - if (dev_conf->phy_advertise_lower) { - /* + 10 MBit/s, full duplex */ - phy_data |= PHY_MRVL_ADV_10BASET_FDX_BIT; - } - } else if (dev_conf->max_link_speed == LINK_10MBIT) { - /* Advertise 10 MBit/s, full duplex */ - phy_data |= PHY_MRVL_ADV_10BASET_FDX_BIT; - } - } else { - if (dev_conf->max_link_speed == LINK_1GBIT) { - /* Advertise 1 GBit/s, half duplex */ - phy_data_gbit = PHY_MRVL_ADV_1000BASET_HDX_BIT; - if (dev_conf->phy_advertise_lower) { - /* + 100 MBit/s, half duplex */ - phy_data |= PHY_MRVL_ADV_100BASET_HDX_BIT; - /* + 10 MBit/s, half duplex */ - phy_data |= PHY_MRVL_ADV_10BASET_HDX_BIT; - } - } else if (dev_conf->max_link_speed == LINK_100MBIT) { - /* Advertise 100 MBit/s, half duplex */ - phy_data |= PHY_MRVL_ADV_100BASET_HDX_BIT; - if (dev_conf->phy_advertise_lower) { - /* + 10 MBit/s, half duplex */ - phy_data |= PHY_MRVL_ADV_10BASET_HDX_BIT; - } - } else if (dev_conf->max_link_speed == LINK_10MBIT) { - /* Advertise 10 MBit/s, half duplex */ - phy_data |= PHY_MRVL_ADV_10BASET_HDX_BIT; - } - } - - phy_xlnx_gem_mdio_write(dev_conf->base_addr, dev_data->phy_addr, - PHY_MRVL_1000BASET_CONTROL_REGISTER, phy_data_gbit); - phy_xlnx_gem_mdio_write(dev_conf->base_addr, dev_data->phy_addr, - PHY_MRVL_COPPER_AUTONEG_ADV_REGISTER, phy_data); - - /* - * Trigger a PHY reset, affecting pages 0, 2, 3, 5, 7. - * Afterwards, set the auto-negotiation enable bit [12] in the - * Copper Control Register. - */ - phy_xlnx_gem_marvell_alaska_reset(dev); - phy_data = phy_xlnx_gem_mdio_read(dev_conf->base_addr, dev_data->phy_addr, - PHY_MRVL_COPPER_CONTROL_REGISTER); - phy_data |= PHY_MRVL_COPPER_CONTROL_AUTONEG_ENABLE_BIT; - phy_xlnx_gem_mdio_write(dev_conf->base_addr, dev_data->phy_addr, - PHY_MRVL_COPPER_CONTROL_REGISTER, phy_data); - - /* - * Set the link speed to 'link down' for now, once auto-negotiation - * is complete, the result will be handled by the system work queue. - */ - dev_data->eff_link_speed = LINK_DOWN; -} - -/** - * @brief Marvell Alaska PHY status change polling function - * Status change polling function for the Marvell Alaska PHY series - * - * @param dev Pointer to the device data - * @return A set of bits indicating whether one or more of the following - * events has occurred: auto-negotiation completed, link state - * changed, link speed changed. - */ -static uint16_t phy_xlnx_gem_marvell_alaska_poll_sc(const struct device *dev) -{ - const struct eth_xlnx_gem_dev_cfg *dev_conf = dev->config; - struct eth_xlnx_gem_dev_data *dev_data = dev->data; - uint16_t phy_data; - uint16_t phy_status = 0; - - /* - * PHY status change detection is implemented by reading the - * interrupt status register. - * Page 0, register address 19 = Copper Interrupt Status Register - * bit [14] = Speed changed interrupt, - * bit [13] = Duplex changed interrupt, - * bit [11] = Auto-negotiation completed interrupt, - * bit [10] = Link status changed interrupt. - * Comp. datasheet table 79 - */ - phy_data = phy_xlnx_gem_mdio_read(dev_conf->base_addr, dev_data->phy_addr, - PHY_MRVL_COPPER_INT_STATUS_REGISTER); - - if ((phy_data & PHY_MRVL_COPPER_AUTONEG_COMPLETED_INT_BIT) != 0) { - phy_status |= PHY_XLNX_GEM_EVENT_AUTONEG_COMPLETE; - } - if (((phy_data & PHY_MRVL_COPPER_DUPLEX_CHANGED_INT_BIT) != 0) || - ((phy_data & PHY_MRVL_COPPER_LINK_STATUS_CHANGED_INT_BIT) != 0)) { - phy_status |= PHY_XLNX_GEM_EVENT_LINK_STATE_CHANGED; - } - if ((phy_data & PHY_MRVL_COPPER_SPEED_CHANGED_INT_BIT) != 0) { - phy_status |= PHY_XLNX_GEM_EVENT_LINK_SPEED_CHANGED; - } - - /* - * Clear the status register, preserve reserved bit [3] as indicated - * by the datasheet - */ - phy_xlnx_gem_mdio_write(dev_conf->base_addr, dev_data->phy_addr, - PHY_MRVL_COPPER_INT_STATUS_REGISTER, (phy_data & 0x8)); - - return phy_status; -} - -/** - * @brief Marvell Alaska PHY link status polling function - * Link status polling function for the Marvell Alaska PHY series - * - * @param dev Pointer to the device data - * @return 1 if the PHY indicates link up, 0 if the link is down - */ -static uint8_t phy_xlnx_gem_marvell_alaska_poll_lsts(const struct device *dev) -{ - const struct eth_xlnx_gem_dev_cfg *dev_conf = dev->config; - struct eth_xlnx_gem_dev_data *dev_data = dev->data; - uint16_t phy_data; - - /* - * Current link status is obtained from: - * Page 0, register address 1 = Copper Status Register - * bit [2] = Copper Link Status - */ - phy_data = phy_xlnx_gem_mdio_read(dev_conf->base_addr, dev_data->phy_addr, - PHY_MRVL_COPPER_STATUS_REGISTER); - - return ((phy_data >> PHY_MRVL_COPPER_LINK_STATUS_BIT_SHIFT) & 0x0001); -} - -/** - * @brief Marvell Alaska PHY link speed polling function - * Link speed polling function for the Marvell Alaska PHY series - * - * @param dev Pointer to the device data - * @return Enum containing the current link speed reported by the PHY - */ -static enum eth_xlnx_link_speed phy_xlnx_gem_marvell_alaska_poll_lspd( - const struct device *dev) -{ - const struct eth_xlnx_gem_dev_cfg *dev_conf = dev->config; - struct eth_xlnx_gem_dev_data *dev_data = dev->data; - enum eth_xlnx_link_speed link_speed; - uint16_t phy_data; - - /* - * Current link speed is obtained from: - * Page 0, register address 17 = Copper Specific Status Register 1 - * bits [15 .. 14] = Speed. - */ - phy_data = phy_xlnx_gem_mdio_read(dev_conf->base_addr, dev_data->phy_addr, - PHY_MRVL_COPPER_STATUS_1_REGISTER); - phy_data >>= PHY_MRVL_LINK_SPEED_SHIFT; - phy_data &= PHY_MRVL_LINK_SPEED_MASK; - - /* - * Link speed bit masks: comp. datasheet, table 77 @ description - * of the 'Speed' bits. - */ - switch (phy_data) { - case PHY_MRVL_LINK_SPEED_10MBIT: - link_speed = LINK_10MBIT; - break; - case PHY_MRVL_LINK_SPEED_100MBIT: - link_speed = LINK_100MBIT; - break; - case PHY_MRVL_LINK_SPEED_1GBIT: - link_speed = LINK_1GBIT; - break; - default: - link_speed = LINK_DOWN; - break; - }; - - return link_speed; -} - -/* - * Vendor-specific PHY management functions for: - * Texas Instruments TLK105 - * Texas Instruments DP83822 - * with the DP83822 being the successor to the deprecated TLK105. - * Register IDs & procedures are based on the corresponding datasheets: - * https://www.ti.com/lit/gpn/tlk105 - * https://www.ti.com/lit/gpn/dp83822i - */ - -/** - * @brief TI TLK105 & DP83822 PHY reset function - * Reset function for the TI TLK105 & DP83822 PHYs - * - * @param dev Pointer to the device data - */ -static void phy_xlnx_gem_ti_dp83822_reset(const struct device *dev) -{ - const struct eth_xlnx_gem_dev_cfg *dev_conf = dev->config; - struct eth_xlnx_gem_dev_data *dev_data = dev->data; - uint16_t phy_data; - uint32_t retries = 0; - - phy_data = phy_xlnx_gem_mdio_read(dev_conf->base_addr, dev_data->phy_addr, - PHY_TI_BASIC_MODE_CONTROL_REGISTER); - phy_data |= PHY_TI_BASIC_MODE_CONTROL_RESET_BIT; - phy_xlnx_gem_mdio_write(dev_conf->base_addr, dev_data->phy_addr, - PHY_TI_BASIC_MODE_CONTROL_REGISTER, phy_data); - - while (((phy_data & PHY_TI_BASIC_MODE_CONTROL_RESET_BIT) != 0) && (retries++ < 10)) { - phy_data = phy_xlnx_gem_mdio_read(dev_conf->base_addr, dev_data->phy_addr, - PHY_TI_BASIC_MODE_CONTROL_REGISTER); - } - if (retries == 10) { - LOG_ERR("%s reset PHY address %hhu (TI TLK105/DP83822) timed out", - dev->name, dev_data->phy_addr); - } -} - -/** - * @brief TI TLK105 & DP83822 PHY configuration function - * Configuration function for the TI TLK105 & DP83822 PHYs - * - * @param dev Pointer to the device data - */ -static void phy_xlnx_gem_ti_dp83822_cfg(const struct device *dev) -{ - const struct eth_xlnx_gem_dev_cfg *dev_conf = dev->config; - struct eth_xlnx_gem_dev_data *dev_data = dev->data; - uint16_t phy_data = PHY_TI_ADV_SELECTOR_802_3; - - /* Configure link advertisement */ - if (dev_conf->enable_fdx) { - if (dev_conf->max_link_speed == LINK_100MBIT) { - /* Advertise 100BASE-TX, full duplex */ - phy_data |= PHY_TI_ADV_100BASET_FDX_BIT; - if (dev_conf->phy_advertise_lower) { - /* + 10BASE-TX, full duplex */ - phy_data |= PHY_TI_ADV_10BASET_FDX_BIT; - } - } else if (dev_conf->max_link_speed == LINK_10MBIT) { - /* Advertise 10BASE-TX, full duplex */ - phy_data |= PHY_TI_ADV_10BASET_FDX_BIT; - } - } else { - if (dev_conf->max_link_speed == LINK_100MBIT) { - /* Advertise 100BASE-TX, half duplex */ - phy_data |= PHY_TI_ADV_100BASET_HDX_BIT; - if (dev_conf->phy_advertise_lower) { - /* + 10BASE-TX, half duplex */ - phy_data |= PHY_TI_ADV_10BASET_HDX_BIT; - } - } else if (dev_conf->max_link_speed == LINK_10MBIT) { - /* Advertise 10BASE-TX, half duplex */ - phy_data |= PHY_TI_ADV_10BASET_HDX_BIT; - } - } - phy_xlnx_gem_mdio_write(dev_conf->base_addr, dev_data->phy_addr, - PHY_TI_AUTONEG_ADV_REGISTER, phy_data); - - /* Enable auto-negotiation */ - phy_data = phy_xlnx_gem_mdio_read(dev_conf->base_addr, dev_data->phy_addr, - PHY_TI_BASIC_MODE_CONTROL_REGISTER); - phy_data |= PHY_TI_BASIC_MODE_CONTROL_AUTONEG_ENABLE_BIT; - phy_xlnx_gem_mdio_write(dev_conf->base_addr, dev_data->phy_addr, - PHY_TI_BASIC_MODE_CONTROL_REGISTER, phy_data); - - /* Robust Auto MDIX */ - phy_data = phy_xlnx_gem_mdio_read(dev_conf->base_addr, dev_data->phy_addr, - PHY_TI_CONTROL_REGISTER_1); - phy_data |= PHY_TI_CR1_ROBUST_AUTO_MDIX_BIT; - phy_xlnx_gem_mdio_write(dev_conf->base_addr, dev_data->phy_addr, - PHY_TI_CONTROL_REGISTER_1, phy_data); - - phy_data = phy_xlnx_gem_mdio_read(dev_conf->base_addr, dev_data->phy_addr, - PHY_TI_PHY_CONTROL_REGISTER); - /* Auto MDIX enable */ - phy_data |= PHY_TI_PHY_CONTROL_AUTO_MDIX_ENABLE_BIT; - /* Link LED shall only indicate link up or down, no RX/TX activity */ - phy_data |= PHY_TI_PHY_CONTROL_LED_CONFIG_LINK_ONLY_BIT; - /* Force MDIX disable */ - phy_data &= ~PHY_TI_PHY_CONTROL_FORCE_MDIX_BIT; - phy_xlnx_gem_mdio_write(dev_conf->base_addr, dev_data->phy_addr, - PHY_TI_PHY_CONTROL_REGISTER, phy_data); - - /* Set blink rate to 5 Hz */ - phy_data = (PHY_TI_LED_CONTROL_BLINK_RATE_5HZ << - PHY_TI_LED_CONTROL_BLINK_RATE_SHIFT); - phy_xlnx_gem_mdio_write(dev_conf->base_addr, dev_data->phy_addr, - PHY_TI_LED_CONTROL_REGISTER, phy_data); - - /* - * Set the link speed to 'link down' for now, once auto-negotiation - * is complete, the result will be handled by the system work queue. - */ - dev_data->eff_link_speed = LINK_DOWN; -} - -/** - * @brief TI TLK105 & DP83822 PHY status change polling function - * Status change polling function for the TI TLK105 & DP83822 PHYs - * - * @param dev Pointer to the device data - * @return A set of bits indicating whether one or more of the following - * events has occurred: auto-negotiation completed, link state - * changed, link speed changed. - */ -static uint16_t phy_xlnx_gem_ti_dp83822_poll_sc(const struct device *dev) -{ - const struct eth_xlnx_gem_dev_cfg *dev_conf = dev->config; - struct eth_xlnx_gem_dev_data *dev_data = dev->data; - uint16_t phy_data; - uint16_t phy_status = 0; - - /* - * The relevant status bits are obtained from the MII Interrupt - * Status Register 1. The upper byte of the register's data word - * contains the status bits which are set regardless of whether - * the corresponding interrupt enable bits are set in the lower - * byte or not (comp. TLK105 documentation, chapter 8.1.16). - */ - phy_data = phy_xlnx_gem_mdio_read(dev_conf->base_addr, dev_data->phy_addr, - PHY_TI_MII_INTERRUPT_STATUS_REGISTER_1); - - if ((phy_data & PHY_TI_AUTONEG_COMPLETED_INT_BIT) != 0) { - phy_status |= PHY_XLNX_GEM_EVENT_AUTONEG_COMPLETE; - } - if ((phy_data & PHY_TI_DUPLEX_CHANGED_INT_BIT) != 0) { - phy_status |= PHY_XLNX_GEM_EVENT_LINK_STATE_CHANGED; - } - if ((phy_data & PHY_TI_LINK_STATUS_CHANGED_INT_BIT) != 0) { - phy_status |= PHY_XLNX_GEM_EVENT_LINK_STATE_CHANGED; - } - if ((phy_data & PHY_TI_SPEED_CHANGED_INT_BIT) != 0) { - phy_status |= PHY_XLNX_GEM_EVENT_LINK_SPEED_CHANGED; - } - - return phy_status; -} - -/** - * @brief TI TLK105 & DP83822 PHY link status polling function - * Link status polling function for the TI TLK105 & DP83822 PHYs - * - * @param dev Pointer to the device data - * @return 1 if the PHY indicates link up, 0 if the link is down - */ -static uint8_t phy_xlnx_gem_ti_dp83822_poll_lsts(const struct device *dev) -{ - const struct eth_xlnx_gem_dev_cfg *dev_conf = dev->config; - struct eth_xlnx_gem_dev_data *dev_data = dev->data; - uint16_t phy_data; - - /* - * Double read of the BMSR is intentional - the relevant bit is latched - * low so that after a link down -> link up transition, the first read - * of the BMSR will still return the latched link down status rather - * than the current status. - */ - phy_data = phy_xlnx_gem_mdio_read(dev_conf->base_addr, dev_data->phy_addr, - PHY_TI_BASIC_MODE_STATUS_REGISTER); - phy_data = phy_xlnx_gem_mdio_read(dev_conf->base_addr, dev_data->phy_addr, - PHY_TI_BASIC_MODE_STATUS_REGISTER); - - return ((phy_data & PHY_TI_BASIC_MODE_STATUS_LINK_STATUS_BIT) != 0); -} - -/** - * @brief TI TLK105 & DP83822 PHY link speed polling function - * Link speed polling function for the TI TLK105 & DP83822 PHYs - * - * @param dev Pointer to the device data - * @return Enum containing the current link speed reported by the PHY - */ -static enum eth_xlnx_link_speed phy_xlnx_gem_ti_dp83822_poll_lspd( - const struct device *dev) -{ - const struct eth_xlnx_gem_dev_cfg *dev_conf = dev->config; - struct eth_xlnx_gem_dev_data *dev_data = dev->data; - enum eth_xlnx_link_speed link_speed; - uint16_t phy_data; - - phy_data = phy_xlnx_gem_mdio_read(dev_conf->base_addr, dev_data->phy_addr, - PHY_TI_PHY_STATUS_REGISTER); - - /* PHYSCR[0] is the link established indication bit */ - if ((phy_data & PHY_TI_PHY_STATUS_LINK_BIT) != 0) { - /* PHYSCR[1] is the speed status bit: 0 = 100 Mbps, 1 = 10 Mbps. */ - if ((phy_data & PHY_TI_PHY_STATUS_SPEED_BIT) != 0) { - link_speed = LINK_10MBIT; - } else { - link_speed = LINK_100MBIT; - } - } else { - link_speed = LINK_DOWN; - } - - return link_speed; -} - -/** - * @brief Marvell Alaska PHY function pointer table - * Function pointer table for the Marvell Alaska PHY series - * specific management functions - */ -static struct phy_xlnx_gem_api phy_xlnx_gem_marvell_alaska_api = { - .phy_reset_func = phy_xlnx_gem_marvell_alaska_reset, - .phy_configure_func = phy_xlnx_gem_marvell_alaska_cfg, - .phy_poll_status_change_func = phy_xlnx_gem_marvell_alaska_poll_sc, - .phy_poll_link_status_func = phy_xlnx_gem_marvell_alaska_poll_lsts, - .phy_poll_link_speed_func = phy_xlnx_gem_marvell_alaska_poll_lspd -}; - -/** - * @brief Texas Instruments TLK105 & DP83822 PHY function pointer table - * Function pointer table for the Texas Instruments TLK105 / DP83822 PHY - * series specific management functions - */ -static struct phy_xlnx_gem_api phy_xlnx_gem_ti_dp83822_api = { - .phy_reset_func = phy_xlnx_gem_ti_dp83822_reset, - .phy_configure_func = phy_xlnx_gem_ti_dp83822_cfg, - .phy_poll_status_change_func = phy_xlnx_gem_ti_dp83822_poll_sc, - .phy_poll_link_status_func = phy_xlnx_gem_ti_dp83822_poll_lsts, - .phy_poll_link_speed_func = phy_xlnx_gem_ti_dp83822_poll_lspd -}; - -/* - * All vendor-specific API structs & code are located above - * -> assemble the top-level list of supported devices the - * upcoming function phy_xlnx_gem_detect will work with. - */ - -/** - * @brief Top-level table of supported PHYs - * Top-level table of PHYs supported by the GEM driver. Contains 1..n - * supported PHY specifications, consisting of the PHY ID plus a mask - * for masking out variable parts of the PHY ID such as hardware revisions, - * as well as a textual description of the PHY model and a pointer to - * the corresponding PHY management function pointer table. - */ -static struct phy_xlnx_gem_supported_dev phy_xlnx_gem_supported_devs[] = { - { - .phy_id = PHY_MRVL_PHY_ID_MODEL_88E1111, - .phy_id_mask = PHY_MRVL_PHY_ID_MODEL_MASK, - .api = &phy_xlnx_gem_marvell_alaska_api, - .identifier = "Marvell Alaska 88E1111" - }, - { - .phy_id = PHY_MRVL_PHY_ID_MODEL_88E151X, - .phy_id_mask = PHY_MRVL_PHY_ID_MODEL_MASK, - .api = &phy_xlnx_gem_marvell_alaska_api, - .identifier = "Marvell Alaska 88E151x" - }, - { - .phy_id = PHY_TI_PHY_ID_MODEL_DP83822, - .phy_id_mask = PHY_TI_PHY_ID_MODEL_MASK, - .api = &phy_xlnx_gem_ti_dp83822_api, - .identifier = "Texas Instruments DP83822" - }, - { - .phy_id = PHY_TI_PHY_ID_MODEL_TLK105, - .phy_id_mask = PHY_TI_PHY_ID_MODEL_MASK, - .api = &phy_xlnx_gem_ti_dp83822_api, - .identifier = "Texas Instruments TLK105" - } -}; - -/** - * @brief Top-level PHY detection function - * Top-level PHY detection function called by the GEM driver if PHY management - * is enabled for the current GEM device instance. This function is generic - * and does not require any knowledge regarding PHY vendors, models etc. - * - * @param dev Pointer to the device data - * @retval -ENOTSUP if PHY management is disabled for the current GEM - * device instance - * @retval -EIO if no (supported) PHY was detected - * @retval 0 if a supported PHY has been detected - */ -int phy_xlnx_gem_detect(const struct device *dev) -{ - const struct eth_xlnx_gem_dev_cfg *dev_conf = dev->config; - struct eth_xlnx_gem_dev_data *dev_data = dev->data; - - uint8_t phy_curr_addr; - uint8_t phy_first_addr = dev_conf->phy_mdio_addr_fix; - uint8_t phy_last_addr = (dev_conf->phy_mdio_addr_fix != 0) ? - dev_conf->phy_mdio_addr_fix : 31; - uint32_t phy_id; - uint16_t phy_data; - uint32_t list_iter; - - /* - * Clear the PHY address & ID in the device data struct -> may be - * pre-initialized with a non-zero address meaning auto detection - * is disabled. If eventually a supported PHY is found, a non- - * zero address will be written back to the data struct. - */ - dev_data->phy_addr = 0; - dev_data->phy_id = 0; - dev_data->phy_access_api = NULL; - - if (!dev_conf->init_phy) { - return -ENOTSUP; - } - - /* - * PHY detection as described in Zynq-7000 TRM, chapter 16.3.4, - * p. 517 - */ - for (phy_curr_addr = phy_first_addr; - phy_curr_addr <= phy_last_addr; - phy_curr_addr++) { - /* Read the upper & lower PHY ID 16-bit words */ - phy_data = phy_xlnx_gem_mdio_read( - dev_conf->base_addr, phy_curr_addr, - PHY_IDENTIFIER_1_REGISTER); - phy_id = (((uint32_t)phy_data << 16) & 0xFFFF0000); - phy_data = phy_xlnx_gem_mdio_read( - dev_conf->base_addr, phy_curr_addr, - PHY_IDENTIFIER_2_REGISTER); - phy_id |= ((uint32_t)phy_data & 0x0000FFFF); - - if (phy_id != 0x00000000 && phy_id != 0xFFFFFFFF) { - LOG_DBG("%s detected PHY at address %hhu: " - "ID 0x%08X", - dev->name, - phy_curr_addr, phy_id); - - /* - * Iterate the list of all supported PHYs -> if the - * current PHY is supported, store all related data - * in the device's run-time data struct. - */ - for (list_iter = 0; list_iter < ARRAY_SIZE(phy_xlnx_gem_supported_devs); - list_iter++) { - if (phy_xlnx_gem_supported_devs[list_iter].phy_id == - (phy_xlnx_gem_supported_devs[list_iter].phy_id_mask - & phy_id)) { - LOG_DBG("%s identified supported PHY: %s", - dev->name, - phy_xlnx_gem_supported_devs[list_iter].identifier); - - /* - * Store the numeric values of the PHY ID and address - * as well as the corresponding set of function pointers - * in the device's run-time data struct. - */ - dev_data->phy_addr = phy_curr_addr; - dev_data->phy_id = phy_id; - dev_data->phy_access_api = - phy_xlnx_gem_supported_devs[list_iter].api; - - return 0; - } - } - } - } - - LOG_ERR("%s PHY detection failed", dev->name); - return -EIO; -} diff --git a/drivers/ethernet/phy_xlnx_gem.h b/drivers/ethernet/phy_xlnx_gem.h deleted file mode 100644 index 4c93036644549..0000000000000 --- a/drivers/ethernet/phy_xlnx_gem.h +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Xilinx Processor System Gigabit Ethernet controller (GEM) driver - * - * PHY management interface and related data - * - * Copyright (c) 2021, Weidmueller Interface GmbH & Co. KG - * SPDX-License-Identifier: Apache-2.0 - */ - -#ifndef _ZEPHYR_DRIVERS_ETHERNET_PHY_XLNX_GEM_H_ -#define _ZEPHYR_DRIVERS_ETHERNET_PHY_XLNX_GEM_H_ - -#include -#include - -/* Event codes used to indicate a particular state change to the driver */ -#define PHY_XLNX_GEM_EVENT_LINK_SPEED_CHANGED (1 << 0) -#define PHY_XLNX_GEM_EVENT_LINK_STATE_CHANGED (1 << 1) -#define PHY_XLNX_GEM_EVENT_AUTONEG_COMPLETE (1 << 2) - -/* PHY register addresses & constants that are not vendor-specific */ -#define PHY_IDENTIFIER_1_REGISTER 2 -#define PHY_IDENTIFIER_2_REGISTER 3 - -/* PHY registers & constants -> Marvell Alaska specific */ - -/* Marvell PHY ID bits [3..0] = revision -> discard during ID check */ -#define PHY_MRVL_PHY_ID_MODEL_MASK 0xFFFFFFF0 -#define PHY_MRVL_PHY_ID_MODEL_88E1111 0x01410CC0 -#define PHY_MRVL_PHY_ID_MODEL_88E151X 0x01410DD0 - -#define PHY_MRVL_BASE_REGISTERS_PAGE 0 -#define PHY_MRVL_COPPER_CONTROL_REGISTER 0 -#define PHY_MRVL_COPPER_STATUS_REGISTER 1 -#define PHY_MRVL_COPPER_AUTONEG_ADV_REGISTER 4 -#define PHY_MRVL_COPPER_LINK_PARTNER_ABILITY_REGISTER 5 -#define PHY_MRVL_1000BASET_CONTROL_REGISTER 9 -#define PHY_MRVL_COPPER_CONTROL_1_REGISTER 16 -#define PHY_MRVL_COPPER_STATUS_1_REGISTER 17 -#define PHY_MRVL_COPPER_INT_ENABLE_REGISTER 18 -#define PHY_MRVL_COPPER_INT_STATUS_REGISTER 19 -#define PHY_MRVL_COPPER_PAGE_SWITCH_REGISTER 22 -#define PHY_MRVL_GENERAL_CONTROL_1_REGISTER 20 -#define PHY_MRVL_GENERAL_CONTROL_1_PAGE 18 - -#define PHY_MRVL_GENERAL_CONTROL_1_RESET_BIT (1 << 15) - -#define PHY_MRVL_COPPER_CONTROL_RESET_BIT (1 << 15) -#define PHY_MRVL_COPPER_CONTROL_AUTONEG_ENABLE_BIT (1 << 12) - -#define PHY_MRVL_ADV_1000BASET_FDX_BIT (1 << 9) -#define PHY_MRVL_ADV_1000BASET_HDX_BIT (1 << 8) -#define PHY_MRVL_ADV_100BASET_FDX_BIT (1 << 8) -#define PHY_MRVL_ADV_100BASET_HDX_BIT (1 << 7) -#define PHY_MRVL_ADV_10BASET_FDX_BIT (1 << 6) -#define PHY_MRVL_ADV_10BASET_HDX_BIT (1 << 5) -#define PHY_MRVL_ADV_SELECTOR_802_3 0x0001 - -#define PHY_MRVL_MDIX_CONFIG_MASK 0x0003 -#define PHY_MRVL_MDIX_CONFIG_SHIFT 5 -#define PHY_MRVL_MDIX_AUTO_CROSSOVER_ENABLE 0x0003 -#define PHY_MRVL_MODE_CONFIG_MASK 0x0007 -#define PHY_MRVL_MODE_CONFIG_SHIFT 0 - -#define PHY_MRVL_COPPER_SPEED_CHANGED_INT_BIT (1 << 14) -#define PHY_MRVL_COPPER_DUPLEX_CHANGED_INT_BIT (1 << 13) -#define PHY_MRVL_COPPER_AUTONEG_COMPLETED_INT_BIT (1 << 11) -#define PHY_MRVL_COPPER_LINK_STATUS_CHANGED_INT_BIT (1 << 10) -#define PHY_MRVL_COPPER_LINK_STATUS_BIT_SHIFT 5 - -#define PHY_MRVL_LINK_SPEED_SHIFT 14 -#define PHY_MRVL_LINK_SPEED_MASK 0x3 -#define PHY_MRVL_LINK_SPEED_10MBIT 0 -#define PHY_MRVL_LINK_SPEED_100MBIT 1 -#define PHY_MRVL_LINK_SPEED_1GBIT 2 - -/*TI TLK105 & DP83822*/ - -/* TI PHY ID bits [3..0] = revision -> discard during ID check */ -#define PHY_TI_PHY_ID_MODEL_MASK 0xFFFFFFF0 -#define PHY_TI_PHY_ID_MODEL_DP83822 0x2000A240 -#define PHY_TI_PHY_ID_MODEL_TLK105 0x2000A210 - -#define PHY_TI_PHY_SPECIFIC_CONTROL_REGISTER 0x0010 -#define PHY_TI_BASIC_MODE_CONTROL_REGISTER 0x0000 -#define PHY_TI_BASIC_MODE_STATUS_REGISTER 0x0001 -#define PHY_TI_AUTONEG_ADV_REGISTER 0x0004 -#define PHY_TI_CONTROL_REGISTER_1 0x0009 -#define PHY_TI_PHY_STATUS_REGISTER 0x0010 -#define PHY_TI_MII_INTERRUPT_STATUS_REGISTER_1 0x0012 -#define PHY_TI_LED_CONTROL_REGISTER 0x0018 -#define PHY_TI_PHY_CONTROL_REGISTER 0x0019 - -#define PHY_TI_BASIC_MODE_CONTROL_RESET_BIT (1 << 15) -#define PHY_TI_BASIC_MODE_CONTROL_AUTONEG_ENABLE_BIT (1 << 12) - -#define PHY_TI_BASIC_MODE_STATUS_LINK_STATUS_BIT (1 << 2) - -#define PHY_TI_LINK_STATUS_CHANGED_INT_BIT (1 << 13) -#define PHY_TI_SPEED_CHANGED_INT_BIT (1 << 12) -#define PHY_TI_DUPLEX_CHANGED_INT_BIT (1 << 11) -#define PHY_TI_AUTONEG_COMPLETED_INT_BIT (1 << 10) - -#define PHY_TI_ADV_SELECTOR_802_3 0x0001 -#define PHY_TI_ADV_100BASET_FDX_BIT (1 << 8) -#define PHY_TI_ADV_100BASET_HDX_BIT (1 << 7) -#define PHY_TI_ADV_10BASET_FDX_BIT (1 << 6) -#define PHY_TI_ADV_10BASET_HDX_BIT (1 << 5) - -#define PHY_TI_CR1_ROBUST_AUTO_MDIX_BIT (1 << 5) - -#define PHY_TI_PHY_CONTROL_AUTO_MDIX_ENABLE_BIT (1 << 15) -#define PHY_TI_PHY_CONTROL_FORCE_MDIX_BIT (1 << 14) -#define PHY_TI_PHY_CONTROL_LED_CONFIG_LINK_ONLY_BIT (1 << 5) - -#define PHY_TI_LED_CONTROL_BLINK_RATE_SHIFT 9 -#define PHY_TI_LED_CONTROL_BLINK_RATE_20HZ 0 -#define PHY_TI_LED_CONTROL_BLINK_RATE_10HZ 1 -#define PHY_TI_LED_CONTROL_BLINK_RATE_5HZ 2 -#define PHY_TI_LED_CONTROL_BLINK_RATE_2HZ 3 - -#define PHY_TI_PHY_STATUS_LINK_BIT (1 << 0) -#define PHY_TI_PHY_STATUS_SPEED_BIT (1 << 1) - -/** - * @brief Vendor-specific PHY management function pointer table struct - * - * Contains the PHY management function pointers for a specific PHY - * make or model. - */ -struct phy_xlnx_gem_api { - void (*phy_reset_func)(const struct device *dev); - void (*phy_configure_func)(const struct device *dev); - uint16_t (*phy_poll_status_change_func)(const struct device *dev); - uint8_t (*phy_poll_link_status_func)(const struct device *dev); - enum eth_xlnx_link_speed (*phy_poll_link_speed_func)(const struct device *dev); -}; - -/** - * @brief Supported PHY list entry struct - * - * Contains the PHY management function pointers for a specific PHY - * make or model. - */ -struct phy_xlnx_gem_supported_dev { - uint32_t phy_id; - uint32_t phy_id_mask; - struct phy_xlnx_gem_api *api; - const char *identifier; -}; - -/* PHY identification function -> generic, not vendor-specific */ -int phy_xlnx_gem_detect(const struct device *dev); - -#endif /* _ZEPHYR_DRIVERS_ETHERNET_PHY_XLNX_GEM_H_ */ From ff7fc31839b4d0d70041d467833306b6e8f0770c Mon Sep 17 00:00:00 2001 From: Immo Birnbaum Date: Tue, 25 Feb 2025 09:33:23 +0100 Subject: [PATCH 2/9] drivers: mdio: add MDIO driver for Xilinx GEM Separate the MDIO functionality from the Xilinx GEM MAC driver into a separate driver conforming with the standard MDIO API. Signed-off-by: Immo Birnbaum --- drivers/mdio/CMakeLists.txt | 1 + drivers/mdio/Kconfig | 1 + drivers/mdio/Kconfig.xlnx_gem | 28 +++ drivers/mdio/mdio_xlnx_gem.c | 302 +++++++++++++++++++++++++++ dts/bindings/mdio/xlnx,gem-mdio.yaml | 9 + 5 files changed, 341 insertions(+) create mode 100644 drivers/mdio/Kconfig.xlnx_gem create mode 100644 drivers/mdio/mdio_xlnx_gem.c create mode 100644 dts/bindings/mdio/xlnx,gem-mdio.yaml diff --git a/drivers/mdio/CMakeLists.txt b/drivers/mdio/CMakeLists.txt index fc3ce620c202e..b58ba4bf6a0d3 100644 --- a/drivers/mdio/CMakeLists.txt +++ b/drivers/mdio/CMakeLists.txt @@ -20,3 +20,4 @@ zephyr_library_sources_ifdef(CONFIG_MDIO_RENESAS_RA mdio_renesas_ra.c) zephyr_library_sources_ifdef(CONFIG_MDIO_LAN865X mdio_lan865x.c) zephyr_library_sources_ifdef(CONFIG_MDIO_SENSRY_SY1XX mdio_sy1xx.c) zephyr_library_sources_ifdef(CONFIG_MDIO_XILINX_AXI_ENET mdio_xilinx_axienet.c) +zephyr_library_sources_ifdef(CONFIG_MDIO_XLNX_GEM mdio_xlnx_gem.c) diff --git a/drivers/mdio/Kconfig b/drivers/mdio/Kconfig index 658bbe78b7b99..b7c996b681812 100644 --- a/drivers/mdio/Kconfig +++ b/drivers/mdio/Kconfig @@ -41,6 +41,7 @@ source "drivers/mdio/Kconfig.renesas_ra" source "drivers/mdio/Kconfig.lan865x" source "drivers/mdio/Kconfig.sy1xx" source "drivers/mdio/Kconfig.xilinx_axienet" +source "drivers/mdio/Kconfig.xlnx_gem" config MDIO_INIT_PRIORITY int "Init priority" diff --git a/drivers/mdio/Kconfig.xlnx_gem b/drivers/mdio/Kconfig.xlnx_gem new file mode 100644 index 0000000000000..0c9b9d1261f21 --- /dev/null +++ b/drivers/mdio/Kconfig.xlnx_gem @@ -0,0 +1,28 @@ +# Copyright (c) 2025 Immo Birnbaum +# SPDX-License-Identifier: Apache-2.0 + +config MDIO_XLNX_GEM + bool "Xilinx GEM MDIO controller driver" + default y + depends on DT_HAS_XLNX_GEM_MDIO_ENABLED + help + Enable Xilinx GEM MDIO support. + +if MDIO_XLNX_GEM + +config MDIO_XLNX_GEM_MAX_POLL_RETRIES + int "Poll MDIO transaction completion max. retries" + default 10 + help + The number of times the completion of an MDIO + transaction is polled before it is considered + timed out. + +config MDIO_XLNX_GEM_POLL_DELAY + int "Poll MDIO transaction completion delay" + default 50 + help + Delay in microseconds between two MDIO transaction + completion polling operations. + +endif # MDIO_XLNX_GEM diff --git a/drivers/mdio/mdio_xlnx_gem.c b/drivers/mdio/mdio_xlnx_gem.c new file mode 100644 index 0000000000000..df35fa4d39053 --- /dev/null +++ b/drivers/mdio/mdio_xlnx_gem.c @@ -0,0 +1,302 @@ +/* + * Copyright (c) 2021 Weidmueller Interface GmbH & Co. KG + * Copyright (c) 2025 Immo Birnbaum + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#include +LOG_MODULE_REGISTER(xlnx_gem_mdio, CONFIG_MDIO_LOG_LEVEL); + +#define DT_DRV_COMPAT xlnx_gem_mdio + +/* + * Subset of register offsets and control bits / masks required for MDIO: + * + * Register offsets within the respective GEM's address space: + * NWCTRL = gem.net_ctrl Network Control register + * NWCFG = gem.net_cfg Network Configuration register + * NWSR = gem.net_status Network Status register + * PHYMNTNC = gem.phy_maint PHY maintenance register + * + * gem.net_ctrl: + * [04] Enable MDIO port + * gem.net_cfg: + * [20 .. 18] MDC clock division setting + * gem.net_status: + * [02] PHY management idle bit + * [01] MDIO input status + * gem.phy_maint: + * [31 .. 30] constant values + * [17 .. 16] constant values + * [29] Read operation control bit + * [28] Write operation control bit + * [27 .. 23] PHY address + * [22 .. 18] Register address + * [15 .. 00] 16-bit data word + */ +#define ETH_XLNX_GEM_NWCTRL_OFFSET 0x00000000 +#define ETH_XLNX_GEM_NWCTRL_MDEN_BIT BIT(4) + +#define ETH_XLNX_GEM_NWCFG_OFFSET 0x00000004 +#define ETH_XLNX_GEM_NWCFG_MDC_MASK 0x7 +#define ETH_XLNX_GEM_NWCFG_MDC_SHIFT 18 + +#define ETH_XLNX_GEM_NWSR_OFFSET 0x00000008 +#define ETH_XLNX_GEM_NWSR_MDIO_IDLE_BIT BIT(2) + +#define ETH_XLNX_GEM_PHY_MAINTENANCE_OFFSET 0x00000034 +#define ETH_XLNX_GEM_PHY_MAINT_CONST_BITS 0x40020000 +#define ETH_XLNX_GEM_PHY_MAINT_READ_OP_BIT BIT(29) +#define ETH_XLNX_GEM_PHY_MAINT_WRITE_OP_BIT BIT(28) +#define ETH_XLNX_GEM_PHY_MAINT_PHY_ADDRESS_MASK 0x0000001F +#define ETH_XLNX_GEM_PHY_MAINT_PHY_ADDRESS_SHIFT 23 +#define ETH_XLNX_GEM_PHY_MAINT_REGISTER_ID_MASK 0x0000001F +#define ETH_XLNX_GEM_PHY_MAINT_REGISTER_ID_SHIFT 18 +#define ETH_XLNX_GEM_PHY_MAINT_DATA_MASK 0x0000FFFF + +/** + * @brief MDC clock divider configuration enumeration type. + * + * Enumeration type containing the supported clock divider values + * used to generate the MDIO interface clock (MDC) from either the + * cpu_1x clock (Zynq-7000) or the LPD LSBUS clock (ZynqMP). + * This is a configuration item in the controller's net_cfg register. + */ +enum eth_xlnx_mdc_clock_divider { + MDC_DIVIDER_8 = 0, + MDC_DIVIDER_16, + MDC_DIVIDER_32, + MDC_DIVIDER_48, + MDC_DIVIDER_64, + MDC_DIVIDER_96, + MDC_DIVIDER_128, + MDC_DIVIDER_224 +}; + +/** + * @brief Constant device configuration data structure. + * + * This struct contains all device configuration data for a GEM + * MDIO interface instance which is constant. + */ +struct xlnx_gem_mdio_config { + uint32_t gem_base_addr; +}; + +/** + * @brief GEM MDIO interface data read function + * + * @param dev Pointer to the GEM MDIO device + * @param prtad MDIO address of the PHY to be accessed + * @param regad Index of the PHY register to be read + * @param data Read data output pointer + * @return 0 in case of success, -ETIMEDOUT if the read operation + * timed out (idle bit not set as expected) + */ +static int xlnx_gem_mdio_read(const struct device *dev, uint8_t prtad, uint8_t regad, + uint16_t *data) +{ + const struct xlnx_gem_mdio_config *const dev_conf = dev->config; + + uint32_t reg_val; + uint32_t poll_cnt = 0; + + /* + * MDIO read operation as described in Zynq-7000 TRM, + * chapter 16.3.4, p. 517. + */ + + /* + * Wait until gem.net_status[phy_mgmt_idle] == 1 before issuing the + * current command. + */ + do { + if (poll_cnt++ > 0) { + k_busy_wait(CONFIG_MDIO_XLNX_GEM_POLL_DELAY); + } + reg_val = sys_read32(dev_conf->gem_base_addr + ETH_XLNX_GEM_NWSR_OFFSET); + } while ((reg_val & ETH_XLNX_GEM_NWSR_MDIO_IDLE_BIT) == 0 && + poll_cnt < CONFIG_MDIO_XLNX_GEM_MAX_POLL_RETRIES); + if (poll_cnt == CONFIG_MDIO_XLNX_GEM_MAX_POLL_RETRIES) { + LOG_ERR("%s: read from PHY address %hhu, register address %hhu timed out", + dev->name, prtad, regad); + return -ETIMEDOUT; + } + + /* Assemble & write the read command to the gem.phy_maint register */ + + /* Set the bits constant for any operation */ + reg_val = ETH_XLNX_GEM_PHY_MAINT_CONST_BITS; + /* Indicate a read operation */ + reg_val |= ETH_XLNX_GEM_PHY_MAINT_READ_OP_BIT; + /* PHY address */ + reg_val |= (((uint32_t)prtad & ETH_XLNX_GEM_PHY_MAINT_PHY_ADDRESS_MASK) + << ETH_XLNX_GEM_PHY_MAINT_PHY_ADDRESS_SHIFT); + /* Register address */ + reg_val |= (((uint32_t)regad & ETH_XLNX_GEM_PHY_MAINT_REGISTER_ID_MASK) + << ETH_XLNX_GEM_PHY_MAINT_REGISTER_ID_SHIFT); + + sys_write32(reg_val, dev_conf->gem_base_addr + ETH_XLNX_GEM_PHY_MAINTENANCE_OFFSET); + + /* + * Wait until gem.net_status[phy_mgmt_idle] == 1 -> current command + * completed. + */ + poll_cnt = 0; + do { + if (poll_cnt++ > 0) { + k_busy_wait(CONFIG_MDIO_XLNX_GEM_POLL_DELAY); + } + reg_val = sys_read32(dev_conf->gem_base_addr + ETH_XLNX_GEM_NWSR_OFFSET); + } while ((reg_val & ETH_XLNX_GEM_NWSR_MDIO_IDLE_BIT) == 0 && + poll_cnt < CONFIG_MDIO_XLNX_GEM_MAX_POLL_RETRIES); + if (poll_cnt == CONFIG_MDIO_XLNX_GEM_MAX_POLL_RETRIES) { + LOG_ERR("%s: read from PHY address %hhu, register address %hhu timed out", + dev->name, prtad, regad); + return -ETIMEDOUT; + } + + /* + * Read the data returned by the PHY -> lower 16 bits of the PHY main- + * tenance register + */ + reg_val = sys_read32(dev_conf->gem_base_addr + ETH_XLNX_GEM_PHY_MAINTENANCE_OFFSET); + + *data = (uint16_t)reg_val; + return 0; +} + +/** + * @brief GEM MDIO interface data write function + * + * @param dev Pointer to the GEM MDIO device + * @param prtad MDIO address of the PHY to be accessed + * @param regad Index of the PHY register to write to + * @param data Data word to be written to the target register + * @return 0 in case of success, -ETIMEDOUT if the read operation + * timed out (idle bit not set as expected) + */ +static int xlnx_gem_mdio_write(const struct device *dev, uint8_t prtad, uint8_t regad, + uint16_t data) +{ + const struct xlnx_gem_mdio_config *const dev_conf = dev->config; + + uint32_t reg_val; + uint32_t poll_cnt = 0; + + /* + * MDIO write operation as described in Zynq-7000 TRM, + * chapter 16.3.4, p. 517. + */ + + /* + * Wait until gem.net_status[phy_mgmt_idle] == 1 before issuing the + * current command. + */ + do { + if (poll_cnt++ > 0) { + k_busy_wait(CONFIG_MDIO_XLNX_GEM_POLL_DELAY); + } + reg_val = sys_read32(dev_conf->gem_base_addr + ETH_XLNX_GEM_NWSR_OFFSET); + } while ((reg_val & ETH_XLNX_GEM_NWSR_MDIO_IDLE_BIT) == 0 && + poll_cnt < CONFIG_MDIO_XLNX_GEM_MAX_POLL_RETRIES); + if (poll_cnt == CONFIG_MDIO_XLNX_GEM_MAX_POLL_RETRIES) { + LOG_ERR("%s: write to PHY address %hhu, register address %hhu timed out", dev->name, + prtad, regad); + return -ETIMEDOUT; + } + + /* Assemble & write the read command to the gem.phy_maint register */ + + /* Set the bits constant for any operation */ + reg_val = ETH_XLNX_GEM_PHY_MAINT_CONST_BITS; + /* Indicate a read operation */ + reg_val |= ETH_XLNX_GEM_PHY_MAINT_WRITE_OP_BIT; + /* PHY address */ + reg_val |= (((uint32_t)prtad & ETH_XLNX_GEM_PHY_MAINT_PHY_ADDRESS_MASK) + << ETH_XLNX_GEM_PHY_MAINT_PHY_ADDRESS_SHIFT); + /* Register address */ + reg_val |= (((uint32_t)regad & ETH_XLNX_GEM_PHY_MAINT_REGISTER_ID_MASK) + << ETH_XLNX_GEM_PHY_MAINT_REGISTER_ID_SHIFT); + /* 16 bits of data for the destination register */ + reg_val |= ((uint32_t)data & ETH_XLNX_GEM_PHY_MAINT_DATA_MASK); + + sys_write32(reg_val, dev_conf->gem_base_addr + ETH_XLNX_GEM_PHY_MAINTENANCE_OFFSET); + + /* + * Wait until gem.net_status[phy_mgmt_idle] == 1 -> current command + * completed. + */ + poll_cnt = 0; + do { + if (poll_cnt++ > 0) { + k_busy_wait(CONFIG_MDIO_XLNX_GEM_POLL_DELAY); + } + reg_val = sys_read32(dev_conf->gem_base_addr + ETH_XLNX_GEM_NWSR_OFFSET); + } while ((reg_val & ETH_XLNX_GEM_NWSR_MDIO_IDLE_BIT) == 0 && + poll_cnt < CONFIG_MDIO_XLNX_GEM_MAX_POLL_RETRIES); + if (poll_cnt == CONFIG_MDIO_XLNX_GEM_MAX_POLL_RETRIES) { + LOG_ERR("%s: write to PHY address %hhu, register address %hhu timed out", dev->name, + prtad, regad); + return -ETIMEDOUT; + } + + return 0; +} + +/** + * @brief GEM MDIO interface initialization function + * Configures the MDC clock divider in the associated GEM instance's + * net_config (NWCFG) register and sets the MDIO enable bit in the + * net_control (NWCTRL) register. + * + * @param dev Pointer to the GEM MDIO device + * @return always returns 0 + */ +static int xlnx_gem_mdio_initialize(const struct device *dev) +{ + const struct xlnx_gem_mdio_config *const dev_conf = dev->config; + + uint32_t reg_val; + uint32_t mdc_divider = (uint32_t)MDC_DIVIDER_224; + + /* Set the MDC divider in gem.net_config */ + reg_val = sys_read32(dev_conf->gem_base_addr + ETH_XLNX_GEM_NWCFG_OFFSET); + reg_val &= ~(ETH_XLNX_GEM_NWCFG_MDC_MASK << ETH_XLNX_GEM_NWCFG_MDC_SHIFT); + reg_val |= ((mdc_divider & ETH_XLNX_GEM_NWCFG_MDC_MASK) << ETH_XLNX_GEM_NWCFG_MDC_SHIFT); + sys_write32(reg_val, dev_conf->gem_base_addr + ETH_XLNX_GEM_NWCFG_OFFSET); + + /* Enable the MDIO interface */ + reg_val = sys_read32(dev_conf->gem_base_addr + ETH_XLNX_GEM_NWCTRL_OFFSET); + reg_val |= ETH_XLNX_GEM_NWCTRL_MDEN_BIT; + sys_write32(reg_val, dev_conf->gem_base_addr + ETH_XLNX_GEM_NWCTRL_OFFSET); + + LOG_DBG("%s: initialized", dev->name); + return 0; +} + +static DEVICE_API(mdio, xlnx_gem_mdio_api) = { + .read = xlnx_gem_mdio_read, + .write = xlnx_gem_mdio_write, +}; + +#define XLNX_GEM_MDIO_DEV_CONFIG(port) \ + static const struct xlnx_gem_mdio_config xlnx_gem##port##_mdio_cfg = { \ + .gem_base_addr = DT_REG_ADDR_BY_IDX(DT_INST(port, xlnx_gem), 0), \ + }; + +#define XLNX_GEM_MDIO_DEV_INIT(port) \ + DEVICE_DT_INST_DEFINE(port, &xlnx_gem_mdio_initialize, NULL, NULL, \ + &xlnx_gem##port##_mdio_cfg, POST_KERNEL, CONFIG_MDIO_INIT_PRIORITY, \ + &xlnx_gem_mdio_api); + +#define XLNX_GEM_MDIO_INITIALIZE(port) \ + XLNX_GEM_MDIO_DEV_CONFIG(port); \ + XLNX_GEM_MDIO_DEV_INIT(port); + +DT_INST_FOREACH_STATUS_OKAY(XLNX_GEM_MDIO_INITIALIZE) diff --git a/dts/bindings/mdio/xlnx,gem-mdio.yaml b/dts/bindings/mdio/xlnx,gem-mdio.yaml new file mode 100644 index 0000000000000..52bb7c9f22f9e --- /dev/null +++ b/dts/bindings/mdio/xlnx,gem-mdio.yaml @@ -0,0 +1,9 @@ +# Copyright (c) 2021, Weidmueller Interface GmbH & Co. KG +# Copyright (c) 2025 Immo Birnbaum +# SPDX-License-Identifier: Apache-2.0 + +description: Xilinx GEM MDIO node + +compatible: "xlnx,gem-mdio" + +include: mdio-controller.yaml From cf4dbad45fc1270179481c07c17a65393cc3ea49 Mon Sep 17 00:00:00 2001 From: Immo Birnbaum Date: Tue, 25 Feb 2025 09:36:52 +0100 Subject: [PATCH 3/9] soc: xlnx: zynq7000: enable control register access Enable access to the registers used to set a GEM instance's TX clock frequency. Without this memory mapping, the GEM controllers are only usable without PHY management via MDIO with a fixed TX clock frequency set by the FSBL. If MDIO and PHY management are enabled for either GEM on the Zynq, the system will crash with a data abort without this mapping. The ZynqMP is not affected by this issue, as even with the MPU enabled, the relevant SLCR register space is covered by the "peripherals" mapping entry in the ZynqMP's MPU region table. Signed-off-by: Immo Birnbaum --- soc/xlnx/zynq7000/xc7zxxx/soc.c | 6 ++++++ soc/xlnx/zynq7000/xc7zxxxs/soc.c | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/soc/xlnx/zynq7000/xc7zxxx/soc.c b/soc/xlnx/zynq7000/xc7zxxx/soc.c index 5bc0c422b1546..b63d3274df49c 100644 --- a/soc/xlnx/zynq7000/xc7zxxx/soc.c +++ b/soc/xlnx/zynq7000/xc7zxxx/soc.c @@ -39,6 +39,12 @@ static const struct arm_mmu_region mmu_regions[] = { /* ARM Arch timer, GIC are covered by the MPCore mapping */ /* GEMs */ +#if DT_NODE_HAS_STATUS_OKAY(DT_NODELABEL(gem0)) || DT_NODE_HAS_STATUS_OKAY(DT_NODELABEL(gem1)) + MMU_REGION_FLAT_ENTRY("slcr", + 0xF8000000, + 0x1000, + MT_STRONGLY_ORDERED | MPERM_R | MPERM_W), +#endif #if DT_NODE_HAS_STATUS_OKAY(DT_NODELABEL(gem0)) MMU_REGION_FLAT_ENTRY("gem0", DT_REG_ADDR(DT_NODELABEL(gem0)), diff --git a/soc/xlnx/zynq7000/xc7zxxxs/soc.c b/soc/xlnx/zynq7000/xc7zxxxs/soc.c index b19e9e43ce16c..1fb7324a54281 100644 --- a/soc/xlnx/zynq7000/xc7zxxxs/soc.c +++ b/soc/xlnx/zynq7000/xc7zxxxs/soc.c @@ -39,6 +39,12 @@ static const struct arm_mmu_region mmu_regions[] = { /* ARM Arch timer, GIC are covered by the MPCore mapping */ /* GEMs */ +#if DT_NODE_HAS_STATUS_OKAY(DT_NODELABEL(gem0)) || DT_NODE_HAS_STATUS_OKAY(DT_NODELABEL(gem1)) + MMU_REGION_FLAT_ENTRY("slcr", + 0xF8000000, + 0x1000, + MT_STRONGLY_ORDERED | MPERM_R | MPERM_W), +#endif #if DT_NODE_HAS_STATUS_OKAY(DT_NODELABEL(gem0)) MMU_REGION_FLAT_ENTRY("gem0", DT_REG_ADDR(DT_NODELABEL(gem0)), From 4dc4dc1c282bd9ef7656a03480e49a291fb0e98f Mon Sep 17 00:00:00 2001 From: Immo Birnbaum Date: Tue, 25 Feb 2025 09:40:27 +0100 Subject: [PATCH 4/9] dts: bindings: remove legacy MDIO-related properties/defines Remove any properties from the Xilinx GEM DT node specification and the associated header in dt-bindings that relate to the legacy MDIO/PHY support. Signed-off-by: Immo Birnbaum --- dts/bindings/ethernet/xlnx,gem.yaml | 65 ------------------- .../zephyr/dt-bindings/ethernet/xlnx_gem.h | 33 ---------- 2 files changed, 98 deletions(-) diff --git a/dts/bindings/ethernet/xlnx,gem.yaml b/dts/bindings/ethernet/xlnx,gem.yaml index b2f0f2a3e6fc1..9281c5471d292 100644 --- a/dts/bindings/ethernet/xlnx,gem.yaml +++ b/dts/bindings/ethernet/xlnx,gem.yaml @@ -28,66 +28,6 @@ properties: item must be set to the clock frequency of the PLL supplying the respective GEM's TX clock - by default, this is the IO PLL. - mdc-divider: - type: int - required: true - description: | - The MDC clock divider for the respective GEM. This is the divider - applied to the LPD_LSBUS clock in order to derive MDIO interface - clock driving communications with the attached PHY. Refer to the - ZynqMP register documentation (ug1087), network_config (GEM) Register - Description, bits [20:18] to determine the appropriate divider for - the current target's LPD LSBUS clock frequency. - - init-mdio-phy: - type: boolean - description: | - Activates the management of a PHY associated with the controller in- - stance. If this parameter is activated at the board level, the de- - fault values of the associated parameters mdio-phy-address, phy-poll- - interval, link-speed and advertise-lower-link-speeds should be checked - and overwritten at the board level if required. - - mdio-phy-address: - type: int - required: true - description: | - The address on the MDIO bus of the PHY associated with the controller - instance. Set the address to 0 for auto-detection (first responding - PHY will be claimed by the driver, watch out in case of shared MDIO - use), or to a fixed address between 1 and 32. - - phy-poll-interval: - type: int - required: true - description: | - PHY status polling interval in milliseconds for a driver instance - managing an associated PHY. - - link-speed: - type: int - required: true - description: | - Nominal link speed. If no PHY is managed by an instance of this driver, - the respective controller will be configured to match the link speed - specified here. If a PHY is managed by the driver, advertisement of - the link speed specified here will be requested. If the optional pro- - perty advertise-lower-link-speeds is set, advertisement of the link - speed specified here plus any valid link speed below this value will - be requested. - enum: - - 1 - - 2 - - 3 - - advertise-lower-link-speeds: - type: boolean - description: | - Indicates to a driver instance which manages an associated PHY on - the MDIO bus to include link speeds lower than the nominal value - set in the link-speed property in the advertisement when requesting - link speed auto-negotiation with a peer system. - handle-rx-in-isr: type: boolean description: | @@ -318,11 +258,6 @@ properties: description: Optional feature flag - Discard non-VLAN frames. When set, only VLAN tagged frames will be passed to the address matching logic. - full-duplex: - type: boolean - description: | - Optional feature flag - Enables full duplex reception and transmission. - discard-rx-frame-ahb-unavail: type: boolean description: | diff --git a/include/zephyr/dt-bindings/ethernet/xlnx_gem.h b/include/zephyr/dt-bindings/ethernet/xlnx_gem.h index bfb0e0cc06214..11f8e548d17ba 100644 --- a/include/zephyr/dt-bindings/ethernet/xlnx_gem.h +++ b/include/zephyr/dt-bindings/ethernet/xlnx_gem.h @@ -6,39 +6,6 @@ #ifndef ZEPHYR_INCLUDE_DT_BINDINGS_ETHERNET_XLNX_GEM_H_ #define ZEPHYR_INCLUDE_DT_BINDINGS_ETHERNET_XLNX_GEM_H_ -/* PHY auto-detection alias */ -#define XLNX_GEM_PHY_AUTO_DETECT 0 - -/* - * MDC divider values - * - * According to the ZynqMP's gem.network_config register documentation (UG1087), - * divider /32 is the reset value. The network_config[mdc_clock_division] - * documentation in UG1087 is likely wrong (copied directly from the Zynq-7000), - * as it claims that the MDC clock division is applied to the cpu_1x clock - * which the UltraScale doesn't have. Contradicting information is provided in - * the UltraScale TRM (UG1085), which mentions in chapter 34, section "Configure - * the PHY", p. 1074, that the MDC clock division is applied to the IOU_SWITCH_CLK. - * Xilinx's emacps driver doesn't (or no longer does) limit the range of dividers - * on the UltraScale compared to the Zynq-7000. - * -> Contrary to earlier revisions of this driver, all dividers are available - * to both the UltraScale and the Zynq-7000. - */ - -#define XLNX_GEM_MDC_DIVIDER_8 0 /* cpu_1x or IOU_SWITCH_CLK < 20 MHz */ -#define XLNX_GEM_MDC_DIVIDER_16 1 /* cpu_1x or IOU_SWITCH_CLK 20 - 40 MHz */ -#define XLNX_GEM_MDC_DIVIDER_32 2 /* cpu_1x or IOU_SWITCH_CLK 40 - 80 MHz */ -#define XLNX_GEM_MDC_DIVIDER_48 3 /* cpu_1x or IOU_SWITCH_CLK 80 - 120 MHz */ -#define XLNX_GEM_MDC_DIVIDER_64 4 /* cpu_1x or IOU_SWITCH_CLK 120 - 160 MHz */ -#define XLNX_GEM_MDC_DIVIDER_96 5 /* cpu_1x or IOU_SWITCH_CLK 160 - 240 MHz */ -#define XLNX_GEM_MDC_DIVIDER_128 6 /* cpu_1x or IOU_SWITCH_CLK 240 - 320 MHz */ -#define XLNX_GEM_MDC_DIVIDER_224 7 /* cpu_1x or IOU_SWITCH_CLK 320 - 540 MHz */ - -/* Link speed values */ -#define XLNX_GEM_LINK_SPEED_10MBIT 1 -#define XLNX_GEM_LINK_SPEED_100MBIT 2 -#define XLNX_GEM_LINK_SPEED_1GBIT 3 - /* AMBA AHB data bus width */ #define XLNX_GEM_AMBA_AHB_DBUS_WIDTH_32BIT 0 #define XLNX_GEM_AMBA_AHB_DBUS_WIDTH_64BIT 1 From 34a640a92c4a30cb1d602ac9083e506d0269e345 Mon Sep 17 00:00:00 2001 From: Immo Birnbaum Date: Tue, 25 Feb 2025 09:43:18 +0100 Subject: [PATCH 5/9] dts: zynq7000: remove legacy MDIO properties, add MDIO nodes Remove all instances of DT properties relating to the legacy MDIO/PHY support. Add the new MDIO nodes for GEM0 and GEM1. Signed-off-by: Immo Birnbaum --- dts/arm/xilinx/zynq7000.dtsi | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/dts/arm/xilinx/zynq7000.dtsi b/dts/arm/xilinx/zynq7000.dtsi index cbbc8eecaba81..1f62453952fdc 100644 --- a/dts/arm/xilinx/zynq7000.dtsi +++ b/dts/arm/xilinx/zynq7000.dtsi @@ -58,9 +58,6 @@ ; interrupt-names = "irq_0", "irq_1"; - mdio-phy-address = ; - phy-poll-interval = <1000>; - link-speed = ; amba-ahb-dbus-width = ; amba-ahb-burst-length = ; hw-rx-buffer-size = ; @@ -72,7 +69,13 @@ tx-buffer-size = <512>; discard-rx-fcs; unicast-hash; - full-duplex; + }; + + gem0_mdio: gem0_mdio { + compatible = "xlnx,gem-mdio"; + status = "disabled"; + #address-cells = <1>; + #size-cells = <0>; }; gem1: ethernet@e000c000 { @@ -85,9 +88,6 @@ ; interrupt-names = "irq_0", "irq_1"; - mdio-phy-address = ; - phy-poll-interval = <1000>; - link-speed = ; amba-ahb-dbus-width = ; amba-ahb-burst-length = ; hw-rx-buffer-size = ; @@ -99,7 +99,13 @@ tx-buffer-size = <512>; discard-rx-fcs; unicast-hash; - full-duplex; + }; + + gem1_mdio: gem1_mdio { + compatible = "xlnx,gem-mdio"; + status = "disabled"; + #address-cells = <1>; + #size-cells = <0>; }; uart0: uart@e0000000 { From f42cabfc637ed3d70e5006fe87f76cf28e5d522e Mon Sep 17 00:00:00 2001 From: Immo Birnbaum Date: Tue, 25 Feb 2025 10:14:59 +0100 Subject: [PATCH 6/9] dts: zynqmp: remove legacy MDIO properties, add MDIO nodes Remove all instances of DT properties relating to the legacy MDIO/PHY support. Add the new MDIO nodes for GEM[0..3]. Signed-off-by: Immo Birnbaum --- dts/arm/xilinx/zynqmp.dtsi | 44 ++++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/dts/arm/xilinx/zynqmp.dtsi b/dts/arm/xilinx/zynqmp.dtsi index ecadbdfda532c..be9c29bc7a70a 100644 --- a/dts/arm/xilinx/zynqmp.dtsi +++ b/dts/arm/xilinx/zynqmp.dtsi @@ -111,9 +111,6 @@ ; interrupt-names = "irq_0", "irq_1"; - mdio-phy-address = ; - phy-poll-interval = <1000>; - link-speed = ; amba-ahb-dbus-width = ; amba-ahb-burst-length = ; hw-rx-buffer-size = ; @@ -125,7 +122,13 @@ tx-buffer-size = <512>; discard-rx-fcs; unicast-hash; - full-duplex; + }; + + gem0_mdio: gem0_mdio { + compatible = "xlnx,gem-mdio"; + status = "disabled"; + #address-cells = <1>; + #size-cells = <0>; }; gem1: ethernet@ff0c0000 { @@ -138,9 +141,6 @@ ; interrupt-names = "irq_0", "irq_1"; - mdio-phy-address = ; - phy-poll-interval = <1000>; - link-speed = ; amba-ahb-dbus-width = ; amba-ahb-burst-length = ; hw-rx-buffer-size = ; @@ -152,7 +152,13 @@ tx-buffer-size = <512>; discard-rx-fcs; unicast-hash; - full-duplex; + }; + + gem1_mdio: gem1_mdio { + compatible = "xlnx,gem-mdio"; + status = "disabled"; + #address-cells = <1>; + #size-cells = <0>; }; gem2: ethernet@ff0d0000 { @@ -165,9 +171,6 @@ ; interrupt-names = "irq_0", "irq_1"; - mdio-phy-address = ; - phy-poll-interval = <1000>; - link-speed = ; amba-ahb-dbus-width = ; amba-ahb-burst-length = ; hw-rx-buffer-size = ; @@ -179,7 +182,13 @@ tx-buffer-size = <512>; discard-rx-fcs; unicast-hash; - full-duplex; + }; + + gem2_mdio: gem2_mdio { + compatible = "xlnx,gem-mdio"; + status = "disabled"; + #address-cells = <1>; + #size-cells = <0>; }; gem3: ethernet@ff0e0000 { @@ -192,9 +201,6 @@ ; interrupt-names = "irq_0", "irq_1"; - mdio-phy-address = ; - phy-poll-interval = <1000>; - link-speed = ; amba-ahb-dbus-width = ; amba-ahb-burst-length = ; hw-rx-buffer-size = ; @@ -206,7 +212,13 @@ tx-buffer-size = <512>; discard-rx-fcs; unicast-hash; - full-duplex; + }; + + gem3_mdio: gem3_mdio { + compatible = "xlnx,gem-mdio"; + status = "disabled"; + #address-cells = <1>; + #size-cells = <0>; }; psgpio: gpio@ff0a0000 { From 9cb9042f4db93e207de30c0cef579e3046ba1fe2 Mon Sep 17 00:00:00 2001 From: Immo Birnbaum Date: Tue, 25 Feb 2025 09:58:54 +0100 Subject: [PATCH 7/9] drivers: ethernet: xlnx_gem: switch over to new MDIO driver Remove all data/functions/function calls relating to the now removed legacy MDIO/PHY support directly integrated into the GEM device driver. Switch over to using the standard MDIO/PHY interfaces which will now refer to the new separate MDIO driver and the separated PHY drivers. Signed-off-by: Immo Birnbaum --- drivers/ethernet/eth_xlnx_gem.c | 428 ++++++++------------------- drivers/ethernet/eth_xlnx_gem_priv.h | 109 +------ 2 files changed, 138 insertions(+), 399 deletions(-) diff --git a/drivers/ethernet/eth_xlnx_gem.c b/drivers/ethernet/eth_xlnx_gem.c index 1a99dbbe393fd..37ff18764ae49 100644 --- a/drivers/ethernet/eth_xlnx_gem.c +++ b/drivers/ethernet/eth_xlnx_gem.c @@ -26,6 +26,7 @@ #include #include +#include #include #include #include @@ -51,33 +52,38 @@ static int eth_xlnx_gem_get_config(const struct device *dev, static int eth_xlnx_gem_set_config(const struct device *dev, enum ethernet_config_type type, const struct ethernet_config *config); -#if defined(CONFIG_NET_STATISTICS_ETHERNET) +static const struct device *eth_xlnx_gem_get_phy(const struct device *dev); +#ifdef CONFIG_NET_STATISTICS_ETHERNET static struct net_stats_eth *eth_xlnx_gem_stats(const struct device *dev); #endif static void eth_xlnx_gem_reset_hw(const struct device *dev); -static void eth_xlnx_gem_configure_clocks(const struct device *dev); static void eth_xlnx_gem_set_initial_nwcfg(const struct device *dev); -static void eth_xlnx_gem_set_nwcfg_link_speed(const struct device *dev); static void eth_xlnx_gem_set_mac_address(const struct device *dev); static void eth_xlnx_gem_set_initial_dmacr(const struct device *dev); -static void eth_xlnx_gem_init_phy(const struct device *dev); -static void eth_xlnx_gem_poll_phy(struct k_work *item); static void eth_xlnx_gem_configure_buffers(const struct device *dev); static void eth_xlnx_gem_rx_pending_work(struct k_work *item); static void eth_xlnx_gem_handle_rx_pending(const struct device *dev); static void eth_xlnx_gem_tx_done_work(struct k_work *item); static void eth_xlnx_gem_handle_tx_done(const struct device *dev); +static void eth_xlnx_gem_configure_clocks(const struct device *dev, + struct phy_link_state *state); +static void eth_xlnx_gem_set_nwcfg_link_speed(const struct device *dev, + struct phy_link_state *state); +static void eth_xlnx_gem_phy_cb(const struct device *phy, + struct phy_link_state *state, + void *eth_dev); static const struct ethernet_api eth_xlnx_gem_apis = { .iface_api.init = eth_xlnx_gem_iface_init, .get_capabilities = eth_xlnx_gem_get_capabilities, + .get_phy = eth_xlnx_gem_get_phy, .send = eth_xlnx_gem_send, .start = eth_xlnx_gem_start_device, .stop = eth_xlnx_gem_stop_device, .get_config = eth_xlnx_gem_get_config, .set_config = eth_xlnx_gem_set_config, -#if defined(CONFIG_NET_STATISTICS_ETHERNET) +#ifdef CONFIG_NET_STATISTICS_ETHERNET .get_stats = eth_xlnx_gem_stats, #endif }; @@ -90,8 +96,8 @@ DT_INST_FOREACH_STATUS_OKAY(ETH_XLNX_GEM_INITIALIZE) /** * @brief GEM device initialization function - * Initializes the GEM itself, the DMA memory area used by the GEM and, - * if enabled, an associated PHY attached to the GEM's MDIO interface. + * Initializes the respective GEM controller instance itself and its + * associated DMA area. * * @param dev Pointer to the device data * @retval 0 if the device initialization completed successfully @@ -99,42 +105,9 @@ DT_INST_FOREACH_STATUS_OKAY(ETH_XLNX_GEM_INITIALIZE) static int eth_xlnx_gem_dev_init(const struct device *dev) { const struct eth_xlnx_gem_dev_cfg *dev_conf = dev->config; - uint32_t reg_val; /* Precondition checks using assertions */ - /* Valid PHY address and polling interval, if PHY is to be managed */ - if (dev_conf->init_phy) { - __ASSERT((dev_conf->phy_mdio_addr_fix >= 0 && - dev_conf->phy_mdio_addr_fix <= 32), - "%s invalid PHY address %u, must be in range " - "1 to 32, or 0 for auto-detection", - dev->name, dev_conf->phy_mdio_addr_fix); - __ASSERT(dev_conf->phy_poll_interval > 0, - "%s has an invalid zero PHY status polling " - "interval", dev->name); - } - - /* Valid max. / nominal link speed value */ - __ASSERT((dev_conf->max_link_speed == LINK_10MBIT || - dev_conf->max_link_speed == LINK_100MBIT || - dev_conf->max_link_speed == LINK_1GBIT), - "%s invalid max./nominal link speed value %u", - dev->name, (uint32_t)dev_conf->max_link_speed); - - /* MDC clock divider validity check, SoC dependent */ -#if defined(CONFIG_SOC_XILINX_ZYNQMP) - __ASSERT(dev_conf->mdc_divider <= MDC_DIVIDER_48, - "%s invalid MDC clock divider value %u, must be in " - "range 0 to %u", dev->name, dev_conf->mdc_divider, - (uint32_t)MDC_DIVIDER_48); -#elif defined(CONFIG_SOC_FAMILY_XILINX_ZYNQ7000) - __ASSERT(dev_conf->mdc_divider <= MDC_DIVIDER_224, - "%s invalid MDC clock divider value %u, must be in " - "range 0 to %u", dev->name, dev_conf->mdc_divider, - (uint32_t)MDC_DIVIDER_224); -#endif - /* AMBA AHB configuration options */ __ASSERT((dev_conf->amba_dbus_width == AMBA_AHB_DBUS_WIDTH_32BIT || dev_conf->amba_dbus_width == AMBA_AHB_DBUS_WIDTH_64BIT || @@ -193,26 +166,16 @@ static int eth_xlnx_gem_dev_init(const struct device *dev) /* * Initialization procedure as described in the Zynq-7000 TRM, - * chapter 16.3.x. + * chapter 16.3.x. MDIO initialization (16.3.4) is handled prior + * to the following initialization procedure within the separate + * GEM MDIO driver. TX clock divisor configuration (16.3.3) is + * handled from within the PHY state change callback function + * (if applicable). */ eth_xlnx_gem_reset_hw(dev); /* Chapter 16.3.1 */ eth_xlnx_gem_set_initial_nwcfg(dev); /* Chapter 16.3.2 */ eth_xlnx_gem_set_mac_address(dev); /* Chapter 16.3.2 */ eth_xlnx_gem_set_initial_dmacr(dev); /* Chapter 16.3.2 */ - - /* Enable MDIO -> set gem.net_ctrl[mgmt_port_en] */ - if (dev_conf->init_phy) { - reg_val = sys_read32(dev_conf->base_addr + - ETH_XLNX_GEM_NWCTRL_OFFSET); - reg_val |= ETH_XLNX_GEM_NWCTRL_MDEN_BIT; - sys_write32(reg_val, dev_conf->base_addr + - ETH_XLNX_GEM_NWCTRL_OFFSET); - } - - eth_xlnx_gem_configure_clocks(dev); /* Chapter 16.3.3 */ - if (dev_conf->init_phy) { - eth_xlnx_gem_init_phy(dev); /* Chapter 16.3.4 */ - } eth_xlnx_gem_configure_buffers(dev); /* Chapter 16.3.5 */ return 0; @@ -229,21 +192,20 @@ static void eth_xlnx_gem_iface_init(struct net_if *iface) const struct device *dev = net_if_get_device(iface); const struct eth_xlnx_gem_dev_cfg *dev_conf = dev->config; struct eth_xlnx_gem_dev_data *dev_data = dev->data; + struct phy_link_state state = {}; + int ret; /* Set the initial contents of the current instance's run-time data */ dev_data->iface = iface; net_if_set_link_addr(iface, dev_data->mac_addr, 6, NET_LINK_ETHERNET); ethernet_init(iface); - net_if_carrier_off(iface); /* - * Initialize the (delayed) work items for RX pending, TX done - * and PHY status polling handlers + * Initialize the (delayed) work items for RX pending and TX done + * handling. */ k_work_init(&dev_data->tx_done_work, eth_xlnx_gem_tx_done_work); k_work_init(&dev_data->rx_pend_work, eth_xlnx_gem_rx_pending_work); - k_work_init_delayable(&dev_data->phy_poll_delayed_work, - eth_xlnx_gem_poll_phy); /* Initialize TX completion semaphore */ k_sem_init(&dev_data->tx_done_sem, 0, 1); @@ -258,8 +220,30 @@ static void eth_xlnx_gem_iface_init(struct net_if *iface) /* Initialize the device's interrupt */ dev_conf->config_func(dev); - /* Submit initial PHY status polling delayed work */ - k_work_reschedule(&dev_data->phy_poll_delayed_work, K_NO_WAIT); + /* + * If PHY is available: register callback & pick up current link state immediately. + * For PHY-less operation: fixed link is pre-configured, indicate link up. + */ + if (dev_conf->phy_dev != NULL) { + net_eth_carrier_off(iface); + + ret = phy_link_callback_set(dev_conf->phy_dev, eth_xlnx_gem_phy_cb, + (void *)dev); + if (ret) { + LOG_ERR("%s: set PHY callback failed", dev->name); + return ret; + } + + ret = phy_get_link_state(dev_conf->phy_dev, &state); + if (ret) { + LOG_ERR("%s: get PHY link state failed", dev->name); + return ret; + } + + eth_xlnx_gem_phy_cb(dev_conf->phy_dev, &state, (void *)dev); + } else { + net_eth_carrier_on(iface); + } } /** @@ -367,8 +351,7 @@ static int eth_xlnx_gem_send(const struct device *dev, struct net_pkt *pkt) uint32_t reg_val; int sem_status; - if (!dev_data->started || dev_data->eff_link_speed == LINK_DOWN || - (!net_if_flag_is_set(dev_data->iface, NET_IF_UP))) { + if (!dev_data->started || !net_if_flag_is_set(dev_data->iface, NET_IF_UP)) { #ifdef CONFIG_NET_STATISTICS_ETHERNET dev_data->stats.tx_dropped++; #endif @@ -555,11 +538,6 @@ static int eth_xlnx_gem_start_device(const struct device *dev) sys_write32(ETH_XLNX_GEM_IXR_ALL_MASK, dev_conf->base_addr + ETH_XLNX_GEM_IER_OFFSET); - /* Submit the delayed work for polling the link state */ - if (k_work_delayable_remaining_get(&dev_data->phy_poll_delayed_work) == 0) { - k_work_reschedule(&dev_data->phy_poll_delayed_work, K_NO_WAIT); - } - LOG_DBG("%s started", dev->name); return 0; } @@ -585,11 +563,6 @@ static int eth_xlnx_gem_stop_device(const struct device *dev) } dev_data->started = false; - /* Cancel the delayed work that polls the link state */ - if (k_work_delayable_remaining_get(&dev_data->phy_poll_delayed_work) != 0) { - k_work_cancel_delayable(&dev_data->phy_poll_delayed_work); - } - /* RX and TX disable */ reg_val = sys_read32(dev_conf->base_addr + ETH_XLNX_GEM_NWCTRL_OFFSET); reg_val &= (~(ETH_XLNX_GEM_NWCTRL_RXEN_BIT | ETH_XLNX_GEM_NWCTRL_TXEN_BIT)); @@ -624,22 +597,9 @@ static enum ethernet_hw_caps eth_xlnx_gem_get_capabilities( const struct eth_xlnx_gem_dev_cfg *dev_conf = dev->config; enum ethernet_hw_caps caps = (enum ethernet_hw_caps)0; - if (dev_conf->max_link_speed == LINK_1GBIT) { - if (dev_conf->phy_advertise_lower) { - caps |= (ETHERNET_LINK_1000BASE | ETHERNET_LINK_100BASE | - ETHERNET_LINK_10BASE); - } else { - caps |= ETHERNET_LINK_1000BASE; - } - } else if (dev_conf->max_link_speed == LINK_100MBIT) { - if (dev_conf->phy_advertise_lower) { - caps |= (ETHERNET_LINK_100BASE | ETHERNET_LINK_10BASE); - } else { - caps |= ETHERNET_LINK_100BASE; - } - } else { - caps |= ETHERNET_LINK_10BASE; - } + caps |= ETHERNET_LINK_1000BASE | + ETHERNET_LINK_100BASE | + ETHERNET_LINK_10BASE; if (dev_conf->enable_rx_chksum_offload) { caps |= ETHERNET_HW_RX_CHKSUM_OFFLOAD; @@ -649,15 +609,23 @@ static enum ethernet_hw_caps eth_xlnx_gem_get_capabilities( caps |= ETHERNET_HW_TX_CHKSUM_OFFLOAD; } - if (dev_conf->enable_fdx) { - caps |= ETHERNET_DUPLEX_SET; - } - caps |= ETHERNET_PROMISC_MODE; return caps; } +/** + * @brief Returns a pointer to the associated PHY device + * @param dev Parent GEM device of the requested PHY device + * @return Pointer to the associated PHY device + */ +static const struct device *eth_xlnx_gem_get_phy(const struct device *dev) +{ + const struct eth_xlnx_gem_dev_cfg *dev_conf = dev->config; + + return dev_conf->phy_dev; +} + /** * @brief GEM hardware configuration data request function * Returns hardware configuration details of the specified device @@ -785,19 +753,18 @@ static struct net_stats_eth *eth_xlnx_gem_stats(const struct device *dev) static void eth_xlnx_gem_reset_hw(const struct device *dev) { const struct eth_xlnx_gem_dev_cfg *dev_conf = dev->config; + uint32_t nwctrl; /* * Controller reset sequence as described in the Zynq-7000 TRM, * chapter 16.3.1. */ - /* Clear the NWCTRL register */ - sys_write32(0x00000000, - dev_conf->base_addr + ETH_XLNX_GEM_NWCTRL_OFFSET); - - /* Clear the statistics counters */ - sys_write32(ETH_XLNX_GEM_STATCLR_MASK, - dev_conf->base_addr + ETH_XLNX_GEM_NWCTRL_OFFSET); + /* Prepare the NWCTRL register, preserve the MDEN bit */ + nwctrl = sys_read32(dev_conf->base_addr + ETH_XLNX_GEM_NWCTRL_OFFSET); + nwctrl &= ETH_XLNX_GEM_NWCTRL_MDEN_BIT; + nwctrl |= ETH_XLNX_GEM_NWCTRL_STATCLR_BIT; /* clear statistics counters */ + sys_write32(nwctrl, dev_conf->base_addr + ETH_XLNX_GEM_NWCTRL_OFFSET); /* Clear the RX/TX status registers */ sys_write32(ETH_XLNX_GEM_TXSRCLR_MASK, @@ -823,8 +790,10 @@ static void eth_xlnx_gem_reset_hw(const struct device *dev) * from within the device initialization function. * * @param dev Pointer to the device data + * @param state pointer to the current PHY link state data */ -static void eth_xlnx_gem_configure_clocks(const struct device *dev) +static void eth_xlnx_gem_configure_clocks(const struct device *dev, + struct phy_link_state *state) { /* * Clock source configuration for the respective GEM as described @@ -834,7 +803,6 @@ static void eth_xlnx_gem_configure_clocks(const struct device *dev) */ const struct eth_xlnx_gem_dev_cfg *dev_conf = dev->config; - struct eth_xlnx_gem_dev_data *dev_data = dev->data; uint32_t div0; uint32_t div1; @@ -842,34 +810,12 @@ static void eth_xlnx_gem_configure_clocks(const struct device *dev) uint32_t tmp; uint32_t clk_ctrl_reg; - if ((!dev_conf->init_phy) || dev_data->eff_link_speed == LINK_DOWN) { - /* - * Run-time data indicates 'link down' or PHY management - * is disabled for the current device -> this indicates the - * initial device initialization. Once the PHY status polling - * delayed work handler has picked up the result of the auto- - * negotiation (if enabled), this if-statement will evaluate - * to false. - */ - if (dev_conf->max_link_speed == LINK_10MBIT) { - target = 2500000; /* Target frequency: 2.5 MHz */ - } else if (dev_conf->max_link_speed == LINK_100MBIT) { - target = 25000000; /* Target frequency: 25 MHz */ - } else if (dev_conf->max_link_speed == LINK_1GBIT) { - target = 125000000; /* Target frequency: 125 MHz */ - } - } else if (dev_data->eff_link_speed != LINK_DOWN) { - /* - * Use the effective link speed instead of the maximum/nominal - * link speed for clock configuration. - */ - if (dev_data->eff_link_speed == LINK_10MBIT) { - target = 2500000; /* Target frequency: 2.5 MHz */ - } else if (dev_data->eff_link_speed == LINK_100MBIT) { - target = 25000000; /* Target frequency: 25 MHz */ - } else if (dev_data->eff_link_speed == LINK_1GBIT) { - target = 125000000; /* Target frequency: 125 MHz */ - } + if (PHY_LINK_IS_SPEED_1000M(state->speed)) { + target = 125000000; /* Target frequency: 125 MHz */ + } else if (PHY_LINK_IS_SPEED_100M(state->speed)) { + target = 25000000; /* Target frequency: 25 MHz */ + } else { + target = 2500000; /* Target frequency: 2.5 MHz */ } /* @@ -953,7 +899,9 @@ static void eth_xlnx_gem_configure_clocks(const struct device *dev) static void eth_xlnx_gem_set_initial_nwcfg(const struct device *dev) { const struct eth_xlnx_gem_dev_cfg *dev_conf = dev->config; - uint32_t reg_val = 0; + uint32_t reg_val = sys_read32(dev_conf->base_addr + ETH_XLNX_GEM_NWCFG_OFFSET); + + reg_val &= (ETH_XLNX_GEM_NWCFG_MDC_MASK << ETH_XLNX_GEM_NWCFG_MDC_SHIFT); if (dev_conf->ignore_ipg_rxer) { /* [30] ignore IPG rx_er */ @@ -991,10 +939,6 @@ static void eth_xlnx_gem_set_initial_nwcfg(const struct device *dev) reg_val |= (((uint32_t)(dev_conf->amba_dbus_width) & ETH_XLNX_GEM_NWCFG_DBUSW_MASK) << ETH_XLNX_GEM_NWCFG_DBUSW_SHIFT); - /* [20..18] MDC clock divider */ - reg_val |= (((uint32_t)dev_conf->mdc_divider & - ETH_XLNX_GEM_NWCFG_MDC_MASK) << - ETH_XLNX_GEM_NWCFG_MDC_SHIFT); if (dev_conf->discard_rx_fcs) { /* [17] Discard FCS from received frames */ reg_val |= ETH_XLNX_GEM_NWCFG_FCSREM_BIT; @@ -1043,17 +987,6 @@ static void eth_xlnx_gem_set_initial_nwcfg(const struct device *dev) /* [01] enable Full duplex */ reg_val |= ETH_XLNX_GEM_NWCFG_FDEN_BIT; } - if (dev_conf->max_link_speed == LINK_100MBIT) { - /* [00] 10 or 100 Mbps */ - reg_val |= ETH_XLNX_GEM_NWCFG_100_BIT; - } else if (dev_conf->max_link_speed == LINK_1GBIT) { - /* [10] Gigabit mode enable */ - reg_val |= ETH_XLNX_GEM_NWCFG_1000_BIT; - } - /* - * No else-branch for 10Mbit/s mode: - * in 10 Mbit/s mode, both bits [00] and [10] remain 0 - */ /* Write the assembled register contents to gem.net_cfg */ sys_write32(reg_val, dev_conf->base_addr + ETH_XLNX_GEM_NWCFG_OFFSET); @@ -1065,26 +998,34 @@ static void eth_xlnx_gem_set_initial_nwcfg(const struct device *dev) * register. This is called from within #eth_xlnx_gem_poll_phy. * * @param dev Pointer to the device data + * @param state pointer to the current PHY link state data */ -static void eth_xlnx_gem_set_nwcfg_link_speed(const struct device *dev) +static void eth_xlnx_gem_set_nwcfg_link_speed(const struct device *dev, + struct phy_link_state *state) { const struct eth_xlnx_gem_dev_cfg *dev_conf = dev->config; - struct eth_xlnx_gem_dev_data *dev_data = dev->data; uint32_t reg_val; /* - * Read the current gem.net_cfg register contents and mask out - * the link speed-related bits + * Read the current gem.net_cfg register, mask out the link speed + * and duplex related bits. Replace their contents with those + * matching the current PHY state. */ reg_val = sys_read32(dev_conf->base_addr + ETH_XLNX_GEM_NWCFG_OFFSET); - reg_val &= ~(ETH_XLNX_GEM_NWCFG_1000_BIT | ETH_XLNX_GEM_NWCFG_100_BIT); + reg_val &= ~(ETH_XLNX_GEM_NWCFG_1000_BIT | + ETH_XLNX_GEM_NWCFG_100_BIT | + ETH_XLNX_GEM_NWCFG_FDEN_BIT); /* No bits to set for 10 Mbps. 100 Mbps and 1 Gbps set one bit each. */ - if (dev_data->eff_link_speed == LINK_100MBIT) { + if (PHY_LINK_IS_SPEED_100M(state->speed)) { reg_val |= ETH_XLNX_GEM_NWCFG_100_BIT; - } else if (dev_data->eff_link_speed == LINK_1GBIT) { + } else if (PHY_LINK_IS_SPEED_1000M(state->speed)) { reg_val |= ETH_XLNX_GEM_NWCFG_1000_BIT; } + /* Set FDEN bit for full-duplex operation */ + if (PHY_LINK_IS_FULL_DUPLEX(state->speed)) { + reg_val |= ETH_XLNX_GEM_NWCFG_FDEN_BIT; + } /* Write the assembled register contents to gem.net_cfg */ sys_write32(reg_val, dev_conf->base_addr + ETH_XLNX_GEM_NWCFG_OFFSET); @@ -1194,154 +1135,6 @@ static void eth_xlnx_gem_set_initial_dmacr(const struct device *dev) sys_write32(reg_val, dev_conf->base_addr + ETH_XLNX_GEM_DMACR_OFFSET); } -/** - * @brief GEM associated PHY detection and setup function - * If the current GEM device shall manage an associated PHY, its detection - * and configuration is performed from within this function. Called from - * within the device initialization function. This function refers to - * functionality implemented in the phy_xlnx_gem module. - * - * @param dev Pointer to the device data - */ -static void eth_xlnx_gem_init_phy(const struct device *dev) -{ - struct eth_xlnx_gem_dev_data *dev_data = dev->data; - int detect_rc; - - LOG_DBG("%s attempting to initialize associated PHY", dev->name); - - /* - * The phy_xlnx_gem_detect function checks if a valid PHY - * ID is returned when reading the corresponding high / low - * ID registers for all valid MDIO addresses. If a compatible - * PHY is detected, the function writes a pointer to the - * vendor-specific implementations of the PHY management - * functions to the run-time device data struct, along with - * the ID and the MDIO address of the detected PHY (dev_data-> - * phy_id, dev_data->phy_addr, dev_data->phy_access_api). - */ - detect_rc = phy_xlnx_gem_detect(dev); - - if (detect_rc == 0 && dev_data->phy_id != 0x00000000 && - dev_data->phy_id != 0xFFFFFFFF && - dev_data->phy_access_api != NULL) { - /* A compatible PHY was detected -> reset & configure it */ - dev_data->phy_access_api->phy_reset_func(dev); - dev_data->phy_access_api->phy_configure_func(dev); - } else { - LOG_WRN("%s no compatible PHY detected", dev->name); - } -} - -/** - * @brief GEM associated PHY status polling function - * This handler of a delayed work item is called from the context of - * the system work queue. It is always scheduled at least once during the - * interface initialization. If the current driver instance manages a - * PHY, the delayed work item will be re-scheduled in order to continuously - * monitor the link state and speed while the device is active. Link state - * and link speed changes are polled, which may result in the link state - * change being propagated (carrier on/off) and / or the TX clock being - * reconfigured to match the current link speed. If PHY management is dis- - * abled for the current driver instance or no compatible PHY was detected, - * the work item will not be re-scheduled and default link speed and link - * state values are applied. This function refers to functionality imple- - * mented in the phy_xlnx_gem module. - * - * @param work Pointer to the delayed work item which facilitates - * access to the current device's configuration data - */ -static void eth_xlnx_gem_poll_phy(struct k_work *work) -{ - struct k_work_delayable *dwork = k_work_delayable_from_work(work); - struct eth_xlnx_gem_dev_data *dev_data = CONTAINER_OF(dwork, - struct eth_xlnx_gem_dev_data, phy_poll_delayed_work); - const struct device *dev = net_if_get_device(dev_data->iface); - const struct eth_xlnx_gem_dev_cfg *dev_conf = dev->config; - - uint16_t phy_status; - uint8_t link_status; - - if (dev_data->phy_access_api != NULL) { - /* A supported PHY is managed by the driver */ - phy_status = dev_data->phy_access_api->phy_poll_status_change_func(dev); - - if ((phy_status & ( - PHY_XLNX_GEM_EVENT_LINK_SPEED_CHANGED | - PHY_XLNX_GEM_EVENT_LINK_STATE_CHANGED | - PHY_XLNX_GEM_EVENT_AUTONEG_COMPLETE)) != 0) { - - /* - * Get the PHY's link status. Handling a 'link down' - * event the simplest possible case. - */ - link_status = dev_data->phy_access_api->phy_poll_link_status_func(dev); - - if (link_status == 0) { - /* - * Link is down -> propagate to the Ethernet - * layer that the link has gone down. - */ - dev_data->eff_link_speed = LINK_DOWN; - net_eth_carrier_off(dev_data->iface); - - LOG_WRN("%s link down", dev->name); - } else { - /* - * A link has been detected, which, depending - * on the driver's configuration, might have - * a different speed than the previous link. - * Therefore, the clock dividers must be ad- - * justed accordingly. - */ - dev_data->eff_link_speed = - dev_data->phy_access_api->phy_poll_link_speed_func(dev); - - eth_xlnx_gem_configure_clocks(dev); - eth_xlnx_gem_set_nwcfg_link_speed(dev); - net_eth_carrier_on(dev_data->iface); - - LOG_INF("%s link up, %s", dev->name, - (dev_data->eff_link_speed == LINK_1GBIT) - ? "1 GBit/s" - : (dev_data->eff_link_speed == LINK_100MBIT) - ? "100 MBit/s" - : (dev_data->eff_link_speed == LINK_10MBIT) - ? "10 MBit/s" : "undefined / link down"); - } - } - - /* - * Re-submit the delayed work using the interval from the device - * configuration data. - */ - k_work_reschedule(&dev_data->phy_poll_delayed_work, - K_MSEC(dev_conf->phy_poll_interval)); - } else { - /* - * The current driver instance doesn't manage a PHY or no - * supported PHY was detected -> pretend the configured max. - * link speed is the effective link speed and that the link - * is up. The delayed work item won't be re-scheduled, as - * there isn't anything to poll for. - */ - dev_data->eff_link_speed = dev_conf->max_link_speed; - - eth_xlnx_gem_configure_clocks(dev); - eth_xlnx_gem_set_nwcfg_link_speed(dev); - net_eth_carrier_on(dev_data->iface); - - LOG_WRN("%s PHY not managed by the driver or no compatible " - "PHY detected, assuming link up at %s", dev->name, - (dev_conf->max_link_speed == LINK_1GBIT) - ? "1 GBit/s" - : (dev_conf->max_link_speed == LINK_100MBIT) - ? "100 MBit/s" - : (dev_conf->max_link_speed == LINK_10MBIT) - ? "10 MBit/s" : "undefined"); - } -} - /** * @brief GEM DMA memory area setup function * Sets up the DMA memory area to be used by the current GEM device. @@ -1727,3 +1520,32 @@ static void eth_xlnx_gem_handle_tx_done(const struct device *dev) /* Indicate completion to a blocking eth_xlnx_gem_send() call */ k_sem_give(&dev_data->tx_done_sem); } + +/** + * @brief PHY event callback function + * This handler function is called whenever a link state change or a + * link speed change is indicated by the associated PHY. + * + * @param dev Pointer to the device data + * @param state Updated PHY link/speed state + * @param eth_dev Pointer to the GEM instance's device struct + */ +static void eth_xlnx_gem_phy_cb(const struct device *phy, + struct phy_link_state *state, + void *eth_dev) +{ + const struct device *dev = (const struct device *)eth_dev; + struct eth_xlnx_gem_dev_data *dev_data = dev->data; + + if (dev_data->iface == NULL) { + return; + } + + if (state->is_up) { + eth_xlnx_gem_configure_clocks(dev, state); + eth_xlnx_gem_set_nwcfg_link_speed(dev, state); + net_eth_carrier_on(dev_data->iface); + } else { + net_eth_carrier_off(dev_data->iface); + } +} diff --git a/drivers/ethernet/eth_xlnx_gem_priv.h b/drivers/ethernet/eth_xlnx_gem_priv.h index a4cb88f93b98c..5ec2409132feb 100644 --- a/drivers/ethernet/eth_xlnx_gem_priv.h +++ b/drivers/ethernet/eth_xlnx_gem_priv.h @@ -17,8 +17,6 @@ #include #include -#include "phy_xlnx_gem.h" - #define ETH_XLNX_BUFFER_ALIGNMENT 4 /* RX/TX buffer alignment (in bytes) */ /* Buffer descriptor (BD) related defines */ @@ -277,7 +275,6 @@ #define ETH_XLNX_GEM_NWCFG_DBUSW_SHIFT 21 #define ETH_XLNX_GEM_NWCFG_MDC_MASK 0x7 #define ETH_XLNX_GEM_NWCFG_MDC_SHIFT 18 -#define ETH_XLNX_GEM_NWCFG_MDCCLKDIV_MASK 0x001C0000 #define ETH_XLNX_GEM_NWCFG_FCSREM_BIT 0x00020000 #define ETH_XLNX_GEM_NWCFG_LENGTHERRDSCRD_BIT 0x00010000 #define ETH_XLNX_GEM_NWCFG_RXOFFS_MASK 0x00000003 @@ -374,37 +371,8 @@ #define ETH_XLNX_GEM_IXR_ALL_MASK 0x03FC7FFE #define ETH_XLNX_GEM_IXR_ERRORS_MASK 0x00000C60 -/* Bits / bit masks relating to the GEM's MDIO interface */ - -/* - * gem.net_status: - * [02] PHY management idle bit - * [01] MDIO input status - */ -#define ETH_XLNX_GEM_MDIO_IDLE_BIT 0x00000004 -#define ETH_XLNX_GEM_MDIO_IN_STATUS_BIT 0x00000002 - -/* - * gem.phy_maint: - * [31 .. 30] constant values - * [17 .. 16] constant values - * [29] Read operation control bit - * [28] Write operation control bit - * [27 .. 23] PHY address - * [22 .. 18] Register address - * [15 .. 00] 16-bit data word - */ -#define ETH_XLNX_GEM_PHY_MAINT_CONST_BITS 0x40020000 -#define ETH_XLNX_GEM_PHY_MAINT_READ_OP_BIT 0x20000000 -#define ETH_XLNX_GEM_PHY_MAINT_WRITE_OP_BIT 0x10000000 -#define ETH_XLNX_GEM_PHY_MAINT_PHY_ADDRESS_MASK 0x0000001F -#define ETH_XLNX_GEM_PHY_MAINT_PHY_ADDRESS_SHIFT 23 -#define ETH_XLNX_GEM_PHY_MAINT_REGISTER_ID_MASK 0x0000001F -#define ETH_XLNX_GEM_PHY_MAINT_REGISTER_ID_SHIFT 18 -#define ETH_XLNX_GEM_PHY_MAINT_DATA_MASK 0x0000FFFF - /* Device initialization macro */ -#define ETH_XLNX_GEM_NET_DEV_INIT(port) \ +#define ETH_XLNX_GEM_NET_DEV_INIT(port)\ ETH_NET_DEVICE_DT_INST_DEFINE(port,\ eth_xlnx_gem_dev_init,\ NULL,\ @@ -414,21 +382,22 @@ ETH_NET_DEVICE_DT_INST_DEFINE(port,\ ð_xlnx_gem_apis,\ NET_ETH_MTU); +/* Helpers for resolving the MDIO and PHY devices */ +#define ETH_XLNX_GEM_MDIO_DEV(port)\ + DEVICE_DT_GET_OR_NULL(DT_NODELABEL(gem##port##_mdio)) + +#define ETH_XLNX_GEM_PHY_DEV(port)\ + DEVICE_DT_GET_OR_NULL(DT_INST_PHANDLE(port, phy_handle)) + /* Device configuration data declaration macro */ #define ETH_XLNX_GEM_DEV_CONFIG(port) \ static const struct eth_xlnx_gem_dev_cfg eth_xlnx_gem##port##_dev_cfg = {\ + .mdio_dev = ETH_XLNX_GEM_MDIO_DEV(port),\ + .phy_dev = ETH_XLNX_GEM_PHY_DEV(port),\ .base_addr = DT_REG_ADDR_BY_IDX(DT_INST(port, xlnx_gem), 0),\ .config_func = eth_xlnx_gem##port##_irq_config,\ .pll_clock_frequency = DT_INST_PROP(port, clock_frequency),\ .clk_ctrl_reg_address = DT_REG_ADDR_BY_IDX(DT_INST(port, xlnx_gem), 1),\ - .mdc_divider = (enum eth_xlnx_mdc_clock_divider)\ - (DT_INST_PROP(port, mdc_divider)),\ - .max_link_speed = (enum eth_xlnx_link_speed)\ - (DT_INST_PROP(port, link_speed)),\ - .init_phy = DT_INST_PROP(port, init_mdio_phy),\ - .phy_mdio_addr_fix = DT_INST_PROP(port, mdio_phy_address),\ - .phy_advertise_lower = DT_INST_PROP(port, advertise_lower_link_speeds),\ - .phy_poll_interval = DT_INST_PROP(port, phy_poll_interval),\ .defer_rxp_to_queue = !DT_INST_PROP(port, handle_rx_in_isr),\ .defer_txd_to_queue = DT_INST_PROP(port, handle_tx_in_workq),\ .amba_dbus_width = (enum eth_xlnx_amba_dbus_width)\ @@ -465,7 +434,6 @@ static const struct eth_xlnx_gem_dev_cfg eth_xlnx_gem##port##_dev_cfg = {\ .enable_mcast_hash = DT_INST_PROP(port, multicast_hash),\ .disable_bcast = DT_INST_PROP(port, reject_broadcast),\ .discard_non_vlan = DT_INST_PROP(port, discard_non_vlan),\ - .enable_fdx = DT_INST_PROP(port, full_duplex),\ .disc_rx_ahb_unavail = DT_INST_PROP(port, discard_rx_frame_ahb_unavail),\ .enable_tx_chksum_offload = DT_INST_PROP(port, tx_checksum_offload),\ .tx_buffer_size_full = DT_INST_PROP(port, hw_tx_buffer_size_full),\ @@ -478,10 +446,6 @@ static const struct eth_xlnx_gem_dev_cfg eth_xlnx_gem##port##_dev_cfg = {\ static struct eth_xlnx_gem_dev_data eth_xlnx_gem##port##_dev_data = {\ .mac_addr = DT_INST_PROP(port, local_mac_address),\ .started = 0,\ - .eff_link_speed = LINK_DOWN,\ - .phy_addr = 0,\ - .phy_id = 0,\ - .phy_access_api = NULL,\ .first_rx_buffer = NULL,\ .first_tx_buffer = NULL\ }; @@ -534,27 +498,13 @@ ETH_XLNX_GEM_DEV_CONFIG(port);\ ETH_XLNX_GEM_DEV_DATA(port);\ ETH_XLNX_GEM_DMA_AREA_DECL(port);\ ETH_XLNX_GEM_DMA_AREA_INST(port);\ -ETH_XLNX_GEM_NET_DEV_INIT(port);\ +ETH_XLNX_GEM_NET_DEV_INIT(port); /* IRQ handler function type */ typedef void (*eth_xlnx_gem_config_irq_t)(const struct device *dev); /* Enums for bitfields representing configuration settings */ -/** - * @brief Link speed configuration enumeration type. - * - * Enumeration type for link speed indication, contains 'link down' - * plus all link speeds supported by the controller (10/100/1000). - */ -enum eth_xlnx_link_speed { - /* The values of this enum are consecutively numbered */ - LINK_DOWN = 0, - LINK_10MBIT, - LINK_100MBIT, - LINK_1GBIT -}; - /** * @brief AMBA AHB data bus width configuration enumeration type. * @@ -569,29 +519,6 @@ enum eth_xlnx_amba_dbus_width { AMBA_AHB_DBUS_WIDTH_128BIT }; -/** - * @brief MDC clock divider configuration enumeration type. - * - * Enumeration type containing the supported clock divider values - * used to generate the MDIO interface clock (MDC) from either the - * cpu_1x clock (Zynq-7000) or the LPD LSBUS clock (UltraScale). - * This is a configuration item in the controller's net_cfg register. - */ -enum eth_xlnx_mdc_clock_divider { - /* The values of this enum are consecutively numbered */ - MDC_DIVIDER_8 = 0, - MDC_DIVIDER_16, - MDC_DIVIDER_32, - MDC_DIVIDER_48, -#ifdef CONFIG_SOC_FAMILY_XILINX_ZYNQ7000 - /* Dividers > 48 are only available in the Zynq-7000 */ - MDC_DIVIDER_64, - MDC_DIVIDER_96, - MDC_DIVIDER_128, - MDC_DIVIDER_224 -#endif -}; - /** * @brief DMA RX buffer size configuration enumeration type. * @@ -679,18 +606,14 @@ struct eth_xlnx_gem_bdring { * UltraScale SoCs, which both contain the GEM. */ struct eth_xlnx_gem_dev_cfg { + const struct device *mdio_dev; + const struct device *phy_dev; uint32_t base_addr; eth_xlnx_gem_config_irq_t config_func; uint32_t pll_clock_frequency; uint32_t clk_ctrl_reg_address; - enum eth_xlnx_mdc_clock_divider mdc_divider; - enum eth_xlnx_link_speed max_link_speed; - bool init_phy; - uint8_t phy_mdio_addr_fix; - uint8_t phy_advertise_lower; - uint32_t phy_poll_interval; uint8_t defer_rxp_to_queue; uint8_t defer_txd_to_queue; @@ -740,17 +663,11 @@ struct eth_xlnx_gem_dev_cfg { struct eth_xlnx_gem_dev_data { struct net_if *iface; uint8_t mac_addr[6]; - enum eth_xlnx_link_speed eff_link_speed; struct k_work tx_done_work; struct k_work rx_pend_work; struct k_sem tx_done_sem; - uint8_t phy_addr; - uint32_t phy_id; - struct k_work_delayable phy_poll_delayed_work; - struct phy_xlnx_gem_api *phy_access_api; - uint8_t *first_rx_buffer; uint8_t *first_tx_buffer; From e0bda5d4c29e003c896b8637b1ad5bc45c7bdf03 Mon Sep 17 00:00:00 2001 From: Immo Birnbaum Date: Tue, 25 Feb 2025 10:04:00 +0100 Subject: [PATCH 8/9] drivers: phy: add drivers for PHYs supported by legacy MDIO driver The legacy MDIO/PHY driver integrated into the Xilinx GEM device driver supported two PHY families: the Marvell Alaska 88E1xxx series PHYs and the TI DP83822 which is a pin- and register compatible drop-in replacement of the TI TLK105, which is therefore also supported by the same driver. These two PHY families are now provided as separate drivers implemented against the standard PHY driver APIs, therefore also enabling their use with MACs other than the GEM. Signed-off-by: Immo Birnbaum --- drivers/ethernet/phy/CMakeLists.txt | 2 + drivers/ethernet/phy/Kconfig | 16 + drivers/ethernet/phy/phy_marvell_88e1xxx.c | 626 ++++++++++++++++++ drivers/ethernet/phy/phy_ti_dp83822.c | 473 +++++++++++++ .../ethernet/marvell,88e1xxx-phy.yaml | 15 + dts/bindings/ethernet/ti,dp83822-phy.yaml | 16 + 6 files changed, 1148 insertions(+) create mode 100644 drivers/ethernet/phy/phy_marvell_88e1xxx.c create mode 100644 drivers/ethernet/phy/phy_ti_dp83822.c create mode 100644 dts/bindings/ethernet/marvell,88e1xxx-phy.yaml create mode 100644 dts/bindings/ethernet/ti,dp83822-phy.yaml diff --git a/drivers/ethernet/phy/CMakeLists.txt b/drivers/ethernet/phy/CMakeLists.txt index 34d769eca6c56..a5e1e440ee53b 100644 --- a/drivers/ethernet/phy/CMakeLists.txt +++ b/drivers/ethernet/phy/CMakeLists.txt @@ -5,12 +5,14 @@ zephyr_library_sources_ifdef(CONFIG_PHY_GENERIC_MII phy_mii.c) # zephyr-keep-sorted-start zephyr_library_sources_ifdef(CONFIG_PHY_ADIN2111 phy_adin2111.c) zephyr_library_sources_ifdef(CONFIG_PHY_DM8806 phy_dm8806.c) +zephyr_library_sources_ifdef(CONFIG_PHY_MARVELL_ALASKA_88E1XXX phy_marvell_88e1xxx.c) zephyr_library_sources_ifdef(CONFIG_PHY_MICROCHIP_KSZ8081 phy_microchip_ksz8081.c) zephyr_library_sources_ifdef(CONFIG_PHY_MICROCHIP_T1S phy_microchip_t1s.c) zephyr_library_sources_ifdef(CONFIG_PHY_MICROCHIP_VSC8541 phy_microchip_vsc8541.c) zephyr_library_sources_ifdef(CONFIG_PHY_OA_TC14_PLCA_LIB phy_oa_tc14_plca.c) zephyr_library_sources_ifdef(CONFIG_PHY_QUALCOMM_AR8031 phy_qualcomm_ar8031.c) zephyr_library_sources_ifdef(CONFIG_PHY_REALTEK_RTL8211F phy_realtek_rtl8211f.c) +zephyr_library_sources_ifdef(CONFIG_PHY_TI_DP83822 phy_ti_dp83822.c) zephyr_library_sources_ifdef(CONFIG_PHY_TI_DP83825 phy_ti_dp83825.c) zephyr_library_sources_ifdef(CONFIG_PHY_TI_DP83867 phy_ti_dp83867.c) zephyr_library_sources_ifdef(CONFIG_PHY_TJA1103 phy_tja1103.c) diff --git a/drivers/ethernet/phy/Kconfig b/drivers/ethernet/phy/Kconfig index c9d0c61baa227..3a16e08b39f67 100644 --- a/drivers/ethernet/phy/Kconfig +++ b/drivers/ethernet/phy/Kconfig @@ -96,6 +96,22 @@ config PHY_QUALCOMM_AR8031 help Enable Qualcomm Atheros AR8031 Ethernet PHY Driver +config PHY_MARVELL_ALASKA_88E1XXX + bool "Marvell Alaska 88E1xxx PHY Driver" + default y + depends on DT_HAS_MARVELL_88E1XXX_ENABLED + depends on MDIO + help + Enable Marvell Alaska 88E1xxx PHY Driver + +config PHY_TI_DP83822 + bool "TI DP83822 PHY Driver" + default y + depends on DT_HAS_TI_DP83822_ENABLED + depends on MDIO + help + Enable TI DP83822 PHY Driver + config PHY_AUTONEG_TIMEOUT_MS int "Auto-negotiation timeout value in milliseconds" default 4000 diff --git a/drivers/ethernet/phy/phy_marvell_88e1xxx.c b/drivers/ethernet/phy/phy_marvell_88e1xxx.c new file mode 100644 index 0000000000000..46889c6bfa110 --- /dev/null +++ b/drivers/ethernet/phy/phy_marvell_88e1xxx.c @@ -0,0 +1,626 @@ +/* + * Copyright (c) 2021 Weidmueller Interface GmbH & Co. KG + * Copyright (c) 2025 Immo Birnbaum + * + * Definitions and procedures moved here from the former proprietary + * Xilinx GEM PHY driver (phy_xlnx_gem.c), adjusted to use the MDIO + * framework provided by Zephyr. The TI DP83825 driver was used as a + * template, which is (c) Bernhard Kraemer. + * + * Register IDs & procedures are based on the corresponding datasheets: + * https://www.marvell.com/content/dam/marvell/en/public-collateral/transceivers/marvell-phys-transceivers-alaska-88e1111-datasheet.pdf + * https://www.marvell.com/content/dam/marvell/en/public-collateral/phys-transceivers/marvell-phys-transceivers-alaska-88e151x-datasheet.pdf + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT marvell_88e1xxx + +#include +#include +#include +#include + +#define DT_DRV_COMPAT marvell_88e1xxx +#define LOG_MODULE_NAME phy_marvell_88e1xxx + +#define LOG_LEVEL CONFIG_PHY_LOG_LEVEL +#include +LOG_MODULE_REGISTER(LOG_MODULE_NAME); + +/* Marvell PHY ID: bits [3..0] = revision -> discard during ID check */ +#define PHY_MRVL_PHY_ID_MODEL_MASK 0xFFFFFFF0 +#define PHY_MRVL_PHY_ID_MODEL_88E151X 0x01410DD0 + +#define PHY_MRVL_BASE_REGISTERS_PAGE 0 +#define PHY_MRVL_COPPER_CONTROL_REGISTER MII_BMCR +#define PHY_MRVL_COPPER_AUTONEG_ADV_REGISTER MII_ANAR +#define PHY_MRVL_1000BASET_CONTROL_REGISTER 0x09 +#define PHY_MRVL_COPPER_CONTROL_1_REGISTER 0x10 +#define PHY_MRVL_COPPER_STATUS_1_REGISTER 0x11 +#define PHY_MRVL_COPPER_PAGE_SWITCH_REGISTER 0x16 + +#define PHY_MRVL_GENERAL_CONTROL_1_PAGE 0x12 +#define PHY_MRVL_GENERAL_CONTROL_1_REGISTER 0x14 + +#define PHY_MRVL_COPPER_CONTROL_RESET_BIT BIT(15) +#define PHY_MRVL_COPPER_CONTROL_AUTONEG_ENABLE_BIT BIT(12) + +#define PHY_MRVL_GENERAL_CONTROL_1_RESET_BIT BIT(15) + +#define PHY_MRVL_ADVERTISE_1000_FULL BIT(9) +#define PHY_MRVL_ADVERTISE_1000_HALF BIT(8) + +#define PHY_MRVL_MDIX_CONFIG_MASK 0x0003 +#define PHY_MRVL_MDIX_CONFIG_SHIFT 5 +#define PHY_MRVL_MDIX_AUTO_CROSSOVER_ENABLE 0x0003 +#define PHY_MRVL_MODE_CONFIG_MASK 0x0007 +#define PHY_MRVL_MODE_CONFIG_SHIFT 0 + +#define PHY_MRVL_LINK_SPEED_SHIFT 14 +#define PHY_MRVL_LINK_SPEED_MASK 0x3 +#define PHY_MRVL_LINK_SPEED_10MBIT 0 +#define PHY_MRVL_LINK_SPEED_100MBIT BIT(0) +#define PHY_MRVL_LINK_SPEED_1GBIT BIT(1) +#define PHY_MRVL_LINK_DUPLEX_FDX BIT(13) +#define PHY_MRVL_LINK_STATUS BIT(10) + +struct marvell_alaska_config { + const struct device *mdio_dev; + uint8_t addr; +}; + +struct marvell_alaska_data { + const struct device *dev; + + struct phy_link_state state; + phy_callback_t cb; + void *cb_data; + + struct k_mutex mutex; + struct k_work_delayable phy_monitor_work; + + uint32_t phy_id; +}; + +static int phy_marvell_alaska_read(const struct device *dev, uint16_t reg_addr, uint32_t *data) +{ + const struct marvell_alaska_config *const dev_conf = dev->config; + *data = 0U; /* Clear unused bits [31..16] */ + + return mdio_read(dev_conf->mdio_dev, dev_conf->addr, reg_addr, (uint16_t *)data); +} + +static int phy_marvell_alaska_write(const struct device *dev, uint16_t reg_addr, uint32_t data) +{ + const struct marvell_alaska_config *const dev_conf = dev->config; + + return mdio_write(dev_conf->mdio_dev, dev_conf->addr, reg_addr, (uint16_t)data); +} + +static int phy_marvell_alaska_reset(const struct device *dev) +{ + int ret = 0; + uint32_t phy_data; + uint32_t poll_cnt = 0; + + /* + * Page 0, register address 0 = Copper control register, + * bit [15] = PHY Software reset. Register 0/0 access is R/M/W. + * Comp. datasheet chapter 2.6 and table 64 "Copper Control + * Register". Triggering a PHY Software reset affects pages + * 0, 2, 3, 5, 7. + */ + + ret = phy_marvell_alaska_read(dev, PHY_MRVL_COPPER_CONTROL_REGISTER, &phy_data); + if (ret) { + LOG_ERR("%s: reset PHY: read Copper Control Register failed", dev->name); + return ret; + } + + phy_data |= PHY_MRVL_COPPER_CONTROL_RESET_BIT; + ret = phy_marvell_alaska_write(dev, PHY_MRVL_COPPER_CONTROL_REGISTER, phy_data); + if (ret) { + LOG_ERR("%s: reset PHY: write Copper Control Register failed", dev->name); + return ret; + } + + while (((phy_data & PHY_MRVL_COPPER_CONTROL_RESET_BIT) != 0) && (poll_cnt++ < 10)) { + ret = phy_marvell_alaska_read(dev, PHY_MRVL_COPPER_CONTROL_REGISTER, &phy_data); + if (ret) { + LOG_ERR("%s: reset PHY: read Copper Control Register " + "(poll completion) failed", + dev->name); + return ret; + } + } + if (poll_cnt == 10) { + LOG_ERR("%s: reset PHY: reset completion timed out", dev->name); + ret = -ETIMEDOUT; + } + + return ret; +} + +static int phy_marvell_alaska_autonegotiate(const struct device *dev) +{ + int ret = 0; + uint32_t phy_data; + + /* + * Trigger a PHY reset, affecting pages 0, 2, 3, 5, 7. + * Afterwards, set the auto-negotiation enable bit [12] in the + * Copper Control Register. + */ + ret = phy_marvell_alaska_reset(dev); + if (ret != 0) { + LOG_ERR("%s: start auto-neg: reset call within " + "%s failed", dev->name, __func__); + + return ret; + } + + ret = phy_marvell_alaska_read(dev, PHY_MRVL_COPPER_CONTROL_REGISTER, &phy_data); + if (ret) { + LOG_ERR("%s: start auto-neg: read Copper Control Register failed", dev->name); + return ret; + } + + phy_data |= PHY_MRVL_COPPER_CONTROL_AUTONEG_ENABLE_BIT; + + ret = phy_marvell_alaska_write(dev, PHY_MRVL_COPPER_CONTROL_REGISTER, phy_data); + if (ret) { + LOG_ERR("%s: start auto-neg: write Copper Control Register failed", dev->name); + return ret; + } + + return 0; +} + +static int phy_marvell_alaska_static_cfg(const struct device *dev) +{ + struct marvell_alaska_data *dev_data = dev->data; + + int ret; + uint32_t phy_data; + uint32_t phy_id; + uint32_t poll_cnt = 0; + + /* Read & store PHY ID */ + ret = phy_marvell_alaska_read(dev, MII_PHYID1R, &phy_data); + if (ret) { + LOG_ERR("%s: configure PHY: read PHYID1R failed", dev->name); + return ret; + } + phy_id = ((phy_data << 16) & 0xFFFF0000); + ret = phy_marvell_alaska_read(dev, MII_PHYID2R, &phy_data); + if (ret) { + LOG_ERR("%s: configure PHY: read PHYID2R failed", dev->name); + return ret; + } + phy_id |= (phy_data & 0x0000FFFF); + + LOG_DBG("%s: configure PHY: read PHY ID 0x%08X", dev->name, phy_id); + + if (phy_id == 0 || phy_id == 0xFFFFFFFF) { + LOG_ERR("%s: configure PHY: no reply from PHY while reading PHY ID", dev->name); + return -EIO; + } + + dev_data->phy_id = phy_id; + + /* + * Page 0, register address 0 = Copper control register, + * bit [12] = auto-negotiation enable bit is to be cleared + * for now, afterwards, trigger a PHY Software reset. + * Register 0/0 access is R/M/W. Comp. datasheet chapter 2.6 + * and table 64 "Copper Control Register". + */ + ret = phy_marvell_alaska_read(dev, PHY_MRVL_COPPER_CONTROL_REGISTER, &phy_data); + if (ret) { + LOG_ERR("%s: configure PHY: read Copper Control Register failed", dev->name); + return ret; + } + phy_data &= ~PHY_MRVL_COPPER_CONTROL_AUTONEG_ENABLE_BIT; + ret = phy_marvell_alaska_write(dev, PHY_MRVL_COPPER_CONTROL_REGISTER, phy_data); + if (ret) { + LOG_ERR("%s: configure PHY: write Copper Control Register failed", dev->name); + return ret; + } + + ret = phy_marvell_alaska_reset(dev); + if (ret) { + LOG_ERR("%s: configure PHY: reset PHY failed (1)", dev->name); + return ret; + } + + if ((dev_data->phy_id & PHY_MRVL_PHY_ID_MODEL_MASK) == PHY_MRVL_PHY_ID_MODEL_88E151X) { + /* + * 88E151x only: configure the system interface and media type + * (i.e. "RGMII to Copper", 0x0). On the 88E1111, this setting + * is configured using I/O pins on the device. + * Page 18, register address 20 = General Control Register 1, + * bits [2..0] = mode configuration + * Comp. datasheet table 129 "General Control Register 1" + * NOTICE: a change of this value requires a subsequent software + * reset command via the same register's bit [15]. + */ + ret = phy_marvell_alaska_write(dev, PHY_MRVL_COPPER_PAGE_SWITCH_REGISTER, + PHY_MRVL_GENERAL_CONTROL_1_PAGE); + if (ret) { + LOG_ERR("%s: configure PHY: write Page Switch Register failed", dev->name); + return ret; + } + + ret = phy_marvell_alaska_read(dev, PHY_MRVL_GENERAL_CONTROL_1_REGISTER, &phy_data); + if (ret) { + LOG_ERR("%s: configure PHY: read General Control Register 1 failed", + dev->name); + return ret; + } + phy_data &= ~(PHY_MRVL_MODE_CONFIG_MASK << PHY_MRVL_MODE_CONFIG_SHIFT); + ret = phy_marvell_alaska_write(dev, PHY_MRVL_GENERAL_CONTROL_1_REGISTER, phy_data); + if (ret) { + LOG_ERR("%s: configure PHY: write General Control Register 1 failed", + dev->name); + return ret; + } + + /* + * [15] Mode Software Reset bit, affecting pages 6 and 18 + * Reset is performed immediately, bit [15] is self-clearing. + * This reset bit mirrors that in the Copper Control Register + * without the need for a prior register page switch. + */ + phy_data |= PHY_MRVL_GENERAL_CONTROL_1_RESET_BIT; + ret = phy_marvell_alaska_write(dev, PHY_MRVL_GENERAL_CONTROL_1_REGISTER, phy_data); + if (ret) { + LOG_ERR("%s: configure PHY: write General Control Register 1 failed", + dev->name); + return ret; + } + + /* Bit [15] reverts to 0 once the reset is complete. */ + while (((phy_data & PHY_MRVL_GENERAL_CONTROL_1_RESET_BIT) != 0) && + (poll_cnt++ < 10)) { + ret = phy_marvell_alaska_read(dev, PHY_MRVL_GENERAL_CONTROL_1_REGISTER, + &phy_data); + } + if (poll_cnt == 10) { + LOG_ERR("%s: configure PHY: software-reset PHY completion timed out", + dev->name); + return -ETIMEDOUT; + } + + /* Revert to register page 0 */ + ret = phy_marvell_alaska_write(dev, PHY_MRVL_COPPER_PAGE_SWITCH_REGISTER, + PHY_MRVL_BASE_REGISTERS_PAGE); + if (ret) { + LOG_ERR("%s: configure PHY: write Page Switch Register failed", dev->name); + return ret; + } + } + + /* + * Configure MDIX + * 88E151x: Page 0, register address 16 = Copper specific control register 1, + * 88E1111: Page any, register address 16 = PHY specific control register, + * bits [6..5] = MDIO crossover mode. Comp. datasheet table 76. + * NOTICE: a change of this value requires a subsequent software + * reset command via Copper Control Register's bit [15]. + */ + + /* [6..5] 11 = Enable auto cross over detection */ + ret = phy_marvell_alaska_read(dev, PHY_MRVL_COPPER_CONTROL_1_REGISTER, &phy_data); + if (ret) { + LOG_ERR("%s: configure PHY: read Copper spec. Control Register 1 failed", + dev->name); + return ret; + } + + phy_data &= ~(PHY_MRVL_MDIX_CONFIG_MASK << PHY_MRVL_MDIX_CONFIG_SHIFT); + phy_data |= (PHY_MRVL_MDIX_AUTO_CROSSOVER_ENABLE << PHY_MRVL_MDIX_CONFIG_SHIFT); + ret = phy_marvell_alaska_write(dev, PHY_MRVL_COPPER_CONTROL_1_REGISTER, phy_data); + if (ret) { + LOG_ERR("%s: configure PHY: write Copper spec. Control Register 1 failed", + dev->name); + return ret; + } + + /* Trigger a PHY Reset, affecting pages 0, 2, 3, 5, 7. */ + ret = phy_marvell_alaska_reset(dev); + if (ret) { + LOG_ERR("%s: configure PHY: reset PHY failed (2)", dev->name); + return ret; + } + + return 0; +} + +static int phy_marvell_alaska_cfg_link(const struct device *dev, enum phy_link_speed speeds) +{ + struct marvell_alaska_data *dev_data = dev->data; + + int ret = 0; + uint32_t phy_data; + uint32_t phy_data_gbit; + + ret = k_mutex_lock(&dev_data->mutex, K_FOREVER); + if (ret) { + LOG_ERR("%s: configure PHY link: mutex lock error", dev->name); + return ret; + } + + /* Cancel monitoring delayable work during link re-configuration */ + k_work_cancel_delayable(&dev_data->phy_monitor_work); + + /* Reset PHY */ + ret = phy_marvell_alaska_reset(dev); + if (ret) { + goto out; + } + + /* Common configuration items */ + ret = phy_marvell_alaska_static_cfg(dev); + if (ret) { + goto out; + } + + /* + * Advertise the link speed from the device configuration & perform + * auto-negotiation. This process involves: + * + * Page 0, register address 4 = + * Copper Auto-Negotiation Advertisement Register, + * Page 0, register address 0 = + * Copper Control Register, bit [15] = Reset -> apply all changes + * made regarding advertisement, + * Page 0, register address 9 = + * 1000BASE-T Control Register (if link speed = 1GBit/s), + * Page 0, register address 1 = + * Copper Status Register, bit [5] = Copper Auto-Negotiation + * Complete. + * + * Comp. datasheet tables 68 & 73. + */ + + phy_data = MII_ADVERTISE_SEL_IEEE_802_3; + + /* + * Read 1000BASE-T Control Register, mask out GBit bits, + * modify & write this register. 10/100 advertisement data + * in the ANAR register is assembled from scratch. + */ + ret = phy_marvell_alaska_read(dev, PHY_MRVL_1000BASET_CONTROL_REGISTER, &phy_data_gbit); + if (ret) { + goto out; + } + phy_data_gbit &= ~PHY_MRVL_ADVERTISE_1000_FULL; + phy_data_gbit &= ~PHY_MRVL_ADVERTISE_1000_HALF; + + if (speeds & LINK_FULL_1000BASE) { + phy_data_gbit |= PHY_MRVL_ADVERTISE_1000_FULL; + } + if (speeds & LINK_HALF_1000BASE) { + phy_data_gbit |= PHY_MRVL_ADVERTISE_1000_HALF; + } + if (speeds & LINK_FULL_100BASE) { + phy_data |= MII_ADVERTISE_100_FULL; + } + if (speeds & LINK_HALF_100BASE) { + phy_data |= MII_ADVERTISE_100_HALF; + } + if (speeds & LINK_FULL_10BASE) { + phy_data |= MII_ADVERTISE_10_FULL; + } + if (speeds & LINK_HALF_10BASE) { + phy_data |= MII_ADVERTISE_10_HALF; + } + + LOG_DBG("%s: configure PHY link: 1000CTRL 0x%08X ANAR 0x%08X", dev->name, phy_data_gbit, + phy_data); + + ret = phy_marvell_alaska_write(dev, PHY_MRVL_1000BASET_CONTROL_REGISTER, phy_data_gbit); + if (ret) { + goto out; + } + ret = phy_marvell_alaska_write(dev, PHY_MRVL_COPPER_AUTONEG_ADV_REGISTER, phy_data); + if (ret) { + goto out; + } + + /* Start auto-negotiation */ + ret = phy_marvell_alaska_autonegotiate(dev); + if (ret) { + LOG_ERR("%s: configure PHY link: auto-negotiation failed", dev->name); + } + +out: + k_mutex_unlock(&dev_data->mutex); + k_work_reschedule(&dev_data->phy_monitor_work, K_MSEC(CONFIG_PHY_MONITOR_PERIOD)); + + return ret; +} + +static int phy_marvell_alaska_get_link(const struct device *dev, struct phy_link_state *state) +{ + struct marvell_alaska_data *dev_data = dev->data; + + int ret; + uint32_t phy_data; + uint32_t speed; + bool fdx; + bool link_up; + + struct phy_link_state old_state = dev_data->state; + + ret = k_mutex_lock(&dev_data->mutex, K_FOREVER); + if (ret) { + LOG_ERR("%s: get PHY link state: mutex lock error", dev->name); + return ret; + } + + ret = phy_marvell_alaska_read(dev, PHY_MRVL_COPPER_STATUS_1_REGISTER, &phy_data); + if (ret) { + LOG_ERR("%s: get PHY link state: read Copper Specific Status Register 1 failed", + dev->name); + k_mutex_unlock(&dev_data->mutex); + return ret; + } + + /* + * Copper Specific Status Register 1 (88E15xx) / + * PHY Specific Status Register - Copper (88E1111): + * + * Link speed: [15 .. 14] + * 00b = 10 MBit/s + * 01b = 100 MBit/s + * 10b = 1 GBit/s + * Duplex: [13] + * 0b = half duplex + * 1b = full duplex + * Link State: [10] + * 0b = link down + * 1b = link up + */ + speed = (phy_data >> PHY_MRVL_LINK_SPEED_SHIFT) & PHY_MRVL_LINK_SPEED_MASK; + fdx = (phy_data & PHY_MRVL_LINK_DUPLEX_FDX) ? true : false; + link_up = (phy_data & PHY_MRVL_LINK_STATUS) ? true : false; + + if (speed == PHY_MRVL_LINK_SPEED_10MBIT) { + if (fdx) { + state->speed = LINK_FULL_10BASE; + } else { + state->speed = LINK_HALF_10BASE; + } + } else if (speed == PHY_MRVL_LINK_SPEED_100MBIT) { + if (fdx) { + state->speed = LINK_FULL_100BASE; + } else { + state->speed = LINK_HALF_100BASE; + } + } else if (speed == PHY_MRVL_LINK_SPEED_1GBIT) { + if (fdx) { + state->speed = LINK_FULL_1000BASE; + } else { + state->speed = LINK_HALF_1000BASE; + } + } + state->is_up = link_up; + + k_mutex_unlock(&dev_data->mutex); + + if (memcmp(&old_state, state, sizeof(struct phy_link_state)) != 0) { + LOG_DBG("%s: PHY link is %s", dev->name, state->is_up ? "up" : "down"); + if (state->is_up) { + LOG_DBG("%s: PHY configured for %s MBit/s %s", dev->name, + PHY_LINK_IS_SPEED_1000M(state->speed) ? "1000" + : PHY_LINK_IS_SPEED_100M(state->speed) ? "100" + : "10", + PHY_LINK_IS_FULL_DUPLEX(state->speed) ? "FDX" : "HDX"); + } + } + + return ret; +} + +static int phy_marvell_alaska_link_cb_set(const struct device *dev, phy_callback_t cb, + void *user_data) +{ + struct marvell_alaska_data *dev_data = dev->data; + + int ret = 0; + + ret = k_mutex_lock(&dev_data->mutex, K_FOREVER); + if (ret) { + LOG_ERR("%s: set link state callback: mutex lock error", dev->name); + return ret; + } + + dev_data->cb = cb; + dev_data->cb_data = user_data; + + k_mutex_unlock(&dev_data->mutex); + + /* Initial state propagation to the newly registered callback function */ + phy_marvell_alaska_get_link(dev, &dev_data->state); + dev_data->cb(dev, &dev_data->state, dev_data->cb_data); + + return 0; +} + +static void phy_marvell_alaska_monitor_work_handler(struct k_work *work) +{ + struct k_work_delayable *dwork = k_work_delayable_from_work(work); + struct marvell_alaska_data *dev_data = + CONTAINER_OF(dwork, struct marvell_alaska_data, phy_monitor_work); + const struct device *dev = dev_data->dev; + + struct phy_link_state state = {}; + int ret = phy_marvell_alaska_get_link(dev, &state); + + if (ret == 0 && memcmp(&state, &dev_data->state, sizeof(struct phy_link_state)) != 0) { + memcpy(&dev_data->state, &state, sizeof(struct phy_link_state)); + if (dev_data->cb) { + dev_data->cb(dev, &dev_data->state, dev_data->cb_data); + } + } + + k_work_reschedule(&dev_data->phy_monitor_work, K_MSEC(CONFIG_PHY_MONITOR_PERIOD)); +} + +static int phy_marvell_alaska_init(const struct device *dev) +{ + const struct marvell_alaska_config *const dev_conf = dev->config; + struct marvell_alaska_data *dev_data = dev->data; + + int ret; + + dev_data->dev = dev; + + ret = k_mutex_init(&dev_data->mutex); + if (ret) { + LOG_ERR("%s: init PHY: initialize mutex failed", dev->name); + return ret; + } + + mdio_bus_enable(dev_conf->mdio_dev); + + k_work_init_delayable(&dev_data->phy_monitor_work, phy_marvell_alaska_monitor_work_handler); + k_work_reschedule(&dev_data->phy_monitor_work, K_MSEC(CONFIG_PHY_MONITOR_PERIOD)); + + LOG_DBG("%s: init PHY: completed", dev->name); + + return 0; +} + +static DEVICE_API(ethphy, marvell_alaska_phy_api) = { + .get_link = phy_marvell_alaska_get_link, + .cfg_link = phy_marvell_alaska_cfg_link, + .link_cb_set = phy_marvell_alaska_link_cb_set, + .read = phy_marvell_alaska_read, + .write = phy_marvell_alaska_write, +}; + +#define PHY_MARVELL_ALASKA_DEV_CONFIG(n) \ + static const struct marvell_alaska_config marvell_alaska_##n##_config = { \ + .mdio_dev = DEVICE_DT_GET(DT_INST_PARENT(n)), \ + .addr = DT_INST_REG_ADDR(n), \ + }; + +#define PHY_MARVELL_ALASKA_DEV_DATA(n) \ + static struct marvell_alaska_data marvell_alaska_##n##_data = { \ + .phy_id = 0, \ + }; + +#define PHY_MARVELL_ALASKA_DEV_INIT(n) \ + DEVICE_DT_INST_DEFINE(n, phy_marvell_alaska_init, NULL, &marvell_alaska_##n##_data, \ + &marvell_alaska_##n##_config, POST_KERNEL, CONFIG_PHY_INIT_PRIORITY, \ + &marvell_alaska_phy_api); + +#define PHY_MARVELL_ALASKA_INITIALIZE(n) \ + PHY_MARVELL_ALASKA_DEV_CONFIG(n); \ + PHY_MARVELL_ALASKA_DEV_DATA(n); \ + PHY_MARVELL_ALASKA_DEV_INIT(n); + +DT_INST_FOREACH_STATUS_OKAY(PHY_MARVELL_ALASKA_INITIALIZE) diff --git a/drivers/ethernet/phy/phy_ti_dp83822.c b/drivers/ethernet/phy/phy_ti_dp83822.c new file mode 100644 index 0000000000000..e3d7218b8544d --- /dev/null +++ b/drivers/ethernet/phy/phy_ti_dp83822.c @@ -0,0 +1,473 @@ +/* + * Copyright (c) 2021 Weidmueller Interface GmbH & Co. KG + * Copyright (c) 2025 Immo Birnbaum + * + * Definitions and procedures moved here from the former proprietary + * Xilinx GEM PHY driver (phy_xlnx_gem.c), adjusted to use the MDIO + * framework provided by Zephyr. The TI DP83825 driver was used as a + * template, which is (c) Bernhard Kraemer. + * + * Register IDs & procedures are based on the corresponding datasheet: + * https://www.ti.com/lit/ds/symlink/dp83822i.pdf + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +#define DT_DRV_COMPAT ti_dp83822 +#define LOG_MODULE_NAME phy_ti_dp83822 + +#define LOG_LEVEL CONFIG_PHY_LOG_LEVEL +#include +LOG_MODULE_REGISTER(LOG_MODULE_NAME); + +#define PHY_TI_CONTROL_REGISTER_1 0x0009 +#define PHY_TI_PHY_STATUS_REGISTER 0x0010 +#define PHY_TI_MII_INTERRUPT_STATUS_REGISTER_1 0x0012 +#define PHY_TI_LED_CONTROL_REGISTER 0x0018 +#define PHY_TI_PHY_CONTROL_REGISTER 0x0019 + +#define PHY_TI_CR1_ROBUST_AUTO_MDIX_BIT BIT(5) + +#define PHY_TI_PHY_CONTROL_AUTO_MDIX_ENABLE_BIT BIT(15) +#define PHY_TI_PHY_CONTROL_FORCE_MDIX_BIT BIT(14) +#define PHY_TI_PHY_CONTROL_LED_CONFIG_LINK_ONLY_BIT BIT(5) + +#define PHY_TI_LED_CONTROL_BLINK_RATE_SHIFT 9 +#define PHY_TI_LED_CONTROL_BLINK_RATE_20HZ 0 +#define PHY_TI_LED_CONTROL_BLINK_RATE_10HZ 1 +#define PHY_TI_LED_CONTROL_BLINK_RATE_5HZ 2 +#define PHY_TI_LED_CONTROL_BLINK_RATE_2HZ 3 + +#define PHY_TI_PHY_STATUS_LINK_BIT BIT(0) +#define PHY_TI_PHY_STATUS_SPEED_BIT BIT(1) +#define PHY_TI_PHY_STATUS_DUPLEX_BIT BIT(2) + +struct ti_dp83822_config { + const struct device *mdio_dev; + uint8_t addr; +}; + +struct ti_dp83822_data { + const struct device *dev; + + struct phy_link_state state; + phy_callback_t cb; + void *cb_data; + + struct k_mutex mutex; + struct k_work_delayable phy_monitor_work; +}; + +static int phy_ti_dp83822_read(const struct device *dev, uint16_t reg_addr, uint32_t *data) +{ + const struct ti_dp83822_config *const dev_conf = dev->config; + + /* Make sure excessive bits 16-31 are reset */ + *data = 0U; + + return mdio_read(dev_conf->mdio_dev, dev_conf->addr, reg_addr, (uint16_t *)data); +} + +static int phy_ti_dp83822_write(const struct device *dev, uint16_t reg_addr, uint32_t data) +{ + const struct ti_dp83822_config *const dev_conf = dev->config; + + return mdio_write(dev_conf->mdio_dev, dev_conf->addr, reg_addr, (uint16_t)data); +} + +static int phy_ti_dp83822_reset(const struct device *dev) +{ + const struct ti_dp83822_config *const dev_conf = dev->config; + + int ret = 0; + uint32_t phy_data; + uint32_t poll_cnt = 0; + + ret = phy_ti_dp83822_read(dev, MII_BMCR, &phy_data); + if (ret) { + LOG_ERR("%s: reset PHY: read BCMR failed", dev->name); + return ret; + } + + phy_data |= MII_BMCR_RESET; + ret = phy_ti_dp83822_write(dev, MII_BMCR, phy_data); + if (ret) { + LOG_ERR("%s: reset PHY: write BCMR failed", dev->name); + return ret; + } + + while (((phy_data & MII_BMCR_RESET) != 0) && (poll_cnt++ < 10)) { + ret = phy_ti_dp83822_read(dev, MII_BMCR, &phy_data); + if (ret) { + LOG_ERR("%s: reset PHY: read BCMR (poll completion) failed", dev->name); + return ret; + } + } + if (poll_cnt == 10) { + LOG_ERR("%s: reset PHY: reset completion timed out", dev->name); + ret = -ETIMEDOUT; + } + + return ret; +} + +static int phy_ti_dp83822_autonegotiate(const struct device *dev) +{ + const struct ti_dp83822_config *const dev_conf = dev->config; + + int ret; + uint32_t bmcr; + + ret = phy_ti_dp83822_read(dev, MII_BMCR, &bmcr); + if (ret) { + LOG_ERR("%s: trigger auto-neg: read BMCR failed", dev->name); + return ret; + } + + LOG_DBG("%s: triggering PHY link auto-negotiation", dev->name); + bmcr |= MII_BMCR_AUTONEG_ENABLE | MII_BMCR_AUTONEG_RESTART; + bmcr &= ~MII_BMCR_ISOLATE; + + ret = phy_ti_dp83822_write(dev, MII_BMCR, bmcr); + if (ret) { + LOG_ERR("%s: trigger auto-neg: write BMCR failed", dev->name); + return ret; + } + + return 0; +} + +static int phy_ti_dp83822_static_cfg(const struct device *dev) +{ + const struct ti_dp83822_config *const dev_conf = dev->config; + + int ret; + uint32_t phy_data; + uint32_t phy_id; + + /* Read PHY ID */ + ret = phy_ti_dp83822_read(dev, MII_PHYID1R, &phy_data); + if (ret) { + LOG_ERR("%s: configure PHY: read PHYID1R failed", dev->name); + return ret; + } + phy_id = ((phy_data << 16) & 0xFFFF0000); + ret = phy_ti_dp83822_read(dev, MII_PHYID2R, &phy_data); + if (ret) { + LOG_ERR("%s: configure PHY: read PHYID2R failed", dev->name); + return ret; + } + phy_id |= (phy_data & 0x0000FFFF); + + if (phy_id == 0 || phy_id == 0xFFFFFFFF) { + LOG_ERR("%s: configure PHY: no reply from PHY while reading PHY ID", dev->name); + return -EIO; + } + + LOG_DBG("%s: configure PHY: read PHY ID 0x%08X", dev->name, phy_id); + + /* Enable auto-negotiation */ + ret = phy_ti_dp83822_read(dev, MII_BMCR, &phy_data); + if (ret) { + LOG_ERR("%s: configure PHY: read BMCR failed", dev->name); + return ret; + } + phy_data |= MII_BMCR_AUTONEG_ENABLE; + ret = phy_ti_dp83822_write(dev, MII_BMCR, phy_data); + if (ret) { + LOG_ERR("%s: configure PHY: write BMCR failed", dev->name); + return ret; + } + + /* Robust Auto MDIX */ + ret = phy_ti_dp83822_read(dev, PHY_TI_CONTROL_REGISTER_1, &phy_data); + if (ret) { + LOG_ERR("%s: configure PHY: read CR1 failed", dev->name); + return ret; + } + phy_data |= PHY_TI_CR1_ROBUST_AUTO_MDIX_BIT; + ret = phy_ti_dp83822_write(dev, PHY_TI_CONTROL_REGISTER_1, phy_data); + if (ret) { + LOG_ERR("%s: configure PHY: write CR1 failed", dev->name); + return ret; + } + + ret = phy_ti_dp83822_read(dev, PHY_TI_PHY_CONTROL_REGISTER, &phy_data); + if (ret) { + LOG_ERR("%s: configure PHY: read PHYCR failed", dev->name); + return ret; + } + /* Auto MDIX enable */ + phy_data |= PHY_TI_PHY_CONTROL_AUTO_MDIX_ENABLE_BIT; + /* Link LED shall only indicate link up or down, no RX/TX activity */ + phy_data |= PHY_TI_PHY_CONTROL_LED_CONFIG_LINK_ONLY_BIT; + /* Force MDIX disable */ + phy_data &= ~PHY_TI_PHY_CONTROL_FORCE_MDIX_BIT; + ret = phy_ti_dp83822_write(dev, PHY_TI_PHY_CONTROL_REGISTER, phy_data); + if (ret) { + LOG_ERR("%s: configure PHY: write PHYCR failed", dev->name); + return ret; + } + + /* Set blink rate to 5 Hz */ + phy_data = (PHY_TI_LED_CONTROL_BLINK_RATE_5HZ << PHY_TI_LED_CONTROL_BLINK_RATE_SHIFT); + ret = phy_ti_dp83822_write(dev, PHY_TI_LED_CONTROL_REGISTER, phy_data); + if (ret) { + LOG_ERR("%s: configure PHY: write LEDCR failed", dev->name); + } + + return ret; +} + +static int phy_ti_dp83822_cfg_link(const struct device *dev, enum phy_link_speed speeds) +{ + const struct ti_dp83822_config *const dev_conf = dev->config; + struct ti_dp83822_data *dev_data = dev->data; + + int ret = 0; + uint32_t anar = MII_ADVERTISE_SEL_IEEE_802_3; + + ret = k_mutex_lock(&dev_data->mutex, K_FOREVER); + if (ret) { + LOG_ERR("%s: configure PHY link: mutex lock error", dev->name); + return ret; + } + + /* Cancel monitoring delayable work during link re-configuration */ + k_work_cancel_delayable(&dev_data->phy_monitor_work); + + /* Reset PHY */ + ret = phy_ti_dp83822_reset(dev); + if (ret) { + goto out; + } + + /* Common configuration items */ + ret = phy_ti_dp83822_static_cfg(dev); + if (ret) { + goto out; + } + + /* Configure Auto-Negotiation Advertisement Register (ANAR) */ + ret = phy_ti_dp83822_read(dev, MII_ANAR, &anar); + if (ret) { + LOG_ERR("%s: configure PHY link: read ANAR failed", dev->name); + goto out; + } + + /* Set link configuration(s) to be advertised in ANAR */ + if (speeds & LINK_FULL_100BASE) { + anar |= MII_ADVERTISE_100_FULL; + } else { + anar &= ~MII_ADVERTISE_100_FULL; + } + + if (speeds & LINK_HALF_100BASE) { + anar |= MII_ADVERTISE_100_HALF; + } else { + anar &= ~MII_ADVERTISE_100_HALF; + } + + if (speeds & LINK_FULL_10BASE) { + anar |= MII_ADVERTISE_10_FULL; + } else { + anar &= ~MII_ADVERTISE_10_FULL; + } + + if (speeds & LINK_HALF_10BASE) { + anar |= MII_ADVERTISE_10_HALF; + } else { + anar &= ~MII_ADVERTISE_10_HALF; + } + + /* Write assembled ANAR contents */ + ret = phy_ti_dp83822_write(dev, MII_ANAR, anar); + if (ret) { + LOG_ERR("%s: configure PHY link: write ANAR failed", dev->name); + goto out; + } + + /* Start auto-negotiation */ + ret = phy_ti_dp83822_autonegotiate(dev); + if (ret) { + LOG_ERR("%s: configure PHY link: auto-negotiation failed", dev->name); + } + +out: + k_mutex_unlock(&dev_data->mutex); + k_work_reschedule(&dev_data->phy_monitor_work, K_MSEC(CONFIG_PHY_MONITOR_PERIOD)); + + return ret; +} + +static int phy_ti_dp83822_get_link(const struct device *dev, struct phy_link_state *state) +{ + const struct ti_dp83822_config *const dev_conf = dev->config; + struct ti_dp83822_data *dev_data = dev->data; + + int ret; + uint32_t physts; + + struct phy_link_state old_state = dev_data->state; + + ret = k_mutex_lock(&dev_data->mutex, K_FOREVER); + if (ret) { + LOG_ERR("%s: get PHY link state: mutex lock error", dev->name); + return ret; + } + + /* Get link state from PHYSTS */ + ret = phy_ti_dp83822_read(dev, PHY_TI_PHY_STATUS_REGISTER, &physts); + if (ret) { + LOG_ERR("%s: get PHY link state: read PHYSTS failed", dev->name); + k_mutex_unlock(&dev_data->mutex); + return ret; + } + + /* + * Get link status from PHYSTS: + * [0] Link: 1 = up, 0 = down + * (Mirrored from BMSR) + */ + state->is_up = physts & PHY_TI_PHY_STATUS_LINK_BIT; + if (!state->is_up) { + k_mutex_unlock(&dev_data->mutex); + goto result; + } + + /* + * Get link speed and duplex from PHYSTS: + * [2] Duplex: 1 = full, 0 = half + * [1] Speed: 1 = 100 MBit/s, 0 = 10 MBit/s + */ + if (physts & PHY_TI_PHY_STATUS_DUPLEX_BIT) { + if (physts & PHY_TI_PHY_STATUS_SPEED_BIT) { + state->speed = LINK_FULL_100BASE; + } else { + state->speed = LINK_HALF_100BASE; + } + } else { + if (physts & PHY_TI_PHY_STATUS_SPEED_BIT) { + state->speed = LINK_FULL_10BASE; + } else { + state->speed = LINK_HALF_10BASE; + } + } + + k_mutex_unlock(&dev_data->mutex); + +result: + if (memcmp(&old_state, state, sizeof(struct phy_link_state)) != 0) { + LOG_DBG("%s: PHY link is %s", dev->name, state->is_up ? "up" : "down"); + if (state->is_up) { + LOG_DBG("%s: PHY configured for %s MBit/s %s", dev->name, + PHY_LINK_IS_SPEED_100M(state->speed) ? "100" : "10", + PHY_LINK_IS_FULL_DUPLEX(state->speed) ? "FDX" : "HDX"); + } + } + + return ret; +} + +static int phy_ti_dp83822_link_cb_set(const struct device *dev, phy_callback_t cb, void *user_data) +{ + const struct ti_dp83822_config *const dev_conf = dev->config; + struct ti_dp83822_data *dev_data = dev->data; + + int ret = 0; + + ret = k_mutex_lock(&dev_data->mutex, K_FOREVER); + if (ret) { + LOG_ERR("%s: set link state callback: mutex lock error", dev->name); + return ret; + } + + dev_data->cb = cb; + dev_data->cb_data = user_data; + + k_mutex_unlock(&dev_data->mutex); + + /* Initial state propagation to the newly registered callback function */ + phy_ti_dp83822_get_link(dev, &dev_data->state); + dev_data->cb(dev, &dev_data->state, dev_data->cb_data); + + return 0; +} + +static void phy_ti_dp83822_monitor_work_handler(struct k_work *work) +{ + struct k_work_delayable *dwork = k_work_delayable_from_work(work); + struct ti_dp83822_data *dev_data = + CONTAINER_OF(dwork, struct ti_dp83822_data, phy_monitor_work); + const struct device *dev = dev_data->dev; + + struct phy_link_state state = {}; + int ret = phy_ti_dp83822_get_link(dev, &state); + + if (ret == 0 && memcmp(&state, &dev_data->state, sizeof(struct phy_link_state)) != 0) { + memcpy(&dev_data->state, &state, sizeof(struct phy_link_state)); + if (dev_data->cb) { + dev_data->cb(dev, &dev_data->state, dev_data->cb_data); + } + } + + k_work_reschedule(&dev_data->phy_monitor_work, K_MSEC(CONFIG_PHY_MONITOR_PERIOD)); +} + +static int phy_ti_dp83822_init(const struct device *dev) +{ + const struct ti_dp83822_config *const dev_conf = dev->config; + struct ti_dp83822_data *dev_data = dev->data; + + int ret; + + dev_data->dev = dev; + + ret = k_mutex_init(&dev_data->mutex); + if (ret) { + LOG_ERR("%s: init PHY: initialize mutex failed", dev->name); + return ret; + } + + mdio_bus_enable(dev_conf->mdio_dev); + + k_work_init_delayable(&dev_data->phy_monitor_work, phy_ti_dp83822_monitor_work_handler); + k_work_reschedule(&dev_data->phy_monitor_work, K_MSEC(CONFIG_PHY_MONITOR_PERIOD)); + + LOG_DBG("%s: init PHY: completed", dev->name); + + return 0; +} + +static DEVICE_API(ethphy, ti_dp83822_phy_api) = { + .get_link = phy_ti_dp83822_get_link, + .cfg_link = phy_ti_dp83822_cfg_link, + .link_cb_set = phy_ti_dp83822_link_cb_set, + .read = phy_ti_dp83822_read, + .write = phy_ti_dp83822_write, +}; + +#define PHY_TI_DP83822_DEV_CONFIG(n) \ + static const struct ti_dp83822_config ti_dp83822_##n##_config = { \ + .mdio_dev = DEVICE_DT_GET(DT_INST_PARENT(n)), \ + .addr = DT_INST_REG_ADDR(n), \ + }; + +#define PHY_TI_DP83822_DEV_DATA(n) static struct ti_dp83822_data ti_dp83822_##n##_data; + +#define PHY_TI_DP83822_DEV_INIT(n) \ + DEVICE_DT_INST_DEFINE(n, phy_ti_dp83822_init, NULL, &ti_dp83822_##n##_data, \ + &ti_dp83822_##n##_config, POST_KERNEL, CONFIG_PHY_INIT_PRIORITY, \ + &ti_dp83822_phy_api); + +#define PHY_TI_DP83822_INITIALIZE(n) \ + PHY_TI_DP83822_DEV_CONFIG(n); \ + PHY_TI_DP83822_DEV_DATA(n); \ + PHY_TI_DP83822_DEV_INIT(n); + +DT_INST_FOREACH_STATUS_OKAY(PHY_TI_DP83822_INITIALIZE) diff --git a/dts/bindings/ethernet/marvell,88e1xxx-phy.yaml b/dts/bindings/ethernet/marvell,88e1xxx-phy.yaml new file mode 100644 index 0000000000000..0fe9dca6208d4 --- /dev/null +++ b/dts/bindings/ethernet/marvell,88e1xxx-phy.yaml @@ -0,0 +1,15 @@ +# Copyright (c) 2025 Immo Birnbaum +# SPDX-License-Identifier: Apache-2.0 + +description: Marvell Alaska 88E1xxx 1 GBit/s PHY family + +compatible: "marvell,88e1xxx" + +include: phy.yaml + +on-bus: mdio + +properties: + reg: + required: true + description: 5-bit physical/port address (PRTAD). diff --git a/dts/bindings/ethernet/ti,dp83822-phy.yaml b/dts/bindings/ethernet/ti,dp83822-phy.yaml new file mode 100644 index 0000000000000..06dcea6e33202 --- /dev/null +++ b/dts/bindings/ethernet/ti,dp83822-phy.yaml @@ -0,0 +1,16 @@ +# Copyright (c) 2025 Immo Birnbaum +# SPDX-License-Identifier: Apache-2.0 + +description: | + Texas Instruments DP83822 100 MBit/s PHY + +compatible: "ti,dp83822" + +include: ethernet-phy.yaml + +on-bus: mdio + +properties: + reg: + required: true + description: 5-bit physical/port address (PRTAD). From defbbb80376b0683bfce13600cc6067402db862c Mon Sep 17 00:00:00 2001 From: Immo Birnbaum Date: Tue, 25 Feb 2025 10:08:20 +0100 Subject: [PATCH 9/9] boards: qemu_cortex_a9: add MDIO and PHY enable MDIO in the board's Kconfig, specify the Ethernet operational mode for QEMU, add/configure MDIO/PHY nodes in the device tree. Signed-off-by: Immo Birnbaum --- boards/qemu/cortex_a9/Kconfig.defconfig | 3 +++ boards/qemu/cortex_a9/qemu_cortex_a9.dts | 12 +++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/boards/qemu/cortex_a9/Kconfig.defconfig b/boards/qemu/cortex_a9/Kconfig.defconfig index c2051fe54087f..83330b8ffc145 100644 --- a/boards/qemu/cortex_a9/Kconfig.defconfig +++ b/boards/qemu/cortex_a9/Kconfig.defconfig @@ -29,6 +29,9 @@ if NETWORKING config NET_L2_ETHERNET default y +config MDIO + default y + config NET_TX_STACK_SIZE default 8192 diff --git a/boards/qemu/cortex_a9/qemu_cortex_a9.dts b/boards/qemu/cortex_a9/qemu_cortex_a9.dts index 8a12ba3080299..02f5d163b34ed 100644 --- a/boards/qemu/cortex_a9/qemu_cortex_a9.dts +++ b/boards/qemu/cortex_a9/qemu_cortex_a9.dts @@ -57,7 +57,17 @@ &gem0 { status = "okay"; + phy-handle = <&gem0_phy>; clock-frequency = <1000000000>; - mdc-divider = ; local-mac-address = [00 00 00 01 02 03]; }; + +&gem0_mdio { + status = "okay"; + + gem0_phy: phy@1 { + compatible = "marvell,88e1xxx"; + reg = <1>; + status = "okay"; + }; +};