Part of the split. Please, take a look at the cover letter for more details Reviewed-by: Viktor Barna <viktor.barna.rj@xxxxxxxxxxxxxx> Reviewed-by: Gal Gur <gal.gur.jx@xxxxxxxxxxx> Signed-off-by: Alexander Savchenko <oleksandr.savchenko.dn@xxxxxxxxxxxxxx> --- drivers/net/wireless/renesas/ra6w/core.c | 286 +++++++++++++++++++++++ 1 file changed, 286 insertions(+) create mode 100644 drivers/net/wireless/renesas/ra6w/core.c diff --git a/drivers/net/wireless/renesas/ra6w/core.c b/drivers/net/wireless/renesas/ra6w/core.c new file mode 100644 index 000000000000..5691ec6d07f9 --- /dev/null +++ b/drivers/net/wireless/renesas/ra6w/core.c @@ -0,0 +1,286 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * This file contains the main events processing thread. + * + * Copyright (C) [2022-2025] Renesas Electronics Corporation and/or its affiliates. + */ + +#include <linux/firmware.h> + +#include "core.h" +#include "if.h" +#include "params.h" +#include "dbg.h" + +#define RA6W_CORE_THREAD_NAME "ra6w_core_thread" + +static inline u8 ra6w_core_cmd_to_hndl_type(u8 cmd) +{ + if (cmd == RA6W_CMD_DATA_RX) + return RA6W_CORE_HNDL_RX; + + if (cmd == RA6W_CMD_DATA_STATUS_RX) + return RA6W_CORE_HNDL_STATUS; + + if (cmd >= RA6W_CMD_COMMON_START && cmd < RA6W_CMD_COMMON_MAX) + return RA6W_CORE_HNDL_CTRL; + + if (cmd >= RA6W_CMD_FULLMAC_START && cmd < RA6W_CMD_FULLMAC_MAX) + return RA6W_CORE_HNDL_CTRL; + + if (cmd >= RA6W_CMD_DBG_START && cmd < RA6W_CMD_DBG_MAX) + return RA6W_CORE_HNDL_CTRL; + + if (cmd >= RA6W_CMD_IND_FULLMAC_START && cmd < RA6W_CMD_IND_FULLMAC_MAX) + return RA6W_CORE_HNDL_INDI; + + return RA6W_CORE_HNDL_MAX; +} + +static void ra6w_core_event_handler(struct ra6w_core *core) +{ + struct ra6w_if *ifp = container_of(core, struct ra6w_if, core); + struct ra6w_status *status = &core->status; + struct ra6w_indi *indi = &core->indi; + struct ra6w_rx *rx = &core->rx; + int ret; + union ra6w_core_data *data = NULL; + struct sk_buff *skb = NULL; + u8 cmd; + + do { + if (!skb) { + skb = dev_alloc_skb(sizeof(*data)); + if (!skb) + return; + + skb_put(skb, sizeof(*data)); + } + + ret = ra6w_if_read(ifp, skb->data, sizeof(*data)); + if (ret) + break; + + data = (union ra6w_core_data *)skb->data; + cmd = ra6w_core_cmd_to_hndl_type(data->rx.cmd); + switch (cmd) { + case RA6W_CORE_HNDL_RX: + ret = ra6w_rx_event_post(rx, skb); + if (ret == 0) + skb = NULL; + break; + case RA6W_CORE_HNDL_CTRL: + ra6w_ctrl_event_post(&core->ctrl, skb); + break; + case RA6W_CORE_HNDL_INDI: + ret = ra6w_indi_event_post(indi, skb); + if (ret == 0) + skb = NULL; + break; + case RA6W_CORE_HNDL_STATUS: + ra6w_status_event_post(&core->status, skb); + break; + default: + ret = -EINVAL; + break; + } + } while (!kthread_should_stop() && !ret && !ra6w_status_rx_get(status)); + + dev_kfree_skb(skb); +} + +static int core_thread_handler(void *arg) +{ + struct ra6w_core *core = arg; + int event = 0; + + while (!kthread_should_stop()) { + event = ra6w_q_wait(&core->event, RA6W_CORE_EVENT_MASK); + if (event & BIT(RA6W_CORE_EVENT_DATA)) + ra6w_core_event_handler(core); + + if (event & BIT(RA6W_CORE_EVENT_RESET)) + break; + + atomic_set(&core->event.condition, 0); + } + + return 0; +} + +static int ra6w_core_event_init(struct ra6w_core *core) +{ + atomic_set(&core->event.condition, 0); + init_waitqueue_head(&core->event.wait_queue); + core->task = kthread_run(core_thread_handler, core, RA6W_CORE_THREAD_NAME); + if (!core->task) { + ra6w_err("[%s] kthread_run %s failed\n", __func__, RA6W_CORE_THREAD_NAME); + return -ENOENT; + } + + return 0; +} + +static void ra6w_core_event_deinit(struct ra6w_core *core) +{ + if (!core->task) + return; + + atomic_set(&core->event.condition, BIT(RA6W_CORE_EVENT_RESET)); + kthread_stop(core->task); + core->task = NULL; +} + +int ra6w_core_init(struct ra6w_core *core) +{ + int ret; + + ret = ra6w_tx_init(&core->tx); + if (ret) + return ret; + + ra6w_ctrl_init(&core->ctrl); + + ret = ra6w_indi_init(&core->indi); + if (ret) + goto ctrl_deinit; + + ret = ra6w_rx_init(&core->rx); + if (ret) + goto indi_deinit; + + ra6w_status_init(&core->status); + + ret = ra6w_core_event_init(core); + if (ret) + goto rx_deinit; + + return 0; + +rx_deinit: + ra6w_rx_deinit(&core->rx); + +indi_deinit: + ra6w_indi_deinit(&core->indi); + +ctrl_deinit: + ra6w_tx_deinit(&core->tx); + + return ret; +} + +static u8 RENESAS_OUI[] = { 0xd4, 0x3d, 0x39 }; + +static void ra6w_core_rand_mac_addr_gen(u8 *mac) +{ + memcpy(mac, RENESAS_OUI, 3); + get_random_bytes(&mac[3], 3); +} + +static const u8 *ra6w_core_find_tag(const u8 *file_data, const u8 *tag_name, u32 tag_name_len) +{ + const char *tag_data = NULL; + + if (!tag_name || tag_name_len == 0) + return NULL; + + tag_data = strnstr(file_data, tag_name, tag_name_len); + if (!tag_data) + return NULL; + + return tag_data + tag_name_len; +} + +#define RA6W_CORE_DEFAULT_MAC_NAME "default_mac.ini" +#define RA6W_CORE_MAC_TAG "MAC_ADDR=" +#define RA6W_CORE_MAC_TAG_LEN strlen(RA6W_CORE_MAC_TAG) + +static int ra6w_core_fw_mac_addr_get(struct ra6w_core *core, u8 *mac) +{ + struct ra6w_if *ifp = container_of(core, struct ra6w_if, core); + const struct firmware *fw = NULL; + const u8 *fw_mac = NULL; + char path[100] = { 0 }; + int ret; + int n; + + snprintf(path, sizeof(path), "%s/%s", KBUILD_MODNAME, RA6W_CORE_DEFAULT_MAC_NAME); + + ret = request_firmware(&fw, path, &ifp->dev.func->dev); + if (ret) { + ra6w_warn("request_firmware %s failed: %d\n", path, ret); + return -ENOENT; + } + + if (RA6W_CORE_MAC_TAG_LEN + RA6W_MAC_ADDR_STR_LEN > fw->size) { + ret = -ENOENT; + goto free_fw; + } + + fw_mac = ra6w_core_find_tag(fw->data, RA6W_CORE_MAC_TAG, RA6W_CORE_MAC_TAG_LEN); + if (!fw_mac) { + ra6w_warn("Tag %s not found in %s\n", RA6W_CORE_MAC_TAG, path); + ret = -ENOENT; + goto free_fw; + } + + n = sscanf(fw_mac, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", + mac + 0, mac + 1, mac + 2, mac + 3, mac + 4, mac + 5); + if (n != ETH_ALEN || !is_valid_ether_addr(mac)) + ret = -ENOENT; + +free_fw: + release_firmware(fw); + + return ret; +} + +static int ra6w_core_mac_addr_init(struct ra6w_core *core) +{ + u8 mac[ETH_ALEN] = { 0 }; + int ret; + + ret = ra6w_ctrl_otp_mac_addr_get(&core->ctrl, mac); + if (ret == 0) { + ra6w_info("OTP MAC Address : %pM\n", mac); + goto set; + } + + ret = ra6w_core_fw_mac_addr_get(core, mac); + if (ret == 0) { + ra6w_info("FW MAC Address %pM\n", mac); + goto set; + } + + ra6w_core_rand_mac_addr_gen(mac); + ra6w_info("Random MAC Address %pM\n", mac); + +set: + ether_addr_copy(core->sinfo.default_mac, mac); + + return 0; +} + +int ra6w_core_post_init(struct ra6w_core *core) +{ + int ret; + struct ra6w_ctrl *ctrl = &core->ctrl; + + ret = ra6w_ctrl_dev_reset(ctrl); + if (ret) + return ret; + + ret = ra6w_ctrl_update_fw_ver(ctrl); + if (ret) + return ret; + + return ra6w_core_mac_addr_init(core); +} + +void ra6w_core_deinit(struct ra6w_core *core) +{ + ra6w_core_event_deinit(core); + ra6w_rx_deinit(&core->rx); + ra6w_indi_deinit(&core->indi); + ra6w_tx_deinit(&core->tx); +} -- 2.17.1