1
0
mirror of https://github.com/mist-devel/mist-firmware.git synced 2026-04-19 00:47:25 +00:00
Files
Gyorgy Szombathelyi ceb1fc0271 Add a SAMV71 port
2024-03-11 08:53:34 +01:00

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;
}
/**@}*/