From f2f62b0dc33fe7a2cfa6dad67f83a83aacccf947 Mon Sep 17 00:00:00 2001 From: Robert Slawinski Date: Fri, 3 Jan 2025 15:06:40 +0100 Subject: [PATCH] drivers: dm8806: smi bus error check SMI bus error check mechanism is preventing the host SMI bus to be interferred by noise on board level. Current implementation is checking if data which is writing to/reading from the PHY has correct CRC sum. If not, then writing/rading process is repeated by the number of attempts defined in the KConfig. If repeating transmission will fail by the numbers of ettemps defined in KConfing, drivers returns an error. Signed-off-by: Robert Slawinski --- drivers/ethernet/phy/Kconfig.dm8806 | 23 +- drivers/ethernet/phy/phy_dm8806.c | 294 +++++++++++++++++++++---- drivers/ethernet/phy/phy_dm8806_priv.h | 143 +++++++----- 3 files changed, 355 insertions(+), 105 deletions(-) diff --git a/drivers/ethernet/phy/Kconfig.dm8806 b/drivers/ethernet/phy/Kconfig.dm8806 index bab9a5d0ad8..fc68709bba5 100644 --- a/drivers/ethernet/phy/Kconfig.dm8806 +++ b/drivers/ethernet/phy/Kconfig.dm8806 @@ -46,4 +46,25 @@ config PHY_DM8806_THREAD_STACK_SIZE help Stack size of thread used by the driver to handle interrupts. -endif # PHY_DM8806_TRIGGER +config PHY_DM8806_SMI_BUS_CHECK + bool "Host SMI bus error check function" + default y + help + This functionality prevents the host SMI bus to be interferered by the + noise on board-level. During write procedure, the written value in + register will be applied until the correct checksum is written. In read + procedure, the hardware calculated checksum is compared with the software + calculated one to detect correctness of received data. + +config PHY_DM8806_SMI_BUS_CHECK_REPETITION + int "SMI bus transmission repetitions" + depends on PHY_DM8806_SMI_BUS_CHECK + default 5 + help + The numbers of SMI bus transmission repetition in case if CRC checksum + fails during read or write. After this numbers of repetition, the reading + message is dropped and will not be passed to the system. In write + procedure, the written value in register will be applied until the correct + checksum is written to the PHY register. + +endif # PHY_DM8806 diff --git a/drivers/ethernet/phy/phy_dm8806.c b/drivers/ethernet/phy/phy_dm8806.c index 76ae387316d..edac250c25a 100644 --- a/drivers/ethernet/phy/phy_dm8806.c +++ b/drivers/ethernet/phy/phy_dm8806.c @@ -42,6 +42,204 @@ struct phy_dm8806_data { #endif }; +#ifdef CONFIG_PHY_DM8806_SMI_BUS_CHECK +static uint16_t phy_calculate_checksum(uint16_t data, uint16_t reg_addr, uint8_t opcode) +{ + uint16_t csum[8]; + uint16_t checksum = 0; + + /* Checksum calculated formula proposed by Davicom on datasheet: + * DM8806-DAVICOM - par. 7.2.1: + * Host SMI Bus Error Check Function, page 141. + */ + csum[0] = (((data >> 0) & 1) ^ ((data >> 8) & 1) ^ ((reg_addr >> 0) & 1) ^ + ((reg_addr >> 8) & 1)); + csum[1] = (((data >> 1) & 1) ^ ((data >> 9) & 1) ^ ((reg_addr >> 1) & 1) ^ + ((reg_addr >> 9) & 1)); + csum[2] = (((data >> 2) & 1) ^ ((data >> 10) & 1) ^ ((reg_addr >> 2) & 1) ^ + ((opcode >> 0) & 1)); + csum[3] = (((data >> 3) & 1) ^ ((data >> 11) & 1) ^ ((reg_addr >> 3) & 1) ^ + ((opcode >> 1) & 1)); + csum[4] = (((data >> 4) & 1) ^ ((data >> 12) & 1) ^ ((reg_addr >> 4) & 1)); + csum[5] = (((data >> 5) & 1) ^ ((data >> 13) & 1) ^ ((reg_addr >> 5) & 1)); + csum[6] = (((data >> 6) & 1) ^ ((data >> 14) & 1) ^ ((reg_addr >> 6) & 1)); + csum[7] = (((data >> 7) & 1) ^ ((data >> 15) & 1) ^ ((reg_addr >> 7) & 1)); + for (int cnt = 0; cnt < 8; cnt++) { + checksum |= (csum[cnt] << cnt); + } + return checksum; +} +#endif + +static int phy_dm8806_write_reg(const struct device *dev, uint8_t phyad, uint8_t regad, + uint16_t data) +{ + int res = 0; + const struct phy_dm8806_config *cfg = dev->config; + +/* SMI bus check function should be activated each time, before writing + * procedure to the DM8806 registers. This is standard procedure described in + * the datasheet of the DM8806. + */ +#ifdef CONFIG_PHY_DM8806_SMI_BUS_CHECK + uint16_t checksum_status; + bool checksum_mismatch; + uint16_t sw_checksum = 0; + uint16_t abs_reg; + int repetition = 0; + + do { + /* Set register 33AH.[0] = 1 to enable SMI Bus Error Check function. */ + res = mdio_write(cfg->mdio, DM8806_SMI_BUS_CTRL_PHY_ADDRESS, + DM8806_SMI_BUS_CTRL_REG_ADDRESS, DM8806_SMI_ECE); + if (res < 0) { + LOG_ERR("Failed to write data to PHY register: SMI_BUS_CTRL_REG_ADDRESS, " + "error code: %d", + res); + return res; + } +#endif + res = mdio_write(cfg->mdio, phyad, regad, data); + if (res < 0) { + LOG_ERR("Failed to read data from PHY, error code: %d", res); + return res; + } +#ifdef CONFIG_PHY_DM8806_SMI_BUS_CHECK + /* Calculate checksum */ + abs_reg = (phyad << DM8806_REGAD_WIDTH); + abs_reg |= (regad & BIT_MASK(DM8806_REGAD_WIDTH)); + sw_checksum = phy_calculate_checksum(data, abs_reg, DM8806_PHY_WRITE); + sw_checksum &= BIT_MASK(8); + /* Write calculated checksum to the PHY register 339H.[7:0] */ + res = mdio_write(cfg->mdio, DM8806_SMI_BUS_ERR_CHK_PHY_ADDRESS, + DM8806_SMI_BUS_ERR_CHK_REG_ADDRESS, sw_checksum); + if (res < 0) { + LOG_ERR("Failed to write calculated checksum to the PHY register, " + "error code: %d", + res); + return res; + } + + /* Read status of the checksum from Serial Bus Error Check Register + * 339H.[8]. + */ + res = mdio_read(cfg->mdio, DM8806_SMI_BUS_ERR_CHK_PHY_ADDRESS, + DM8806_SMI_BUS_ERR_CHK_REG_ADDRESS, &checksum_status); + if (res < 0) { + LOG_ERR("Failed to read hardware calculated checksum from PHY, error code: " + "%d", + res); + return res; + } + /* Checksum status is present on the 8-th bit of the Serial Bus Error + * Check Register (339h) [8]. + */ + checksum_mismatch = (bool)(checksum_status & BIT(DM8806_SMI_ERR)); + + /* Repeat the writing procedure for the number of attempts defined in + * KConfig after which the transfer will failed. + */ + if (CONFIG_PHY_DM8806_SMI_BUS_CHECK_REPETITION > 0) { + repetition++; + if (checksum_mismatch) { + LOG_WRN("%d repeat of PHY read procedure due to checksum error.", + repetition); + if (repetition >= CONFIG_PHY_DM8806_SMI_BUS_CHECK_REPETITION) { + LOG_ERR("Maximum number of PHY write repetition exceed."); + res = (-EIO); + } + } else { + break; + } + /* Do not repeat the transfer if repetition number is set to 0. Just check + * the checksum in this case and report the error in case of wrong checksum + * sum. + */ + } else { + if (checksum_mismatch) { + LOG_ERR("Wrong checksum, during PHY write procedure."); + res = (-EIO); + break; + } + } + } while (repetition < CONFIG_PHY_DM8806_SMI_BUS_CHECK_REPETITION); +#endif + + return res; +} + +static int phy_dm8806_read_reg(const struct device *dev, uint8_t phyad, uint8_t regad, + uint16_t *data) +{ + int res = 0; + const struct phy_dm8806_config *cfg = dev->config; + +/* SMI bus check function should be activated each time, before reading + * procedure to the DM8806 registers. This is standard procedure described in + * the datasheet of the DM8806. + */ +#ifdef CONFIG_PHY_DM8806_SMI_BUS_CHECK + uint16_t hw_checksum; + uint16_t sw_checksum = 0; + uint16_t abs_reg; + int repetition = 0; + + do { + /* Set register 33AH.[0] = 1 to enable SMI Bus Error Check function. */ + res = mdio_write(cfg->mdio, DM8806_SMI_BUS_CTRL_PHY_ADDRESS, + DM8806_SMI_BUS_CTRL_REG_ADDRESS, DM8806_SMI_ECE); + if (res < 0) { + LOG_ERR("Failed to write data to PHY register: SMI_BUS_CTRL_REG_ADDRESS, " + "error code: %d", + res); + return res; + } +#endif + res = mdio_read(cfg->mdio, phyad, regad, data); + if (res < 0) { + LOG_ERR("Failed to read data from PHY, error code: %d", res); + return res; + } +#ifdef CONFIG_PHY_DM8806_SMI_BUS_CHECK + /* Read hardware calculated checksum from Serial Bus Error Check Register. */ + res = mdio_read(cfg->mdio, DM8806_SMI_BUS_ERR_CHK_PHY_ADDRESS, + DM8806_SMI_BUS_ERR_CHK_REG_ADDRESS, &hw_checksum); + if (res < 0) { + LOG_ERR("Failed to read hardware calculated checksum from PHY, error code: " + "%d", + res); + return res; + } + /* Calculate checksum */ + abs_reg = (phyad << DM8806_REGAD_WIDTH); + abs_reg |= (regad & BIT_MASK(DM8806_REGAD_WIDTH)); + sw_checksum = phy_calculate_checksum(*data, abs_reg, DM8806_PHY_READ); + + if (CONFIG_PHY_DM8806_SMI_BUS_CHECK_REPETITION > 0) { + repetition++; + if (hw_checksum != sw_checksum) { + LOG_WRN("%d repeat of PHY read procedure due to checksum error.", + repetition); + if (repetition >= CONFIG_PHY_DM8806_SMI_BUS_CHECK_REPETITION) { + LOG_ERR("Maximum number of PHY read repetition exceed."); + res = (-EIO); + } + } else { + break; + } + } else { + if (hw_checksum != sw_checksum) { + LOG_ERR("Wrong checksum, during PHY read procedure."); + res = (-EIO); + break; + } + } + } while (repetition < CONFIG_PHY_DM8806_SMI_BUS_CHECK_REPETITION); +#endif + + return res; +} + static void phy_dm8806_gpio_callback(const struct device *dev, struct gpio_callback *cb, uint32_t pins) { @@ -66,9 +264,9 @@ static void phy_dm8806_thread_cb(const struct device *dev, struct phy_link_state /* Clear the interrupt flag, by writing "1" to LNKCHG bit of Interrupt Status * Register (318h) */ - mdio_read(cfg->mdio, INT_STAT_PHY_ADDR, INT_STAT_REG_ADDR, &data); + mdio_read(cfg->mdio, DM8806_INT_STAT_PHY_ADDR, DM8806_INT_STAT_REG_ADDR, &data); data |= 0x1; - mdio_write(cfg->mdio, INT_STAT_PHY_ADDR, INT_STAT_REG_ADDR, data); + mdio_write(cfg->mdio, DM8806_INT_STAT_PHY_ADDR, DM8806_INT_STAT_REG_ADDR, data); gpio_pin_interrupt_configure_dt(&cfg->gpio_int, GPIO_INT_EDGE_TO_ACTIVE); } @@ -90,13 +288,13 @@ int phy_dm8806_port_init(const struct device *dev) const struct phy_dm8806_config *cfg = dev->config; res = gpio_pin_configure_dt(&cfg->gpio_rst, (GPIO_OUTPUT_INACTIVE | GPIO_PULL_UP)); - if (res != 0) { + if (res < 0) { LOG_ERR("Failed to configure gpio reset pin for PHY DM886 as an output"); return res; } /* Hardware reset of the PHY DM8806 */ gpio_pin_set_dt(&cfg->gpio_rst, true); - if (res != 0) { + if (res < 0) { LOG_ERR("Failed to assert gpio reset pin of the PHY DM886 to physical 0"); return res; } @@ -105,7 +303,7 @@ int phy_dm8806_port_init(const struct device *dev) */ k_msleep(10); res = gpio_pin_set_dt(&cfg->gpio_rst, false); - if (res != 0) { + if (res < 0) { LOG_ERR("Failed to assert gpio reset pin of the PHY DM886 to physical 1"); return res; } @@ -125,13 +323,15 @@ int phy_dm8806_init_interrupt(const struct device *dev) * Activate global interrupt by writing "1" to LNKCHG of Interrupt Mask * And Control Register (319h) */ - res = mdio_read(cfg->mdio, INT_MASK_CTRL_PHY_ADDR, INT_MASK_CTRL_REG_ADDR, &data); + res = mdio_read(cfg->mdio, DM8806_INT_MASK_CTRL_PHY_ADDR, DM8806_INT_MASK_CTRL_REG_ADDR, + &data); if (res) { LOG_ERR("Failed to read IRQ_LED_CONTROL, %i", res); return res; } data |= 0x1; - res = mdio_write(cfg->mdio, INT_MASK_CTRL_PHY_ADDR, INT_MASK_CTRL_REG_ADDR, data); + res = mdio_write(cfg->mdio, DM8806_INT_MASK_CTRL_PHY_ADDR, DM8806_INT_MASK_CTRL_REG_ADDR, + data); if (res) { LOG_ERR("Failed to read IRQ_LED_CONTROL, %i", res); return res; @@ -140,13 +340,15 @@ int phy_dm8806_init_interrupt(const struct device *dev) /* Activate interrupt per Ethernet port by writing "1" to LNK_EN0~3 * of WoL Control Register (2BBh) */ - res = mdio_read(cfg->mdio, WOLL_CTRL_REG_PHY_ADDR, WOLL_CTRL_REG_REG_ADDR, &data); + res = mdio_read(cfg->mdio, DM8806_WOLL_CTRL_REG_PHY_ADDR, DM8806_WOLL_CTRL_REG_REG_ADDR, + &data); if (res) { LOG_ERR("Failed to read IRQ_LED_CONTROL, %i", res); return res; } data |= 0xF; - res = mdio_write(cfg->mdio, WOLL_CTRL_REG_PHY_ADDR, WOLL_CTRL_REG_REG_ADDR, data); + res = mdio_write(cfg->mdio, DM8806_WOLL_CTRL_REG_PHY_ADDR, DM8806_WOLL_CTRL_REG_REG_ADDR, + data); if (res) { LOG_ERR("Failed to read IRQ_LED_CONTROL, %i", res); return res; @@ -162,7 +364,7 @@ int phy_dm8806_init_interrupt(const struct device *dev) } drv_data->dev = dev; res = gpio_pin_configure_dt(&cfg->gpio_int, GPIO_INPUT); - if (res != 0) { + if (res < 0) { LOG_ERR("Failed to configure gpio interrupt pin for PHY DM886 as an input"); return res; } @@ -171,7 +373,7 @@ int phy_dm8806_init_interrupt(const struct device *dev) */ gpio_init_callback(&drv_data->gpio_cb, phy_dm8806_gpio_callback, BIT(cfg->gpio_int.pin)); res = gpio_add_callback(cfg->gpio_int.port, &drv_data->gpio_cb); - if (res != 0) { + if (res < 0) { LOG_ERR("Failed to set PHY DM886 gpio callback"); return res; } @@ -183,7 +385,7 @@ int phy_dm8806_init_interrupt(const struct device *dev) * level 1 asserted by Davicom PHY DM8806 interrupt Pin */ gpio_pin_interrupt_configure_dt(&cfg->gpio_int, GPIO_INT_EDGE_TO_ACTIVE); - if (res != 0) { + if (res < 0) { LOG_ERR("Failed to configure PHY DM886 gpio interrupt pin trigger for " "active edge"); return res; @@ -207,31 +409,31 @@ static int phy_dm8806_init(const struct device *dev) return ret; } - ret = mdio_read(cfg->mdio, PHY_ADDRESS_18H, PORT5_MAC_CONTROL, &val); + ret = mdio_read(cfg->mdio, DM8806_PHY_ADDRESS_18H, DM8806_PORT5_MAC_CONTROL, &val); if (ret) { LOG_ERR("Failed to read PORT5_MAC_CONTROL: %i", ret); return ret; } /* Activate default working mode*/ - val |= (P5_50M_INT_CLK_SOURCE | P5_50M_CLK_OUT_ENABLE | P5_EN_FORCE); - val &= (P5_SPEED_100M | P5_FULL_DUPLEX | P5_FORCE_LINK_ON); + val |= (DM8806_P5_50M_INT_CLK_SOURCE | DM8806_P5_50M_CLK_OUT_ENABLE | DM8806_P5_EN_FORCE); + val &= (DM8806_P5_SPEED_100M | DM8806_P5_FULL_DUPLEX | DM8806_P5_FORCE_LINK_ON); - ret = mdio_write(cfg->mdio, PHY_ADDRESS_18H, PORT5_MAC_CONTROL, val); + ret = mdio_write(cfg->mdio, DM8806_PHY_ADDRESS_18H, DM8806_PORT5_MAC_CONTROL, val); if (ret) { LOG_ERR("Failed to write PORT5_MAC_CONTROL, %i", ret); return ret; } - ret = mdio_read(cfg->mdio, PHY_ADDRESS_18H, IRQ_LED_CONTROL, &val); + ret = mdio_read(cfg->mdio, DM8806_PHY_ADDRESS_18H, DM8806_IRQ_LED_CONTROL, &val); if (ret) { LOG_ERR("Failed to read IRQ_LED_CONTROL, %i", ret); return ret; } /* Activate LED blinking mode indicator mode 0. */ - val &= LED_MODE_0; - ret = mdio_write(cfg->mdio, PHY_ADDRESS_18H, IRQ_LED_CONTROL, val); + val &= DM8806_LED_MODE_0; + ret = mdio_write(cfg->mdio, DM8806_PHY_ADDRESS_18H, DM8806_IRQ_LED_CONTROL, val); if (ret) { LOG_ERR("Failed to write IRQ_LED_CONTROL, %i", ret); return ret; @@ -262,7 +464,7 @@ static int phy_dm8806_get_link_state(const struct device *dev, struct phy_link_s } #endif /* Read data from Switch Per-Port Register. */ - ret = mdio_read(cfg->mdio, cfg->switch_addr, PORTX_SWITCH_STATUS, &data); + ret = phy_dm8806_read_reg(dev, cfg->switch_addr, DM8806_PORTX_SWITCH_STATUS, &data); if (ret) { LOG_ERR("Failes to read data drom DM8806 Switch Per-Port Registers area"); return ret; @@ -271,18 +473,18 @@ static int phy_dm8806_get_link_state(const struct device *dev, struct phy_link_s * Status Data Register */ status = data; - status >>= SPEED_AND_DUPLEX_OFFSET; - switch (status & SPEED_AND_DUPLEX_MASK) { - case SPEED_10MBPS_HALF_DUPLEX: + status >>= DM8806_SPEED_AND_DUPLEX_OFFSET; + switch (status & DM8806_SPEED_AND_DUPLEX_MASK) { + case DM8806_SPEED_10MBPS_HALF_DUPLEX: state->speed = LINK_HALF_10BASE_T; break; - case SPEED_10MBPS_FULL_DUPLEX: + case DM8806_SPEED_10MBPS_FULL_DUPLEX: state->speed = LINK_FULL_10BASE_T; break; - case SPEED_100MBPS_HALF_DUPLEX: + case DM8806_SPEED_100MBPS_HALF_DUPLEX: state->speed = LINK_HALF_100BASE_T; break; - case SPEED_100MBPS_FULL_DUPLEX: + case DM8806_SPEED_100MBPS_FULL_DUPLEX: state->speed = LINK_FULL_100BASE_T; break; } @@ -290,7 +492,7 @@ static int phy_dm8806_get_link_state(const struct device *dev, struct phy_link_s * Register */ status = data; - if (status & LINK_STATUS_MASK) { + if (status & DM8806_LINK_STATUS_MASK) { state->is_up = true; } else { state->is_up = false; @@ -308,31 +510,31 @@ static int phy_dm8806_cfg_link(const struct device *dev, enum phy_link_speed adv req_speed = adv_speeds; switch (req_speed) { case LINK_HALF_10BASE_T: - req_speed = MODE_10_BASET_HALF_DUPLEX; + req_speed = DM8806_MODE_10_BASET_HALF_DUPLEX; break; case LINK_FULL_10BASE_T: - req_speed = MODE_10_BASET_FULL_DUPLEX; + req_speed = DM8806_MODE_10_BASET_FULL_DUPLEX; break; case LINK_HALF_100BASE_T: - req_speed = MODE_100_BASET_HALF_DUPLEX; + req_speed = DM8806_MODE_100_BASET_HALF_DUPLEX; break; case LINK_FULL_100BASE_T: - req_speed = MODE_100_BASET_FULL_DUPLEX; + req_speed = DM8806_MODE_100_BASET_FULL_DUPLEX; break; } /* Power down */ - ret = mdio_read(cfg->mdio, cfg->phy_addr, PORTX_PHY_CONTROL_REGISTER, &data); + ret = phy_dm8806_read_reg(dev, cfg->phy_addr, DM8806_PORTX_PHY_CONTROL_REGISTER, &data); if (ret) { LOG_ERR("Failes to read data drom DM8806"); return ret; } k_busy_wait(500); - data |= POWER_DOWN; - ret = mdio_write(cfg->mdio, cfg->phy_addr, PORTX_PHY_CONTROL_REGISTER, data); + data |= DM8806_POWER_DOWN; + ret = phy_dm8806_write_reg(dev, cfg->phy_addr, DM8806_PORTX_PHY_CONTROL_REGISTER, data); if (ret) { LOG_ERR("Failed to write data to DM8806"); return ret; @@ -340,14 +542,14 @@ static int phy_dm8806_cfg_link(const struct device *dev, enum phy_link_speed adv k_busy_wait(500); /* Turn off the auto-negotiation process. */ - ret = mdio_read(cfg->mdio, cfg->phy_addr, PORTX_PHY_CONTROL_REGISTER, &data); + ret = phy_dm8806_read_reg(dev, cfg->phy_addr, DM8806_PORTX_PHY_CONTROL_REGISTER, &data); if (ret) { LOG_ERR("Failed to write data to DM8806"); return ret; } k_busy_wait(500); - data &= ~(AUTO_NEGOTIATION); - ret = mdio_write(cfg->mdio, cfg->phy_addr, PORTX_PHY_CONTROL_REGISTER, data); + data &= ~(DM8806_AUTO_NEGOTIATION); + ret = phy_dm8806_write_reg(dev, cfg->phy_addr, DM8806_PORTX_PHY_CONTROL_REGISTER, data); if (ret) { LOG_ERR("Failed to write data to DM8806"); return ret; @@ -355,15 +557,15 @@ static int phy_dm8806_cfg_link(const struct device *dev, enum phy_link_speed adv k_busy_wait(500); /* Change the link speed. */ - ret = mdio_read(cfg->mdio, cfg->phy_addr, PORTX_PHY_CONTROL_REGISTER, &data); + ret = phy_dm8806_read_reg(dev, cfg->phy_addr, DM8806_PORTX_PHY_CONTROL_REGISTER, &data); if (ret) { LOG_ERR("Failed to read data from DM8806"); return ret; } k_busy_wait(500); - data &= ~(LINK_SPEED | DUPLEX_MODE); + data &= ~(DM8806_LINK_SPEED | DM8806_DUPLEX_MODE); data |= req_speed; - ret = mdio_write(cfg->mdio, cfg->phy_addr, PORTX_PHY_CONTROL_REGISTER, data); + ret = phy_dm8806_write_reg(dev, cfg->phy_addr, DM8806_PORTX_PHY_CONTROL_REGISTER, data); if (ret) { LOG_ERR("Failed to write data to DM8806"); return ret; @@ -371,20 +573,20 @@ static int phy_dm8806_cfg_link(const struct device *dev, enum phy_link_speed adv k_busy_wait(500); /* Power up ethernet port*/ - ret = mdio_read(cfg->mdio, cfg->phy_addr, PORTX_PHY_CONTROL_REGISTER, &data); + ret = phy_dm8806_read_reg(dev, cfg->phy_addr, DM8806_PORTX_PHY_CONTROL_REGISTER, &data); if (ret) { LOG_ERR("Failes to read data drom DM8806"); return ret; } k_busy_wait(500); - data &= ~(POWER_DOWN); - ret = mdio_write(cfg->mdio, cfg->phy_addr, PORTX_PHY_CONTROL_REGISTER, data); + data &= ~(DM8806_POWER_DOWN); + ret = phy_dm8806_write_reg(dev, cfg->phy_addr, DM8806_PORTX_PHY_CONTROL_REGISTER, data); if (ret) { LOG_ERR("Failed to write data to DM8806"); return ret; } k_busy_wait(500); - return -ENOTSUP; + return ret; } static int phy_dm8806_reg_read(const struct device *dev, uint16_t reg_addr, uint32_t *data) @@ -420,14 +622,14 @@ static int phy_dm8806_link_cb_set(const struct device *dev, phy_callback_t cb, v const struct phy_dm8806_config *cfg = dev->config; res = gpio_pin_interrupt_configure_dt(&cfg->gpio_int, GPIO_INT_DISABLE); - if (res != 0) { + if (res < 0) { LOG_WRN("Failed to disable DM8806 interrupt: %i", res); return res; } data->link_speed_chenge_cb = cb; data->cb_data = user_data; gpio_pin_interrupt_configure_dt(&cfg->gpio_int, GPIO_INT_EDGE_TO_ACTIVE); - if (res != 0) { + if (res < 0) { LOG_WRN("Failed to enable DM8806 interrupt: %i", res); return res; } diff --git a/drivers/ethernet/phy/phy_dm8806_priv.h b/drivers/ethernet/phy/phy_dm8806_priv.h index 4c4a292cadf..0efe3b962b9 100644 --- a/drivers/ethernet/phy/phy_dm8806_priv.h +++ b/drivers/ethernet/phy/phy_dm8806_priv.h @@ -4,142 +4,169 @@ * SPDX-License-Identifier: Apache-2.0 */ +/* The absolute PHY address is 10 bit length. Last 5 bits for oldest + * part of address - see Clause 22 of IEEE 802.3. + */ +#define DM8806_REGAD_WIDTH 0x5u + +/* Mask for checksum error. Checksum status is present on the 8-th bit of the + * Serial Bus Error Check Register (339h) [8] + */ +#define DM8806_SMI_ERR 8 + +/* PHY read opcode as part of the MDIO frame in Clause 22 */ +#define DM8806_PHY_READ 0x2u +/* PHY write opcode as part of the MDIO frame in Clause 22 */ +#define DM8806_PHY_WRITE 0x1u + /* Port 0~4 PHY Control Register. */ -#define PORTX_PHY_CONTROL_REGISTER 0x0u +#define DM8806_PORTX_PHY_CONTROL_REGISTER 0x0u /* 10 Mbit/s transfer with half duplex mask. */ -#define MODE_10_BASET_HALF_DUPLEX 0x0u +#define DM8806_MODE_10_BASET_HALF_DUPLEX 0x0u /* 10 Mbit/s transfer with full duplex mask. */ -#define MODE_10_BASET_FULL_DUPLEX 0x100u +#define DM8806_MODE_10_BASET_FULL_DUPLEX 0x100u /* 100 Mbit/s transfer with half duplex mask. */ -#define MODE_100_BASET_HALF_DUPLEX 0x2000u +#define DM8806_MODE_100_BASET_HALF_DUPLEX 0x2000u /* 100 Mbit/s transfer with full duplex mask. */ -#define MODE_100_BASET_FULL_DUPLEX 0x2100u +#define DM8806_MODE_100_BASET_FULL_DUPLEX 0x2100u /* Duplex mode ability offset. */ -#define DUPLEX_MODE (1 << 8) +#define DM8806_DUPLEX_MODE (1 << 8) /* Power down mode offset. */ -#define POWER_DOWN (1 << 11) +#define DM8806_POWER_DOWN (1 << 11) /* Auto negotiation mode offset. */ -#define AUTO_NEGOTIATION (1 << 12) +#define DM8806_AUTO_NEGOTIATION (1 << 12) /* Link speed selection offset. */ -#define LINK_SPEED (1 << 13) +#define DM8806_LINK_SPEED (1 << 13) /* Port 0~4 Status Data Register. */ -#define PORTX_SWITCH_STATUS 0x10u +#define DM8806_PORTX_SWITCH_STATUS 0x10u /* 10 Mbit/s transfer speed with half duplex. */ -#define SPEED_10MBPS_HALF_DUPLEX 0x00u +#define DM8806_SPEED_10MBPS_HALF_DUPLEX 0x00u /* 10 Mbit/s transfer speed with full duplex. */ -#define SPEED_10MBPS_FULL_DUPLEX 0x01u +#define DM8806_SPEED_10MBPS_FULL_DUPLEX 0x01u /* 100 Mbit/s transfer speed with half duplex. */ -#define SPEED_100MBPS_HALF_DUPLEX 0x02u +#define DM8806_SPEED_100MBPS_HALF_DUPLEX 0x02u /* 100 Mbit/s transfer speed with full duplex. */ -#define SPEED_100MBPS_FULL_DUPLEX 0x03u +#define DM8806_SPEED_100MBPS_FULL_DUPLEX 0x03u /* Speed and duplex mode status offset. */ -#define SPEED_AND_DUPLEX_OFFSET 0x01u +#define DM8806_SPEED_AND_DUPLEX_OFFSET 0x01u /* Speed and duplex mode staus mask. */ -#define SPEED_AND_DUPLEX_MASK 0x07u +#define DM8806_SPEED_AND_DUPLEX_MASK 0x07u /* Link status mask. */ -#define LINK_STATUS_MASK 0x1u +#define DM8806_LINK_STATUS_MASK 0x1u /* Switch Engine Registers */ /* Address Table Control And Status Register PHY Address */ -#define ADDR_TAB_CTRL_STAT_PHY_ADDR 0x15u +#define DM8806_ADDR_TAB_CTRL_STAT_PHY_ADDR 0x15u /* Address Table Control And Status Register Register SAddress */ -#define ADDR_TAB_CTRL_STAT_REG_ADDR 0x10u +#define DM8806_ADDR_TAB_CTRL_STAT_REG_ADDR 0x10u /* Address Table Access bussy flag offset */ -#define ATB_S_OFFSET 0xf +#define DM8806_ATB_S_OFFSET 0xf /* Address Table Command Result flag offset */ -#define ATB_CR_OFFSET 0xd +#define DM8806_ATB_CR_OFFSET 0xd /* Address Table Command Result flag mask */ -#define ATB_CR_MASK 0x3 +#define DM8806_ATB_CR_MASK 0x3 /* Unicast Address Table Index*/ -#define UNICAST_ADDR_TAB (1 << 0 | 1 << 1) +#define DM8806_UNICAST_ADDR_TAB (1 << 0 | 1 << 1) /* Multicast Address Table Index*/ -#define MULTICAST_ADDR_TAB (1 << 0) +#define DM8806_MULTICAST_ADDR_TAB (1 << 0) /* IGMP Table Index*/ -#define IGMP_ADDR_TAB (1 << 1) +#define DM8806_IGMP_ADDR_TAB (1 << 1) /* Read a entry with sequence number of address table */ -#define ATB_CMD_READ (1 << 2 | 1 << 3 | 1 << 4) +#define DM8806_ATB_CMD_READ (1 << 2 | 1 << 3 | 1 << 4) /* Write a entry with MAC address */ -#define ATB_CMD_WRITE (1 << 2) +#define DM8806_ATB_CMD_WRITE (1 << 2) /* Delete a entry with MAC address */ -#define ATB_CMD_DELETE (1 << 3) +#define DM8806_ATB_CMD_DELETE (1 << 3) /* Search a entry with MAC address */ -#define ATB_CMD_SEARCH (1 << 2 | 1 << 3) +#define DM8806_ATB_CMD_SEARCH (1 << 2 | 1 << 3) /* Clear one or more than one entries with Port or FID */ -#define ATB_CMD_CLEAR (1 << 4) +#define DM8806_ATB_CMD_CLEAR (1 << 4) /* Address Table Data 0 PHY Address */ -#define ADDR_TAB_DATA0_PHY_ADDR 0x15u +#define DM8806_ADDR_TAB_DATA0_PHY_ADDR 0x15u /* Address Table Data 0 Register Address */ -#define ADDR_TAB_DATA0_REG_ADDR 0x11u +#define DM8806_ADDR_TAB_DATA0_REG_ADDR 0x11u /* Port number or port map mask*/ -#define ATB_PORT_MASK 0x1f +#define DM8806_ATB_PORT_MASK 0x1f /* Address Table Data 1 PHY Address */ -#define ADDR_TAB_DATA1_PHY_ADDR 0x15u +#define DM8806_ADDR_TAB_DATA1_PHY_ADDR 0x15u /* Address Table Data 1 Register Address */ -#define ADDR_TAB_DATA1_REG_ADDR 0x12u +#define DM8806_ADDR_TAB_DATA1_REG_ADDR 0x12u /* Address Table Data 2 PHY Address */ -#define ADDR_TAB_DATA2_PHY_ADDR 0x15u +#define DM8806_ADDR_TAB_DATA2_PHY_ADDR 0x15u /* Address Table Data 2 Register Address */ -#define ADDR_TAB_DATA2_REG_ADDR 0x13u +#define DM8806_ADDR_TAB_DATA2_REG_ADDR 0x13u /* Address Table Data 3 PHY Address */ -#define ADDR_TAB_DATA3_PHY_ADDR 0x15u +#define DM8806_ADDR_TAB_DATA3_PHY_ADDR 0x15u /* Address Table Data 3 Register Address */ -#define ADDR_TAB_DATA3_REG_ADDR 0x14u +#define DM8806_ADDR_TAB_DATA3_REG_ADDR 0x14u /* Address Table Data 4 PHY Address */ -#define ADDR_TAB_DATA4_PHY_ADDR 0x15u +#define DM8806_ADDR_TAB_DATA4_PHY_ADDR 0x15u /* Address Table Data 4 Register Address */ -#define ADDR_TAB_DATA4_REG_ADDR 0x15u +#define DM8806_ADDR_TAB_DATA4_REG_ADDR 0x15u /* WoL Control Register PHY Address */ -#define WOLL_CTRL_REG_PHY_ADDR 0x15u +#define DM8806_WOLL_CTRL_REG_PHY_ADDR 0x15u /* WoL Control Register Register Address */ -#define WOLL_CTRL_REG_REG_ADDR 0x1bu +#define DM8806_WOLL_CTRL_REG_REG_ADDR 0x1bu + +/* Serial Bus Error Check PHY Address. */ +#define DM8806_SMI_BUS_ERR_CHK_PHY_ADDRESS 0x19u +/* Serial Bus Error Check Register Address. */ +#define DM8806_SMI_BUS_ERR_CHK_REG_ADDRESS 0x19u + +/* Serial Bus Control PHY Address. */ +#define DM8806_SMI_BUS_CTRL_PHY_ADDRESS 0x19u +/* Serial Bus Control Register Address. */ +#define DM8806_SMI_BUS_CTRL_REG_ADDRESS 0x1au +/* SMI Bus Error Check Enable. */ +#define DM8806_SMI_ECE BIT(0) /* PHY address 0x18h */ -#define PHY_ADDRESS_18H 0x18u +#define DM8806_PHY_ADDRESS_18H 0x18u /* Interrupt Status Register PHY Address. */ -#define INT_STAT_PHY_ADDR 0x18u +#define DM8806_INT_STAT_PHY_ADDR 0x18u /* Interrupt Status Register Register Address. */ -#define INT_STAT_REG_ADDR 0x18u +#define DM8806_INT_STAT_REG_ADDR 0x18u /* Interrupt Mask & Control Register PHY Address. */ -#define INT_MASK_CTRL_PHY_ADDR 0x18u +#define DM8806_INT_MASK_CTRL_PHY_ADDR 0x18u /* Interrupt Mask & Control Register Register Address. */ -#define INT_MASK_CTRL_REG_ADDR 0x19u +#define DM8806_INT_MASK_CTRL_REG_ADDR 0x19u -#define PORT5_MAC_CONTROL 0x15u +#define DM8806_PORT5_MAC_CONTROL 0x15u /* Port 5 Force Speed control bit */ -#define P5_SPEED_100M ~BIT(0) +#define DM8806_P5_SPEED_100M ~BIT(0) /* Port 5 Force Duplex control bit */ -#define P5_FULL_DUPLEX ~BIT(1) +#define DM8806_P5_FULL_DUPLEX ~BIT(1) /* Port 5 Force Link control bit. Only available in force mode. */ -#define P5_FORCE_LINK_ON ~BIT(2) +#define DM8806_P5_FORCE_LINK_ON ~BIT(2) /* Port 5 Force Mode Enable control bit. Only available for * MII/RevMII/RMII */ -#define P5_EN_FORCE BIT(3) +#define DM8806_P5_EN_FORCE BIT(3) /* Bit 4 is reserved and should not be use */ /* Port 5 50MHz Clock Output Enable control bit. Only available when Port 5 * be configured as RMII */ -#define P5_50M_CLK_OUT_ENABLE BIT(5) +#define DM8806_P5_50M_CLK_OUT_ENABLE BIT(5) /* Port 5 Clock Source Selection control bit. Only available when Port 5 * is configured as RMII */ -#define P5_50M_INT_CLK_SOURCE BIT(6) +#define DM8806_P5_50M_INT_CLK_SOURCE BIT(6) /* Port 5 Output Pin Slew Rate. */ -#define P5_NORMAL_SLEW_RATE ~BIT(7) +#define DM8806_P5_NORMAL_SLEW_RATE ~BIT(7) /* IRQ and LED Control Register. */ -#define IRQ_LED_CONTROL 0x17u +#define DM8806_IRQ_LED_CONTROL 0x17u /* LED mode 0: * LNK_LED: * 100M link fail - LED off @@ -153,4 +180,4 @@ * 10M link ok and no TX/RX activity - LED on * 10M link ok and TX/RX activity - LED blinking */ -#define LED_MODE_0 ~(BIT(0) | BIT(1)) +#define DM8806_LED_MODE_0 ~(BIT(0) | BIT(1))