You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
980 lines
33 KiB
980 lines
33 KiB
/* |
|
* 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/kernel.h> |
|
#include <zephyr/device.h> |
|
|
|
#include "eth_xlnx_gem_priv.h" |
|
|
|
#define LOG_MODULE_NAME phy_xlnx_gem |
|
#define LOG_LEVEL CONFIG_ETHERNET_LOG_LEVEL |
|
#include <zephyr/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->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; |
|
}
|
|
|