Implementation of unified EMAC driver for Renesas mbed boards

Implementation of unified EMAC driver for Renesas mbed boards

Based on the driver so far, Renesas implemented the emac driver for GR-PEACH and VK-RZ/A1H.
The mainly changes is below.
- Add the connection part with LWIP according to the unified emac specification.
- Add three new multicast functions(add, remove, set_all).

The Greentea test netsocket and emac test passed.
pull/6847/head
TomoYamanaka 2018-05-11 12:20:17 +09:00 committed by Kevin Bracey
parent 7bfa362492
commit bad530ab0d
5 changed files with 471 additions and 2 deletions

View File

@ -0,0 +1,207 @@
#include "cmsis_os.h"
#include "netsocket/nsapi_types.h"
#include "mbed_shared_queues.h"
#include "ethernet_api.h"
#include "ethernetext_api.h"
#include "rza1_emac.h"
#define RZ_A1_ETH_IF_NAME "en"
// Weak so a module can override
MBED_WEAK EMAC &EMAC::get_default_instance() {
return RZ_A1_EMAC::get_instance();
}
RZ_A1_EMAC &RZ_A1_EMAC::get_instance() {
static RZ_A1_EMAC emac;
return emac;
}
RZ_A1_EMAC::RZ_A1_EMAC() : hwaddr(), hwaddr_set(false), power_on(false), connect_sts(false),
link_mode_last(NEGO_FAIL), recvThread(osPriorityNormal, 896)
{
}
uint32_t RZ_A1_EMAC::get_mtu_size() const
{
return 1500;
}
uint32_t RZ_A1_EMAC::get_align_preference() const
{
return 0;
}
void RZ_A1_EMAC::get_ifname(char *name, uint8_t size) const
{
memcpy(name, RZ_A1_ETH_IF_NAME, (size < sizeof(RZ_A1_ETH_IF_NAME)) ? size : sizeof(RZ_A1_ETH_IF_NAME));
}
uint8_t RZ_A1_EMAC::get_hwaddr_size() const
{
return 6;
}
bool RZ_A1_EMAC::get_hwaddr(uint8_t *addr) const
{
return false;
}
void RZ_A1_EMAC::set_hwaddr(const uint8_t *addr)
{
memcpy(hwaddr, addr, sizeof(hwaddr));
hwaddr_set = true;
/* Reconnect */
if (power_on != false) {
ethernet_cfg_t ethcfg;
ethcfg.int_priority = 6;
ethcfg.recv_cb = &_recv_callback;
ethcfg.ether_mac = NULL;
ethcfg.ether_mac = (char *)hwaddr;
ethernetext_init(&ethcfg);
}
}
bool RZ_A1_EMAC::link_out(emac_mem_buf_t *buf)
{
emac_mem_buf_t *copy_buf = buf;
uint32_t retry_cnt;
bool result = false;
int write_size;
int total_write_size = 0;
while ((copy_buf != NULL) && (memory_manager->get_ptr(copy_buf) != NULL) && (memory_manager->get_len(copy_buf) != 0)) {
for (retry_cnt = 0; retry_cnt < 100; retry_cnt++) {
write_size = ethernet_write((char *)memory_manager->get_ptr(copy_buf), memory_manager->get_len(copy_buf));
if (write_size != 0) {
total_write_size += write_size;
break;
}
osDelay(1);
}
copy_buf = memory_manager->get_next(copy_buf);
}
memory_manager->free(buf);
if (total_write_size > 0) {
if (ethernet_send() == 1) {
result = true;
}
}
return result;
}
bool RZ_A1_EMAC::power_up()
{
if (power_on != false) {
return true;
}
ethernet_cfg_t ethcfg;
ethcfg.int_priority = 6;
ethcfg.recv_cb = &_recv_callback;
ethcfg.ether_mac = NULL;
if (hwaddr_set) {
ethcfg.ether_mac = (char *)hwaddr;
}
ethernetext_init(&ethcfg);
/* task */
recvThread.start(mbed::callback(this, &RZ_A1_EMAC::recv_task));
phy_task_handle = mbed::mbed_event_queue()->call_every(200, mbed::callback(this, &RZ_A1_EMAC::phy_task));
power_on = true;
return true;
}
void RZ_A1_EMAC::power_down()
{
power_on = false;
}
void RZ_A1_EMAC::set_link_input_cb(emac_link_input_cb_t input_cb)
{
emac_link_input_cb = input_cb;
}
void RZ_A1_EMAC::set_link_state_cb(emac_link_state_change_cb_t state_cb)
{
emac_link_state_cb = state_cb;
}
void RZ_A1_EMAC::add_multicast_group(const uint8_t *addr)
{
ethernetext_add_multicast_group(addr);
}
void RZ_A1_EMAC::remove_multicast_group(const uint8_t *addr)
{
ethernetext_remove_multicast_group(addr);
}
void RZ_A1_EMAC::set_all_multicast(bool all)
{
ethernetext_set_all_multicast(all);
}
void RZ_A1_EMAC::set_memory_manager(EMACMemoryManager &mem_mngr)
{
memory_manager = &mem_mngr;
}
void RZ_A1_EMAC::_recv_callback(void) {
get_instance().recv_callback();
}
void RZ_A1_EMAC::recv_callback(void) {
recvThread.signal_set(1);
}
void RZ_A1_EMAC::recv_task(void) {
uint16_t recv_size;
emac_mem_buf_t *buf;
int cnt;
while (1) {
rtos::Thread::signal_wait(1);
for (cnt = 0; cnt < 16; cnt++) {
recv_size = ethernet_receive();
if (recv_size == 0) {
break;
}
buf = memory_manager->alloc_heap(recv_size, 0);
if (buf != NULL) {
(void)ethernet_read((char *)memory_manager->get_ptr(buf), memory_manager->get_len(buf));
emac_link_input_cb(buf);
}
}
}
}
void RZ_A1_EMAC::phy_task(void)
{
if (ethernet_link() == 1) {
int link_mode = ethernetext_chk_link_mode();
if (link_mode != link_mode_last) {
if (connect_sts != false) {
emac_link_state_cb(false);
}
if (link_mode != NEGO_FAIL) {
ethernetext_set_link_mode(link_mode);
emac_link_state_cb(true);
connect_sts = true;
}
link_mode_last = link_mode;
}
} else {
if (connect_sts != false) {
emac_link_state_cb(false);
link_mode_last = NEGO_FAIL;
connect_sts = false;
}
}
}

View File

@ -0,0 +1,169 @@
/* mbed Microcontroller Library
* 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.
*/
#ifndef RZ_A1_EMAC_H
#define RZ_A1_EMAC_H
#include "EMAC.h"
#include "rtos/Thread.h"
class RZ_A1_EMAC : public EMAC {
public:
RZ_A1_EMAC();
static RZ_A1_EMAC &get_instance();
/**
* Return maximum transmission unit
*
* @return MTU in bytes
*/
virtual uint32_t get_mtu_size() const;
/**
* Gets memory buffer alignment preference
*
* Gets preferred memory buffer alignment of the Emac device. IP stack may or may not
* align link out memory buffer chains using the alignment.
*
* @return Memory alignment requirement in bytes
*/
virtual uint32_t get_align_preference() const;
/**
* Return interface name
*
* @param name Pointer to where the name should be written
* @param size Maximum number of character to copy
*/
virtual void get_ifname(char *name, uint8_t size) const;
/**
* Returns size of the underlying interface HW address size.
*
* @return HW address size in bytes
*/
virtual uint8_t get_hwaddr_size() const;
/**
* Return interface-supplied HW address
*
* Copies HW address to provided memory, @param addr has to be of correct size see @a get_hwaddr_size
*
* HW address need not be provided if this interface does not have its own HW
* address configuration; stack will choose address from central system
* configuration if the function returns false and does not write to addr.
*
* @param addr HW address for underlying interface
* @return true if HW address is available
*/
virtual bool get_hwaddr(uint8_t *addr) const;
/**
* Set HW address for interface
*
* Provided address has to be of correct size, see @a get_hwaddr_size
*
* Called to set the MAC address to actually use - if @a get_hwaddr is provided
* the stack would normally use that, but it could be overridden, eg for test
* purposes.
*
* @param addr Address to be set
*/
virtual void set_hwaddr(const uint8_t *addr);
/**
* Sends the packet over the link
*
* That can not be called from an interrupt context.
*
* @param buf Packet to be send
* @return True if the packet was send successfully, False otherwise
*/
virtual bool link_out(emac_mem_buf_t *buf);
/**
* Initializes the HW
*
* @return True on success, False in case of an error.
*/
virtual bool power_up();
/**
* Deinitializes the HW
*
*/
virtual void power_down();
/**
* Sets a callback that needs to be called for packets received for that interface
*
* @param input_cb Function to be register as a callback
*/
virtual void set_link_input_cb(emac_link_input_cb_t input_cb);
/**
* Sets a callback that needs to be called on link status changes for given interface
*
* @param state_cb Function to be register as a callback
*/
virtual void set_link_state_cb(emac_link_state_change_cb_t state_cb);
/** Add device to a multicast group
*
* @param address A multicast group hardware address
*/
virtual void add_multicast_group(const uint8_t *address);
/** Remove device from a multicast group
*
* @param address A multicast group hardware address
*/
virtual void remove_multicast_group(const uint8_t *address);
/** Request reception of all multicast packets
*
* @param all True to receive all multicasts
* False to receive only multicasts addressed to specified groups
*/
virtual void set_all_multicast(bool all);
/** Sets memory manager that is used to handle memory buffers
*
* @param mem_mngr Pointer to memory manager
*/
virtual void set_memory_manager(EMACMemoryManager &mem_mngr);
private:
EMACMemoryManager *memory_manager; /**< Memory manager */
uint8_t hwaddr[6];
bool hwaddr_set;
bool power_on;
emac_link_input_cb_t emac_link_input_cb; /**< Callback for incoming data */
emac_link_state_change_cb_t emac_link_state_cb; /**< Link state change callback */
bool connect_sts;
int link_mode_last;
rtos::Thread recvThread;
int phy_task_handle; /**< Handle for phy task event */
static void _recv_callback(void);
void recv_callback(void);
void recv_task(void);
void phy_task(void);
};
#endif /* RZ_A1_EMAC_H */

View File

@ -138,6 +138,12 @@ static ethernetext_cb_fnc *p_recv_cb_fnc = NULL;
static char mac_addr[6] = {0x00, 0x02, 0xF7, 0xF0, 0x00, 0x00}; /* MAC Address */
static uint32_t phy_id = 0;
static uint32_t start_stop = 1; /* 0:stop 1:start */
static uint32_t tsu_ten_tmp = 0;
volatile struct st_ether_from_tsu_adrh0* ETHER_FROM_TSU_ADRH0_ARRAY[ ETHER_FROM_TSU_ADRH0_ARRAY_COUNT ] =
/* ->MISRA 11.3 */ /* ->SEC R2.7.1 */
ETHER_FROM_TSU_ADRH0_ARRAY_ADDRESS_LIST;
/* <-MISRA 11.3 */ /* <-SEC R2.7.1 */
/* function */
static void lan_reg_reset(void);
@ -315,6 +321,75 @@ void ethernetext_set_link_mode(int32_t link) {
lan_reg_set(link); /* E-DMAC, E-MAC initialization */
}
void ethernetext_add_multicast_group(const uint8_t *addr) {
uint32_t cnt;
uint32_t tmp_data_h;
uint32_t tmp_data_l;
if (tsu_ten_tmp == 0xFFFFFFFF) {
ethernetext_set_all_multicast(1);
} else {
tmp_data_h = ((uint32_t)addr[0] << 24) | ((uint32_t)addr[1] << 16) | ((uint32_t)addr[2] << 8) | ((uint32_t)addr[3]);
tmp_data_l = ((uint32_t)addr[4] << 8) | ((uint32_t)addr[5]);
for (cnt = 0; cnt < 32; cnt++) {
if ((tsu_ten_tmp & (0x80000000 >> cnt)) == 0) {
while ((ETHERTSU_ADSBSY & 0x00000001) != 0) {
;
}
ETHER_FROM_TSU_ADRH0_ARRAY[cnt]->TSU_ADRH0 = tmp_data_h;
while ((ETHERTSU_ADSBSY & 0x00000001) != 0) {
;
}
ETHER_FROM_TSU_ADRH0_ARRAY[cnt]->TSU_ADRL0 = tmp_data_l;
if ((ETHERECMR0 & 0x00002000) != 0) {
ETHERTSU_TEN |= (0x80000000 >> cnt);
}
tsu_ten_tmp |= (0x80000000 >> cnt);
break;
}
}
}
}
void ethernetext_remove_multicast_group(const uint8_t *addr) {
uint32_t cnt;
uint32_t tmp_data_h;
uint32_t tmp_data_l;
tmp_data_h = ((uint32_t)addr[0] << 24) | ((uint32_t)addr[1] << 16) | ((uint32_t)addr[2] << 8) | ((uint32_t)addr[3]);
tmp_data_l = ((uint32_t)addr[4] << 8) | ((uint32_t)addr[5]);
for (cnt = 0; cnt< 32; cnt++) {
if ((ETHER_FROM_TSU_ADRH0_ARRAY[cnt]->TSU_ADRH0 == tmp_data_h) &&
(ETHER_FROM_TSU_ADRH0_ARRAY[cnt]->TSU_ADRL0 == tmp_data_l)) {
while ((ETHERTSU_ADSBSY & 0x00000001) != 0) {
;
}
ETHER_FROM_TSU_ADRH0_ARRAY[cnt]->TSU_ADRH0 = 0;
while ((ETHERTSU_ADSBSY & 0x00000001) != 0) {
;
}
ETHER_FROM_TSU_ADRH0_ARRAY[cnt]->TSU_ADRL0 = 0;
ETHERTSU_TEN &= ~(0x80000000 >> cnt);
tsu_ten_tmp &= ~(0x80000000 >> cnt);
break;
}
}
}
void ethernetext_set_all_multicast(int all) {
if (all != 0) {
ETHERECMR0 &= ~(0x00002000);
ETHERTSU_TEN = 0x00000000;
} else {
ETHERECMR0 |= 0x00002000;
ETHERTSU_TEN = tsu_ten_tmp;
}
}
int ethernet_init() {
ethernet_cfg_t ethcfg;
@ -611,6 +686,7 @@ static void lan_reg_set(int32_t link) {
} else {
ETHERECMR0 &= ~0x00000002; /* Set to half-duplex mode */
}
ETHERECMR0 |= 0x00002000; /* MCT = 1 */
/* Interrupt-related */
if (p_recv_cb_fnc != NULL) {

View File

@ -1,4 +1,11 @@
#ifndef ETHERNETEXT_H
#define ETHERNETEXT_H
#ifdef __cplusplus
extern "C" {
#endif
/* PHY link mode */
#define NEGO_FAIL (0)
#define HALF_10M (1)
@ -18,3 +25,11 @@ extern int ethernetext_init(ethernet_cfg_t *p_ethcfg);
extern void ethernetext_start_stop(int32_t mode);
extern int ethernetext_chk_link_mode(void);
extern void ethernetext_set_link_mode(int32_t link);
extern void ethernetext_add_multicast_group(const uint8_t *addr);
extern void ethernetext_remove_multicast_group(const uint8_t *addr);
extern void ethernetext_set_all_multicast(int all);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -2726,12 +2726,14 @@
"RZ_A1H": {
"inherits": ["RZ_A1XX"],
"supported_form_factors": ["ARDUINO"],
"extra_labels_add": ["RZA1H", "MBRZA1H"],
"extra_labels_add": ["RZA1H", "MBRZA1H", "RZ_A1_EMAC"],
"device_has_add": ["EMAC"],
"release_versions": ["2", "5"]
},
"VK_RZ_A1H": {
"inherits": ["RZ_A1XX"],
"extra_labels_add": ["RZA1H", "VKRZA1H"],
"extra_labels_add": ["RZA1H", "VKRZA1H", "RZ_A1_EMAC"],
"device_has_add": ["EMAC"],
"release_versions": ["2", "5"]
},
"GR_LYCHEE": {