mirror of https://github.com/ARMmbed/mbed-os.git
Ethernet driver for MPS2
Refactor SMSC9220 Ethernet controller driver Change-Id: I75c3c42d5675441de1292100a54c50d990070c6f Signed-off-by: Gabor Kertesz <gabor.kertesz@arm.com>pull/4414/head
parent
fdec3f51eb
commit
e4501f320b
|
@ -0,0 +1,694 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2017 ARM Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Code implementation file for the LAN Ethernet interface.
|
||||
*
|
||||
* This file is the based on mps2_ethernet_api and Selftest's ETH_MPS2.
|
||||
* MPS2 Selftest:https://silver.arm.com/browse/VEI10 ->
|
||||
* \ISCM-1-0\AN491\software\Selftest\v2m_mps2\
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "mbed_retarget.h"
|
||||
#include "mbed_wait_api.h"
|
||||
#include "SMM_MPS2.h"
|
||||
#include "smsc9220_eth.h"
|
||||
|
||||
#define REG_WRITE_TIME_OUT 50
|
||||
#define RESET_TIME_OUT 10
|
||||
#define PHY_RESET_TIME_OUT_MS 100
|
||||
|
||||
/* Forward declarations */
|
||||
|
||||
static unsigned int smsc9220_mac_regread(unsigned char regoffset, unsigned int *data);
|
||||
static unsigned int smsc9220_mac_regwrite(unsigned char regoffset, unsigned int data);
|
||||
static unsigned int smsc9220_phy_regread(unsigned char regoffset, unsigned int *data);
|
||||
static unsigned int smsc9220_phy_regwrite(unsigned char regoffset, unsigned int data);
|
||||
|
||||
static unsigned int smsc9220_read_id(void);
|
||||
static unsigned int smsc9220_soft_reset(void);
|
||||
static void smsc9220_set_txfifo(unsigned int val);
|
||||
static unsigned int smsc9220_wait_eeprom(void);
|
||||
static void smsc9220_init_irqs(void);
|
||||
static unsigned int smsc9220_check_phy(void);
|
||||
static unsigned int smsc9220_reset_phy(void);
|
||||
|
||||
static void smsc9220_advertise_cap(void);
|
||||
static void smsc9220_enable_xmit(void);
|
||||
static void smsc9220_enable_mac_xmit(void);
|
||||
static void smsc9220_enable_mac_recv(void);
|
||||
|
||||
/* SMSC9220 low-level operations */
|
||||
|
||||
/**
|
||||
* \brief Read MAC register.
|
||||
*
|
||||
* \param[in] regoffset Register offset
|
||||
* \param[out] data Register value is read
|
||||
*
|
||||
* \return 0 in case of success, 1 otherwise
|
||||
*/
|
||||
static unsigned int smsc9220_mac_regread(unsigned char regoffset, unsigned int *data)
|
||||
{
|
||||
unsigned int val = 0;
|
||||
unsigned int maccmd = 0;
|
||||
int time_out = REG_WRITE_TIME_OUT;
|
||||
|
||||
val = SMSC9220->MAC_CSR_CMD;
|
||||
if(!(val & ((unsigned int)1 << 31))) { /* Make sure there's no pending operation */
|
||||
maccmd |= regoffset;
|
||||
maccmd |= ((unsigned int)1 << 30); /* Indicates read */
|
||||
maccmd |= ((unsigned int)1 << 31); /* Start bit */
|
||||
SMSC9220->MAC_CSR_CMD = maccmd; /* Start operation */
|
||||
|
||||
do {
|
||||
val = SMSC9220->BYTE_TEST; /* A no-op read. */
|
||||
wait_ms(1);
|
||||
time_out--;
|
||||
} while(time_out && (SMSC9220->MAC_CSR_CMD & ((unsigned int)1 << 31)));
|
||||
|
||||
if(!time_out) {
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
*data = SMSC9220->MAC_CSR_DATA;
|
||||
}
|
||||
} else {
|
||||
*data = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Write MAC register.
|
||||
*
|
||||
* \param[in] regoffset Register offset
|
||||
* \param[in] data Register value to write
|
||||
*
|
||||
* \return 0 in case of success, 1 otherwise
|
||||
*/
|
||||
static unsigned int smsc9220_mac_regwrite(unsigned char regoffset, unsigned int data)
|
||||
{
|
||||
unsigned int read = 0;
|
||||
unsigned int maccmd = 0;
|
||||
int time_out = REG_WRITE_TIME_OUT;
|
||||
|
||||
read = SMSC9220->MAC_CSR_CMD;
|
||||
if(!(read & ((unsigned int)1 << 31))) { /* Make sure there's no pending operation */
|
||||
SMSC9220->MAC_CSR_DATA = data; /* Store data. */
|
||||
maccmd |= regoffset;
|
||||
maccmd &= ~((unsigned int)1 << 30); /* Clear indicates write */
|
||||
maccmd |= ((unsigned int)1 << 31); /* Indicate start of operation */
|
||||
SMSC9220->MAC_CSR_CMD = maccmd;
|
||||
|
||||
do {
|
||||
read = SMSC9220->BYTE_TEST; /* A no-op read. */
|
||||
wait_ms(1);
|
||||
time_out--;
|
||||
} while(time_out && (SMSC9220->MAC_CSR_CMD & ((unsigned int)1 << 31)));
|
||||
|
||||
if(!time_out) {
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
printf("Error: SMSC9220 MAC CSR is busy. No data written.\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Read PHY register.
|
||||
*
|
||||
* \param[in] regoffset Register offset
|
||||
* \param[out] data Register value is read
|
||||
*
|
||||
* \return 0 in case of success, 1 otherwise
|
||||
*/
|
||||
static unsigned int smsc9220_phy_regread(unsigned char regoffset, unsigned int *data)
|
||||
{
|
||||
unsigned int val = 0;
|
||||
unsigned int phycmd = 0;
|
||||
int time_out = REG_WRITE_TIME_OUT;
|
||||
|
||||
if (smsc9220_mac_regread(SMSC9220_MAC_MII_ACC, &val)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(!(val & 1)) { /* Not busy */
|
||||
phycmd = 0;
|
||||
phycmd |= (1 << 11); /* 1 to [15:11] */
|
||||
phycmd |= ((regoffset & 0x1F) << 6); /* Put regoffset to [10:6] */
|
||||
phycmd &= ~(1 << 1); /* Clear [1] indicates read. */
|
||||
phycmd |= (1 << 0); /* Set [0] indicates operation start */
|
||||
|
||||
if (smsc9220_mac_regwrite(SMSC9220_MAC_MII_ACC, phycmd)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
val = 0;
|
||||
do {
|
||||
wait_ms(1);
|
||||
time_out--;
|
||||
if (smsc9220_mac_regread(SMSC9220_MAC_MII_ACC,&val)) {
|
||||
return 1;
|
||||
}
|
||||
} while(time_out && (val & ((unsigned int)1 << 0)));
|
||||
|
||||
if (!time_out) {
|
||||
return 1;
|
||||
} else if (smsc9220_mac_regread(SMSC9220_MAC_MII_DATA, data)) {
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
*data = 0;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Write PHY register.
|
||||
*
|
||||
* \param[in] regoffset Register offset
|
||||
* \param[in] data Register value to write
|
||||
*
|
||||
* \return 0 in case of success, 1 otherwise
|
||||
*/
|
||||
static unsigned int smsc9220_phy_regwrite(unsigned char regoffset, unsigned int data)
|
||||
{
|
||||
unsigned int val = 0;
|
||||
unsigned int phycmd = 0;
|
||||
int time_out = REG_WRITE_TIME_OUT;
|
||||
|
||||
if (smsc9220_mac_regread(SMSC9220_MAC_MII_ACC, &val)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(!(val & 1)) { /* Not busy */
|
||||
/* Load the data */
|
||||
if (smsc9220_mac_regwrite(SMSC9220_MAC_MII_DATA, (data & 0xFFFF))) {
|
||||
return 1;
|
||||
}
|
||||
phycmd = 0;
|
||||
phycmd |= (1 << 11); /* 1 to [15:11] */
|
||||
phycmd |= ((regoffset & 0x1F) << 6); /* Put regoffset to [10:6] */
|
||||
phycmd |= (1 << 1); /* Set [1] indicates write. */
|
||||
phycmd |= (1 << 0); /* Set [0] indicates operation start */
|
||||
/* Start operation */
|
||||
if (smsc9220_mac_regwrite(SMSC9220_MAC_MII_ACC, phycmd)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
phycmd = 0;
|
||||
|
||||
do {
|
||||
wait_ms(1);
|
||||
time_out--;
|
||||
if (smsc9220_mac_regread(SMSC9220_MAC_MII_ACC, &phycmd)){
|
||||
return 1;
|
||||
}
|
||||
} while(time_out && (phycmd & (1 << 0)));
|
||||
|
||||
if (!time_out) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
} else {
|
||||
printf("Error: SMSC9220 MAC MII is busy. No data written.\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Read SMSC9220 ID.
|
||||
*
|
||||
* \return ID number
|
||||
*/
|
||||
inline static unsigned int smsc9220_read_id(void)
|
||||
{
|
||||
return SMSC9220->ID_REV;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Initiates a soft reset, returns failure or success.
|
||||
*
|
||||
* \return 0 in case of success, 1 otherwise
|
||||
*/
|
||||
static unsigned int smsc9220_soft_reset(void)
|
||||
{
|
||||
int time_out = RESET_TIME_OUT;
|
||||
|
||||
/* Soft reset */
|
||||
SMSC9220->HW_CFG |= 1;
|
||||
|
||||
do {
|
||||
wait_ms(1);
|
||||
time_out--;
|
||||
} while(time_out && (SMSC9220->HW_CFG & 1));
|
||||
|
||||
if (!time_out) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set maximum transition unit by Tx fifo size.
|
||||
* Note: The MTU will be smaller by 512 bytes,
|
||||
* because the status uses this fixed space.
|
||||
*
|
||||
* \param[in] val Size of the fifo in kbytes, 2-14
|
||||
*/
|
||||
static void smsc9220_set_txfifo(unsigned int val)
|
||||
{
|
||||
/* 2kb minimum, 14kb maximum */
|
||||
if(val >= 2 && val <= 14) {
|
||||
SMSC9220->HW_CFG = val << 16;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Wait for EEPROM to be ready to use.
|
||||
*
|
||||
* \return 0 if ready, 1 in case of timeout
|
||||
*/
|
||||
static unsigned int smsc9220_wait_eeprom(void)
|
||||
{
|
||||
int time_out = REG_WRITE_TIME_OUT;
|
||||
|
||||
do {
|
||||
wait_ms(1);
|
||||
time_out--;
|
||||
} while(time_out && (SMSC9220->E2P_CMD & ((unsigned int) 1 << 31)));
|
||||
|
||||
if (!time_out) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Initialise irqs
|
||||
*/
|
||||
static void smsc9220_init_irqs(void)
|
||||
{
|
||||
SMSC9220->INT_EN = 0x0;
|
||||
SMSC9220->INT_STS = 0xFFFFFFFF; /* clear all interrupts */
|
||||
SMSC9220->IRQ_CFG = 0x22000100; /* irq deassertion at 220 usecs and master IRQ enable. */
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Check PHY ID registers.
|
||||
*
|
||||
* \return 0 in case of success, 1 otherwise
|
||||
*/
|
||||
static unsigned int smsc9220_check_phy(void)
|
||||
{
|
||||
unsigned int phyid1, phyid2;
|
||||
|
||||
if (smsc9220_phy_regread(SMSC9220_PHY_ID1,&phyid1)) {
|
||||
return 1;
|
||||
}
|
||||
if (smsc9220_phy_regread(SMSC9220_PHY_ID2,&phyid2)) {
|
||||
return 1;
|
||||
}
|
||||
return ((phyid1 == 0xFFFF && phyid2 == 0xFFFF) ||
|
||||
(phyid1 == 0x0 && phyid2 == 0x0));
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Reset PHY
|
||||
*
|
||||
* \return 0 in case of success, 1 otherwise
|
||||
*/
|
||||
static unsigned int smsc9220_reset_phy(void)
|
||||
{
|
||||
unsigned int read;
|
||||
|
||||
if(smsc9220_phy_regread(SMSC9220_PHY_BCONTROL, &read)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
read |= (1 << 15);
|
||||
if(smsc9220_phy_regwrite(SMSC9220_PHY_BCONTROL, read)) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Advertise all speeds and pause capabilities
|
||||
*
|
||||
* \return 0 in case of success, 1 otherwise
|
||||
*/
|
||||
static void smsc9220_advertise_cap(void)
|
||||
{
|
||||
unsigned int aneg_adv = 0;
|
||||
|
||||
smsc9220_phy_regread(SMSC9220_PHY_ANEG_ADV, &aneg_adv);
|
||||
aneg_adv |= 0xDE0;
|
||||
|
||||
smsc9220_phy_regwrite(SMSC9220_PHY_ANEG_ADV, aneg_adv);
|
||||
smsc9220_phy_regread(SMSC9220_PHY_ANEG_ADV, &aneg_adv);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Enable trasmission
|
||||
*/
|
||||
inline static void smsc9220_enable_xmit(void)
|
||||
{
|
||||
SMSC9220->TX_CFG = 0x2;
|
||||
}
|
||||
|
||||
static void smsc9220_enable_mac_xmit(void)
|
||||
{
|
||||
unsigned int mac_cr = 0;
|
||||
|
||||
smsc9220_mac_regread(SMSC9220_MAC_CR, &mac_cr);
|
||||
|
||||
mac_cr |= (1 << 3); /* xmit enable */
|
||||
mac_cr |= (1 << 28); /* Heartbeat disable */
|
||||
|
||||
smsc9220_mac_regwrite(SMSC9220_MAC_CR, mac_cr);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Enable receive
|
||||
*/
|
||||
static void smsc9220_enable_mac_recv(void)
|
||||
{
|
||||
unsigned int mac_cr = 0;
|
||||
|
||||
smsc9220_mac_regread(SMSC9220_MAC_CR, &mac_cr);
|
||||
mac_cr |= (1 << 2); /* Recv enable */
|
||||
smsc9220_mac_regwrite(SMSC9220_MAC_CR, mac_cr);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Check device ID.
|
||||
*
|
||||
* \return 0 in case of success, 1 otherwise
|
||||
*/
|
||||
static int smsc9220_check_id(void)
|
||||
{
|
||||
unsigned int id = smsc9220_read_id();
|
||||
|
||||
/* If bottom and top halves of the word are the same */
|
||||
if(((id >> 16) & 0xFFFF) == (id & 0xFFFF)) {
|
||||
return 1;
|
||||
}
|
||||
switch(((id >> 16) & 0xFFFF)) {
|
||||
case 0x9220:
|
||||
break;
|
||||
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
Public API
|
||||
*----------------------------------------------------------------------------*/
|
||||
int smsc9220_init(void)
|
||||
{
|
||||
unsigned int phyreset = 0;
|
||||
|
||||
if(smsc9220_check_id()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(smsc9220_soft_reset()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
smsc9220_set_txfifo(5);
|
||||
|
||||
/* Sets automatic flow control thresholds, and backpressure */
|
||||
/* threshold to defaults specified. */
|
||||
SMSC9220->AFC_CFG = 0x006E3740;
|
||||
|
||||
if(smsc9220_wait_eeprom()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Configure GPIOs as LED outputs. */
|
||||
SMSC9220->GPIO_CFG = 0x70070000;
|
||||
|
||||
smsc9220_init_irqs();
|
||||
|
||||
/* Configure MAC addresses here if needed. */
|
||||
|
||||
if(smsc9220_check_phy()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(smsc9220_reset_phy()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
wait_ms(PHY_RESET_TIME_OUT_MS);
|
||||
/* Checking whether phy reset completed successfully.*/
|
||||
if (smsc9220_phy_regread(SMSC9220_PHY_BCONTROL, &phyreset)) {
|
||||
return 1;
|
||||
}
|
||||
if(phyreset & (1 << 15)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
smsc9220_advertise_cap();
|
||||
smsc9220_establish_link(); /* bit [12] of BCONTROL seems self-clearing. */
|
||||
/* Although it's not so in the manual. */
|
||||
|
||||
/* Interrupt threshold */
|
||||
SMSC9220->FIFO_INT = 0xFF000000;
|
||||
|
||||
smsc9220_enable_mac_xmit();
|
||||
smsc9220_enable_xmit();
|
||||
SMSC9220->RX_CFG = 0;
|
||||
smsc9220_enable_mac_recv();
|
||||
|
||||
/* Rx status FIFO level irq threshold */
|
||||
SMSC9220->FIFO_INT &= ~(0xFF); /* Clear 2 bottom nibbles */
|
||||
|
||||
/* This sleep is compulsory otherwise txmit/receive will fail. */
|
||||
wait_ms(2000);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void smsc9220_enable_interrupt(enum smsc9220_interrupt_source source)
|
||||
{
|
||||
SMSC9220->INT_EN |= (1 << source);
|
||||
}
|
||||
|
||||
void smsc9220_disable_interrupt(enum smsc9220_interrupt_source source)
|
||||
{
|
||||
SMSC9220->INT_EN &= ~(1 << source);
|
||||
}
|
||||
|
||||
void smsc9220_clear_interrupt(enum smsc9220_interrupt_source source)
|
||||
{
|
||||
SMSC9220->INT_STS |= (1 << source);
|
||||
}
|
||||
|
||||
int smsc9220_get_interrupt(enum smsc9220_interrupt_source source)
|
||||
{
|
||||
return (SMSC9220->INT_STS & (1 << source));
|
||||
}
|
||||
|
||||
void smsc9220_establish_link(void)
|
||||
{
|
||||
unsigned int bcr = 0;
|
||||
unsigned int hw_cfg = 0;
|
||||
|
||||
smsc9220_phy_regread(SMSC9220_PHY_BCONTROL, &bcr);
|
||||
bcr |= (1 << 12) | (1 << 9);
|
||||
smsc9220_phy_regwrite(SMSC9220_PHY_BCONTROL, bcr);
|
||||
smsc9220_phy_regread(SMSC9220_PHY_BCONTROL, &bcr);
|
||||
|
||||
hw_cfg = SMSC9220->HW_CFG;
|
||||
hw_cfg &= 0xF0000;
|
||||
hw_cfg |= (1 << 20);
|
||||
SMSC9220->HW_CFG = hw_cfg;
|
||||
}
|
||||
|
||||
int smsc9220_read_mac_address(char *mac)
|
||||
{
|
||||
unsigned int mac_low = 0;
|
||||
unsigned int mac_high = 0;
|
||||
|
||||
if( !mac ) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Read current mac address. */
|
||||
if (smsc9220_mac_regread(SMSC9220_MAC_ADDRH, &mac_high)) {
|
||||
return 1;
|
||||
}
|
||||
if (smsc9220_mac_regread(SMSC9220_MAC_ADDRL, &mac_low)) {
|
||||
return 1;
|
||||
}
|
||||
mac[0] = mac_low & 0xFF;
|
||||
mac[1] = (mac_low >> 8) & 0xFF;
|
||||
mac[2] = (mac_low >> 16) & 0xFF;
|
||||
mac[3] = (mac_low >> 24) & 0xFF;
|
||||
mac[4] = mac_high & 0xFF;
|
||||
mac[5] = (mac_high >> 8) & 0xFF;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int smsc9220_get_tx_data_fifo_size(void)
|
||||
{
|
||||
const unsigned int tx_status_fifo_size = 512; /* fixed allocation in bytes */
|
||||
unsigned int tx_fifo_size = SMSC9220->HW_CFG;
|
||||
tx_fifo_size = (( tx_fifo_size >> 16 ) & 0x0F) * 1024; /* size is set in kbytes */
|
||||
return (tx_fifo_size - tx_status_fifo_size);
|
||||
}
|
||||
|
||||
int smsc9220_send_by_chunks(unsigned int total_packet_length, int is_new_packet,
|
||||
const char *data, unsigned int current_size)
|
||||
{
|
||||
static unsigned int ongoing_packet_length = 0; /* size in bytes of the packet is sending */
|
||||
static unsigned int ongoing_packet_length_sent = 0; /* size in bytes of the packet has been sent */
|
||||
int is_first_segment = 0; /* signing this is the first segment of the packet to be sent */
|
||||
int is_last_segment = 0; /* signing this is the last segment of the packet to be sent */
|
||||
unsigned int txcmd_a, txcmd_b = 0;
|
||||
unsigned int dwords_to_write = 0;
|
||||
unsigned int *pktptr = 0;
|
||||
unsigned int xmit_inf = 0;
|
||||
unsigned int tx_buffer_free_space = 0;
|
||||
volatile unsigned int xmit_stat = 0;
|
||||
|
||||
if (!data) {
|
||||
return -1; /* Invalid input parameter */
|
||||
}
|
||||
|
||||
if (is_new_packet) {
|
||||
is_first_segment = 1;
|
||||
ongoing_packet_length = total_packet_length;
|
||||
ongoing_packet_length_sent = 0;
|
||||
} else if (ongoing_packet_length != total_packet_length ||
|
||||
ongoing_packet_length_sent >= total_packet_length) {
|
||||
return -1; /* Invalid input parameter */
|
||||
}
|
||||
|
||||
/* Would next chunk fit into buffer? */
|
||||
xmit_inf = SMSC9220->TX_FIFO_INF;
|
||||
tx_buffer_free_space = xmit_inf & 0xFFFF;
|
||||
if (current_size > tx_buffer_free_space) {
|
||||
return -1; /* Not enough space in FIFO */
|
||||
}
|
||||
if ((ongoing_packet_length_sent + current_size) == total_packet_length) {
|
||||
is_last_segment = 1;
|
||||
}
|
||||
|
||||
pktptr = (unsigned int *) data;
|
||||
txcmd_a = 0;
|
||||
txcmd_b = 0;
|
||||
|
||||
txcmd_a |= (is_last_segment << 12) | (is_first_segment << 13); /* Last and first segments */
|
||||
txcmd_a |= current_size & 0x7FF; /* [10:0] contains length */
|
||||
|
||||
txcmd_b |= ((current_size & 0xFFFF) << 16); /* [31:16] contains length */
|
||||
txcmd_b |= current_size & 0x7FF; /* [10:0] also contains length */
|
||||
|
||||
SMSC9220->TX_DATA_PORT = txcmd_a;
|
||||
SMSC9220->TX_DATA_PORT = txcmd_b;
|
||||
dwords_to_write = (current_size + 3) >> 2;
|
||||
|
||||
/* PIO Copy to FIFO. Could replace this with DMA. */
|
||||
while(dwords_to_write > 0) {
|
||||
SMSC9220->TX_DATA_PORT = *pktptr;
|
||||
pktptr++;
|
||||
dwords_to_write--;
|
||||
}
|
||||
|
||||
if (is_last_segment) {
|
||||
/* pop status port */
|
||||
/* for error check it should be checked "at a later time" according to data sheet */
|
||||
xmit_stat = SMSC9220->TX_STAT_PORT;
|
||||
}
|
||||
ongoing_packet_length_sent += current_size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int smsc9220_get_rxfifo_data_used_space(void)
|
||||
{
|
||||
unsigned int rxfifo_inf = SMSC9220->RX_FIFO_INF;
|
||||
return rxfifo_inf & 0xFFFF;
|
||||
}
|
||||
|
||||
unsigned int smsc9220_receive_by_chunks(char *data, unsigned int dlen)
|
||||
{
|
||||
static unsigned int current_packet_size_words = 0;
|
||||
unsigned int rxfifo_inf = 0;
|
||||
unsigned int rxfifo_stat = 0;
|
||||
unsigned int dlen_word = 0;
|
||||
unsigned int read_length_word = 0;
|
||||
unsigned int i = 0;
|
||||
|
||||
if (!data) {
|
||||
return 0; /* Invalid input parameter */
|
||||
}
|
||||
|
||||
if (current_packet_size_words == 0) {
|
||||
/* First the packet status word should be read, */
|
||||
/* which tells the size of the data, */
|
||||
/* after the data can be read in synchron. */
|
||||
rxfifo_inf = SMSC9220->RX_FIFO_INF;
|
||||
|
||||
if(rxfifo_inf & 0xFFFF) { /* If there's data */
|
||||
rxfifo_stat = SMSC9220->RX_STAT_PORT;
|
||||
if(rxfifo_stat != 0) { /* Fetch status of this packet */
|
||||
if(rxfifo_stat & (1 << 15)) {
|
||||
current_packet_size_words = 0; /* error */
|
||||
}
|
||||
else {
|
||||
/* Ethernet controller is padding to 32bit aligned data */
|
||||
current_packet_size_words = (((rxfifo_stat >> 16) & 0x3FFF) + 3) >> 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
dlen_word = dlen / 4;
|
||||
read_length_word = (dlen_word < current_packet_size_words) ? dlen_word : current_packet_size_words;
|
||||
|
||||
for (i = 0; i < read_length_word; i++) {
|
||||
((unsigned int*)data)[i] = SMSC9220->RX_DATA_PORT;
|
||||
current_packet_size_words--;
|
||||
}
|
||||
return (current_packet_size_words * 4);
|
||||
}
|
||||
|
||||
unsigned int smsc9220_peek_next_packet_size(void)
|
||||
{
|
||||
unsigned int packet_size = 0;
|
||||
unsigned int rx_stat_peek = 0;
|
||||
|
||||
if(smsc9220_get_rxfifo_data_used_space()) {
|
||||
rx_stat_peek = SMSC9220->RX_STAT_PEEK;
|
||||
packet_size = ((rx_stat_peek >> 16) & 0x3FFF);
|
||||
}
|
||||
return (((packet_size + 3) >> 2) << 2);
|
||||
}
|
||||
|
|
@ -0,0 +1,170 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2017 ARM Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/* This file is the re-implementation of mps2_ethernet_api and Selftest's ETH_MPS2.
|
||||
* MPS2 Selftest:https://silver.arm.com/browse/VEI10 ->
|
||||
* \ISCM-1-0\AN491\software\Selftest\v2m_mps2\
|
||||
*/
|
||||
#ifndef _SMSC9220_ETH_H_
|
||||
#define _SMSC9220_ETH_H_
|
||||
|
||||
enum smsc9220_interrupt_source {
|
||||
enum_smsc9220_interrupt_gpio0 = 0,
|
||||
enum_smsc9220_interrupt_gpio1 = 1,
|
||||
enum_smsc9220_interrupt_gpio2 = 2,
|
||||
enum_smsc9220_interrupt_rxstatus_fifo_level = 3,
|
||||
enum_smsc9220_interrupt_rxstatus_fifo_full = 4,
|
||||
/* 5 Reserved according to Datasheet */
|
||||
enum_smsc9220_interrupt_rx_dropped_frame = 6,
|
||||
enum_smsc9220_interrupt_txstatus_fifo_level = 7,
|
||||
enum_smsc9220_interrupt_txstatus_fifo_full = 8,
|
||||
enum_smsc9220_interrupt_txdata_fifo_available = 9,
|
||||
enum_smsc9220_interrupt_txdata_fifo_overrun = 10,
|
||||
/* 11, 12 Reserved according to Datasheet */
|
||||
enum_smsc9220_interrupt_transmit_error = 13,
|
||||
enum_smsc9220_interrupt_receive_error = 14,
|
||||
enum_smsc9220_interrupt_receive_watchdog_timeout = 15,
|
||||
enum_smsc9220_interrupt_txstatus_overflow = 16,
|
||||
enum_smsc9220_interrupt_power_management = 17,
|
||||
enum_smsc9220_interrupt_phy = 18,
|
||||
enum_smsc9220_interrupt_gp_timer = 19,
|
||||
enum_smsc9220_interrupt_rx_dma = 20,
|
||||
enum_smsc9220_interrupt_tx_ioc = 21,
|
||||
/* 22 Reserved according to Datasheet*/
|
||||
enum_smsc9220_interrupt_rx_dropped_frame_half = 23,
|
||||
enum_smsc9220_interrupt_rx_stopped = 24,
|
||||
enum_smsc9220_interrupt_tx_stopped = 25,
|
||||
/* 26 - 30 Reserved according to Datasheet*/
|
||||
enum_smsc9220_interrupt_sw = 31
|
||||
};
|
||||
|
||||
/* Function declarations */
|
||||
|
||||
/**
|
||||
* \brief Initialize SMS9220 Ethernet controller
|
||||
*
|
||||
* \return 0 if init is successful, 1 otherwise
|
||||
*/
|
||||
int smsc9220_init(void);
|
||||
|
||||
/**
|
||||
* \brief Enable the given interrupt source.
|
||||
*
|
||||
* \param[in] source Enum of the interrupt source.
|
||||
*/
|
||||
void smsc9220_enable_interrupt(enum smsc9220_interrupt_source source);
|
||||
|
||||
/**
|
||||
* \brief Disable the given interrupt source.
|
||||
*
|
||||
* \param[in] source Enum of the interrupt source.
|
||||
*/
|
||||
void smsc9220_disable_interrupt(enum smsc9220_interrupt_source source);
|
||||
|
||||
/**
|
||||
* \brief Clear the given interrupt source.
|
||||
*
|
||||
* \param[in] source Enum of the interrupt source.
|
||||
*/
|
||||
void smsc9220_clear_interrupt(enum smsc9220_interrupt_source source);
|
||||
|
||||
/**
|
||||
* \brief Get the status of the given interrupt source.
|
||||
*
|
||||
* \param[in] source Enum of the interrupt source.
|
||||
*
|
||||
* \return non-zero if the given interrupt source is triggered, zero otherwise
|
||||
*/
|
||||
int smsc9220_get_interrupt(enum smsc9220_interrupt_source source);
|
||||
|
||||
/**
|
||||
* \brief Establish link
|
||||
*/
|
||||
void smsc9220_establish_link(void);
|
||||
|
||||
/**
|
||||
* \brief Read MAC address from EEPROM.
|
||||
*
|
||||
* \param[in,out] mac array will include the read MAC address in
|
||||
* 6 bytes hexadecimal format.
|
||||
* It should be allocated by the caller to 6 bytes.
|
||||
*
|
||||
* \return 0 if read is successful, 1 otherwise
|
||||
*/
|
||||
int smsc9220_read_mac_address(char *mac);
|
||||
|
||||
/**
|
||||
* \brief Get the data size of the Tx buffer, aka Maximum Transition Unit
|
||||
*
|
||||
* \return Fifo data size in bytes
|
||||
*/
|
||||
unsigned int smsc9220_get_tx_data_fifo_size(void);
|
||||
|
||||
/**
|
||||
* \brief Send Ethernet packet from buffer chain.
|
||||
* The full packet length should be known in the beginning
|
||||
* of a new packet.
|
||||
*
|
||||
* \param[in] total_packet_length Length of the packet. Should be equal to
|
||||
* the sum of passed buffers within a packet.
|
||||
* \param[in] is_new_packet Should be set to non-zero if the passed buffer
|
||||
* should be sent as the start of a new packet.
|
||||
* If the current buffer should be sent as a full packet,
|
||||
* it should be set to non-zero respectively.
|
||||
* \param[in] data Pointer to the data should be sent.
|
||||
* \param[in] current_size Size of the data in bytes.
|
||||
*
|
||||
* \return 0 if the send process is successful, standard C error code otherwise
|
||||
*/
|
||||
int smsc9220_send_by_chunks(unsigned int total_packet_length, int is_new_packet,
|
||||
const char *data, unsigned int current_size);
|
||||
|
||||
/**
|
||||
* \brief Receive Ethernet packet from Rx FIFO to the passed buffer.
|
||||
* Stops reading at packet border.
|
||||
* If the passed buffer is larger than the current packet,
|
||||
* the whole packet will be read into the buffer.
|
||||
* If the current packet is larger than the passed buffer,
|
||||
* the buffer will be filled with data and the next call
|
||||
* will continue the read from that point.
|
||||
*
|
||||
* \param[in,out] data Pointer where the data will be read to.
|
||||
* The caller is responsible to allocate it.
|
||||
* \param[in] dlen Length of the allocated data in bytes.
|
||||
*
|
||||
* \return Remaining bytes left in the fifo of the current packet.
|
||||
*/
|
||||
unsigned int smsc9220_receive_by_chunks(char *data, unsigned int dlen);
|
||||
|
||||
/**
|
||||
* \brief Get the used space of Rx fifo in bytes.
|
||||
*
|
||||
* \return Data received and waiting for read in bytes
|
||||
*/
|
||||
unsigned int smsc9220_get_rxfifo_data_used_space(void);
|
||||
|
||||
/**
|
||||
* \brief Get the size of next unread packet in Rx buffer, using the peak
|
||||
* register, which is not destructive so can be read asynchronously.
|
||||
* Warning: In case of heavy receiving load, it's possible this register
|
||||
* is not perfectly in sync.
|
||||
*
|
||||
* \return Size in bytes of the next packet can be read from Rx fifo, according
|
||||
* to the peek register.
|
||||
*/
|
||||
unsigned int smsc9220_peek_next_packet_size(void);
|
||||
|
||||
#endif
|
|
@ -21,60 +21,57 @@
|
|||
#include "mbed_toolchain.h"
|
||||
#include "mbed_error.h"
|
||||
#include "mbed_wait_api.h"
|
||||
|
||||
#define TX_PKT_SIZE 256
|
||||
#define RX_PKT_SIZE 300
|
||||
|
||||
// Types
|
||||
#undef FALSE
|
||||
#undef TRUE
|
||||
#define FALSE 0
|
||||
#define TRUE 1
|
||||
#include "smsc9220_eth.h"
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
Ethernet Device initialize
|
||||
*----------------------------------------------------------------------------*/
|
||||
int ethernet_init()
|
||||
{
|
||||
return 0;
|
||||
|
||||
return smsc9220_init();
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
Ethernet Device Uninitialize
|
||||
*----------------------------------------------------------------------------*/
|
||||
void ethernet_free() {
|
||||
void ethernet_free()
|
||||
{
|
||||
/* Uninitialize function is not implemented in Ethernet driver. */
|
||||
}
|
||||
|
||||
int ethernet_write(const char *data, int size)
|
||||
{
|
||||
/* smsc9220 cannot provide the functionality of writing into the tx buffer */
|
||||
/* by chunks, without knowing the full size of the packet in the beginning */
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ethernet_send()
|
||||
{
|
||||
/* smsc9220 cannot provide the functionality of writing into the tx buffer */
|
||||
/* by chunks, without knowing the full size of the packet in the beginning */
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ethernet_receive()
|
||||
{
|
||||
return 0;
|
||||
return smsc9220_peek_next_packet_size();
|
||||
}
|
||||
|
||||
// Read from an recevied ethernet packet.
|
||||
// After receive returnd a number bigger than 0 it is
|
||||
// possible to read bytes from this packet.
|
||||
// Read will write up to size bytes into data.
|
||||
// It is possible to use read multible times.
|
||||
// Each time read will start reading after the last read byte before.
|
||||
|
||||
/* Read from an recevied ethernet packet.*/
|
||||
/* After receive returnd a number bigger than 0 it is*/
|
||||
/* possible to read bytes from this packet.*/
|
||||
/* Read will write up to size bytes into data.*/
|
||||
/* It is possible to use read multible times.*/
|
||||
/* Each time read will start reading after the last read byte before. */
|
||||
int ethernet_read(char *data, int dlen)
|
||||
{
|
||||
return 0;
|
||||
return smsc9220_receive_by_chunks(data, dlen);
|
||||
}
|
||||
|
||||
void ethernet_address(char *mac) {
|
||||
mbed_mac_address(mac);
|
||||
void ethernet_address(char *mac)
|
||||
{
|
||||
smsc9220_read_mac_address(mac);
|
||||
}
|
||||
|
||||
int ethernet_link(void)
|
||||
|
@ -84,5 +81,5 @@ int ethernet_link(void)
|
|||
|
||||
void ethernet_set_link(int speed, int duplex)
|
||||
{
|
||||
smsc9220_establish_link();
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue