|  | /* | 
|  | * 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 <zephyr.h> | 
|  | #include <device.h> | 
|  |  | 
|  | #include "eth_xlnx_gem_priv.h" | 
|  |  | 
|  | #define LOG_MODULE_NAME phy_xlnx_gem | 
|  | #define LOG_LEVEL CONFIG_ETHERNET_LOG_LEVEL | 
|  | #include <logging/log.h> | 
|  | 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_CFG(dev); | 
|  | struct eth_xlnx_gem_dev_data *dev_data = DEV_DATA(dev); | 
|  | 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_CFG(dev); | 
|  | struct eth_xlnx_gem_dev_data *dev_data = DEV_DATA(dev); | 
|  | 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: onfigure 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_CFG(dev); | 
|  | struct eth_xlnx_gem_dev_data *dev_data = DEV_DATA(dev); | 
|  | 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_CFG(dev); | 
|  | struct eth_xlnx_gem_dev_data *dev_data = DEV_DATA(dev); | 
|  | 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_CFG(dev); | 
|  | struct eth_xlnx_gem_dev_data *dev_data  = DEV_DATA(dev); | 
|  | 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_CFG(dev); | 
|  | struct eth_xlnx_gem_dev_data *dev_data = DEV_DATA(dev); | 
|  | 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_CFG(dev); | 
|  | struct eth_xlnx_gem_dev_data *dev_data = DEV_DATA(dev); | 
|  | 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_CFG(dev); | 
|  | struct eth_xlnx_gem_dev_data *dev_data = DEV_DATA(dev); | 
|  | 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_CFG(dev); | 
|  | struct eth_xlnx_gem_dev_data *dev_data = DEV_DATA(dev); | 
|  | 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_CFG(dev); | 
|  | struct eth_xlnx_gem_dev_data *dev_data = DEV_DATA(dev); | 
|  | 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_CFG(dev); | 
|  | struct eth_xlnx_gem_dev_data *dev_data = DEV_DATA(dev); | 
|  |  | 
|  | 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; | 
|  | } | 
|  |  | 
|  | /* EOF */ |