//==========================================================================
//
//      stm32_common.c
//
//      Common routines for Ethernet driver for ST STM32 family on-chip EMAC.
//
//==========================================================================
// ####ECOSGPLCOPYRIGHTBEGIN####                                            
// -------------------------------------------                              
// This file is part of eCos, the Embedded Configurable Operating System.   
// Copyright (C) 2007, 2008, 2009 eCosCentric Limited
//
// eCos is free software; you can redistribute it and/or modify it under    
// the terms of the GNU General Public License as published by the Free     
// Software Foundation; either version 2 or (at your option) any later      
// version.                                                                 
//
// eCos is distributed in the hope that it will be useful, but WITHOUT      
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or    
// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License    
// for more details.                                                        
//
// You should have received a copy of the GNU General Public License        
// along with eCos; if not, write to the Free Software Foundation, Inc.,    
// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.            
//
// As a special exception, if other files instantiate templates or use      
// macros or inline functions from this file, or you compile this file      
// and link it with other works to produce a work based on this file,       
// this file does not by itself cause the resulting work to be covered by   
// the GNU General Public License. However the source code for this file    
// must still be made available in accordance with section (3) of the GNU   
// General Public License v2.                                               
//
// This exception does not invalidate any other reasons why a work based    
// on this file might be covered by the GNU General Public License.         
// -------------------------------------------                              
// ####ECOSGPLCOPYRIGHTEND####                                              
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s):    nickg
// Contributors:
// Date:         2009-06-08
//
//####DESCRIPTIONEND####
//==========================================================================

//======================================================================
// Buffer descriptors, for both TX and RX.

typedef struct buffer_descriptor
{
    volatile cyg_uint32 ctrl;           // control and status
    volatile cyg_uint32 bufsize;        // Buffer sizes
    volatile cyg_uint32 buffer1;        // First buffer address
    volatile cyg_uint32 buffer2;        // Second buffer address
} buffer_descriptor;

//======================================================================
// Common data structure between all driver types.

typedef struct stm32_eth_common
{
    cyg_uint32          base;           // EMAC base address
    cyg_uint32          vector;         // interrupt vector
    eth_phy_access_t    *phy;           // Phy control
    
} stm32_eth_common;

//======================================================================

static void
stm32_eth_show_bd( volatile buffer_descriptor *bd )
{
    eth_diag("%08x: %08x %08x %08x %08x", (cyg_uint32)bd, bd->ctrl, bd->bufsize, bd->buffer1, bd->buffer2 );
}

static void
stm32_eth_show_bds( volatile buffer_descriptor *bd, int n, char *title )
{
    int i;
    
    eth_diag("%s", title);
    for( i = 0; i < n; i++,bd++ )
        stm32_eth_show_bd(bd);
}

#define stm32_eth_show_reg( __reg )                             \
{                                                               \
    cyg_uint32 r;                                               \
    HAL_READ_UINT32( base+CYGHWR_HAL_STM32_ETH_##__reg, r );    \
    eth_diag("\t%11s = %08x", #__reg, r);                       \
}

static void
stm32_eth_show_mac( stm32_eth_common *eth )
{
    cyg_uint32 base = eth->base;
    
    eth_diag("MAC:");
    stm32_eth_show_reg( MACCR );
    stm32_eth_show_reg( MACFFR );
    stm32_eth_show_reg( MACHTHR );
    stm32_eth_show_reg( MACHTLR );
    stm32_eth_show_reg( MACMIIAR );
    stm32_eth_show_reg( MACMIIDR );
    stm32_eth_show_reg( MACFCR );
    stm32_eth_show_reg( MACVLANTR );
    stm32_eth_show_reg( MACRWUFFR );
    stm32_eth_show_reg( MACPMTCSR );
    stm32_eth_show_reg( MACSR );
    stm32_eth_show_reg( MACIMR );
    stm32_eth_show_reg( MACA0HR );
    stm32_eth_show_reg( MACA0LR );
    stm32_eth_show_reg( MACA1HR );
    stm32_eth_show_reg( MACA1LR );
    stm32_eth_show_reg( MACA2HR );
    stm32_eth_show_reg( MACA2LR );
    stm32_eth_show_reg( MACA3HR );
    stm32_eth_show_reg( MACA3LR );

    eth_diag("MMC:");    
    stm32_eth_show_reg( MMCCR );
    stm32_eth_show_reg( MMCRIR );
    stm32_eth_show_reg( MMCTIR );
    stm32_eth_show_reg( MMCRIMR );
    stm32_eth_show_reg( MMCTIMR );
    stm32_eth_show_reg( MMCTGFSCCR );
    stm32_eth_show_reg( MMCTGFMSCCR );
    stm32_eth_show_reg( MMCTGFCR );
    stm32_eth_show_reg( MMCRFCECR );
    stm32_eth_show_reg( MMCRFAECR );
    stm32_eth_show_reg( MMCRGUFCR );

    eth_diag("DMA:");    
    stm32_eth_show_reg( DMABMR );
    stm32_eth_show_reg( DMATPDR );
    stm32_eth_show_reg( DMARPDR );
    stm32_eth_show_reg( DMARDLAR );
    stm32_eth_show_reg( DMATDLAR );
    stm32_eth_show_reg( DMASR );
    stm32_eth_show_reg( DMAOMR );
    stm32_eth_show_reg( DMAIER );
    stm32_eth_show_reg( DMAMFBOCR );
    stm32_eth_show_reg( DMACHTDR );
    stm32_eth_show_reg( DMACHRDR );
    stm32_eth_show_reg( DMACHTBAR );
    stm32_eth_show_reg( DMACHRBAR );

}

//======================================================================
// The MAC address. Comes from RedBoot's config info, if CDL indicates we should
// try. Otherwise this will be supplied by the platform HAL, but some
// platforms may not have appropriate hardware. Worst case, we use the CDL-defined
// configurable default value.

static cyg_uint8    stm32_eth_default_mac[6]  = { CYGDAT_DEVS_ETH_CORTEXM_STM32_MACADDR } ;

static void
stm32_eth_get_config_mac_address(cyg_uint8* mac)
{
    int mac_ok  = 0;
    // Do we have virtual vectors?
#if defined(CYGPKG_DEVS_ETH_CORTEXM_STM32_REDBOOT_HOLDS_ESA) // implies VV support
    // There has been an incompatible change to hal_if.h. Cope with both new and old names
# ifdef CYGNUM_FLASH_CFG_TYPE_CONFIG_ESA    
    mac_ok  = CYGACC_CALL_IF_FLASH_CFG_OP(CYGNUM_CALL_IF_FLASH_CFG_GET, "eth0_esa_data", mac, CYGNUM_FLASH_CFG_TYPE_CONFIG_ESA);
# else    
    mac_ok  = CYGACC_CALL_IF_FLASH_CFG_OP(CYGNUM_CALL_IF_FLASH_CFG_GET, "eth0_esa_data", mac, CYGNUM_FLASH_CFG_OP_CONFIG_ESA);
# endif
#endif
#ifdef CYGHWR_DEVS_ETH_CORTEXM_STM32_GET_ESA
    if (!mac_ok) {
        CYGHWR_DEVS_ETH_CORTEXM_STM32_GET_ESA(mac, mac_ok);
    }
#endif
    if (!mac_ok) {
        memcpy(mac, stm32_eth_default_mac, 6);
    }
}

//======================================================================
// Configure pins and clocks

static void stm32_eth_cfg( stm32_eth_common *eth )
{
    cyg_uint32 rcc = CYGHWR_HAL_STM32_RCC;
    cyg_uint32 cfgr;
    cyg_uint32 afio = CYGHWR_HAL_STM32_AFIO;
    cyg_uint32 mapr;

    eth_diag("enter");

    // Route 25MHz crystal to MCO for PHY
    HAL_READ_UINT32( rcc+CYGHWR_HAL_STM32_RCC_CFGR, cfgr );
    cfgr |= CYGHWR_HAL_STM32_RCC_CFGR_MCO_XT1;
    HAL_WRITE_UINT32( rcc+CYGHWR_HAL_STM32_RCC_CFGR, cfgr );

    CYGHWR_HAL_STM32_GPIO_SET( CYGHWR_HAL_STM32_ETH_MCO );
    
    // Enable MAC clocks
    CYGHWR_HAL_STM32_CLOCK_ENABLE( CYGHWR_HAL_STM32_ETH_MAC_CLOCK ); 
    CYGHWR_HAL_STM32_CLOCK_ENABLE( CYGHWR_HAL_STM32_ETH_TX_CLOCK ); 
    CYGHWR_HAL_STM32_CLOCK_ENABLE( CYGHWR_HAL_STM32_ETH_RX_CLOCK ); 

    // Read AFIO MAPR register for possible update
    HAL_READ_UINT32( afio+CYGHWR_HAL_STM32_AFIO_MAPR, mapr );
    
#ifdef CYGSEM_DEVS_ETH_CORTEXM_STM32_RMII

    CYGHWR_HAL_STM32_GPIO_SET( CYGHWR_HAL_STM32_ETH_RMII_MDC );
    CYGHWR_HAL_STM32_GPIO_SET( CYGHWR_HAL_STM32_ETH_RMII_REF_CLK );
    CYGHWR_HAL_STM32_GPIO_SET( CYGHWR_HAL_STM32_ETH_RMII_MDIO );
    CYGHWR_HAL_STM32_GPIO_SET( CYGHWR_HAL_STM32_ETH_RMII_CRS_DV );
    CYGHWR_HAL_STM32_GPIO_SET( CYGHWR_HAL_STM32_ETH_RMII_RXD0 );
    CYGHWR_HAL_STM32_GPIO_SET( CYGHWR_HAL_STM32_ETH_RMII_RXD1 );
    CYGHWR_HAL_STM32_GPIO_SET( CYGHWR_HAL_STM32_ETH_RMII_TX_EN );
    CYGHWR_HAL_STM32_GPIO_SET( CYGHWR_HAL_STM32_ETH_RMII_TXD0 );
    CYGHWR_HAL_STM32_GPIO_SET( CYGHWR_HAL_STM32_ETH_RMII_TXD1 );
    CYGHWR_HAL_STM32_GPIO_SET( CYGHWR_HAL_STM32_ETH_RMII_PPS_OUT );

    // Switch to RMII interface
    mapr |= CYGHWR_HAL_STM32_AFIO_MAPR_ETH_RMII;
    
#else // CYGSEM_DEVS_ETH_CORTEXM_STM32_RMII

    // MII setup

    CYGHWR_HAL_STM32_GPIO_SET( CYGHWR_HAL_STM32_ETH_MII_MDC );
    CYGHWR_HAL_STM32_GPIO_SET( CYGHWR_HAL_STM32_ETH_MII_TXD2 );
    CYGHWR_HAL_STM32_GPIO_SET( CYGHWR_HAL_STM32_ETH_MII_TX_CLK );
    CYGHWR_HAL_STM32_GPIO_SET( CYGHWR_HAL_STM32_ETH_MII_TX_CRS );
    CYGHWR_HAL_STM32_GPIO_SET( CYGHWR_HAL_STM32_ETH_MII_RX_CLK );
    CYGHWR_HAL_STM32_GPIO_SET( CYGHWR_HAL_STM32_ETH_MII_MDIO );
    CYGHWR_HAL_STM32_GPIO_SET( CYGHWR_HAL_STM32_ETH_MII_COL );
    CYGHWR_HAL_STM32_GPIO_SET( CYGHWR_HAL_STM32_ETH_MII_RX_DV );
    CYGHWR_HAL_STM32_GPIO_SET( CYGHWR_HAL_STM32_ETH_MII_RXD0 );
    CYGHWR_HAL_STM32_GPIO_SET( CYGHWR_HAL_STM32_ETH_MII_RXD1 );
    CYGHWR_HAL_STM32_GPIO_SET( CYGHWR_HAL_STM32_ETH_MII_RXD2 );
    CYGHWR_HAL_STM32_GPIO_SET( CYGHWR_HAL_STM32_ETH_MII_RXD3 );
    CYGHWR_HAL_STM32_GPIO_SET( CYGHWR_HAL_STM32_ETH_MII_RX_ER );
    CYGHWR_HAL_STM32_GPIO_SET( CYGHWR_HAL_STM32_ETH_MII_TX_EN );
    CYGHWR_HAL_STM32_GPIO_SET( CYGHWR_HAL_STM32_ETH_MII_TXD0 );
    CYGHWR_HAL_STM32_GPIO_SET( CYGHWR_HAL_STM32_ETH_MII_TXD1 );
    CYGHWR_HAL_STM32_GPIO_SET( CYGHWR_HAL_STM32_ETH_MII_PPS_OUT );
    CYGHWR_HAL_STM32_GPIO_SET( CYGHWR_HAL_STM32_ETH_MII_TXD3 );

#ifdef CYGHWR_HAL_STM32_ETH_MII_REMAP

    // Remap MII interface to alternate pins
    mapr |= CYGHWR_HAL_STM32_AFIO_MAPR_ETH_RMP;
    
#endif // CYGHWR_HAL_STM32_ETH_MII_REMAP
    
#endif // CYGSEM_DEVS_ETH_CORTEXM_STM32_RMII

    // Write back AFIO MAPR register with new mappings.
    HAL_WRITE_UINT32( afio+CYGHWR_HAL_STM32_AFIO_MAPR, mapr );
    
    eth_diag("done");    
}

//======================================================================
// Set MAC address

static void stm32_eth_set_mac( stm32_eth_common *eth, cyg_uint8 *mac )
{
    cyg_uint32 hi, lo;

    eth_diag("mac: %02x:%02x:%02x:%02x:%02x:%02x", mac[0],mac[1],mac[2],mac[3],mac[4],mac[5]);
    
    lo = ( mac[0] <<  0 ) |
         ( mac[1] <<  8 ) |
         ( mac[2] << 16 ) |
         ( mac[3] << 24 ) ;

    hi = ( mac[4] <<  0 ) |
         ( mac[5] <<  8 ) ;

    HAL_WRITE_UINT32( eth->base+CYGHWR_HAL_STM32_ETH_MACA0HR, hi );
    HAL_WRITE_UINT32( eth->base+CYGHWR_HAL_STM32_ETH_MACA0LR, lo );
}


//======================================================================
// Initialize MAC

static cyg_bool stm32_eth_init( stm32_eth_common *eth )
{
    cyg_uint32 base = eth->base;
    cyg_uint32 reg;

    eth_diag("build time: %s %s", __DATE__, __TIME__);

    // Reset DMA subsystem
    HAL_WRITE_UINT32( base+CYGHWR_HAL_STM32_ETH_DMABMR,
                      CYGHWR_HAL_STM32_ETH_DMABMR_SR );

    do
    {
        HAL_READ_UINT32( base+CYGHWR_HAL_STM32_ETH_DMABMR, reg );
    } while( (reg & CYGHWR_HAL_STM32_ETH_DMABMR_SR) != 0 );

#if 0
    // Initialize configuration register
    HAL_WRITE_UINT32( base+CYGHWR_HAL_STM32_ETH_MACCR, 0 );
#endif
    
    // Frame filter register
    HAL_WRITE_UINT32( base+CYGHWR_HAL_STM32_ETH_MACFFR, 0 );

#if 0
    // Hash table registers
    HAL_WRITE_UINT32( base+CYGHWR_HAL_STM32_ETH_MACHTHR, 0 );
    HAL_WRITE_UINT32( base+CYGHWR_HAL_STM32_ETH_MACHTLR, 0 );
    
    // Flow control register
    HAL_WRITE_UINT32( base+CYGHWR_HAL_STM32_ETH_MACFCR, 0 );

    // VLAN register
    HAL_WRITE_UINT32( base+CYGHWR_HAL_STM32_ETH_MACVLANTR, 0 );

    // Power management
    HAL_WRITE_UINT32( base+CYGHWR_HAL_STM32_ETH_MACPMTCSR, 0 );
    
    // Interrupt mask
    HAL_WRITE_UINT32( base+CYGHWR_HAL_STM32_ETH_MACIMR,
                      CYGHWR_HAL_STM32_ETH_MACIMR_PMTIM |
                      CYGHWR_HAL_STM32_ETH_MACIMR_TSTIM );

    // Clear all counters
    HAL_WRITE_UINT32( base+CYGHWR_HAL_STM32_ETH_MMCCR,
                      CYGHWR_HAL_STM32_ETH_MMCCR_CR);

    // Mask counter interrupts
    HAL_WRITE_UINT32( base+CYGHWR_HAL_STM32_ETH_MMCRIMR,
                      CYGHWR_HAL_STM32_ETH_MMCRIR_RFCES |
                      CYGHWR_HAL_STM32_ETH_MMCRIR_RFAES |
                      CYGHWR_HAL_STM32_ETH_MMCRIR_RGUFS );
    HAL_WRITE_UINT32( base+CYGHWR_HAL_STM32_ETH_MMCTIMR,
                      CYGHWR_HAL_STM32_ETH_MMCTIR_TGFSCS  |
                      CYGHWR_HAL_STM32_ETH_MMCTIR_TGFMSCS |
                      CYGHWR_HAL_STM32_ETH_MMCTIR_TGFS    );
#endif

    eth_diag("done");
    return true;
}

//======================================================================
// Enable MAC

static cyg_bool stm32_eth_enable( stm32_eth_common *eth )
{
    cyg_uint32 base = eth->base;
    cyg_uint32 reg;

    eth_diag("enter");
    
    // Enable TX and RX engines in MAC
    HAL_READ_UINT32( base+CYGHWR_HAL_STM32_ETH_MACCR, reg );
    reg |= CYGHWR_HAL_STM32_ETH_MACCR_RE;
    reg |= CYGHWR_HAL_STM32_ETH_MACCR_TE;
    HAL_WRITE_UINT32( base+CYGHWR_HAL_STM32_ETH_MACCR, reg );    

    // Enable RX engine in DMA, TX will be enabled later
    HAL_READ_UINT32( base+CYGHWR_HAL_STM32_ETH_DMAOMR, reg );
    reg |= CYGHWR_HAL_STM32_ETH_DMAOMR_SR;
    HAL_WRITE_UINT32( base+CYGHWR_HAL_STM32_ETH_DMAOMR, reg );

    // Enable DMA interrupts
    HAL_READ_UINT32( base+CYGHWR_HAL_STM32_ETH_DMAIER, reg );
    reg |= CYGHWR_HAL_STM32_ETH_DMAIER_NISE;
    reg |= CYGHWR_HAL_STM32_ETH_DMAIER_TIE;
    reg |= CYGHWR_HAL_STM32_ETH_DMAIER_RIE;
    HAL_WRITE_UINT32( base+CYGHWR_HAL_STM32_ETH_DMAIER, reg );

    // Ensure DMA engine has seen the RX descriptor ring
    HAL_WRITE_UINT32( eth->base+CYGHWR_HAL_STM32_ETH_DMARPDR, 0 );

    eth_diag("done");
    
    return true;
}

//======================================================================

static void stm32_eth_tx_start( stm32_eth_common *eth )
{
    cyg_uint32 base = eth->base;
    cyg_uint32 reg;

    eth_diag("start tx");
    HAL_READ_UINT32( base+CYGHWR_HAL_STM32_ETH_DMAOMR, reg );
    reg |= CYGHWR_HAL_STM32_ETH_DMAOMR_ST;
    HAL_WRITE_UINT32( base+CYGHWR_HAL_STM32_ETH_DMAOMR, reg );

    HAL_WRITE_UINT32( base+CYGHWR_HAL_STM32_ETH_DMATPDR, 0 );
}

//======================================================================

static void stm32_eth_tx_stop( stm32_eth_common *eth )
{
    cyg_uint32 base = eth->base;    
    cyg_uint32 reg;

    eth_diag("stop tx");    
    HAL_READ_UINT32( base+CYGHWR_HAL_STM32_ETH_DMAOMR, reg );
    reg &= ~CYGHWR_HAL_STM32_ETH_DMAOMR_ST;
    HAL_WRITE_UINT32( base+CYGHWR_HAL_STM32_ETH_DMAOMR, reg );
}

//======================================================================

static void stm32_eth_rx_start( stm32_eth_common *eth )
{
    eth_diag("start rx");
    HAL_WRITE_UINT32( eth->base+CYGHWR_HAL_STM32_ETH_DMARPDR, 0 );    
}

//======================================================================
// Get the PHY started, read the state from it and set the MAC up
// to match.

static cyg_bool stm32_eth_phy_init( stm32_eth_common *eth )
{
    unsigned short      phy_state = 0;

    eth_diag("enter");
    
    if( !_eth_phy_init(eth->phy) )
        return false;

    phy_state = _eth_phy_state(eth->phy);

    if ((phy_state & ETH_PHY_STAT_LINK) != 0)
    {
        cyg_uint32          cfg;
        HAL_READ_UINT32( eth->base+CYGHWR_HAL_STM32_ETH_MACCR, cfg );
    
        if (((phy_state & ETH_PHY_STAT_100MB) != 0))
        {
            eth_diag("100Mbyte/s");
            cfg |= CYGHWR_HAL_STM32_ETH_MACCR_FES;
        }
        else
        {
            eth_diag("10Mbyte/s");
            cfg &= ~CYGHWR_HAL_STM32_ETH_MACCR_FES;
        }
        if((phy_state & ETH_PHY_STAT_FDX))
        {
            eth_diag("Full Duplex");
            cfg |= CYGHWR_HAL_STM32_ETH_MACCR_DM;
        }
        else
        {
            eth_diag("Half Duplex");
            cfg &= ~CYGHWR_HAL_STM32_ETH_MACCR_DM;
        }

        HAL_WRITE_UINT32( eth->base+CYGHWR_HAL_STM32_ETH_MACCR, cfg );        
    }
    else
    {
        eth_diag("No Link");
        return false;
    }
    eth_diag("done");
    return true;
}

//======================================================================
// PHY support


// ---------------------------------------------------------------------
// Write one of the PHY registers via the MII bus

static void
stm32_write_phy(int reg_addr, int phy_addr, unsigned short data)
{
    cyg_uint32 base = CYGHWR_HAL_STM32_ETH;
    cyg_uint32 val, cnt;

    phy_debug_printf("phy %d reg %d data %04x", phy_addr, reg_addr, (unsigned)data);

    HAL_READ_UINT32(base+CYGHWR_HAL_STM32_ETH_MACMIIAR , val);
    val &= CYGHWR_HAL_STM32_ETH_MACMIIAR_CR_MASK;
       
    val |= CYGHWR_HAL_STM32_ETH_MACMIIAR_MB           |
           CYGHWR_HAL_STM32_ETH_MACMIIAR_MW           |
           CYGHWR_HAL_STM32_ETH_MACMIIAR_MR(reg_addr) |
           CYGHWR_HAL_STM32_ETH_MACMIIAR_PA(phy_addr) ;
   
    HAL_WRITE_UINT32(base+CYGHWR_HAL_STM32_ETH_MACMIIDR , data);
    HAL_WRITE_UINT32(base+CYGHWR_HAL_STM32_ETH_MACMIIAR , val);

    for (cnt=0; cnt < 1000000; cnt++)
    {
        HAL_READ_UINT32(base+CYGHWR_HAL_STM32_ETH_MACMIIAR, val);
        if((val & CYGHWR_HAL_STM32_ETH_MACMIIAR_MB) == 0)
            break;
    }
    CYG_ASSERTC(cnt < 1000000);
}

// ---------------------------------------------------------------------
// Read one of the PHY registers via the MII bus

static bool
stm32_read_phy(int reg_addr, int phy_addr, unsigned short *data)
{
    cyg_uint32 base = CYGHWR_HAL_STM32_ETH;
    cyg_uint32 val, cnt;

    HAL_READ_UINT32(base+CYGHWR_HAL_STM32_ETH_MACMIIAR , val);
    val &= CYGHWR_HAL_STM32_ETH_MACMIIAR_CR_MASK;
    
    val |= CYGHWR_HAL_STM32_ETH_MACMIIAR_MB           |
           CYGHWR_HAL_STM32_ETH_MACMIIAR_MR(reg_addr) |
           CYGHWR_HAL_STM32_ETH_MACMIIAR_PA(phy_addr) ;

    HAL_WRITE_UINT32(base+CYGHWR_HAL_STM32_ETH_MACMIIAR , val);

    for (cnt=0; cnt < 1000000; cnt++)
    {
        HAL_READ_UINT32(base+CYGHWR_HAL_STM32_ETH_MACMIIAR, val);
        if((val & CYGHWR_HAL_STM32_ETH_MACMIIAR_MB) == 0)
            break;
    }
    CYG_ASSERTC(cnt < 1000000);

    if( cnt == 1000000 )
        return false;
    
    HAL_READ_UINT32(base+CYGHWR_HAL_STM32_ETH_MACMIIDR , val);
    *data = (unsigned short)val;
    
    phy_debug_printf("phy %d reg %d data %04x", phy_addr, reg_addr, (unsigned)*data);   

    return true;
}

// ---------------------------------------------------------------------

__externC cyg_uint32 hal_stm32_hclk;

static void 
stm32_init_phy(void)
{
    cyg_uint32 base = CYGHWR_HAL_STM32_ETH;
    cyg_uint32 val;
    
    if( hal_stm32_hclk >= 60000000 ) val = CYGHWR_HAL_STM32_ETH_MACMIIAR_CR(0);
    else if( hal_stm32_hclk >= 35000000 ) val = CYGHWR_HAL_STM32_ETH_MACMIIAR_CR(3);
    else val = CYGHWR_HAL_STM32_ETH_MACMIIAR_CR(2);
   
    HAL_WRITE_UINT32(base+CYGHWR_HAL_STM32_ETH_MACMIIAR , val);
    
    return;
}

// ---------------------------------------------------------------------
// Instantiate the PHY device access driver

ETH_PHY_REG_LEVEL_ACCESS_FUNS(stm32_phy, 
                              stm32_init_phy,
                              NULL,
                              stm32_write_phy,
                              stm32_read_phy);


//======================================================================
// end of stm32_common.h
