lwip 1.4 - DM9161A PHY

After reading topic http://mbed.org/forum/mbed/topic/3684/?page=1#comment-18473, i created the lpc_phy_DM9161A.c file (used lpc_phy_lan8720.c as a template).

Partially working

Auto negotiation and DHCP are OK - ethernet communication is not working.

There seems to be a communication problem between the EMAC and PHY (timing?). Any ideas on how to resolve this issue are welcome.

A debug log is available at /media/uploads/frankvnk/tcpsocket_helloworld_with_dm9161a_phy_-_debug_log.txt. The log file indicates a pbuf problem. Further investigation led me to following webpage: http://www.lpcware.com/content/forum/lwip-sample-fail#comment-1785. I tried again with the mentioned modification but then DHCP does not work. I don't know if this modification also needs to be implemented (not present in the current lwip library).

How to test

import http://mbed.org/users/mbed_official/code/TCPSocket_HelloWorld/ into the mbed compiler.

Goto folder EthernetInterface\lwip-eth\arch#

Delete lpc_phy_dp83848.c.

Copy lpc_phy_DM9161A.c into this folder.

Add following MAC address definition in main.cpp (before 'EthernetInterface eth;').

       extern "C" void mbed_mac_address(char * mac)
       {
       // define your own MAC Address
         mac[0] = 0x00;  
         mac[1] = 0x1F;  
         mac[2] = 0x33;  
         mac[3] = 0x42;  
         mac[4] = 0xFC;  
         mac[5] = 0x6F;           
       };

When used with the Mini-DK hardware

Modify EthernetInterface\lwip-eth\arch\lpc_emac_config.h:

Change #define LPC_PHYDEF_PHYADDR 1 to #define LPC_PHYDEF_PHYADDR 19

On the Mini-DK, sometimes no IP address is obtained when the 'Reset' button is pressed after programming the device. Disconnect and reconnect the device and press the 'Reset' button again to restart.

lpc_phy_DM9161A.c

Committer:
frankvnk
Date:
2013-03-18
Revision:
2:e8cdfd3c70f5
Parent:
0:6901daab33f0

File content as of revision 2:e8cdfd3c70f5:

/**********************************************************************
* $Id$        lpc_phy_DM9161A.c            2013-01-28
*//**
* @file        lpc_phy_DM9161A.c
* @brief       DM9161A PHY status and control.
* @version     1.0
* @date        14 Jan. 2013
* @author      Frank Vannieuwkerke
*              based on NXP MCU SW Application Team lpc_phy_dp83848.c
*              Copyright(C) 2011, NXP Semiconductor
*              All rights reserved.
*
*
*
* @NOTES:
*  1. How to test : import http://mbed.org/users/mbed_official/code/TCPSocket_HelloWorld/ into the mbed compiler.
*
*  2. Open EthernetInterface\lwip-eth\arch folder
*     Delete 'lpc_phy_dp83848.c'.
*     Copy 'lpc_phy_DM9161A.c' (this file) into this folder.
*
*  3. When used with the Mini-DK hardware:
*     Modify EthernetInterface\lwip-eth\arch\lpc_emac_config.h:
*       Change #define LPC_PHYDEF_PHYADDR 1  to  #define LPC_PHYDEF_PHYADDR 19
*
*     Add following MAC address definition in main.cpp (before 'EthernetInterface eth;').
*       extern "C" void mbed_mac_address(char * mac)
*       {
*       // define your own MAC Address
*         mac[0] = 0x00;  
*         mac[1] = 0x1F;  
*         mac[2] = 0x33;  
*         mac[3] = 0x42;  
*         mac[4] = 0xFC;  
*         mac[5] = 0x6F;           
*       };
*
*
*
*
***********************************************************************
* Software that is described herein is for illustrative purposes only
* which provides customers with programming information regarding the
* products. This software is supplied "AS IS" without any warranties.
* NXP Semiconductors assumes no responsibility or liability for the
* use of the software, conveys no license or title under any patent,
* copyright, or mask work right to the product. NXP Semiconductors
* reserves the right to make changes in the software without
* notification. NXP Semiconductors also make no representation or
* warranty that such application will be suitable for the specified
* use without further testing or modification.
**********************************************************************/

#include "lwip/opt.h"
#include "lwip/err.h"
#include "lwip/tcpip.h"
#include "lwip/snmp.h"
#include "lpc_emac_config.h"
#include "lpc_phy.h"

/** @defgroup DM9161A_phy    PHY status and control for the DM9161A.
 * @ingroup lwip_phy
 *
 * Various functions for controlling and monitoring the status of the
 * DM9161A PHY. In polled (standalone) systems, the PHY state must be
 * monitored as part of the application. In a threaded (RTOS) system,
 * the PHY state is monitored by the PHY handler thread. The MAC
 * driver will not transmit unless the PHY link is active.
 * @{
 */

/** \brief DM9161A PHY register offsets */
#define DM9161A_BMCR_REG         0x00        /**< Basic Mode Control Register */
#define DM9161A_BMSR_REG         0x01        /**< Basic Mode Status Reg */
#define DM9161A_IDR1_REG         0x02        /**< PHY ID 1 Reg  */
#define DM9161A_IDR2_REG         0x03        /**< PHY ID 2 Reg  */
#define DM9161A_ANAR_REG         0x04        /**< Auto_Neg Advt Reg  */
#define DM9161A_ANLPAR_REG       0x05        /**< Auto_neg Link Partner Ability Reg */
#define DM9161A_ANER_REG         0x06        /**< Auto-neg Expansion Reg  */
#define DM9161A_DSCR_REG         0x10        /**< Specified Configuration Reg */
#define DM9161A_DSCSR_REG        0x11        /**< Specified Configuration and Status Reg */
#define DM9161A_10BTCSR_REG      0x12        /**< 10BASE-T Configuration and Satus Reg */
#define DM9161A_MDINTR_REG       0x15        /**< Specified Interrupt Reg */
#define DM9161A_RECR_REG         0x16        /**< Specified Receive Error Counter Reg */
#define DM9161A_DISCR_REG        0x17        /**< Specified Disconnect Counter Reg  */
#define DM9161A_RLSR_REG         0x18        /**< Hardware Reset Latch State Reg  */

/** \brief DM9161A BMCR register definitions */
#define DM9161A_RESET            (1 << 15)   /**< 1= S/W Reset */
#define DM9161A_LOOPBACK         (1 << 14)   /**< 1=loopback Enabled */
#define DM9161A_SPEED_SELECT     (1 << 13)   /**< 1=Select 100MBps */
#define DM9161A_AUTONEG          (1 << 12)   /**< 1=Enable auto-negotiation */ 
#define DM9161A_POWER_DOWN       (1 << 11)   /**< 1=Power down PHY */
#define DM9161A_ISOLATE          (1 << 10)   /**< 1=Isolate PHY */
#define DM9161A_RESTART_AUTONEG  (1 << 9 )   /**< 1=Restart auto-negoatiation */
#define DM9161A_DUPLEX_MODE      (1 << 8 )   /**< 1=Full duplex mode */
#define DM9161A_COLLISION_TEST   (1 << 7 )   /**< 1=Perform collsion test */

/** \brief DM9161A BMSR register definitions */
#define DM9161A_100BASE_T4       (1 << 15)   /**< T4 mode */
#define DM9161A_100BASE_TX_FD    (1 << 14)   /**< 100MBps full duplex */
#define DM9161A_100BASE_TX_HD    (1 << 13)   /**< 100MBps half duplex */
#define DM9161A_10BASE_T_FD      (1 << 12)   /**< 10MBps full duplex */
#define DM9161A_10BASE_T_HD      (1 << 11)   /**< 10MBps half duplex */
#define DM9161A_MF_PREAMB_SUPPR  (1 << 6 )   /**< Preamble suppress */
#define DM9161A_AUTONEG_COMP     (1 << 5 )   /**< Auto-negotation complete */
#define DM9161A_RMT_FAULT        (1 << 4 )   /**< Fault */
#define DM9161A_AUTONEG_ABILITY  (1 << 3 )   /**< Auto-negotation supported */
#define DM9161A_LINK_STATUS      (1 << 2 )   /**< 1=Link active */
#define DM9161A_JABBER_DETECT    (1 << 1 )   /**< Jabber detect */
#define DM9161A_EXTEND_CAPAB     (1 << 0 )   /**< Supports extended capabilities */

/** \brief DM9161A ANAR and ANLPAR register definitions */
#define DM9161A_NP               (1 << 15)   /**< No next page - always = 0 */
#define DM9161A_ACK              (1 << 14)   /**< 1 = Link partner ability data reception acknowledged */
#define DM9161A_RF               (1 << 13)   /**< 1 = Local device senses a fault condition */
#define DM9161A_FCS              (1 << 10)   /**< 1 = Controller chip supports flow control ability */
#define DM9161A_T4               (1 << 9 )   /**< No 100BASE-T4 is supported  - always = 0 */
#define DM9161A_TX_FDX           (1 << 8 )   /**< 1 = 100BASE-TX full duplex is supported by the local device */
#define DM9161A_TX_HDX           (1 << 7 )   /**< 1 = 100BASE-TX half duplex is supported by the local device */
#define DM9161A_10_FDX           (1 << 6 )   /**< 1 = 10BASE-T full duplex is supported by the local device */
#define DM9161A_10_HDX           (1 << 5 )   /**< 1 = 10BASE-T half duplex is supported by the local device */
#define DM9161A_AN_IEEE_802_3    0x0001      /**< this device supports IEEE 802.3 CSMA/CD */

/** \brief DM9161A ANER register definitions */
#define DM9161A_PDF              (1 << 4 )   /**< 1 = A fault detected via parallel detection function */
#define DM9161A_LP_NP_ABLE       (1 << 3 )   /**< 1 = Link partner, next page available */
#define DM9161A_NP_ABLE          (1 << 2 )   /**< No next page - always = 0 */
#define DM9161A_PAGE_RX          (1 << 1 )   /**< New page received - cleared when reg 6 is read */
#define DM9161A_LP_AN_ABLE       (1 << 0 )   /**< 1 = Link partner supports auto-negotiation */

/** \brief DM9161A DSCR register definitions */
#define DM9161A_BP4B5B           (1 << 15)   /**< 1 = 4B5B encoder and 5B4B decoder function bypassed */
#define DM9161A_BP_SCR           (1 << 14)   /**< 1 = Scrambler and descrambler function bypassed */
#define DM9161A_BP_ALIGN         (1 << 13)   /**< 1 = Receive and Transmit functions disabled */
#define DM9161A_BP_ADPOK         (1 << 12)   /**< 1 = Force signal detector (SD) active - ONLY FOR DEBUG */
#define DM9161A_REPEATER         (1 << 11)   /**< 1 = Repeater mode - latched at power up reset */
#define DM9161A_TX               (1 << 10)   /**< 1 = 100BASE-TX operation */
#define DM9161A_RMII_ENABLE      (1 << 8 )   /**< 1 = Enable Reduced MII */
#define DM9161A_F_LINK_100       (1 << 7 )   /**< 1 = Force 100Mbps good link status */
#define DM9161A_SPLED_CTL        (1 << 6 )   /**< 1 = Disable SPEEDLED output and enable SD signal monitor - ONLY FOR DEBUG  */
#define DM9161A_COLLED_CTL       (1 << 5 )   /**< 1 = FDX/COLLED output is configured to indicate Fullduplex Collision status */
#define DM9161A_RPDCTR_EN        (1 << 4 )   /**< 1 = Enable automatic reduced power down */
#define DM9161A_SM_RST           (1 << 3 )   /**< 1 = Reset state machine - auto cleared after reset */
#define DM9161A_MFP_SC           (1 << 2 )   /**< 1 = MF preamble suppression bit on */
#define DM9161A_SLEEP            (1 << 1 )   /**< 1 = sleep mode */
#define DM9161A_RLOUT            (1 << 0 )   /**< 1 = 1, loop out the received data to the transmit channel */

/** \brief DM9161A DSCSR register definitions */
#define DM9161A_SPEEDMASK        (15 << 12)  /**< Speed and duplex mask */
#define DM9161A_100FDX           (1 << 15)   /**< 1 = 100MB full duplex */
#define DM9161A_100HDX           (1 << 14)   /**< 1 = 100MB half duplex */
#define DM9161A_10FDX            (1 << 13)   /**< 1 = 10MB full duplex */
#define DM9161A_10HDX            (1 << 12)   /**< 1 = 10MB half duplex */

/** \brief DM9161A 10BTCSR register definitions */
#define DM9161A_LP_EN            (1 << 14)   /**< 1 = Transmission of link pulses enabled */
#define DM9161A_HBE              (1 << 13)   /**< 1 = Heartbeat function enabled */
#define DM9161A_SQUELCH          (1 << 12)   /**< 1 = normal squelch */
#define DM9161A_JABEN            (1 << 11)   /**< 1 = Jabber function enabled */
#define DM9161A_10BT_SER         (1 << 10)   /**< 1 = 10BASE-T GPSI mode selected */
#define DM9161A_POLR             (1 << 0 )   /**< 1 = 10Mbps cable polarity is reversed */

/** \brief DM9161A MDINTR register definitions */
#define DM9161A_INTR_PEND        (1 << 15)   /**< Interrupt pending */
#define DM9161A_FDX_MASK         (1 << 11)   /**< Full-duplex interrupt mask */
#define DM9161A_SPD_MASK         (1 << 10)   /**< Speed interrupt mask */
#define DM9161A_LINK_MASK        (1 << 9 )   /**< Link interrupt mask */
#define DM9161A_INTR_MASK        (1 << 8 )   /**< Master interrupt mask */
#define DM9161A_FDX_CHANGE       (1 << 4 )   /**< 1 = indicates a change of duplex since last register read */
#define DM9161A_SPD_CHANGE       (1 << 3 )   /**< 1 = indicates a change of speed since last register read */
#define DM9161A_LINK_CHANGE      (1 << 2 )   /**< 1 = indicates a change of link since last register read */
#define DM9161A_INTR_STATUS      (1 << 0 )   /**< 1 = indicates that the interrupt mask is off */

/** \brief DM9161A RLSR register definitions */
#define DM9161A_LH_LEDMODE       (1 << 15)   /**< LEDMODE pin reset latch value */
#define DM9161A_LH_MDINTR        (1 << 14)   /**< MDINTR pin reset latch value */
#define DM9161A_LH_CSTS          (1 << 13)   /**< CABLESTS pin reset latch value */
#define DM9161A_LH_ISO           (1 << 12)   /**< TXCLK pin reset latch value */
#define DM9161A_LH_RMII          (1 << 11)   /**< COL pin reset latch value */
#define DM9161A_LH_TP10SER       (1 << 10)   /**< RXCLK pin reset latch value */
#define DM9161A_LH_REPTR         (1 << 9)    /**< RXER pin reset latch value */
#define DM9161A_LH_TSTMOD        (1 << 8 )   /**< RXDV pin reset latch value */
#define DM9161A_LH_OP2           (1 << 7 )   /**< LINKLED pin reset latch value */
#define DM9161A_LH_OP1           (1 << 6 )   /**< SPOLED pin reset latch value */
#define DM9161A_LH_OP0           (1 << 5 )   /**< FDXLED pin reset latch value */
#define DM9161A_LH_PH4           (1 << 4 )   /**< CRS pin reset latch value */
#define DM9161A_LH_PH3           (1 << 3 )   /**< RXD3 pin reset latch value */
#define DM9161A_LH_PH2           (1 << 2 )   /**< RXD2 pin reset latch value */
#define DM9161A_LH_PH1           (1 << 1 )   /**< RXD1 pin reset latch value */
#define DM9161A_LH_PH0           (1 << 0 )   /**< RXD0 pin reset latch value */

#define DM9161A_FULLD_100M        0x2100      /**< Full Duplex 100Mbit               */
#define DM9161A_HALFD_100M        0x2000      /**< Half Duplex 100Mbit               */
#define DM9161A_FULLD_10M         0x0100      /**< Full Duplex 10Mbit                */
#define DM9161A_HALFD_10M         0x0000      /**< Half Duplex 10MBit                */
#define DM9161A_AUTO_NEG          0x3000      /**< Select Auto Negotiation           */
#define DM9161A_AUTO_NEG_MASK     0xEFFF      /**< Auto Negotiation mask bit         */

/** \brief DM9161A PHY ID register definitions */
#define DM9161A_PHYID1_OUI        0x0181      /**< Expected PHY ID1 */
#define DM9161A_PHYID2_OUI        0xB8A0      /**< Expected PHY ID2 */

#define DM9161A_DEF_ADR           0x1300      /**< Default PHY device address DM9161A */

/** \brief PHY status structure used to indicate current status of PHY.
 */
typedef struct {
    u32_t     phy_speed_100mbs:2; /**< 10/100 MBS connection speed flag. */
    u32_t     phy_full_duplex:2;  /**< Half/full duplex connection speed flag. */
    u32_t     phy_link_active:2;  /**< Phy link active flag. */
} PHY_STATUS_TYPE;

/** \brief  PHY update flags */
static PHY_STATUS_TYPE physts;

/** \brief  Last PHY update flags, used for determing if something has changed */
static PHY_STATUS_TYPE olddphysts;

/** \brief  PHY update counter for state machine */
static s32_t phyustate;

/** \brief  Update PHY status from passed value
 *
 *  This function updates the current PHY status based on the
 *  passed PHY status word. The PHY status indicate if the link
 *  is active, the connection speed, and duplex.
 *
 *  \param[in]    netif   NETIF structure
 *  \param[in]    linksts Status word with link state
 *  \param[in]    sdsts   Status word with speed and duplex states
 *  \return        1 if the status has changed, otherwise 0
 */
static s32_t lpc_update_phy_sts(struct netif *netif, u32_t linksts, u32_t sdsts)
{
    s32_t changed = 0;

//    sdsts &= DM9161A_AUTO_NEG_MASK; // mask DM9161A_AUTO_NEG

    /* Update link active status */
    if (linksts & DM9161A_LINK_STATUS)
        physts.phy_link_active = 1;
    else
        physts.phy_link_active = 0;

    /* Full or half duplex */
    if (sdsts & DM9161A_DUPLEX_MODE)
        physts.phy_full_duplex = 1;
    else
        physts.phy_full_duplex = 0;

    /* Configure 100MBit/10MBit mode. */
    if (sdsts & DM9161A_SPEED_SELECT)
        physts.phy_speed_100mbs = 1;
    else
        physts.phy_speed_100mbs = 0;

    if (physts.phy_speed_100mbs != olddphysts.phy_speed_100mbs) {
        changed = 1;
        if (physts.phy_speed_100mbs) {
            /* 100MBit mode. */
            lpc_emac_set_speed(1);

            NETIF_INIT_SNMP(netif, snmp_ifType_ethernet_csmacd, 100000000);
        }
        else {
            /* 10MBit mode. */
            lpc_emac_set_speed(0);

            NETIF_INIT_SNMP(netif, snmp_ifType_ethernet_csmacd, 10000000);
        }

        olddphysts.phy_speed_100mbs = physts.phy_speed_100mbs;
    }

    if (physts.phy_full_duplex != olddphysts.phy_full_duplex) {
        changed = 1;
        if (physts.phy_full_duplex)
            lpc_emac_set_duplex(1);
        else
            lpc_emac_set_duplex(0);

        olddphysts.phy_full_duplex = physts.phy_full_duplex;
    }

    if (physts.phy_link_active != olddphysts.phy_link_active) {
        changed = 1;
#if NO_SYS == 1
        if (physts.phy_link_active)
            netif_set_link_up(netif);
        else
            netif_set_link_down(netif);
#else
        if (physts.phy_link_active)
            tcpip_callback_with_block((tcpip_callback_fn) netif_set_link_up,
                (void*) netif, 1);
         else
            tcpip_callback_with_block((tcpip_callback_fn) netif_set_link_down,
                (void*) netif, 1);
#endif

        olddphysts.phy_link_active = physts.phy_link_active;
    }

    return changed;
}

/** \brief  Initialize the DM9161A PHY.
 *
 *  This function initializes the DM9161A PHY. It will block until
 *  complete. This function is called as part of the EMAC driver
 *  initialization. Configuration of the PHY at startup is
 *  controlled by setting up configuration defines in lpc_phy.h.
 *
 *  \param[in]     netif   NETIF structure
 *  \param[in]     rmii    If set, configures the PHY for RMII mode
 *  \return         ERR_OK if the setup was successful, otherwise ERR_TIMEOUT
 */
err_t lpc_phy_init(struct netif *netif, int rmii)
{
    u32_t tmp;
    s32_t i;

    physts.phy_speed_100mbs = olddphysts.phy_speed_100mbs = 2;
    physts.phy_full_duplex = olddphysts.phy_full_duplex = 2;
    physts.phy_link_active = olddphysts.phy_link_active = 2;
    phyustate = 0;

    /* Only first read and write are checked for failure */
    /* Put the DM9161A in reset mode and wait for completion */
    if (lpc_mii_write(DM9161A_BMCR_REG, DM9161A_RESET) != 0)
        return ERR_TIMEOUT;
    i = 400;
    while (i > 0) {
        osDelay(1);   // 1 ms
        if (lpc_mii_read(DM9161A_BMCR_REG, &tmp) != 0)
            return ERR_TIMEOUT;

        if (!(tmp & (DM9161A_RESET | DM9161A_POWER_DOWN)))
            i = -1;
        else
            i--;
    }
    // Timeout?
    if (i == 0)
        return ERR_TIMEOUT;

    /* Setup link based on configuration options */
#if PHY_USE_AUTONEG==1
    tmp = DM9161A_AUTONEG | DM9161A_RESTART_AUTONEG;
#else
    tmp = 0;
#endif 
#if PHY_USE_100MBS==1
    tmp |= DM9161A_SPEED_SELECT;
#endif
#if PHY_USE_FULL_DUPLEX==1
    tmp |= DM9161A_DUPLEX_MODE;
#endif

    lpc_mii_write(DM9161A_BMCR_REG, tmp);

    /* Wait for autonegotiation completion */
    i = 400;
    while (i > 0)
    {
        osDelay(1);   // 1 ms
        if (lpc_mii_read(DM9161A_BMSR_REG, &tmp) != 0)
        {
            printf("Timeout\n");
            return ERR_TIMEOUT;
        }
        if (tmp & (DM9161A_AUTONEG_COMP))
            i = -1;
        else
            i--;
    }
    // Timeout?
    if (i == 0)
    {
        printf("Timeout\n");
        return ERR_TIMEOUT;
    }

    /* Enable RMII mode for PHY  - NOT NEEDED FOR MIDI-DK - RMII pin is lathed on reset*/
/*    if (rmii)
        lpc_mii_write(DM9161A_DSCR_REG, DM9161A_RMII_ENABLE);
*/
    /* The link is not set active at this point, but will be detected
       later */

    return ERR_OK;
}

/* Phy status update state machine */
s32_t lpc_phy_sts_sm(struct netif *netif)
{
    static u32_t sts;
    s32_t changed = 0;
    switch (phyustate)
    {
        default:
        case 0:
            // Read BMSR to clear faults
            lpc_mii_read_noblock(DM9161A_BMSR_REG);
            phyustate = 1;
            break;

        case 1:
            // Wait for read status state
            if (!lpc_mii_is_busy())
            {
                // Get autonegotiation complete bit
                sts = lpc_mii_read_data();
                lpc_mii_read_noblock(DM9161A_BMCR_REG);
                phyustate = 2;
            }
            break;

        case 2:
            // Wait for read status state
            if (!lpc_mii_is_busy())
            {
                // Update PHY status (BMCR contains speed and duplex state)
                changed = lpc_update_phy_sts(netif, sts, lpc_mii_read_data());
                phyustate = 0;
            }
            break;
    }
    return changed;
}

/* //Original state machine code
*/



/**
 * @}
 */

/* --------------------------------- End Of File ------------------------------ */