mirror of
https://github.com/mist-devel/mist-firmware.git
synced 2026-04-19 00:47:25 +00:00
404 lines
9.7 KiB
C
404 lines
9.7 KiB
C
/* ----------------------------------------------------------------------------
|
|
* SAM Software Package License
|
|
* ----------------------------------------------------------------------------
|
|
* Copyright (c) 2015, Atmel Corporation
|
|
*
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
*
|
|
* - Redistributions of source code must retain the above copyright notice,
|
|
* this list of conditions and the disclaimer below.
|
|
*
|
|
* Atmel's name may not be used to endorse or promote products derived from
|
|
* this software without specific prior written permission.
|
|
*
|
|
* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
|
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
|
|
/** \file */
|
|
|
|
/** \addtogroup emac_functions
|
|
*@{
|
|
*/
|
|
|
|
/*----------------------------------------------------------------------------
|
|
* Headers
|
|
*----------------------------------------------------------------------------*/
|
|
|
|
#include <errno.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include "chip.h"
|
|
#include "network/emac.h"
|
|
#include "debug.h"
|
|
|
|
/*----------------------------------------------------------------------------
|
|
* Local functions
|
|
*----------------------------------------------------------------------------*/
|
|
|
|
static bool _emac_configure_mdc_clock(Emac *emac)
|
|
{
|
|
uint32_t mck, clk;
|
|
|
|
mck = pmc_get_peripheral_clock(get_emac_id_from_addr(emac));
|
|
|
|
/* Disable RX/TX */
|
|
emac->EMAC_NCR &= ~(EMAC_NCR_RE | EMAC_NCR_TE);
|
|
|
|
/* Find divider */
|
|
if (mck <= 20000000) {
|
|
clk = EMAC_NCFGR_CLK_MCK_8; // MCK/8
|
|
} else if (mck <= 40000000) {
|
|
clk = EMAC_NCFGR_CLK_MCK_16; // MCK/16
|
|
} else if (mck <= 80000000) {
|
|
clk = EMAC_NCFGR_CLK_MCK_32; // MCK/32
|
|
} else {
|
|
clk = EMAC_NCFGR_CLK_MCK_64; // MCK/64
|
|
}
|
|
if (mck > 160000000) {
|
|
eth_info("MDC clock is %dHz\r\n",mck / 64);
|
|
}
|
|
/* configure MDC clock divider and enable RX/TX */
|
|
emac->EMAC_NCFGR = (emac->EMAC_NCFGR & ~EMAC_NCFGR_CLK_Msk) | clk;
|
|
emac->EMAC_NCR |= (EMAC_NCR_RE | EMAC_NCR_TE);
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool _emac_phy_wait_idle(Emac* emac, uint32_t idle_timeout)
|
|
{
|
|
struct _timeout timeout;
|
|
timer_start_timeout(&timeout, idle_timeout);
|
|
while ((emac->EMAC_NSR & EMAC_NSR_IDLE) == 0) {
|
|
if (timer_timeout_reached(&timeout)) {
|
|
eth_debug("Timeout reached while waiting for PHY management logic to become idle");
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static void _emac_set_link_speed(Emac* emac, enum _eth_speed speed, enum _eth_duplex duplex)
|
|
{
|
|
/* Configure duplex */
|
|
switch (duplex) {
|
|
case ETH_DUPLEX_HALF:
|
|
emac->EMAC_NCFGR &= ~EMAC_NCFGR_FD;
|
|
break;
|
|
case ETH_DUPLEX_FULL:
|
|
emac->EMAC_NCFGR |= EMAC_NCFGR_FD;
|
|
break;
|
|
default:
|
|
eth_error("Invalid duplex value %d\r\n", duplex);
|
|
return;
|
|
}
|
|
|
|
/* Configure speed */
|
|
switch (speed) {
|
|
case ETH_SPEED_10M:
|
|
emac->EMAC_NCFGR &= ~EMAC_NCFGR_SPD;
|
|
break;
|
|
case ETH_SPEED_100M:
|
|
emac->EMAC_NCFGR |= EMAC_NCFGR_SPD;
|
|
break;
|
|
default:
|
|
eth_error("Invalid speed value %d\r\n", speed);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------
|
|
* Exported functions
|
|
*----------------------------------------------------------------------------*/
|
|
|
|
bool emac_configure(Emac* emac)
|
|
{
|
|
pmc_configure_peripheral(get_emac_id_from_addr(emac), NULL, true);
|
|
|
|
/* Disable TX & RX and more */
|
|
emac_set_network_control_register(emac, 0);
|
|
emac_set_network_config_register(emac, 0);
|
|
|
|
/* Disable interrupts */
|
|
emac_disable_it(emac, ~0u);
|
|
|
|
/* Clear statistics */
|
|
emac_clear_statistics(emac);
|
|
|
|
/* Clear all status bits in the receive status register. */
|
|
emac_clear_rx_status(emac, EMAC_RSR_OVR | EMAC_RSR_REC | EMAC_RSR_BNA);
|
|
|
|
/* Clear all status bits in the transmit status register */
|
|
emac_clear_tx_status(emac, EMAC_TSR_UBR | EMAC_TSR_COL | EMAC_TSR_RLES | EMAC_TSR_TGO | EMAC_TSR_COMP | EMAC_TSR_UND);
|
|
|
|
/* Clear interrupts */
|
|
emac_get_it_status(emac);
|
|
|
|
return _emac_configure_mdc_clock(emac);
|
|
}
|
|
|
|
void emac_set_network_control_register(Emac* emac, uint32_t ncr)
|
|
{
|
|
emac->EMAC_NCR = ncr;
|
|
}
|
|
|
|
uint32_t emac_get_network_control_register(Emac* emac)
|
|
{
|
|
return emac->EMAC_NCR;
|
|
}
|
|
|
|
void emac_set_network_config_register(Emac* emac, uint32_t ncfgr)
|
|
{
|
|
emac->EMAC_NCFGR = ncfgr;
|
|
}
|
|
|
|
uint32_t emac_get_network_config_register(Emac* emac)
|
|
{
|
|
return emac->EMAC_NCFGR;
|
|
}
|
|
|
|
void emac_enable_mdio(Emac* emac)
|
|
{
|
|
/* Disable RX/TX */
|
|
emac->EMAC_NCR &= ~(EMAC_NCR_RE | EMAC_NCR_TE);
|
|
|
|
/* Enable MDIO */
|
|
emac->EMAC_NCR |= EMAC_NCR_MPE;
|
|
|
|
/* Enable RX/TX */
|
|
emac->EMAC_NCR |= (EMAC_NCR_RE | EMAC_NCR_TE);
|
|
}
|
|
|
|
void emac_disable_mdio(Emac* emac)
|
|
{
|
|
/* Disable RX/TX */
|
|
emac->EMAC_NCR &= ~(EMAC_NCR_RE | EMAC_NCR_TE);
|
|
|
|
/* Disable MDIO */
|
|
emac->EMAC_NCR &= ~EMAC_NCR_MPE;
|
|
|
|
/* Enable RX/TX */
|
|
emac->EMAC_NCR |= (EMAC_NCR_RE | EMAC_NCR_TE);
|
|
}
|
|
|
|
int emac_phy_read(Emac* emac, uint8_t phy_addr, uint8_t reg_addr, uint16_t* data, uint32_t idle_timeout)
|
|
{
|
|
/* Wait until idle */
|
|
if (!_emac_phy_wait_idle(emac, idle_timeout))
|
|
return -EBUSY;
|
|
|
|
/* Write maintenance register */
|
|
emac->EMAC_MAN = EMAC_MAN_SOF(0x1) |
|
|
EMAC_MAN_RW(2) |
|
|
EMAC_MAN_CODE(2) |
|
|
EMAC_MAN_PHYA(phy_addr) |
|
|
EMAC_MAN_REGA(reg_addr);
|
|
|
|
/* Wait until idle */
|
|
if (!_emac_phy_wait_idle(emac, idle_timeout))
|
|
return -EBUSY;
|
|
|
|
*data = (emac->EMAC_MAN & EMAC_MAN_DATA_Msk) >> EMAC_MAN_DATA_Pos;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int emac_phy_write(Emac* emac, uint8_t phy_addr, uint8_t reg_addr, uint16_t data,
|
|
uint32_t idle_timeout)
|
|
{
|
|
/* Wait until idle */
|
|
if (!_emac_phy_wait_idle(emac, idle_timeout))
|
|
return -EBUSY;
|
|
|
|
/* Write maintenance register */
|
|
emac->EMAC_MAN = EMAC_MAN_SOF(0x1) |
|
|
EMAC_MAN_RW(1) |
|
|
EMAC_MAN_CODE(2) |
|
|
EMAC_MAN_PHYA(phy_addr) |
|
|
EMAC_MAN_REGA(reg_addr) |
|
|
EMAC_MAN_DATA(data);
|
|
|
|
/* Wait until idle */
|
|
if (!_emac_phy_wait_idle(emac, idle_timeout))
|
|
return -EBUSY;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void emac_enable_rmii(Emac* emac, enum _eth_speed speed, enum _eth_duplex duplex)
|
|
{
|
|
/* Disable RX/TX */
|
|
emac->EMAC_NCR &= ~(EMAC_NCR_RE | EMAC_NCR_TE);
|
|
|
|
/* Configure speed/duplex */
|
|
_emac_set_link_speed(emac, speed, duplex);
|
|
|
|
/* Enable RMII */
|
|
emac->EMAC_USRIO |= (EMAC_USRIO_RMII | EMAC_USRIO_CLKEN);
|
|
|
|
/* Enable RX/TX */
|
|
emac->EMAC_NCR |= (EMAC_NCR_RE | EMAC_NCR_TE);
|
|
}
|
|
|
|
void emac_set_link_speed(Emac* emac, enum _eth_speed speed, enum _eth_duplex duplex)
|
|
{
|
|
/* Disable RX/TX */
|
|
emac->EMAC_NCR &= ~(EMAC_NCR_RE | EMAC_NCR_TE);
|
|
|
|
/* Configure speed/duplex */
|
|
_emac_set_link_speed(emac, speed, duplex);
|
|
|
|
/* Enable RX/TX */
|
|
emac->EMAC_NCR |= (EMAC_NCR_RE | EMAC_NCR_TE);
|
|
}
|
|
|
|
void emac_enable_local_loopback(Emac* emac)
|
|
{
|
|
emac->EMAC_NCR |= EMAC_NCR_LLB;
|
|
}
|
|
|
|
void emac_disable_local_loopback(Emac* emac)
|
|
{
|
|
emac->EMAC_NCR &= ~EMAC_NCR_LLB;
|
|
}
|
|
|
|
uint32_t emac_get_tx_status(Emac* emac)
|
|
{
|
|
return emac->EMAC_TSR;
|
|
}
|
|
|
|
void emac_clear_tx_status(Emac* emac, uint32_t mask)
|
|
{
|
|
emac->EMAC_TSR = mask;
|
|
}
|
|
|
|
uint32_t emac_get_rx_status(Emac* emac)
|
|
{
|
|
return emac->EMAC_RSR;
|
|
}
|
|
|
|
void emac_clear_rx_status(Emac* emac, uint32_t mask)
|
|
{
|
|
emac->EMAC_RSR = mask;
|
|
}
|
|
|
|
void emac_receive_enable(Emac* emac, bool enable)
|
|
{
|
|
if (enable)
|
|
emac->EMAC_NCR |= EMAC_NCR_RE;
|
|
else
|
|
emac->EMAC_NCR &= ~EMAC_NCR_RE;
|
|
}
|
|
|
|
void emac_transmit_enable(Emac* emac, bool enable)
|
|
{
|
|
if (enable)
|
|
emac->EMAC_NCR |= EMAC_NCR_TE;
|
|
else
|
|
emac->EMAC_NCR &= ~EMAC_NCR_TE;
|
|
}
|
|
|
|
void emac_set_rx_desc(Emac* emac, struct _eth_desc* desc)
|
|
{
|
|
emac->EMAC_RBQP = ((uint32_t)desc) & EMAC_RBQP_ADDR_Msk;
|
|
}
|
|
|
|
struct _eth_desc* emac_get_rx_desc(Emac* emac)
|
|
{
|
|
return (struct _eth_desc*)(emac->EMAC_RBQP & EMAC_RBQP_ADDR_Msk);
|
|
}
|
|
|
|
void emac_set_tx_desc(Emac* emac, struct _eth_desc* desc)
|
|
{
|
|
emac->EMAC_TBQP = ((uint32_t)desc) & EMAC_TBQP_ADDR_Msk;
|
|
}
|
|
|
|
struct _eth_desc* emac_get_tx_desc(Emac* emac)
|
|
{
|
|
return (struct _eth_desc*)(emac->EMAC_TBQP & EMAC_TBQP_ADDR_Msk);
|
|
}
|
|
|
|
uint32_t emac_get_it_mask(Emac* emac)
|
|
{
|
|
return emac->EMAC_IMR;
|
|
}
|
|
|
|
void emac_enable_it(Emac* emac, uint32_t mask)
|
|
{
|
|
emac->EMAC_IER = mask;
|
|
}
|
|
|
|
void emac_disable_it(Emac * emac, uint32_t mask)
|
|
{
|
|
emac->EMAC_IDR = mask;
|
|
}
|
|
|
|
uint32_t emac_get_it_status(Emac* emac)
|
|
{
|
|
return emac->EMAC_ISR;
|
|
}
|
|
|
|
void emac_set_mac_addr(Emac* emac, uint8_t sa_idx, uint8_t* mac)
|
|
{
|
|
emac->EMAC_SA[sa_idx].EMAC_SAB = (mac[3] << 24) | (mac[2] << 16) | (mac[1] << 8) | mac[0];
|
|
emac->EMAC_SA[sa_idx].EMAC_SAT = (mac[5] << 8) | mac[4];
|
|
}
|
|
|
|
void emac_get_mac_addr(Emac* emac, uint8_t sa_idx, uint8_t* mac)
|
|
{
|
|
uint32_t sab = emac->EMAC_SA[sa_idx].EMAC_SAB;
|
|
uint32_t sat = emac->EMAC_SA[sa_idx].EMAC_SAT;
|
|
|
|
mac[0] = (uint8_t)((sab & 0x000000ff) >> 0);
|
|
mac[1] = (uint8_t)((sab & 0x0000ff00) >> 8);
|
|
mac[2] = (uint8_t)((sab & 0x00ff0000) >> 16);
|
|
mac[3] = (uint8_t)((sab & 0xff000000) >> 24);
|
|
mac[4] = (uint8_t)((sat & 0x000000ff) >> 0);
|
|
mac[5] = (uint8_t)((sat & 0x0000ff00) >> 8);
|
|
}
|
|
|
|
void emac_clear_statistics(Emac* emac)
|
|
{
|
|
emac->EMAC_NCR |= EMAC_NCR_CLRSTAT;
|
|
}
|
|
|
|
void emac_increase_statistics(Emac* emac)
|
|
{
|
|
emac->EMAC_NCR |= EMAC_NCR_INCSTAT;
|
|
}
|
|
|
|
void emac_enable_statistics_write(Emac* emac, bool enable)
|
|
{
|
|
if (enable)
|
|
emac->EMAC_NCR |= EMAC_NCR_WESTAT;
|
|
else
|
|
emac->EMAC_NCR &= ~EMAC_NCR_WESTAT;
|
|
}
|
|
|
|
void emac_start_transmission(Emac * emac)
|
|
{
|
|
emac->EMAC_NCR |= EMAC_NCR_TSTART;
|
|
}
|
|
|
|
void emac_halt_transmission(Emac * emac)
|
|
{
|
|
emac->EMAC_NCR |= EMAC_NCR_THALT;
|
|
}
|
|
|
|
/**@}*/
|
|
|