mirror of
https://github.com/zephyrproject-rtos/zephyr
synced 2025-09-02 06:32:24 +00:00
This commit fix the pin function configuration for mii and rmii setting of Renesas RA8 ethernet driver Correct pin function setting for mii is PFENET = (uint8_t)(0x1 << R_PMISC_PFENET_PHYMODE0_Pos); and rmii is: R_PMISC->PFENET = (uint8_t)(0x0 << R_PMISC_PFENET_PHYMODE0_Pos); Change code to intialize ping in driver init function, change method of getting and checking via enum index of device tree. Add build assert for invalid phy type. Signed-off-by: Duy Nguyen <duy.nguyen.xa@renesas.com>
420 lines
13 KiB
C
420 lines
13 KiB
C
/*
|
|
* Copyright (c) 2024 Renesas Electronics Corporation
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#define DT_DRV_COMPAT renesas_ra_ethernet
|
|
|
|
/* Renesas RA ethernet driver */
|
|
|
|
#define LOG_MODULE_NAME eth_ra_ethernet
|
|
#define LOG_LEVEL CONFIG_ETHERNET_LOG_LEVEL
|
|
|
|
#include <zephyr/logging/log.h>
|
|
LOG_MODULE_REGISTER(LOG_MODULE_NAME);
|
|
|
|
#include <soc.h>
|
|
#include <errno.h>
|
|
#include <zephyr/device.h>
|
|
#include <zephyr/irq.h>
|
|
#include <zephyr/net/net_pkt.h>
|
|
#include <zephyr/net/ethernet.h>
|
|
#include <ethernet/eth_stats.h>
|
|
#include <zephyr/drivers/pinctrl.h>
|
|
#include <zephyr/net/phy.h>
|
|
#include "r_ether.h"
|
|
#include "r_ether_phy.h"
|
|
|
|
/* Additional configurations to use with hal_renesas */
|
|
#define ETHER_DEFAULT NULL
|
|
#define ETHER_CHANNEL0 0
|
|
#define ETHER_BUF_SIZE 1536
|
|
#define ETHER_PADDING_OFFSET 1
|
|
#define ETHER_BROADCAST_FILTER 0
|
|
#define ETHER_TOTAL_BUF_NUM (CONFIG_ETH_RENESAS_TX_BUF_NUM + CONFIG_ETH_RENESAS_RX_BUF_NUM)
|
|
#define ETHER_EE_RECEIVE_EVENT_MASK (0x01070000)
|
|
|
|
BUILD_ASSERT(DT_INST_ENUM_IDX(0, phy_connection_type) <= 1, "Invalid PHY connection setting");
|
|
|
|
void ether_eint_isr(void);
|
|
void renesas_ra_eth_callback(ether_callback_args_t *p_args);
|
|
static void renesas_ra_eth_buffer_init(const struct device *dev);
|
|
|
|
extern void ether_init_buffers(ether_instance_ctrl_t *const p_instance_ctrl);
|
|
extern void ether_configure_mac(ether_instance_ctrl_t *const p_instance_ctrl,
|
|
const uint8_t mac_addr[], const uint8_t mode);
|
|
extern void ether_do_link(ether_instance_ctrl_t *const p_instance_ctrl, const uint8_t mode);
|
|
|
|
uint8_t g_ether0_mac_address[6] = DT_INST_PROP(0, local_mac_address);
|
|
struct renesas_ra_eth_context {
|
|
struct net_if *iface;
|
|
uint8_t mac[6];
|
|
|
|
K_KERNEL_STACK_MEMBER(thread_stack, CONFIG_ETH_RA_RX_THREAD_STACK_SIZE);
|
|
struct k_thread thread;
|
|
struct k_sem rx_sem;
|
|
ether_instance_ctrl_t ctrl;
|
|
/** pinctrl configs */
|
|
const struct pinctrl_dev_config *pcfg;
|
|
};
|
|
|
|
struct renesas_ra_eth_config {
|
|
const ether_cfg_t *p_cfg;
|
|
const struct device *phy_dev;
|
|
};
|
|
|
|
#define DECLARE_ETHER_RX_BUFFER(idx, _) \
|
|
static __aligned(32) uint8_t g_ether0_ether_rx_buffer##idx[ETHER_BUF_SIZE];
|
|
|
|
#define DECLARE_ETHER_TX_BUFFER(idx, _) \
|
|
static __aligned(32) uint8_t g_ether0_ether_tx_buffer##idx[ETHER_BUF_SIZE];
|
|
|
|
#define DECLARE_ETHER_RX_BUFFER_PTR(idx, _) (uint8_t *)&g_ether0_ether_rx_buffer##idx[0]
|
|
|
|
#define DECLARE_ETHER_TX_BUFFER_PTR(idx, _) (uint8_t *)&g_ether0_ether_tx_buffer##idx[0]
|
|
|
|
LISTIFY(CONFIG_ETH_RENESAS_RX_BUF_NUM, DECLARE_ETHER_RX_BUFFER, (;));
|
|
LISTIFY(CONFIG_ETH_RENESAS_TX_BUF_NUM, DECLARE_ETHER_TX_BUFFER, (;));
|
|
|
|
uint8_t *pp_g_ether0_ether_buffers[ETHER_TOTAL_BUF_NUM] = {
|
|
LISTIFY(CONFIG_ETH_RENESAS_RX_BUF_NUM, DECLARE_ETHER_RX_BUFFER_PTR, (,)),
|
|
LISTIFY(CONFIG_ETH_RENESAS_TX_BUF_NUM, DECLARE_ETHER_TX_BUFFER_PTR, (,))
|
|
};
|
|
|
|
static __aligned(16) ether_instance_descriptor_t
|
|
g_ether0_tx_descriptors[CONFIG_ETH_RENESAS_TX_BUF_NUM];
|
|
static __aligned(16) ether_instance_descriptor_t
|
|
g_ether0_rx_descriptors[CONFIG_ETH_RENESAS_RX_BUF_NUM];
|
|
|
|
const ether_extended_cfg_t g_ether0_extended_cfg_t = {
|
|
.p_rx_descriptors = g_ether0_rx_descriptors,
|
|
.p_tx_descriptors = g_ether0_tx_descriptors,
|
|
};
|
|
|
|
/* Dummy configuration for ether phy as hal layer require */
|
|
const ether_phy_extended_cfg_t g_ether_phy0_extended_cfg = {
|
|
.p_target_init = ETHER_DEFAULT, .p_target_link_partner_ability_get = ETHER_DEFAULT};
|
|
|
|
const ether_phy_cfg_t g_ether_phy0_cfg;
|
|
|
|
ether_phy_instance_ctrl_t g_ether_phy0_ctrl;
|
|
|
|
const ether_phy_instance_t g_ether_phy0 = {.p_ctrl = &g_ether_phy0_ctrl,
|
|
.p_cfg = &g_ether_phy0_cfg,
|
|
.p_api = &g_ether_phy_on_ether_phy};
|
|
|
|
const ether_cfg_t g_ether0_cfg = {
|
|
.channel = ETHER_CHANNEL0,
|
|
.zerocopy = ETHER_ZEROCOPY_DISABLE,
|
|
.multicast = ETHER_MULTICAST_ENABLE,
|
|
.promiscuous = ETHER_PROMISCUOUS_DISABLE,
|
|
.flow_control = ETHER_FLOW_CONTROL_DISABLE,
|
|
.padding = ETHER_PADDING_DISABLE,
|
|
.padding_offset = ETHER_PADDING_OFFSET,
|
|
.broadcast_filter = ETHER_BROADCAST_FILTER,
|
|
.p_mac_address = g_ether0_mac_address,
|
|
.num_tx_descriptors = CONFIG_ETH_RENESAS_TX_BUF_NUM,
|
|
.num_rx_descriptors = CONFIG_ETH_RENESAS_RX_BUF_NUM,
|
|
.pp_ether_buffers = pp_g_ether0_ether_buffers,
|
|
.ether_buffer_size = ETHER_BUF_SIZE,
|
|
.irq = DT_INST_IRQN(0),
|
|
.interrupt_priority = DT_INST_IRQ(0, priority),
|
|
.p_callback = ETHER_DEFAULT,
|
|
.p_ether_phy_instance = &g_ether_phy0,
|
|
.p_context = ETHER_DEFAULT,
|
|
.p_extend = &g_ether0_extended_cfg_t,
|
|
};
|
|
|
|
static struct renesas_ra_eth_config eth_0_config = {
|
|
.p_cfg = &g_ether0_cfg, .phy_dev = DEVICE_DT_GET(DT_INST_PHANDLE(0, phy_handle))};
|
|
|
|
/* Driver functions */
|
|
static enum ethernet_hw_caps renesas_ra_eth_get_capabilities(const struct device *dev)
|
|
{
|
|
ARG_UNUSED(dev);
|
|
|
|
return ETHERNET_LINK_10BASE_T | ETHERNET_LINK_100BASE_T;
|
|
}
|
|
|
|
void renesas_ra_eth_callback(ether_callback_args_t *p_args)
|
|
{
|
|
struct device *dev = (struct device *)p_args->p_context;
|
|
struct renesas_ra_eth_context *ctx = dev->data;
|
|
|
|
if (p_args->status_eesr & ETHER_EE_RECEIVE_EVENT_MASK) {
|
|
k_sem_give(&ctx->rx_sem);
|
|
}
|
|
}
|
|
|
|
static void renesas_ra_eth_buffer_init(const struct device *dev)
|
|
{
|
|
struct renesas_ra_eth_context *ctx = dev->data;
|
|
ether_extended_cfg_t *p_ether_extended_cfg =
|
|
(ether_extended_cfg_t *)ctx->ctrl.p_ether_cfg->p_extend;
|
|
/* Initialize the transmit and receive descriptor */
|
|
memset(p_ether_extended_cfg->p_rx_descriptors, 0x00,
|
|
sizeof(ether_instance_descriptor_t) * ctx->ctrl.p_ether_cfg->num_rx_descriptors);
|
|
memset(p_ether_extended_cfg->p_tx_descriptors, 0x00,
|
|
sizeof(ether_instance_descriptor_t) * ctx->ctrl.p_ether_cfg->num_tx_descriptors);
|
|
|
|
ether_init_buffers(&ctx->ctrl);
|
|
}
|
|
|
|
static void phy_link_state_changed(const struct device *pdev, struct phy_link_state *state,
|
|
void *user_data)
|
|
{
|
|
const struct device *dev = (struct device *)user_data;
|
|
struct renesas_ra_eth_context *ctx = dev->data;
|
|
R_ETHERC0_Type *p_reg_etherc;
|
|
|
|
p_reg_etherc = (R_ETHERC0_Type *)ctx->ctrl.p_reg_etherc;
|
|
|
|
ARG_UNUSED(pdev);
|
|
|
|
if (state->is_up) {
|
|
|
|
ctx->ctrl.link_change = ETHER_LINK_CHANGE_LINK_UP;
|
|
ctx->ctrl.previous_link_status = ETHER_PREVIOUS_LINK_STATUS_UP;
|
|
|
|
renesas_ra_eth_buffer_init(dev);
|
|
|
|
/*
|
|
* ETHERC and EDMAC are set after ETHERC and EDMAC are reset in software
|
|
* and sending and receiving is permitted.
|
|
*/
|
|
ether_configure_mac(&ctx->ctrl, ctx->ctrl.p_ether_cfg->p_mac_address, 0);
|
|
|
|
switch (state->speed) {
|
|
/* Half duplex link */
|
|
case LINK_HALF_100BASE_T: {
|
|
ctx->ctrl.link_speed_duplex = ETHER_PHY_LINK_SPEED_100H;
|
|
break;
|
|
}
|
|
|
|
case LINK_HALF_10BASE_T: {
|
|
ctx->ctrl.link_speed_duplex = ETHER_PHY_LINK_SPEED_10H;
|
|
break;
|
|
}
|
|
|
|
/* Full duplex link */
|
|
case LINK_FULL_100BASE_T: {
|
|
ctx->ctrl.link_speed_duplex = ETHER_PHY_LINK_SPEED_100F;
|
|
break;
|
|
}
|
|
|
|
case LINK_FULL_10BASE_T: {
|
|
ctx->ctrl.link_speed_duplex = ETHER_PHY_LINK_SPEED_10F;
|
|
break;
|
|
}
|
|
|
|
default: {
|
|
ctx->ctrl.link_speed_duplex = ETHER_PHY_LINK_SPEED_100F;
|
|
break;
|
|
}
|
|
}
|
|
|
|
ether_do_link(&ctx->ctrl, 0);
|
|
ctx->ctrl.link_change = ETHER_LINK_CHANGE_LINK_UP;
|
|
ctx->ctrl.previous_link_status = ETHER_PREVIOUS_LINK_STATUS_UP;
|
|
ctx->ctrl.link_establish_status = ETHER_LINK_ESTABLISH_STATUS_UP;
|
|
LOG_DBG("Link up");
|
|
net_eth_carrier_on(ctx->iface);
|
|
} else {
|
|
ctx->ctrl.link_change = ETHER_LINK_CHANGE_LINK_DOWN;
|
|
ctx->ctrl.previous_link_status = ETHER_PREVIOUS_LINK_STATUS_DOWN;
|
|
ctx->ctrl.link_establish_status = ETHER_LINK_ESTABLISH_STATUS_DOWN;
|
|
net_eth_carrier_off(ctx->iface);
|
|
}
|
|
}
|
|
|
|
static void renesas_ra_eth_initialize(struct net_if *iface)
|
|
{
|
|
fsp_err_t err;
|
|
const struct device *dev = net_if_get_device(iface);
|
|
struct renesas_ra_eth_context *ctx = dev->data;
|
|
const struct renesas_ra_eth_config *cfg = dev->config;
|
|
|
|
LOG_DBG("eth_initialize");
|
|
|
|
net_if_set_link_addr(iface, ctx->mac, sizeof(ctx->mac), NET_LINK_ETHERNET);
|
|
|
|
if (ctx->iface == NULL) {
|
|
ctx->iface = iface;
|
|
}
|
|
|
|
ethernet_init(iface);
|
|
|
|
err = R_ETHER_Open(&ctx->ctrl, cfg->p_cfg);
|
|
|
|
if (err != FSP_SUCCESS) {
|
|
LOG_ERR("Failed to init ether - R_ETHER_Open fail");
|
|
}
|
|
|
|
err = R_ETHER_CallbackSet(&ctx->ctrl, renesas_ra_eth_callback, dev, NULL);
|
|
|
|
if (err != FSP_SUCCESS) {
|
|
LOG_ERR("Failed to init ether - R_ETHER_CallbackSet fail");
|
|
}
|
|
|
|
phy_link_callback_set(cfg->phy_dev, &phy_link_state_changed, (void *)dev);
|
|
/* Do not start the interface until PHY link is up */
|
|
net_if_carrier_off(ctx->iface);
|
|
}
|
|
|
|
static int renesas_ra_eth_tx(const struct device *dev, struct net_pkt *pkt)
|
|
{
|
|
fsp_err_t err = FSP_SUCCESS;
|
|
struct renesas_ra_eth_context *ctx = dev->data;
|
|
uint16_t len = net_pkt_get_len(pkt);
|
|
static uint8_t tx_buf[NET_ETH_MAX_FRAME_SIZE];
|
|
|
|
if (net_pkt_read(pkt, tx_buf, len)) {
|
|
goto error;
|
|
}
|
|
|
|
/* Check if packet length is less than minimum Ethernet frame size */
|
|
if (len < NET_ETH_MINIMAL_FRAME_SIZE) {
|
|
/* Add padding to meet the minimum frame size */
|
|
memset(tx_buf + len, 0, NET_ETH_MINIMAL_FRAME_SIZE - len);
|
|
len = NET_ETH_MINIMAL_FRAME_SIZE;
|
|
}
|
|
|
|
err = R_ETHER_Write(&ctx->ctrl, tx_buf, len);
|
|
if (err != FSP_SUCCESS) {
|
|
goto error;
|
|
}
|
|
|
|
return 0;
|
|
|
|
error:
|
|
LOG_ERR("Writing to FIFO failed");
|
|
return -1;
|
|
}
|
|
|
|
static const struct ethernet_api api_funcs = {
|
|
.iface_api.init = renesas_ra_eth_initialize,
|
|
.get_capabilities = renesas_ra_eth_get_capabilities,
|
|
.send = renesas_ra_eth_tx,
|
|
};
|
|
|
|
static void renesas_ra_eth_isr(const struct device *dev)
|
|
{
|
|
ARG_UNUSED(dev);
|
|
ether_eint_isr();
|
|
}
|
|
|
|
static struct net_pkt *renesas_ra_eth_rx(const struct device *dev)
|
|
{
|
|
fsp_err_t err = FSP_SUCCESS;
|
|
struct renesas_ra_eth_context *ctx;
|
|
struct net_pkt *pkt = NULL;
|
|
uint32_t len = 0;
|
|
static uint8_t rx_buf[NET_ETH_MAX_FRAME_SIZE];
|
|
|
|
__ASSERT_NO_MSG(dev != NULL);
|
|
ctx = dev->data;
|
|
__ASSERT_NO_MSG(ctx != NULL);
|
|
|
|
err = R_ETHER_Read(&ctx->ctrl, rx_buf, &len);
|
|
if ((err != FSP_SUCCESS) && (err != FSP_ERR_ETHER_ERROR_NO_DATA)) {
|
|
LOG_ERR("Failed to read packets");
|
|
goto out;
|
|
}
|
|
|
|
pkt = net_pkt_rx_alloc_with_buffer(ctx->iface, len, AF_UNSPEC, 0, K_MSEC(100));
|
|
if (!pkt) {
|
|
LOG_ERR("Failed to obtain RX buffer");
|
|
goto out;
|
|
}
|
|
|
|
if (net_pkt_write(pkt, rx_buf, len)) {
|
|
LOG_ERR("Failed to append RX buffer to context buffer");
|
|
net_pkt_unref(pkt);
|
|
pkt = NULL;
|
|
goto out;
|
|
}
|
|
|
|
out:
|
|
if (pkt == NULL) {
|
|
eth_stats_update_errors_rx(ctx->iface);
|
|
}
|
|
|
|
return pkt;
|
|
}
|
|
|
|
static void renesas_ra_eth_thread(void *p1, void *p2, void *p3)
|
|
{
|
|
ARG_UNUSED(p2);
|
|
ARG_UNUSED(p3);
|
|
|
|
const struct device *dev = p1;
|
|
struct net_if *iface;
|
|
int res;
|
|
struct net_pkt *pkt = NULL;
|
|
struct renesas_ra_eth_context *ctx = dev->data;
|
|
|
|
while (true) {
|
|
res = k_sem_take(&ctx->rx_sem, K_MSEC(CONFIG_PHY_MONITOR_PERIOD));
|
|
if (res == 0) {
|
|
pkt = renesas_ra_eth_rx(dev);
|
|
|
|
if (pkt != NULL) {
|
|
iface = net_pkt_iface(pkt);
|
|
res = net_recv_data(iface, pkt);
|
|
if (res < 0) {
|
|
net_pkt_unref(pkt);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#define ELC_EVENT_EDMAC_EINT(channel) ELC_EVENT_EDMAC##channel##_EINT
|
|
|
|
/* Bindings to the platform */
|
|
int renesas_ra_eth_init(const struct device *dev)
|
|
{
|
|
struct renesas_ra_eth_context *ctx = dev->data;
|
|
|
|
switch (DT_INST_ENUM_IDX(0, phy_connection_type)) {
|
|
case 0: /* mii */
|
|
R_PMISC->PFENET = (uint8_t)(0x1 << R_PMISC_PFENET_PHYMODE0_Pos);
|
|
break;
|
|
case 1: /* rmii */
|
|
R_PMISC->PFENET = (uint8_t)(0x0 << R_PMISC_PFENET_PHYMODE0_Pos);
|
|
break;
|
|
default:
|
|
/* Build assert at top of file should catch this case */
|
|
LOG_ERR("Failed to init Ethernet driver - phy-connection-type not support");
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
R_ICU->IELSR[DT_INST_IRQN(0)] = ELC_EVENT_EDMAC_EINT(0);
|
|
|
|
IRQ_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority), renesas_ra_eth_isr,
|
|
DEVICE_DT_INST_GET(0), 0);
|
|
|
|
k_thread_create(&ctx->thread, ctx->thread_stack, CONFIG_ETH_RA_RX_THREAD_STACK_SIZE,
|
|
renesas_ra_eth_thread, (void *)dev, NULL, NULL,
|
|
K_PRIO_COOP(CONFIG_ETH_RA_RX_THREAD_PRIORITY), 0, K_NO_WAIT);
|
|
|
|
irq_enable(DT_INST_IRQN(0));
|
|
|
|
return 0;
|
|
}
|
|
|
|
#define ETHER_RA_INIT(idx) \
|
|
PINCTRL_DT_INST_DEFINE(0); \
|
|
static struct renesas_ra_eth_context eth_0_context = { \
|
|
.mac = DT_INST_PROP(0, local_mac_address), \
|
|
.rx_sem = Z_SEM_INITIALIZER(eth_0_context.rx_sem, 0, UINT_MAX), \
|
|
.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(0), \
|
|
}; \
|
|
\
|
|
ETH_NET_DEVICE_DT_INST_DEFINE(0, renesas_ra_eth_init, NULL, ð_0_context, ð_0_config, \
|
|
CONFIG_ETH_INIT_PRIORITY, &api_funcs, NET_ETH_MTU /*MTU*/);
|
|
|
|
DT_INST_FOREACH_STATUS_OKAY(ETHER_RA_INIT);
|