diff --git a/boards/nxp/frdm_imx93/frdm_imx93_mimx9352_a55.dts b/boards/nxp/frdm_imx93/frdm_imx93_mimx9352_a55.dts index 3427d06707afd..05ba8dab11282 100644 --- a/boards/nxp/frdm_imx93/frdm_imx93_mimx9352_a55.dts +++ b/boards/nxp/frdm_imx93/frdm_imx93_mimx9352_a55.dts @@ -82,6 +82,33 @@ }; }; +&enet { + status = "okay"; +}; + +&enet_mac { + pinctrl-0 = <&pinmux_enet>; + pinctrl-names = "default"; + phy-handle = <&phy>; + zephyr,random-mac-address; + phy-connection-type = "rgmii"; + status = "okay"; +}; + +&enet_mdio { + pinctrl-0 = <&pinmux_mdio>; + pinctrl-names = "default"; + status = "okay"; + + phy: phy@2 { + compatible = "motorcomm,yt8521"; + reg = <2>; + status = "okay"; + motorcomm,rx-delay-sel = <13>; + motorcomm,tx-delay-sel = <13>; + }; +}; + &lpuart2 { current-speed = <115200>; pinctrl-0 = <&uart2_default>; diff --git a/drivers/ethernet/phy/CMakeLists.txt b/drivers/ethernet/phy/CMakeLists.txt index 38efd692c2bae..a0846fd0c9959 100644 --- a/drivers/ethernet/phy/CMakeLists.txt +++ b/drivers/ethernet/phy/CMakeLists.txt @@ -8,6 +8,7 @@ zephyr_library_sources_ifdef(CONFIG_PHY_DM8806 phy_dm8806.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_MOTORCOMM_YT8521 phy_motorcomm_yt8521.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) diff --git a/drivers/ethernet/phy/Kconfig b/drivers/ethernet/phy/Kconfig index 5cb3b336499e5..39b242b846a42 100644 --- a/drivers/ethernet/phy/Kconfig +++ b/drivers/ethernet/phy/Kconfig @@ -129,4 +129,12 @@ config PHY_VERIFY_DEVICE_IDENTIFICATION Verify the organizationally unique identifier that is reported by the phy chip. +config PHY_MOTORCOMM_YT8521 + bool "Motorcomm YT8521 Ethernet PHY Driver" + default y + depends on DT_HAS_MOTORCOMM_YT8521_ENABLED + select MDIO + help + Enable Motorcomm YT8521 Ethernet PHY Driver + endif # "Ethernet PHY Drivers" diff --git a/drivers/ethernet/phy/phy_motorcomm_yt8521.c b/drivers/ethernet/phy/phy_motorcomm_yt8521.c new file mode 100644 index 0000000000000..e40725dba0494 --- /dev/null +++ b/drivers/ethernet/phy/phy_motorcomm_yt8521.c @@ -0,0 +1,696 @@ +/* + * Copyright 2025 NXP + * + * Inspiration from phy_mii.c, which is: + * Copyright (c) 2021 IP-Logix Inc. + * Copyright 2022 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT motorcomm_yt8521 + +#include +LOG_MODULE_REGISTER(phy_motorcomm_yt8521, CONFIG_PHY_LOG_LEVEL); + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "phy_mii.h" + +#define ANY_DYNAMIC_LINK UTIL_NOT(DT_ALL_INST_HAS_PROP_STATUS_OKAY(fixed_link)) +#define ANY_FIXED_LINK DT_ANY_INST_HAS_PROP_STATUS_OKAY(fixed_link) + +#define PHY_ID_YT8521 (0x0000011A) + +/* PHY Specific Status Register */ +#define SPEC_STATUS_REG_DUPLEX_MASK (1U << 13) +#define PHY_DUPLEX_HALF (0U << 13) +#define PHY_DUPLEX_FULL (1U << 13) + +#define SPEC_STATUS_REG_SPEED_MASK (0x3U << 14) +#define PHY_SPEED_10M (0U << 14) +#define PHY_SPEED_100M (1U << 14) +#define PHY_SPEED_1000M (2U << 14) + +/* Specific Status Register */ +#define YTPHY_SPECIFIC_STATUS_REG 0x11 +#define YTPHY_SSR_LINK BIT(10) + +/* Extended Register's Address Offset Register */ +#define YTPHY_PAGE_SELECT 0x1E +/* Extended Register's Data Register */ +#define YTPHY_PAGE_DATA 0x1F + +#define YT8521_REG_SPACE_SELECT_REG 0xA000 + +#define YT8521_CHIP_CONFIG_REG 0xA001 +#define YT8521_CCR_RXC_DLY_EN BIT(8) + +#define YT8521_EXTREG_SLEEP_CONTROL1_REG 0x27 +#define YT8521_ESC1R_SLEEP_SW BIT(15) + +#define YT8521_RSSR_UTP_SPACE (0x0 << 1) + +#define YT8521_RGMII_CONFIG1_REG 0xA003 +#define YT8521_RC1R_RX_DELAY_MASK GENMASK(13, 10) +#define YT8521_RC1R_TX_DELAY_MASK GENMASK(3, 0) + +#define YTPHY_WOL_CONFIG_REG 0xA00A +#define YTPHY_WCR_ENABLE BIT(3) + +#define YTPHY_SYNCE_CFG_REG 0xA012 +#define YT8521_SCR_SYNCE_ENABLE BIT(5) + +static int mc_ytphy_get_link_state(const struct device *dev, struct phy_link_state *state); + +struct mc_ytphy_config { + uint8_t phy_addr; + bool fixed; + int fixed_speed; + const struct device *mdio; + uint8_t rx_delay_sel; + uint8_t tx_delay_sel; +}; + +struct mc_ytphy_data { + const struct device *dev; + phy_callback_t cb; + void *cb_data; + struct phy_link_state state; + struct k_sem sem; +#if ANY_DYNAMIC_LINK + struct k_work_delayable monitor_work; + bool autoneg_in_progress; + k_timepoint_t autoneg_timeout; +#endif +}; + +/* How often to poll auto-negotiation status while waiting for it to complete */ +#define MII_AUTONEG_POLL_INTERVAL_MS 100 + +static int mc_ytphy_read(const struct device *dev, uint16_t reg, uint32_t *data) +{ + const struct mc_ytphy_config *config = dev->config; + + /* Make sure excessive bits 16-31 are reset */ + *data = 0U; + + /* Read the PHY register */ + return mdio_read(config->mdio, config->phy_addr, reg, (uint16_t *)data); +} + +static int mc_ytphy_write(const struct device *dev, uint16_t reg, uint32_t data) +{ + const struct mc_ytphy_config *config = dev->config; + + return mdio_write(config->mdio, config->phy_addr, reg, (uint16_t)data); +} + +static int mc_ytphy_modify(const struct device *dev, uint16_t reg, uint16_t mask, uint16_t set) +{ + uint32_t data = 0; + uint32_t new = 0; + int ret; + + ret = mc_ytphy_read(dev, reg, &data); + if (ret) { + return ret; + } + + new = (data & ~mask) | set; + if (new == data) { + return 0; + } + + return mc_ytphy_write(dev, reg, new); +} + +static int mc_ytphy_read_ext(const struct device *dev, uint16_t reg, uint32_t *data) +{ + int ret; + + ret = mc_ytphy_write(dev, YTPHY_PAGE_SELECT, reg); + if (ret) { + return ret; + } + + return mc_ytphy_read(dev, YTPHY_PAGE_DATA, data); +} + +static int mc_ytphy_write_ext(const struct device *dev, uint16_t reg, uint32_t data) +{ + int ret; + + ret = mc_ytphy_write(dev, YTPHY_PAGE_SELECT, reg); + if (ret) { + return ret; + } + + return mc_ytphy_write(dev, YTPHY_PAGE_DATA, data); +} + +static int mc_ytphy_modify_ext(const struct device *dev, uint16_t reg, uint16_t mask, uint16_t set) +{ + int ret; + + ret = mc_ytphy_write(dev, YTPHY_PAGE_SELECT, reg); + if (ret) { + return ret; + } + + return mc_ytphy_modify(dev, YTPHY_PAGE_DATA, mask, set); +} + +static int mc_ytphy_soft_reset(const struct device *dev) +{ + int max_cnt = 500; /* max time of reset ~500ms */ + uint32_t data; + int ret; + + ret = mc_ytphy_modify(dev, MII_BMCR, 0, MII_BMCR_RESET); + if (ret) { + return ret; + } + + while (max_cnt--) { + k_msleep(1); + + ret = mc_ytphy_read(dev, MII_BMCR, &data); + if (ret) { + break; + } + + if (!(data & MII_BMCR_RESET)) { + ret = 0; + break; + } + } + + return ret; +} + +static int mc_ytphy_cfg_clock_delay(const struct device *dev) +{ + const struct mc_ytphy_config *const cfg = dev->config; + uint16_t mask, val = 0; + int ret; + + ret = mc_ytphy_modify_ext(dev, YT8521_CHIP_CONFIG_REG, YT8521_CCR_RXC_DLY_EN, 0); + if (ret) { + return ret; + } + + mask = YT8521_RC1R_RX_DELAY_MASK | YT8521_RC1R_TX_DELAY_MASK; + + val |= FIELD_PREP(YT8521_RC1R_RX_DELAY_MASK, cfg->rx_delay_sel); + val |= FIELD_PREP(YT8521_RC1R_TX_DELAY_MASK, cfg->tx_delay_sel); + + return mc_ytphy_modify_ext(dev, YT8521_RGMII_CONFIG1_REG, mask, val); +} + +static int mc_ytphy_resume(const struct device *dev) +{ + uint32_t wol_config; + int ret; + + /* disable auto sleep */ + ret = mc_ytphy_modify_ext(dev, YT8521_EXTREG_SLEEP_CONTROL1_REG, YT8521_ESC1R_SLEEP_SW, 0); + if (ret) { + return ret; + } + + ret = mc_ytphy_read_ext(dev, YTPHY_WOL_CONFIG_REG, &wol_config); + if (ret) { + return ret; + } + + /* if wol enable, do nothing */ + if (wol_config & YTPHY_WCR_ENABLE) { + return 0; + } + + return mc_ytphy_modify(dev, MII_BMCR, MII_BMCR_POWER_DOWN, 0); +} + +static void invoke_link_cb(const struct device *dev) +{ + struct mc_ytphy_data *const data = dev->data; + struct phy_link_state state; + + if (data->cb == NULL) { + return; + } + + mc_ytphy_get_link_state(dev, &state); + + data->cb(data->dev, &state, data->cb_data); +} + +#if ANY_FIXED_LINK +static inline uint16_t mii_bmcr_encode_fixed(enum phy_link_speed speed) +{ + uint16_t bmcr; + + if (PHY_LINK_IS_SPEED_1000M(speed)) { + bmcr = MII_BMCR_SPEED_1000; + } else if (PHY_LINK_IS_SPEED_100M(speed)) { + bmcr = MII_BMCR_SPEED_100; + } else { + bmcr = MII_BMCR_SPEED_10; + } + + if (PHY_LINK_IS_FULL_DUPLEX(speed)) { + bmcr |= MII_BMCR_DUPLEX_MODE; + } + + return bmcr; +} +#endif /* ANY_FIXED_LINK */ + +#if ANY_DYNAMIC_LINK +static inline enum phy_link_speed mc_ytphy_get_link_speed_stat_reg(const struct device *dev, + uint16_t stat_reg) +{ + enum phy_link_speed speed; + + switch (stat_reg & (SPEC_STATUS_REG_SPEED_MASK | SPEC_STATUS_REG_DUPLEX_MASK)) { + case PHY_SPEED_10M | PHY_DUPLEX_FULL: + speed = LINK_FULL_10BASE; + break; + case PHY_SPEED_10M | PHY_DUPLEX_HALF: + speed = LINK_HALF_10BASE; + break; + case PHY_SPEED_100M | PHY_DUPLEX_FULL: + speed = LINK_FULL_100BASE; + break; + case PHY_SPEED_100M | PHY_DUPLEX_HALF: + speed = LINK_HALF_100BASE; + break; + case PHY_SPEED_1000M | PHY_DUPLEX_FULL: + speed = LINK_FULL_1000BASE; + break; + case PHY_SPEED_1000M | PHY_DUPLEX_HALF: + speed = LINK_HALF_1000BASE; + break; + default: + speed = 0; + break; + } + + return speed; +} + +static int update_link_state(const struct device *dev) +{ + const struct mc_ytphy_config *const cfg = dev->config; + struct mc_ytphy_data *const data = dev->data; + uint32_t stat_reg; + uint32_t bmcr_reg; + bool link_up; + + if (mc_ytphy_read(dev, YTPHY_SPECIFIC_STATUS_REG, &stat_reg) < 0) { + return -EIO; + } + + link_up = (uint16_t)stat_reg & YTPHY_SSR_LINK; + + /* If link is down, we can stop here. */ + if (!link_up) { + data->state.speed = 0; + if (link_up != data->state.is_up) { + data->state.is_up = false; + LOG_INF("PHY (%d) is down", cfg->phy_addr); + return 0; + } + return -EAGAIN; + } + + if (mc_ytphy_read(dev, MII_BMCR, &bmcr_reg) < 0) { + return -EIO; + } + + /* If auto-negotiation is not enabled, we only need to check the link speed */ + if ((bmcr_reg & MII_BMCR_AUTONEG_ENABLE) == 0U) { + enum phy_link_speed new_speed = mc_ytphy_get_link_speed_stat_reg(dev, stat_reg); + + if ((data->state.speed != new_speed) || !data->state.is_up) { + data->state.is_up = true; + data->state.speed = new_speed; + + LOG_INF("PHY (%d) Link speed %s Mb, %s duplex", cfg->phy_addr, + PHY_LINK_IS_SPEED_1000M(data->state.speed) ? "1000" + : (PHY_LINK_IS_SPEED_100M(data->state.speed) ? "100" + : "10"), + PHY_LINK_IS_FULL_DUPLEX(data->state.speed) ? "full" : "half"); + + return 0; + } + return -EAGAIN; + } + + /* If auto-negotiation is enabled and the link was already up last time we checked, + * we can return immediately, as the link state has not changed. + * If the link was down, we will start the auto-negotiation sequence. + */ + if (data->state.is_up) { + return -EAGAIN; + } + + data->state.is_up = true; + + LOG_DBG("PHY (%d) Starting MII PHY auto-negotiate sequence", cfg->phy_addr); + + data->autoneg_timeout = sys_timepoint_calc(K_MSEC(CONFIG_PHY_AUTONEG_TIMEOUT_MS)); + return -EINPROGRESS; +} + +static int check_autonegotiation_completion(const struct device *dev) +{ + const struct mc_ytphy_config *const cfg = dev->config; + struct mc_ytphy_data *const data = dev->data; + + uint32_t stat_reg = 0; + uint32_t bmsr_reg = 0; + + /* On some PHY chips, the BMSR bits are latched, so the first read may + * show incorrect status. A second read ensures correct values. + */ + if (mc_ytphy_read(dev, MII_BMSR, &bmsr_reg) < 0) { + return -EIO; + } + + /* Second read, clears the latched bits and gives the correct status */ + if (mc_ytphy_read(dev, MII_BMSR, &bmsr_reg) < 0) { + return -EIO; + } + + if ((bmsr_reg & MII_BMSR_AUTONEG_COMPLETE) == 0U) { + if (sys_timepoint_expired(data->autoneg_timeout)) { + LOG_DBG("PHY (%d) auto-negotiate timeout", cfg->phy_addr); + return -ETIMEDOUT; + } + return -EINPROGRESS; + } + + LOG_DBG("PHY (%d) auto-negotiate sequence completed", cfg->phy_addr); + + if (mc_ytphy_read(dev, YTPHY_SPECIFIC_STATUS_REG, &stat_reg) < 0) { + return -EIO; + } + + data->state.speed = mc_ytphy_get_link_speed_stat_reg(dev, stat_reg); + + data->state.is_up = (bmsr_reg & MII_BMSR_LINK_STATUS) != 0U; + + LOG_INF("PHY (%d) Link speed %s Mb, %s duplex", cfg->phy_addr, + PHY_LINK_IS_SPEED_1000M(data->state.speed) + ? "1000" + : (PHY_LINK_IS_SPEED_100M(data->state.speed) ? "100" : "10"), + PHY_LINK_IS_FULL_DUPLEX(data->state.speed) ? "full" : "half"); + + return 0; +} + +static void monitor_work_handler(struct k_work *work) +{ + struct k_work_delayable *dwork = k_work_delayable_from_work(work); + struct mc_ytphy_data *const data = CONTAINER_OF(dwork, struct mc_ytphy_data, monitor_work); + const struct device *dev = data->dev; + int rc; + + if (k_sem_take(&data->sem, K_NO_WAIT) == 0) { + if (data->autoneg_in_progress) { + rc = check_autonegotiation_completion(dev); + } else { + /* If autonegotiation is not in progress, just update the link state */ + rc = update_link_state(dev); + } + + data->autoneg_in_progress = (rc == -EINPROGRESS); + + k_sem_give(&data->sem); + + /* If link state has changed and a callback is set, invoke callback */ + if (rc == 0) { + invoke_link_cb(dev); + } + } + + k_work_reschedule(&data->monitor_work, data->autoneg_in_progress + ? K_MSEC(MII_AUTONEG_POLL_INTERVAL_MS) + : K_MSEC(CONFIG_PHY_MONITOR_PERIOD)); +} + +static int mc_ytphy_cfg_link(const struct device *dev, enum phy_link_speed adv_speeds, + enum phy_cfg_link_flag flags) +{ + struct mc_ytphy_data *const data = dev->data; + const struct mc_ytphy_config *const cfg = dev->config; + int ret = 0; + + k_sem_take(&data->sem, K_FOREVER); + + if ((flags & PHY_FLAG_AUTO_NEGOTIATION_DISABLED) != 0U) { + ret = phy_mii_set_bmcr_reg_autoneg_disabled(dev, adv_speeds); + if (ret >= 0) { + data->autoneg_in_progress = false; + k_work_reschedule(&data->monitor_work, K_NO_WAIT); + } + } else { + ret = phy_mii_cfg_link_autoneg(dev, adv_speeds, true); + if (ret >= 0) { + LOG_DBG("PHY (%d) Starting MII PHY auto-negotiate sequence", cfg->phy_addr); + data->autoneg_in_progress = true; + data->autoneg_timeout = + sys_timepoint_calc(K_MSEC(CONFIG_PHY_AUTONEG_TIMEOUT_MS)); + k_work_reschedule(&data->monitor_work, + K_MSEC(MII_AUTONEG_POLL_INTERVAL_MS)); + } + } + + if (ret == -EALREADY) { + LOG_DBG("PHY (%d) Link already configured", cfg->phy_addr); + } + + k_sem_give(&data->sem); + + return ret; + +} +#endif /* ANY_DYNAMIC_LINK */ + +static int mc_ytphy_get_link_state(const struct device *dev, struct phy_link_state *state) +{ + struct mc_ytphy_data *const data = dev->data; + + k_sem_take(&data->sem, K_FOREVER); + + memcpy(state, &data->state, sizeof(struct phy_link_state)); + + if (state->speed == 0) { + /* If speed is 0, then link is also down, happens when autonegotiation is in + * progress + */ + state->is_up = false; + } + + k_sem_give(&data->sem); + + return 0; +} + +static int mc_ytphy_link_cb_set(const struct device *dev, phy_callback_t cb, void *user_data) +{ + struct mc_ytphy_data *const data = dev->data; + + data->cb = cb; + data->cb_data = user_data; + + /** + * Immediately invoke the callback to notify the caller of the + * current link status. + */ + invoke_link_cb(dev); + + return 0; +} + +static int mc_ytphy_get_id(const struct device *dev, uint32_t *phy_id) +{ + const struct mc_ytphy_config *const config = dev->config; + uint32_t cnt = 1000; + uint32_t val = 0; + + do { + if (mc_ytphy_read(dev, MII_PHYID2R, &val) < 0) { + k_msleep(1); + } + } while (val != PHY_ID_YT8521 && --cnt > 0); + + if (cnt == 0U) { + LOG_ERR("PHY (%d) timeout to get PHY ID", config->phy_addr); + return -EIO; + } + + if (val != PHY_ID_YT8521) { + LOG_ERR("PHY (%d) can't get PHY ID, value:0x%X", config->phy_addr, val); + return -EIO; + } + + if (phy_id) { + *phy_id = val; + } else { + LOG_INF("PHY (%d) ID:0x%X", config->phy_addr, val); + } + + return 0; +} + +static int mc_ytphy_init(const struct device *dev) +{ + const struct mc_ytphy_config *const config = dev->config; + struct mc_ytphy_data *const data = dev->data; + int ret; + + k_sem_init(&data->sem, 1, 1); + + mdio_bus_enable(config->mdio); + + data->state.is_up = false; + data->dev = dev; + data->cb = NULL; + + if (mc_ytphy_get_id(dev, NULL)) { + return -EIO; + } + + /* set default reg space */ + mc_ytphy_write_ext(dev, YT8521_REG_SPACE_SELECT_REG, YT8521_RSSR_UTP_SPACE); + + mc_ytphy_modify_ext(dev, YTPHY_SYNCE_CFG_REG, YT8521_SCR_SYNCE_ENABLE, 0); + + /* Reset PHY */ + ret = mc_ytphy_soft_reset(dev); + if (ret) { + return -EIO; + } + + /* Enable clock delay */ + mc_ytphy_cfg_clock_delay(dev); + + mc_ytphy_resume(dev); + + return 0; +} + +#if ANY_FIXED_LINK +static int mc_ytphy_initialize_fixed_link(const struct device *dev) +{ + const struct mc_ytphy_config *const cfg = dev->config; + struct mc_ytphy_data *const data = dev->data; + uint16_t mask, bmcr_val = 0; + int ret; + + const static int speed_to_phy_link_speed[] = { + LINK_HALF_10BASE, LINK_FULL_10BASE, LINK_HALF_100BASE, + LINK_FULL_100BASE, LINK_HALF_1000BASE, LINK_FULL_1000BASE, + }; + + ret = mc_ytphy_init(dev); + if (ret < 0) { + return ret; + } + + data->state.speed = speed_to_phy_link_speed[cfg->fixed_speed]; + + bmcr_val = mii_bmcr_encode_fixed(data->state.speed); + LOG_INF("PHY (%d) Fixed link speed %s Mb, %s duplex", cfg->phy_addr, + PHY_LINK_IS_SPEED_1000M(data->state.speed) + ? "1000" + : (PHY_LINK_IS_SPEED_100M(data->state.speed) ? "100" : "10"), + PHY_LINK_IS_FULL_DUPLEX(data->state.speed) ? "full" : "half"); + + mask = 0xFFFF & ~(MII_BMCR_LOOPBACK | MII_BMCR_ISOLATE | MII_BMCR_POWER_DOWN); + mc_ytphy_modify(dev, MII_BMCR, mask, bmcr_val); + + data->state.is_up = true; + + return 0; +} +#endif /* ANY_FIXED_LINK */ + +#if ANY_DYNAMIC_LINK +static int mc_ytphy_initialize_dynamic_link(const struct device *dev) +{ + struct mc_ytphy_data *const data = dev->data; + uint32_t phy_feature; + int ret = 0; + + ret = mc_ytphy_init(dev); + if (ret < 0) { + return ret; + } + + data->state.is_up = false; + + k_work_init_delayable(&data->monitor_work, monitor_work_handler); + + /* Auto negotiation, advertise all supported speeds */ + phy_feature = LINK_FULL_100BASE | LINK_HALF_100BASE | LINK_FULL_10BASE + | LINK_HALF_10BASE | LINK_FULL_1000BASE; + mc_ytphy_cfg_link(dev, phy_feature, 0); + + /* This will schedule the monitor work, if not already scheduled by mc_ytphy_cfg_link(). */ + k_work_schedule(&data->monitor_work, K_NO_WAIT); + + return 0; +} +#endif /* ANY_DYNAMIC_LINK */ + +static DEVICE_API(ethphy, mc_ytphy_driver_api) = { + .get_link = mc_ytphy_get_link_state, + .link_cb_set = mc_ytphy_link_cb_set, +#if ANY_DYNAMIC_LINK + .cfg_link = mc_ytphy_cfg_link, + .read = mc_ytphy_read, + .write = mc_ytphy_write, +#endif +}; + +#define MC_YTPHY_CONFIG(n) \ + static const struct mc_ytphy_config mc_ytphy_config_##n = { \ + .phy_addr = DT_INST_REG_ADDR(n), \ + .fixed = DT_INST_NODE_HAS_PROP(n, fixed_link), \ + .fixed_speed = DT_INST_ENUM_IDX_OR(n, fixed_link, 0), \ + .mdio = DEVICE_DT_GET(DT_INST_BUS(n)), \ + .rx_delay_sel = DT_INST_PROP_OR(n, motorcomm_rx_delay_sel, 0), \ + .tx_delay_sel = DT_INST_PROP_OR(n, motorcomm_tx_delay_sel, 0), \ + }; + +#define MC_YTPHY_DATA(n) \ + static struct mc_ytphy_data mc_ytphy_data_##n = { \ + .dev = DEVICE_DT_INST_GET(n), \ + .cb = NULL, \ + .sem = Z_SEM_INITIALIZER(mc_ytphy_data_##n.sem, 1, 1), \ + }; + +#if ANY_DYNAMIC_LINK +#define MC_YTPHY_INIT &mc_ytphy_initialize_dynamic_link +#else +#define MC_YTPHY_INIT &mc_ytphy_initialize_fixed_link +#endif + +#define MC_YTPHY_DEVICE(n) \ + MC_YTPHY_CONFIG(n) \ + MC_YTPHY_DATA(n) \ + DEVICE_DT_INST_DEFINE(n, MC_YTPHY_INIT, NULL, &mc_ytphy_data_##n, &mc_ytphy_config_##n, \ + POST_KERNEL, CONFIG_PHY_INIT_PRIORITY, &mc_ytphy_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(MC_YTPHY_DEVICE) diff --git a/dts/bindings/ethernet/phy/motorcomm,yt8521.yaml b/dts/bindings/ethernet/phy/motorcomm,yt8521.yaml new file mode 100644 index 0000000000000..cd43e673f1099 --- /dev/null +++ b/dts/bindings/ethernet/phy/motorcomm,yt8521.yaml @@ -0,0 +1,20 @@ +# Copyright 2025 NXP +# SPDX-License-Identifier: Apache-2.0 + +description: Motorcomm YT8521 Ethernet PHY + +compatible: "motorcomm,yt8521" + +include: ethernet-phy.yaml + +properties: + motorcomm,rx-delay-sel: + type: int + description: | + RGMII RX_CLK delay train configuration, about 150ps per step. + See RGMII_Config1 (EXT_0xA003) register for details + motorcomm,tx-delay-sel: + type: int + description: | + RGMII RX_CLK delay train configuration, about 150ps per step. + See RGMII_Config1 (EXT_0xA003) register for details diff --git a/dts/bindings/vendor-prefixes.txt b/dts/bindings/vendor-prefixes.txt index db92063114842..d17e5794af178 100644 --- a/dts/bindings/vendor-prefixes.txt +++ b/dts/bindings/vendor-prefixes.txt @@ -445,6 +445,7 @@ miramems MiraMEMS Sensing Technology Co., Ltd. mitsubishi Mitsubishi Electric Corporation modtronix Modtronix Engineering mosaixtech Mosaix Technologies, Inc. +motorcomm Motorcomm Electronic Technology Co., Ltd. motorola Motorola, Inc. moxa Moxa Inc. mpl MPL AG