diff --git a/drivers/ethernet/CMakeLists.txt b/drivers/ethernet/CMakeLists.txt index 545187a6b2c..abab18562e1 100644 --- a/drivers/ethernet/CMakeLists.txt +++ b/drivers/ethernet/CMakeLists.txt @@ -41,6 +41,7 @@ zephyr_library_sources_ifdef(CONFIG_ETH_ADIN2111 eth_adin2111.c) zephyr_library_sources_ifdef(CONFIG_ETH_LAN865X eth_lan865x.c oa_tc6.c) zephyr_library_sources_ifdef(CONFIG_ETH_XMC4XXX eth_xmc4xxx.c) zephyr_library_sources_ifdef(CONFIG_ETH_TEST eth_test.c) +zephyr_library_sources_ifdef(CONFIG_ETH_RENESAS_RA eth_renesas_ra.c) if(CONFIG_ETH_NXP_S32_NETC) zephyr_library_sources(eth_nxp_s32_netc.c) diff --git a/drivers/ethernet/Kconfig b/drivers/ethernet/Kconfig index 178733bd247..7164189b1aa 100644 --- a/drivers/ethernet/Kconfig +++ b/drivers/ethernet/Kconfig @@ -82,6 +82,7 @@ source "drivers/ethernet/dwc_xgmac/Kconfig" source "drivers/ethernet/phy/Kconfig" source "drivers/ethernet/nxp_enet/Kconfig" +source "drivers/ethernet/Kconfig.renesas_ra" endif # "Ethernet Drivers" diff --git a/drivers/ethernet/Kconfig.renesas_ra b/drivers/ethernet/Kconfig.renesas_ra new file mode 100644 index 00000000000..a6c04e9392b --- /dev/null +++ b/drivers/ethernet/Kconfig.renesas_ra @@ -0,0 +1,36 @@ +# Copyright (c) 2024 Renesas Electronics Corporation +# SPDX-License-Identifier: Apache-2.0 + +config ETH_RENESAS_RA + bool "Renesas RA Ethernet" + default y + depends on DT_HAS_RENESAS_RA_ETHERNET_ENABLED + select USE_RA_FSP_ETHER + select MDIO + help + Enable Renesas RA Ethernet Driver. + +if ETH_RENESAS_RA + +config ETH_RA_RX_THREAD_STACK_SIZE + int "Stack size for internal incoming packet handler" + default 1500 + help + Size of the stack used for internal thread which is ran for + incoming packet processing. + +config ETH_RA_RX_THREAD_PRIORITY + int "Renesas RA Ethernet receive thread priority" + default 2 + +config ETH_RENESAS_TX_BUF_NUM + int "Number of Transmit buffer" + default 4 + range 1 8 + +config ETH_RENESAS_RX_BUF_NUM + int "Number of Receive buffer" + default 4 + range 1 8 + +endif diff --git a/drivers/ethernet/eth_renesas_ra.c b/drivers/ethernet/eth_renesas_ra.c new file mode 100644 index 00000000000..9ce7d41a7ab --- /dev/null +++ b/drivers/ethernet/eth_renesas_ra.c @@ -0,0 +1,413 @@ +/* + * 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 +LOG_MODULE_REGISTER(LOG_MODULE_NAME); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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) + +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; + const char *phy_connection_type = DT_INST_PROP_OR(0, phy_connection_type, "rmii"); + + 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); + + R_PMISC->PFENET = (uint8_t)(0x0 << R_PMISC_PFENET_PHYMODE0_Pos); + + if (strstr(phy_connection_type, "mii") != NULL) { + /* Configure pins for MII or RMII. Set PHYMODE0 if MII is selected. */ + R_PMISC->PFENET = (uint8_t)(0x1 << R_PMISC_PFENET_PHYMODE0_Pos); + } else { + LOG_ERR("Failed to init ether - phy-connection-type not support"); + } + + 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; + + 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); diff --git a/dts/arm/renesas/ra/ra8/ra8x1.dtsi b/dts/arm/renesas/ra/ra8/ra8x1.dtsi index 96d493e25e4..3d9ad95ec08 100644 --- a/dts/arm/renesas/ra/ra8/ra8x1.dtsi +++ b/dts/arm/renesas/ra/ra8/ra8x1.dtsi @@ -557,6 +557,22 @@ status = "disabled"; }; }; + + eth: ethernet@40354100 { + compatible = "renesas,ra-ethernet"; + reg = <0x40354100 0xfc>; + interrupts = <42 0>; + local-mac-address = [00 11 22 33 44 55]; + phy-connection-type = "rmii"; + status = "disabled"; + }; + + mdio: mdio { + compatible = "renesas,ra-mdio"; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; }; }; diff --git a/dts/bindings/ethernet/renesas,ra-ethernet.yaml b/dts/bindings/ethernet/renesas,ra-ethernet.yaml new file mode 100644 index 00000000000..002510e9c66 --- /dev/null +++ b/dts/bindings/ethernet/renesas,ra-ethernet.yaml @@ -0,0 +1,18 @@ +# Copyright (c) 2024 Renesas Electronics Corporation +# SPDX-License-Identifier: Apache-2.0 + +description: Renesas RA Ethernet + +compatible: "renesas,ra-ethernet" + +include: [ethernet-controller.yaml, pinctrl-device.yaml] + +properties: + reg: + required: true + + interrupts: + required: true + + phy-handle: + required: true diff --git a/modules/Kconfig.renesas_fsp b/modules/Kconfig.renesas_fsp index 1800ee52d52..4b9eb15ddd4 100644 --- a/modules/Kconfig.renesas_fsp +++ b/modules/Kconfig.renesas_fsp @@ -86,4 +86,9 @@ config USE_RA_FSP_ETHER_PHY help Enable RA FSP Ethernet phy driver +config USE_RA_FSP_ETHER + bool + help + Enable RA FSP Ethernet driver + endif # HAS_RENESAS_RA_FSP