diff mbox series

[07/38] ra6w: add core.c

Message ID 20250417135236.52410-8-oleksandr.savchenko.dn@bp.renesas.com
State New
Headers show
Series wireless: ra6w driver for Renesas IEEE 802.11ax devices | expand

Commit Message

Alexander Savchenko April 17, 2025, 1:52 p.m. UTC
Part of the split. Please, take a look at the cover letter for more details

Reviewed-by: Viktor Barna <viktor.barna.rj@bp.renesas.com>
Reviewed-by: Gal Gur <gal.gur.jx@renesas.com>
Signed-off-by: Alexander Savchenko <oleksandr.savchenko.dn@bp.renesas.com>
---
 drivers/net/wireless/renesas/ra6w/core.c | 286 +++++++++++++++++++++++
 1 file changed, 286 insertions(+)
 create mode 100644 drivers/net/wireless/renesas/ra6w/core.c
diff mbox series

Patch

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);
+}