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))