Hi,
We are currently using the DP83843 PHY in one automotive project and have issues with proper configurations of it.
I don't need anything specific right now. For now, it will be enough to check if it works properly (simple server with OK/ERR response).
I am sure that the problem is in the configuration because we have a working application where DP83848 is used.
We tried to adapt the existing driver of DP83848 for DP83843 but got no success.
Does TI have some kind of a driver for DP83843? Some guidance/instructions on how to configure it correctly would also be very useful.
Attached driver for DP83848 and our attempt to adapt it for DP83843.
Thank you,
Ruslan.
dp83848.h
/*********************************************************************************** * * Copyright © 2017-2019 STMicroelectronics - All Rights Reserved * * License terms: STMicroelectronics Proprietary in accordance with licensing * terms SLA0089 at www.st.com * * THIS SOFTWARE IS DISTRIBUTED "AS IS," AND ALL WARRANTIES ARE DISCLAIMED, * INCLUDING MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * * EVALUATION ONLY – NOT FOR USE IN PRODUCTION ************************************************************************************/ /** * @file dp83848.c * @brief Minimal driver to manage the dp83848 10/100 MII/RMII Transceiver * @addtogroup PHY * @{ */ #include <stdint.h> #include <phylib.h> #include <dp83848.h> #include <phydrv.h> #include <projdefs.h> #include <portmacro.h> #include <network_cfg.h> #include <phydev_if.h> /******************************************************************************* * Constants and macros *******************************************************************************/ #define DP83848_ID 0x20005C90U #define DP83848_MASK 0xFFFFFFF0U /******************************************************************************* * Global variables *******************************************************************************/ /******************************************************************************* * Local types *******************************************************************************/ /** * @brief Type of a structure representing a DP83848 PHY driver. */ typedef struct DP83848Driver DP83848Driver; /** * @brief Structure representing a DP83848 PHY driver. */ struct DP83848Driver { void *parent; uint32_t addr; uint32_t mdix; uint32_t interrupts; uint32_t mode; uint32_t loopback; }; /******************************************************************************* * Local function prototypes *******************************************************************************/ /******************************************************************************* * Local variables *******************************************************************************/ #if (SPC5_USE_ETH0 == TRUE) /** * @brief DP83848 driver instance #1. */ static DP83848Driver DP83848D1; #endif #if (SPC5_USE_ETH1 == TRUE) /** * @brief DP83848 driver instance #2. */ static DP83848Driver DP83848D2; #endif /******************************************************************************* * Local functions *******************************************************************************/ /** * @brief Low Level PHY setup * @priv driver private structure * @details This function aims to setup the transciver according to the parameters * passed from the upper layer. * The function reset the transceiver and check if the ID registers are * reporting the correct mask value. * Some internal functionalities can be enabled: e.g. MII or RMII, loopback. * The setup fuction, before exiting, restart the auto-negotiation process. */ static int dp83848_setup(void *priv) { DP83848Driver *dpd; uint16_t val; dpd = (DP83848Driver *) priv; if (phy_reset(dpd->parent)) { return pdFALSE; } if (phy_check_id(dpd->parent, DP83848_ID, DP83848_MASK)) { return pdFALSE; } /* Auto-MDIX */ if (dpd->mdix) { val = phy_read(dpd->parent, dpd->addr, DP83848_PHYCR); val |= DP83848_PHYCR_MDIX_EN; phy_write(dpd->parent, dpd->addr, DP83848_PHYCR, val); } val = phy_read(dpd->parent, dpd->addr, DP83848_RBR); if (dpd->mode == SPC5_PHY_MODE_RMII) { val |= DP83848_RBR_RMII_MODE; } else { val &= ~DP83848_RBR_RMII_MODE; } phy_write(dpd->parent, dpd->addr, DP83848_RBR, val); val = 0; /* Disable all interrupts */ if (dpd->interrupts) { val = (DP83848_MICR_INT_OE | DP83848_MICR_INT_EN); } phy_write(dpd->parent, dpd->addr, DP83848_MICR, val); if (dpd->loopback) { val = phy_read(dpd->parent, dpd->addr, MII_BMCR); val &= ~BMCR_ANENABLE; /* Disable ANE */ phy_write(dpd->parent, dpd->addr, MII_BMCR, val); val = phy_read(dpd->parent, dpd->addr, MII_BMCR); val |= BMCR_LOOPBACK; /* Enable PHY internal loopback */ phy_write(dpd->parent, dpd->addr, MII_BMCR, val); phy_debug(dpd->parent); return pdTRUE; } if (phy_restart_aneg(dpd->parent)) { return pdFALSE; } return pdTRUE; } static int dp83848_get_link(void *priv) { DP83848Driver *dpd; uint16_t status; uint32_t speed, link_mode; dpd = (DP83848Driver *) priv; status = phy_read(dpd->parent, dpd->addr, DP83848_PHYSTS); if ((status & DP83848_PHYSTS_LINK_STATUS) == 0U) { phy_set_link(dpd->parent, 0U); return pdFALSE; } /* Link is up */ phy_set_link(dpd->parent, 1U); if ((status & DP83848_PHYSTS_SPEED_STATUS) != 0U) { speed = SPC5_PHY_SPEED_10; } else { speed = SPC5_PHY_SPEED_100; } if ((status & DP83848_PHYSTS_DUPLEX_STATUS) != 0U) { link_mode = SPC5_PHY_LINK_MODE_FULL_DUPLEX; } else { link_mode = SPC5_PHY_LINK_MODE_HALF_DUPLEX; } phy_set_speed(dpd->parent, speed); phy_set_link_mode(dpd->parent, link_mode); return pdTRUE; } /*===========================================================================*/ /* Driver interrupt handlers. */ /*===========================================================================*/ /******************************************************************************* * Global functions *******************************************************************************/ void dp83848_clean_irq(void *priv) { DP83848Driver *dpd; dpd = (DP83848Driver *) priv; if (dpd->interrupts) { phy_read(dpd->parent, dpd->addr, DP83848_MISR); } } /** * @brief PHY init * @ops driver ops callback pointers * @details This function is ivoked by the upper layer to init the PHY driver. */ uint32_t dp83848_phydev_init(phydev_ops_t * ops) { DP83848Driver *dpd; uint32_t i, val; dpd = NULL; if (ops->instance == SPC5_ETH0_INSTANCE) { #if (SPC5_USE_ETH0 == TRUE) dpd = &DP83848D1; dpd->mode = SPC5_ETH0_PHY_MODE; #endif } else if (ops->instance == SPC5_ETH1_INSTANCE) { #if (SPC5_USE_ETH1 == TRUE) dpd = &DP83848D2; dpd->mode = SPC5_ETH1_PHY_MODE; #endif } else { return 0xFFFF; } if (dpd == NULL) { return 0xFFFF; } /* Initialize driver operations */ ops->setup = dp83848_setup; ops->get_link = dp83848_get_link; /* Set driver private pointer */ ops->priv = dpd; /* Save reference to parent */ dpd->parent = ops->parent; dpd->mdix = pdFALSE; dpd->interrupts = pdFALSE; dpd->loopback = pdFALSE; for (i = 0; i < PHY_REG_N; i++) { val = phy_read(dpd->parent, i, 0x01); if (val != 0xffff) { dpd->addr = i; break; } } if (i == PHY_REG_N) { /* Reached the end but device not found */ return 0xFFFF; } return dpd->addr; } /** @} */
dp83843.h
/*********************************************************************************** * * Copyright © 2017-2019 STMicroelectronics - All Rights Reserved * * License terms: STMicroelectronics Proprietary in accordance with licensing * terms SLA0089 at www.st.com * * THIS SOFTWARE IS DISTRIBUTED "AS IS," AND ALL WARRANTIES ARE DISCLAIMED, * INCLUDING MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * * EVALUATION ONLY – NOT FOR USE IN PRODUCTION ************************************************************************************/ /** * @file dp83848.c * @brief Minimal driver to manage the dp83848 10/100 MII/RMII Transceiver * @addtogroup PHY * @{ */ #include <stdint.h> #include <phylib.h> #include <dp83848.h> #include <phydrv.h> #include <projdefs.h> #include <portmacro.h> #include <network_cfg.h> #include <phydev_if.h> /******************************************************************************* * Constants and macros *******************************************************************************/ #define DP83843_ID 0x20005C10U #define DP83848_MASK 0xFFFFFFF0U /******************************************************************************* * Global variables *******************************************************************************/ /******************************************************************************* * Local types *******************************************************************************/ /** * @brief Type of a structure representing a DP83848 PHY driver. */ typedef struct DP83848Driver DP83848Driver; /** * @brief Structure representing a DP83848 PHY driver. */ struct DP83848Driver { void *parent; uint32_t addr; uint32_t mdix; uint32_t interrupts; uint32_t mode; uint32_t loopback; }; /******************************************************************************* * Local function prototypes *******************************************************************************/ /******************************************************************************* * Local variables *******************************************************************************/ #if (SPC5_USE_ETH0 == TRUE) /** * @brief DP83848 driver instance #1. */ static DP83848Driver DP83848D1; #endif #if (SPC5_USE_ETH1 == TRUE) /** * @brief DP83848 driver instance #2. */ static DP83848Driver DP83848D2; #endif /******************************************************************************* * Local functions *******************************************************************************/ /** * @brief Low Level PHY setup * @priv driver private structure * @details This function aims to setup the transciver according to the parameters * passed from the upper layer. * The function reset the transceiver and check if the ID registers are * reporting the correct mask value. * Some internal functionalities can be enabled: e.g. MII or RMII, loopback. * The setup fuction, before exiting, restart the auto-negotiation process. */ static int dp83848_setup(void *priv) { DP83848Driver *dpd; uint16_t val; dpd = (DP83848Driver *) priv; if (phy_reset(dpd->parent)) { return pdFALSE; } if (phy_check_id(dpd->parent, DP83843_ID, DP83848_MASK)) { return pdFALSE; } /* not supported on DP83843 */ /* Auto-MDIX */ /* if (dpd->mdix) { val = phy_read(dpd->parent, dpd->addr, DP83848_PHYCR); val |= DP83848_PHYCR_MDIX_EN; phy_write(dpd->parent, dpd->addr, DP83848_PHYCR, val); } val = phy_read(dpd->parent, dpd->addr, DP83848_RBR); if (dpd->mode == SPC5_PHY_MODE_RMII) { val |= DP83848_RBR_RMII_MODE; } else { val &= ~DP83848_RBR_RMII_MODE; } phy_write(dpd->parent, dpd->addr, DP83848_RBR, val); */ val = 0; /* Disable all interrupts */ /* if (dpd->interrupts) { val = (DP83848_MICR_INT_OE | DP83848_MICR_INT_EN); } phy_write(dpd->parent, dpd->addr, DP83848_MICR, val);*/ if (dpd->interrupts) { val = (DP83843_MIPSCR_INT_EN); } phy_write(dpd->parent, dpd->addr, DP83843_MIPSCR, val); if (dpd->loopback) { val = phy_read(dpd->parent, dpd->addr, MII_BMCR); val &= ~BMCR_ANENABLE; /* Disable ANE */ phy_write(dpd->parent, dpd->addr, MII_BMCR, val); val = phy_read(dpd->parent, dpd->addr, MII_BMCR); val |= BMCR_LOOPBACK; /* Enable PHY internal loopback */ phy_write(dpd->parent, dpd->addr, MII_BMCR, val); phy_debug(dpd->parent); return pdTRUE; } // if (phy_restart_aneg(dpd->parent)) { // return pdFALSE; // } return pdTRUE; } static int dp83848_get_link(void *priv) { DP83848Driver *dpd; uint16_t status; uint32_t speed, link_mode; dpd = (DP83848Driver *) priv; status = phy_read(dpd->parent, dpd->addr, DP83843_PHYSTS); if ((status & DP83843_PHYSTS_LINK_STATUS) == 0U) { phy_set_link(dpd->parent, 0U); return pdFALSE; } /* Link is up */ phy_set_link(dpd->parent, 1U); if ((status & DP83843_PHYSTS_SPEED_STATUS) != 0U) { speed = SPC5_PHY_SPEED_10; } else { speed = SPC5_PHY_SPEED_100; } if ((status & DP83843_PHYSTS_DUPLEX_STATUS) != 0U) { link_mode = SPC5_PHY_LINK_MODE_FULL_DUPLEX; } else { link_mode = SPC5_PHY_LINK_MODE_HALF_DUPLEX; } phy_set_speed(dpd->parent, speed); phy_set_link_mode(dpd->parent, link_mode); return pdTRUE; } /*===========================================================================*/ /* Driver interrupt handlers. */ /*===========================================================================*/ /******************************************************************************* * Global functions *******************************************************************************/ void dp83848_clean_irq(void *priv) { DP83848Driver *dpd; dpd = (DP83848Driver *) priv; if (dpd->interrupts) { phy_read(dpd->parent, dpd->addr, DP83843_MIPGSR); } } /** * @brief PHY init * @ops driver ops callback pointers * @details This function is ivoked by the upper layer to init the PHY driver. */ uint32_t dp83848_phydev_init(phydev_ops_t * ops) { DP83848Driver *dpd; uint32_t i, val; dpd = NULL; if (ops->instance == SPC5_ETH0_INSTANCE) { #if (SPC5_USE_ETH0 == TRUE) dpd = &DP83848D1; dpd->mode = SPC5_ETH0_PHY_MODE; #endif } else if (ops->instance == SPC5_ETH1_INSTANCE) { #if (SPC5_USE_ETH1 == TRUE) dpd = &DP83848D2; dpd->mode = SPC5_ETH1_PHY_MODE; #endif } else { return 0xFFFF; } if (dpd == NULL) { return 0xFFFF; } /* Initialize driver operations */ ops->setup = dp83848_setup; ops->get_link = dp83848_get_link; /* Set driver private pointer */ ops->priv = dpd; /* Save reference to parent */ dpd->parent = ops->parent; dpd->mdix = pdFALSE; dpd->interrupts = pdFALSE; dpd->loopback = pdFALSE; for (i = 0; i < PHY_REG_N; i++) { val = phy_read(dpd->parent, i, 0x01); if (val != 0xffff) { dpd->addr = i; break; } } if (i == PHY_REG_N) { /* Reached the end but device not found */ return 0xFFFF; } return dpd->addr; } /** @} */