Part Number: DP83843
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;
}
/** @} */