Skip to content

Commit 9e6b124

Browse files
committed
This commit adds EMAC driver for CM3DS that uses an SMSC LAN 9220
Ethernet controller. To ensure proper operation, some methods needed to be updated in the SMSC9220's native driver as well. It passes all related Greentea tests, however when supervised by the Python environment it tends to fail because of Timeout. The current timeout is set to 1200s that seems to be a little bit short to finish all test cases, the timeout happens towards the end of the last test case. Change-Id: I914608c34828b493a80e133cd132537a297bfc84 Signed-off-by: Bence Kaposzta <[email protected]>
1 parent 61a626c commit 9e6b124

File tree

14 files changed

+2221
-1006
lines changed

14 files changed

+2221
-1006
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"name": "smsc9220-emac",
3+
"config": {
4+
"rx-ring-len": 1,
5+
"tx-ring-len": 1
6+
}
7+
}
Lines changed: 338 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,338 @@
1+
/*
2+
* Copyright (c) 2018 Arm Limited
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#include <ctype.h>
18+
#include <stdio.h>
19+
#include <string.h>
20+
#include <stdlib.h>
21+
22+
#include "mbed_interface.h"
23+
#include "mbed_wait_api.h"
24+
#include "mbed_assert.h"
25+
#include "netsocket/nsapi_types.h"
26+
#include "mbed_shared_queues.h"
27+
28+
#include "smsc9220_emac.h"
29+
#include "smsc9220_eth_drv.h"
30+
31+
#ifndef SMSC9220_ETH
32+
#error "SMSC9220_ETH should be defined, check device_cfg.h!"
33+
#endif
34+
35+
#ifndef SMSC9220_Ethernet_Interrupt_Handler
36+
#error "SMSC9220_Ethernet_Interrupt_Handler should be defined to platform's \
37+
Ethernet IRQ handler!"
38+
#endif
39+
40+
static SMSC9220_EMAC *board_emac_pointer = NULL;
41+
const struct smsc9220_eth_dev_t* SMSC9220_EMAC::dev = &SMSC9220_ETH_DEV;
42+
43+
extern "C" void SMSC9220_Ethernet_Interrupt_Handler(void)
44+
{
45+
if (smsc9220_get_interrupt(SMSC9220_EMAC::dev,
46+
SMSC9220_INTERRUPT_RX_STATUS_FIFO_LEVEL)) {
47+
board_emac_pointer->rx_isr();
48+
smsc9220_clear_interrupt(SMSC9220_EMAC::dev,
49+
SMSC9220_INTERRUPT_RX_STATUS_FIFO_LEVEL);
50+
smsc9220_disable_interrupt(SMSC9220_EMAC::dev,
51+
SMSC9220_INTERRUPT_RX_STATUS_FIFO_LEVEL);
52+
}
53+
}
54+
55+
SMSC9220_EMAC::SMSC9220_EMAC() : receiver_thread(LINK_STATUS_THREAD_PRIORITY,
56+
(uint32_t)LINK_STATUS_THREAD_STACKSIZE)
57+
{
58+
}
59+
60+
/** \brief Ethernet receive interrupt handler
61+
*
62+
* This function handles the receive interrupt.
63+
*/
64+
void SMSC9220_EMAC::rx_isr()
65+
{
66+
receiver_thread.flags_set(FLAG_RX);
67+
}
68+
69+
/** \brief Allocates a emac_mem_buf_t and returns the data from the incoming
70+
* packet.
71+
*
72+
* \return a emac_mem_buf_t filled with the received packet
73+
* (including MAC header)
74+
*/
75+
emac_mem_buf_t *SMSC9220_EMAC::low_level_input()
76+
{
77+
emac_mem_buf_t *p = NULL;
78+
uint32_t message_length = 0;
79+
80+
message_length = smsc9220_peek_next_packet_size(dev);
81+
if (message_length == 0) {
82+
return p;
83+
} else {
84+
/* The Ethernet controller cannot remove CRC from the end of the
85+
* incoming packet, thus it should be taken into account when
86+
* calculating the actual message length.*/
87+
message_length -= CRC_LENGTH_BYTES;
88+
}
89+
90+
p = _memory_manager->alloc_heap(message_length, SMSC9220_BUFF_ALIGNMENT);
91+
92+
if (p != NULL) {
93+
_RXLockMutex.lock();
94+
smsc9220_receive_by_chunks(dev, (char*)_memory_manager->get_ptr(p),
95+
_memory_manager->get_len(p));
96+
_RXLockMutex.unlock();
97+
}
98+
99+
return p;
100+
}
101+
102+
/** \brief Receiver thread.
103+
*
104+
* Woken by thread flags to receive packets or clean up transmit
105+
*
106+
* \param[in] params pointer to the interface data
107+
*/
108+
void SMSC9220_EMAC::receiver_thread_function(void* params)
109+
{
110+
struct SMSC9220_EMAC *smsc9220_enet = static_cast<SMSC9220_EMAC *>(params);
111+
112+
while(1) {
113+
uint32_t flags = ThisThread::flags_wait_any(FLAG_RX);
114+
115+
if (flags & FLAG_RX) {
116+
smsc9220_enet->packet_rx();
117+
}
118+
}
119+
}
120+
121+
/** \brief Packet reception task
122+
*
123+
* This task is called when a packet is received. It will
124+
* pass the packet to the Network Stack.
125+
*/
126+
void SMSC9220_EMAC::packet_rx()
127+
{
128+
emac_mem_buf_t *p;
129+
p = low_level_input();
130+
if(p != NULL) {
131+
_emac_link_input_cb(p);
132+
}
133+
smsc9220_enable_interrupt(dev, SMSC9220_INTERRUPT_RX_STATUS_FIFO_LEVEL);
134+
}
135+
136+
bool SMSC9220_EMAC::link_out(emac_mem_buf_t *buf)
137+
{
138+
if(buf == NULL) {
139+
return false;
140+
} else {
141+
uint32_t buffer_chain_length = 0;
142+
enum smsc9220_error_t error = SMSC9220_ERROR_NONE;
143+
/* If buffer is chained or not aligned then
144+
* make a contiguous aligned copy of it */
145+
if (_memory_manager->get_next(buf) ||
146+
reinterpret_cast<uint32_t>(_memory_manager->get_ptr(buf)) %
147+
SMSC9220_BUFF_ALIGNMENT) {
148+
emac_mem_buf_t *copy_buf;
149+
copy_buf = _memory_manager->alloc_heap(
150+
_memory_manager->get_total_len(buf),
151+
SMSC9220_BUFF_ALIGNMENT);
152+
if (NULL == copy_buf) {
153+
_memory_manager->free(buf);
154+
return false;
155+
}
156+
157+
/* Copy to new buffer and free original */
158+
_memory_manager->copy(copy_buf, buf);
159+
_memory_manager->free(buf);
160+
buf = copy_buf;
161+
}
162+
163+
buffer_chain_length = _memory_manager->get_total_len(buf);
164+
165+
_TXLockMutex.lock();
166+
error = smsc9220_send_by_chunks(dev,
167+
buffer_chain_length,
168+
true,
169+
(const char*)_memory_manager->get_ptr(buf),
170+
_memory_manager->get_len(buf));
171+
if (error != SMSC9220_ERROR_NONE) {
172+
_TXLockMutex.unlock();
173+
return false;
174+
}
175+
_TXLockMutex.unlock();
176+
return true;
177+
}
178+
}
179+
180+
void SMSC9220_EMAC::link_status_task()
181+
{
182+
uint32_t phy_basic_status_reg_value = 0;
183+
bool current_link_status_up = false;
184+
185+
/* Get current status */
186+
smsc9220_phy_regread(dev, SMSC9220_PHY_REG_OFFSET_BSTATUS,
187+
&phy_basic_status_reg_value);
188+
189+
current_link_status_up = (bool)(phy_basic_status_reg_value &
190+
(1ul << (PHY_REG_BSTATUS_LINK_STATUS_INDEX)));
191+
192+
/* Compare with previous state */
193+
if (current_link_status_up != _prev_link_status_up) {
194+
_emac_link_state_cb(current_link_status_up);
195+
_prev_link_status_up = current_link_status_up;
196+
}
197+
198+
}
199+
200+
bool SMSC9220_EMAC::power_up()
201+
{
202+
board_emac_pointer = this;
203+
receiver_thread.start(callback(&SMSC9220_EMAC::receiver_thread_function,
204+
this));
205+
206+
/* Initialize the hardware */
207+
enum smsc9220_error_t init_successful = smsc9220_init(dev, &wait_ms);
208+
if (init_successful != SMSC9220_ERROR_NONE) {
209+
return false;
210+
}
211+
212+
/* Init FIFO level interrupts: use Rx status level irq to trigger
213+
* interrupts for any non-processed packets, while Tx is not irq driven */
214+
smsc9220_set_fifo_level_irq(dev, SMSC9220_FIFO_LEVEL_IRQ_RX_STATUS_POS,
215+
SMSC9220_FIFO_LEVEL_IRQ_LEVEL_MIN);
216+
smsc9220_set_fifo_level_irq(dev, SMSC9220_FIFO_LEVEL_IRQ_TX_STATUS_POS,
217+
SMSC9220_FIFO_LEVEL_IRQ_LEVEL_MIN);
218+
smsc9220_set_fifo_level_irq(dev, SMSC9220_FIFO_LEVEL_IRQ_TX_DATA_POS,
219+
SMSC9220_FIFO_LEVEL_IRQ_LEVEL_MAX);
220+
221+
/* Enable Ethernet interrupts in NVIC */
222+
NVIC_EnableIRQ(ETHERNET_IRQn);
223+
smsc9220_enable_interrupt(dev, SMSC9220_INTERRUPT_RX_STATUS_FIFO_LEVEL);
224+
225+
/* Trigger thread to deal with any RX packets that arrived
226+
* before receiver_thread was started */
227+
rx_isr();
228+
_prev_link_status_up = PHY_STATE_LINK_DOWN;
229+
mbed::mbed_event_queue()->call(mbed::callback(this,
230+
&SMSC9220_EMAC::link_status_task));
231+
232+
/* Allow the Link Status task to detect the initial link state */
233+
wait_ms(10);
234+
_link_status_task_handle = mbed::mbed_event_queue()->call_every(
235+
LINK_STATUS_TASK_PERIOD_MS,
236+
mbed::callback(this,
237+
&SMSC9220_EMAC::link_status_task));
238+
239+
return true;
240+
}
241+
242+
uint32_t SMSC9220_EMAC::get_mtu_size() const
243+
{
244+
return SMSC9220_ETH_MTU_SIZE;
245+
}
246+
247+
uint32_t SMSC9220_EMAC::get_align_preference() const
248+
{
249+
return SMSC9220_BUFF_ALIGNMENT;
250+
}
251+
252+
void SMSC9220_EMAC::get_ifname(char *name, uint8_t size) const
253+
{
254+
memcpy(name, SMSC9220_ETH_IF_NAME, (size < sizeof(SMSC9220_ETH_IF_NAME)) ?
255+
size : sizeof(SMSC9220_ETH_IF_NAME));
256+
}
257+
258+
uint8_t SMSC9220_EMAC::get_hwaddr_size() const
259+
{
260+
return SMSC9220_HWADDR_SIZE;
261+
}
262+
263+
bool SMSC9220_EMAC::get_hwaddr(uint8_t *addr) const
264+
{
265+
if(smsc9220_read_mac_address(dev, (char*)addr) == SMSC9220_ERROR_NONE) {
266+
return true;
267+
} else {
268+
return false;
269+
}
270+
}
271+
272+
void SMSC9220_EMAC::set_hwaddr(const uint8_t *addr)
273+
{
274+
if (!addr) {
275+
return;
276+
}
277+
278+
memcpy(_hwaddr, addr, sizeof _hwaddr);
279+
uint32_t mac_low = 0;
280+
uint32_t mac_high = 0;
281+
282+
/* Using local variables to make sure the right alignment is used */
283+
memcpy((void*)&mac_low, (void*)addr, 4);
284+
memcpy((void*)&mac_high, (void*)(addr+4), 2);
285+
286+
if (smsc9220_mac_regwrite(dev, SMSC9220_MAC_REG_OFFSET_ADDRL, mac_low)) {
287+
return;
288+
}
289+
if (smsc9220_mac_regwrite(dev, SMSC9220_MAC_REG_OFFSET_ADDRH, mac_high)) {
290+
return;
291+
}
292+
}
293+
294+
void SMSC9220_EMAC::set_link_input_cb(emac_link_input_cb_t input_cb)
295+
{
296+
_emac_link_input_cb = input_cb;
297+
}
298+
299+
void SMSC9220_EMAC::set_link_state_cb(emac_link_state_change_cb_t state_cb)
300+
{
301+
_emac_link_state_cb = state_cb;
302+
}
303+
304+
void SMSC9220_EMAC::add_multicast_group(const uint8_t *addr)
305+
{
306+
// No action for now
307+
}
308+
309+
void SMSC9220_EMAC::remove_multicast_group(const uint8_t *addr)
310+
{
311+
// No action for now
312+
}
313+
314+
void SMSC9220_EMAC::set_all_multicast(bool all)
315+
{
316+
// No action for now
317+
}
318+
319+
void SMSC9220_EMAC::power_down()
320+
{
321+
// No action for now
322+
}
323+
324+
void SMSC9220_EMAC::set_memory_manager(EMACMemoryManager &mem_mngr)
325+
{
326+
_memory_manager = &mem_mngr;
327+
}
328+
329+
330+
SMSC9220_EMAC &SMSC9220_EMAC::get_instance() {
331+
static SMSC9220_EMAC emac;
332+
return emac;
333+
}
334+
335+
/* Weak so a module can override */
336+
MBED_WEAK EMAC &EMAC::get_default_instance() {
337+
return SMSC9220_EMAC::get_instance();
338+
}

0 commit comments

Comments
 (0)