Skip to content

Add EMAC driver for CM3DS #8444

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 8, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"name": "smsc9220-emac",
"config": {
"rx-ring-len": 1,
"tx-ring-len": 1
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,338 @@
/*
* Copyright (c) 2018 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.
*/

#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include "mbed_interface.h"
#include "mbed_wait_api.h"
#include "mbed_assert.h"
#include "netsocket/nsapi_types.h"
#include "mbed_shared_queues.h"

#include "smsc9220_emac.h"
#include "smsc9220_eth_drv.h"

#ifndef SMSC9220_ETH
#error "SMSC9220_ETH should be defined, check device_cfg.h!"
#endif

#ifndef SMSC9220_Ethernet_Interrupt_Handler
#error "SMSC9220_Ethernet_Interrupt_Handler should be defined to platform's \
Ethernet IRQ handler!"
#endif

static SMSC9220_EMAC *board_emac_pointer = NULL;
const struct smsc9220_eth_dev_t* SMSC9220_EMAC::dev = &SMSC9220_ETH_DEV;

extern "C" void SMSC9220_Ethernet_Interrupt_Handler(void)
{
if (smsc9220_get_interrupt(SMSC9220_EMAC::dev,
SMSC9220_INTERRUPT_RX_STATUS_FIFO_LEVEL)) {
board_emac_pointer->rx_isr();
smsc9220_clear_interrupt(SMSC9220_EMAC::dev,
SMSC9220_INTERRUPT_RX_STATUS_FIFO_LEVEL);
smsc9220_disable_interrupt(SMSC9220_EMAC::dev,
SMSC9220_INTERRUPT_RX_STATUS_FIFO_LEVEL);
}
}

SMSC9220_EMAC::SMSC9220_EMAC() : receiver_thread(LINK_STATUS_THREAD_PRIORITY,
(uint32_t)LINK_STATUS_THREAD_STACKSIZE)
{
}

/** \brief Ethernet receive interrupt handler
*
* This function handles the receive interrupt.
*/
void SMSC9220_EMAC::rx_isr()
{
receiver_thread.flags_set(FLAG_RX);
}

/** \brief Allocates a emac_mem_buf_t and returns the data from the incoming
* packet.
*
* \return a emac_mem_buf_t filled with the received packet
* (including MAC header)
*/
emac_mem_buf_t *SMSC9220_EMAC::low_level_input()
{
emac_mem_buf_t *p = NULL;
uint32_t message_length = 0;

message_length = smsc9220_peek_next_packet_size(dev);
if (message_length == 0) {
return p;
} else {
/* The Ethernet controller cannot remove CRC from the end of the
* incoming packet, thus it should be taken into account when
* calculating the actual message length.*/
message_length -= CRC_LENGTH_BYTES;
}

p = _memory_manager->alloc_heap(message_length, SMSC9220_BUFF_ALIGNMENT);

if (p != NULL) {
_RXLockMutex.lock();
smsc9220_receive_by_chunks(dev, (char*)_memory_manager->get_ptr(p),
_memory_manager->get_len(p));
_RXLockMutex.unlock();
}

return p;
}

/** \brief Receiver thread.
*
* Woken by thread flags to receive packets or clean up transmit
*
* \param[in] params pointer to the interface data
*/
void SMSC9220_EMAC::receiver_thread_function(void* params)
{
struct SMSC9220_EMAC *smsc9220_enet = static_cast<SMSC9220_EMAC *>(params);

while(1) {
uint32_t flags = ThisThread::flags_wait_any(FLAG_RX);

if (flags & FLAG_RX) {
smsc9220_enet->packet_rx();
}
}
}

/** \brief Packet reception task
*
* This task is called when a packet is received. It will
* pass the packet to the Network Stack.
*/
void SMSC9220_EMAC::packet_rx()
{
emac_mem_buf_t *p;
p = low_level_input();
if(p != NULL) {
_emac_link_input_cb(p);
}
smsc9220_enable_interrupt(dev, SMSC9220_INTERRUPT_RX_STATUS_FIFO_LEVEL);
}

bool SMSC9220_EMAC::link_out(emac_mem_buf_t *buf)
{
if(buf == NULL) {
return false;
} else {
uint32_t buffer_chain_length = 0;
enum smsc9220_error_t error = SMSC9220_ERROR_NONE;
/* If buffer is chained or not aligned then
* make a contiguous aligned copy of it */
if (_memory_manager->get_next(buf) ||
reinterpret_cast<uint32_t>(_memory_manager->get_ptr(buf)) %
SMSC9220_BUFF_ALIGNMENT) {
emac_mem_buf_t *copy_buf;
copy_buf = _memory_manager->alloc_heap(
_memory_manager->get_total_len(buf),
SMSC9220_BUFF_ALIGNMENT);
if (NULL == copy_buf) {
_memory_manager->free(buf);
return false;
}

/* Copy to new buffer and free original */
_memory_manager->copy(copy_buf, buf);
_memory_manager->free(buf);
buf = copy_buf;
}

buffer_chain_length = _memory_manager->get_total_len(buf);

_TXLockMutex.lock();
error = smsc9220_send_by_chunks(dev,
buffer_chain_length,
true,
(const char*)_memory_manager->get_ptr(buf),
_memory_manager->get_len(buf));
if (error != SMSC9220_ERROR_NONE) {
_TXLockMutex.unlock();
return false;
}
_TXLockMutex.unlock();
return true;
}
}

void SMSC9220_EMAC::link_status_task()
{
uint32_t phy_basic_status_reg_value = 0;
bool current_link_status_up = false;

/* Get current status */
smsc9220_phy_regread(dev, SMSC9220_PHY_REG_OFFSET_BSTATUS,
&phy_basic_status_reg_value);

current_link_status_up = (bool)(phy_basic_status_reg_value &
(1ul << (PHY_REG_BSTATUS_LINK_STATUS_INDEX)));

/* Compare with previous state */
if (current_link_status_up != _prev_link_status_up) {
_emac_link_state_cb(current_link_status_up);
_prev_link_status_up = current_link_status_up;
}

}

bool SMSC9220_EMAC::power_up()
{
board_emac_pointer = this;
receiver_thread.start(callback(&SMSC9220_EMAC::receiver_thread_function,
this));

/* Initialize the hardware */
enum smsc9220_error_t init_successful = smsc9220_init(dev, &wait_ms);
if (init_successful != SMSC9220_ERROR_NONE) {
return false;
}

/* Init FIFO level interrupts: use Rx status level irq to trigger
* interrupts for any non-processed packets, while Tx is not irq driven */
smsc9220_set_fifo_level_irq(dev, SMSC9220_FIFO_LEVEL_IRQ_RX_STATUS_POS,
SMSC9220_FIFO_LEVEL_IRQ_LEVEL_MIN);
smsc9220_set_fifo_level_irq(dev, SMSC9220_FIFO_LEVEL_IRQ_TX_STATUS_POS,
SMSC9220_FIFO_LEVEL_IRQ_LEVEL_MIN);
smsc9220_set_fifo_level_irq(dev, SMSC9220_FIFO_LEVEL_IRQ_TX_DATA_POS,
SMSC9220_FIFO_LEVEL_IRQ_LEVEL_MAX);

/* Enable Ethernet interrupts in NVIC */
NVIC_EnableIRQ(ETHERNET_IRQn);
smsc9220_enable_interrupt(dev, SMSC9220_INTERRUPT_RX_STATUS_FIFO_LEVEL);

/* Trigger thread to deal with any RX packets that arrived
* before receiver_thread was started */
rx_isr();
_prev_link_status_up = PHY_STATE_LINK_DOWN;
mbed::mbed_event_queue()->call(mbed::callback(this,
&SMSC9220_EMAC::link_status_task));

/* Allow the Link Status task to detect the initial link state */
wait_ms(10);
_link_status_task_handle = mbed::mbed_event_queue()->call_every(
LINK_STATUS_TASK_PERIOD_MS,
mbed::callback(this,
&SMSC9220_EMAC::link_status_task));

return true;
}

uint32_t SMSC9220_EMAC::get_mtu_size() const
{
return SMSC9220_ETH_MTU_SIZE;
}

uint32_t SMSC9220_EMAC::get_align_preference() const
{
return SMSC9220_BUFF_ALIGNMENT;
}

void SMSC9220_EMAC::get_ifname(char *name, uint8_t size) const
{
memcpy(name, SMSC9220_ETH_IF_NAME, (size < sizeof(SMSC9220_ETH_IF_NAME)) ?
size : sizeof(SMSC9220_ETH_IF_NAME));
}

uint8_t SMSC9220_EMAC::get_hwaddr_size() const
{
return SMSC9220_HWADDR_SIZE;
}

bool SMSC9220_EMAC::get_hwaddr(uint8_t *addr) const
{
if(smsc9220_read_mac_address(dev, (char*)addr) == SMSC9220_ERROR_NONE) {
return true;
} else {
return false;
}
}

void SMSC9220_EMAC::set_hwaddr(const uint8_t *addr)
{
if (!addr) {
return;
}

memcpy(_hwaddr, addr, sizeof _hwaddr);
uint32_t mac_low = 0;
uint32_t mac_high = 0;

/* Using local variables to make sure the right alignment is used */
memcpy((void*)&mac_low, (void*)addr, 4);
memcpy((void*)&mac_high, (void*)(addr+4), 2);

if (smsc9220_mac_regwrite(dev, SMSC9220_MAC_REG_OFFSET_ADDRL, mac_low)) {
return;
}
if (smsc9220_mac_regwrite(dev, SMSC9220_MAC_REG_OFFSET_ADDRH, mac_high)) {
return;
}
}

void SMSC9220_EMAC::set_link_input_cb(emac_link_input_cb_t input_cb)
{
_emac_link_input_cb = input_cb;
}

void SMSC9220_EMAC::set_link_state_cb(emac_link_state_change_cb_t state_cb)
{
_emac_link_state_cb = state_cb;
}

void SMSC9220_EMAC::add_multicast_group(const uint8_t *addr)
{
// No action for now
}

void SMSC9220_EMAC::remove_multicast_group(const uint8_t *addr)
{
// No action for now
}

void SMSC9220_EMAC::set_all_multicast(bool all)
{
// No action for now
}

void SMSC9220_EMAC::power_down()
{
// No action for now
}

void SMSC9220_EMAC::set_memory_manager(EMACMemoryManager &mem_mngr)
{
_memory_manager = &mem_mngr;
}


SMSC9220_EMAC &SMSC9220_EMAC::get_instance() {
static SMSC9220_EMAC emac;
return emac;
}

/* Weak so a module can override */
MBED_WEAK EMAC &EMAC::get_default_instance() {
return SMSC9220_EMAC::get_instance();
}
Loading