wifi: iwlwifi: mld: add the new operation mode

Add a new operation mode, called iwlmld.
The new operation mode will be supported starting from BZ devices.

Currently, in order to load iwlmld instead of iwlmvm, the mld_op_mod
module parameter of iwlwifi (added in this patch) needs to be set to 1.
Later, the right operation mode will be selected according to
the FW version.

Create a new 'mld' subfolder of iwlwifi, and update the project Makefile to
add the option to create a kernel module of the new operation mode -
iwlmld.ko.

This patch is implementing the very basic operations of starting and
stopping the operation mode (i.e. modprobe/modprobe -r), and adds
the other 'ops' (iwl_op_mode_ops) as stubs, to be implemented later.

type=feature
ticket=none

Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
Change-Id: Ib58a6d779cb85a52868f7dfb5f28221f35e5aedf
Reviewed-on: https://gerritwcs.ir.intel.com/c/iwlwifi-stack-dev/+/108530
tested: iil_jenkins iil_jenkins <EC.GER.UNIX.IIL.JENKINS@INTEL.COM>
Tested-by: iil_jenkins iil_jenkins <EC.GER.UNIX.IIL.JENKINS@INTEL.COM>
Reviewed-by: Johannes Berg <johannes.berg@intel.com>
x-iwlwifi-stack-dev: 1c898c5e1256a2bbcdb388cbf4361cf9959361ba
diff --git a/Kconfig.local b/Kconfig.local
index 22a396c..0b6a245 100644
--- a/Kconfig.local
+++ b/Kconfig.local
@@ -220,6 +220,9 @@
 config BACKPORTED_IWLMVM
 	tristate
 	default IWLMVM
+config BACKPORTED_IWLMLD
+	tristate
+	default IWLMLD
 config BACKPORTED_IWLWIFI_OPMODE_MODULAR
 	tristate
 	default IWLWIFI_OPMODE_MODULAR
diff --git a/defconfigs/iwlwifi-public b/defconfigs/iwlwifi-public
index 6c8a475..267045c 100644
--- a/defconfigs/iwlwifi-public
+++ b/defconfigs/iwlwifi-public
@@ -13,6 +13,7 @@
 CPTCFG_WLAN=y
 CPTCFG_IWLWIFI=m
 CPTCFG_IWLMVM=m
+CPTCFG_IWLMLD=m
 CPTCFG_IWLWIFI_DEBUG=y
 CPTCFG_IWLWIFI_DEBUGFS=y
 CPTCFG_IWLWIFI_DEVICE_TRACING=y
diff --git a/defconfigs/prune-public b/defconfigs/prune-public
index 1ed38a1..1774938 100644
--- a/defconfigs/prune-public
+++ b/defconfigs/prune-public
@@ -12,6 +12,7 @@
 CPTCFG_WLAN=y
 CPTCFG_IWLWIFI=m
 CPTCFG_IWLMVM=m
+CPTCFG_IWLMLD=m
 CPTCFG_IWLWIFI_DEBUG=y
 CPTCFG_IWLWIFI_DEBUGFS=y
 CPTCFG_IWLWIFI_DEVICE_TRACING=y
diff --git a/drivers/net/wireless/intel/iwlwifi/Kconfig b/drivers/net/wireless/intel/iwlwifi/Kconfig
index ef2e5c5..ae59c3a 100644
--- a/drivers/net/wireless/intel/iwlwifi/Kconfig
+++ b/drivers/net/wireless/intel/iwlwifi/Kconfig
@@ -78,15 +78,27 @@
 	  of the devices that use this firmware is available here:
 	  https://wireless.wiki.kernel.org/en/users/drivers/iwlwifi#firmware
 
+config IWLMLD
+	tristate "Intel Wireless WiFi MLD Firmware support"
+	depends on m
+	select BPAUTO_WANT_DEV_COREDUMP
+	depends on MAC80211
+	depends on PTP_1588_CLOCK_OPTIONAL
+	help
+	  This is the driver that supports firmwares of MLD capable devices.
+	  The list of the devices that use this firmware is available here:
+	  https://wireless.wiki.kernel.org/en/users/drivers/iwlwifi#firmware
+
 # don't call it _MODULE -- will confuse Kconfig/fixdep/...
 config IWLWIFI_OPMODE_MODULAR
 	bool
 	default y if IWLDVM=m
 	default y if IWLMVM=m
 	default y if IWLXVT=m
+	default y if IWLMLD=m
 
-comment "WARNING: iwlwifi is useless without IWLDVM or IWLMVM"
-	depends on IWLDVM=n && IWLMVM=n
+comment "WARNING: iwlwifi is useless without IWLDVM or IWLMVM or IWLMLD"
+	depends on IWLDVM=n && IWLMVM=n && IWLMLD=n
 
 menu "Debugging Options"
 
diff --git a/drivers/net/wireless/intel/iwlwifi/Makefile b/drivers/net/wireless/intel/iwlwifi/Makefile
index 0a59509..9186309 100644
--- a/drivers/net/wireless/intel/iwlwifi/Makefile
+++ b/drivers/net/wireless/intel/iwlwifi/Makefile
@@ -29,6 +29,7 @@
 iwlwifi-$(CPTCFG_IWLMVM) += cfg/7000.o cfg/8000.o
 iwlwifi-$(CPTCFG_IWLMVM) += cfg/9000.o cfg/22000.o
 iwlwifi-$(CPTCFG_IWLMVM) += cfg/ax210.o cfg/bz.o cfg/sc.o
+iwlwifi-$(CPTCFG_IWLMLD) += cfg/bz.o cfg/sc.o
 
 iwlwifi-$(CPTCFG_IWLWIFI_SUPPORT_DEBUG_OVERRIDES) += iwl-dbg-cfg.o
 
@@ -46,6 +47,7 @@
 obj-$(CPTCFG_IWLMVM)	+= mvm/
 obj-$(CPTCFG_IWLMEI)	+= mei/
 obj-$(CPTCFG_IWLXVT)	+= xvt/
+obj-$(CPTCFG_IWLMLD)	+= mld/
 
 obj-$(CPTCFG_IWLWIFI_KUNIT_TESTS) += tests/
 
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-config.h b/drivers/net/wireless/intel/iwlwifi/iwl-config.h
index 0c739ae..a85d254 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-config.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-config.h
@@ -605,13 +605,15 @@
 extern const struct iwl_cfg iwl_cfg_so_a0_hr_a0;
 extern const struct iwl_cfg iwl_cfg_so_a0_ms_a0;
 extern const struct iwl_cfg iwl_cfg_quz_a0_hr_b0;
+#endif /* CPTCFG_IWLMVM */
 
+#if IS_ENABLED(CPTCFG_IWLMVM) || IS_ENABLED(CPTCFG_IWLMLD)
 extern const struct iwl_cfg iwl_cfg_bz;
 extern const struct iwl_cfg iwl_cfg_gl;
 
 extern const struct iwl_cfg iwl_cfg_sc;
 extern const struct iwl_cfg iwl_cfg_sc2;
 extern const struct iwl_cfg iwl_cfg_sc2f;
-#endif /* CPTCFG_IWLMVM */
+#endif /* CPTCFG_IWLMVM || CPTCFG_IWLMLD */
 
 #endif /* __IWL_CONFIG_H__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c
index 93bcf20..e336d7b 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c
@@ -86,6 +86,9 @@
 enum {
 	DVM_OP_MODE,
 	MVM_OP_MODE,
+#if IS_ENABLED(CPTCFG_IWLMLD)
+	MLD_OP_MODE,
+#endif
 #if IS_ENABLED(CPTCFG_IWLXVT)
 	XVT_OP_MODE,
 #endif
@@ -100,6 +103,9 @@
 } iwlwifi_opmode_table[] = {		/* ops set when driver is initialized */
 	[DVM_OP_MODE] = { .name = "iwldvm", .ops = NULL },
 	[MVM_OP_MODE] = { .name = "iwlmvm", .ops = NULL },
+#if IS_ENABLED(CPTCFG_IWLMLD)
+	[MLD_OP_MODE] = { .name = "iwlmld", .ops = NULL },
+#endif
 #if IS_ENABLED(CPTCFG_IWLXVT)
 	[XVT_OP_MODE] = { .name = "iwlxvt", .ops = NULL },
 #endif
@@ -2013,6 +2019,10 @@
 		op = &iwlwifi_opmode_table[MVM_OP_MODE];
 		break;
 	}
+#if IS_ENABLED(CPTCFG_IWLMLD)
+	if (iwlwifi_mod_params.mld_op_mode && drv->fw.type == IWL_FW_MVM)
+		op = &iwlwifi_opmode_table[MLD_OP_MODE];
+#endif
 
 #if IS_ENABLED(CPTCFG_IWLXVT)
 	if (iwlwifi_mod_params.xvt_default_mode && drv->fw.type == IWL_FW_MVM)
@@ -2410,3 +2420,10 @@
 module_param_named(disable_11be, iwlwifi_mod_params.disable_11be, bool, 0444);
 MODULE_PARM_DESC(disable_11be, "Disable EHT capabilities (default: false)");
 
+#if IS_ENABLED(CPTCFG_IWLMLD)
+#ifdef CPTCFG_IWLWIFI_SUPPORT_DEBUG_OVERRIDES
+module_param_named(mld_op_mode, iwlwifi_mod_params.mld_op_mode,
+		   bool, 0444);
+MODULE_PARM_DESC(mld_op_mode, "Load iwlwifi using the MLD operation mode (default: false)");
+#endif
+#endif
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-modparams.h b/drivers/net/wireless/intel/iwlwifi/iwl-modparams.h
index 86f82f8..a4a127a 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-modparams.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-modparams.h
@@ -65,6 +65,7 @@
  * @remove_when_gone: remove an inaccessible device from the PCIe bus.
  * @enable_ini: enable new FW debug infratructure (INI TLVs)
  * @disable_11be: disable EHT capabilities, default = false.
+ * @mld_op_mode: Load the MLD operation mode. Default = false.
  */
 struct iwl_mod_params {
 	int swcrypto;
@@ -92,6 +93,9 @@
 	bool remove_when_gone;
 	u32 enable_ini;
 	bool disable_11be;
+#if IS_ENABLED(CPTCFG_IWLMLD)
+	bool mld_op_mode;
+#endif
 
 };
 
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/Makefile b/drivers/net/wireless/intel/iwlwifi/mld/Makefile
new file mode 100644
index 0000000..98d5195
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/Makefile
@@ -0,0 +1,5 @@
+obj-$(CPTCFG_IWLMLD)   += iwlmld.o
+
+iwlmld-y += mld.o
+
+subdir-ccflags-y += -I$(src)/../
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mld.c b/drivers/net/wireless/intel/iwlwifi/mld/mld.c
new file mode 100644
index 0000000..bbb7b71
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/mld.c
@@ -0,0 +1,218 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/*
+ * Copyright (C) 2024 Intel Corporation
+ */
+
+#include "mld.h"
+
+#define DRV_DESCRIPTION "Intel(R) MLD wireless driver for Linux"
+MODULE_DESCRIPTION(DRV_DESCRIPTION);
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(IWLWIFI);
+
+static const struct iwl_op_mode_ops iwl_mld_ops;
+
+static int __init iwl_mld_init(void)
+{
+	int ret = iwl_opmode_register("iwlmld", &iwl_mld_ops);
+
+	if (ret)
+		pr_err("Unable to register MLD op_mode: %d\n", ret);
+
+	return ret;
+}
+module_init(iwl_mld_init);
+
+static void __exit iwl_mld_exit(void)
+{
+	iwl_opmode_deregister("iwlmld");
+}
+module_exit(iwl_mld_exit);
+
+static bool
+iwl_is_mld_op_mode_supported(struct iwl_trans *trans)
+{
+	/* TODO: Verify also by FW version */
+	return trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ;
+}
+
+static struct iwl_op_mode *
+iwl_mld_allocate_op_mode(void)
+{
+	struct iwl_op_mode *op_mode;
+	size_t alloc_size =
+		sizeof(struct iwl_op_mode) + sizeof(struct iwl_mld);
+
+	/*
+	 * TODO: when the mac80211 ops is added, use ieee80211_alloc_hw instead
+	 */
+	op_mode = kzalloc(alloc_size, GFP_KERNEL);
+	if (!op_mode)
+		return NULL;
+
+	op_mode->ops = &iwl_mld_ops;
+
+	return op_mode;
+}
+
+static void
+iwl_mld_add_debugfs_files(struct iwl_mld *mld, struct dentry *debugfs_dir)
+{
+	/*TODO: add debugfs files */
+}
+
+static void
+iwl_construct_mld(struct iwl_mld *mld, struct iwl_trans *trans,
+		  const struct iwl_cfg *cfg, const struct iwl_fw *fw,
+		  struct dentry *debugfs_dir)
+{
+	mld->dev = trans->dev;
+	mld->trans = trans;
+	mld->cfg = cfg;
+	mld->fw = fw;
+
+	iwl_mld_add_debugfs_files(mld, debugfs_dir);
+}
+
+static void
+iwl_mld_construct_fw_runtime(struct iwl_mld *mld, struct iwl_trans *trans,
+			     const struct iwl_fw *fw,
+			     struct dentry *debugfs_dir)
+{
+	iwl_fw_runtime_init(&mld->fwrt, trans, fw, NULL, mld,
+			    NULL, NULL, debugfs_dir);
+
+	iwl_fw_set_current_image(&mld->fwrt, IWL_UCODE_REGULAR);
+}
+
+static void
+iwl_mld_configure_trans(struct iwl_op_mode *op_mode)
+{
+	struct iwl_trans_config trans_cfg = {
+		.op_mode = op_mode,
+		/* Rx is not supported yet, but add it to avoid warnings */
+		.rx_buf_size = iwl_amsdu_size_to_rxb_size(),
+	};
+	struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode);
+
+	/*TODO: add more configurations here */
+
+	iwl_trans_configure(mld->trans, &trans_cfg);
+}
+
+/*
+ *****************************************************
+ * op mode ops functions
+ *****************************************************
+ */
+static struct iwl_op_mode *
+iwl_op_mode_mld_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
+		      const struct iwl_fw *fw, struct dentry *dbgfs_dir)
+{
+	struct iwl_op_mode *op_mode;
+	struct iwl_mld *mld;
+
+	if (WARN_ON(!iwl_is_mld_op_mode_supported(trans)))
+		return NULL;
+
+	op_mode = iwl_mld_allocate_op_mode();
+	if (!op_mode)
+		return NULL;
+
+	mld = IWL_OP_MODE_GET_MLD(op_mode);
+
+	iwl_construct_mld(mld, trans, cfg, fw, dbgfs_dir);
+
+	iwl_mld_construct_fw_runtime(mld, trans, fw, dbgfs_dir);
+
+	/* Configure transport layer with the opmode specific params */
+	iwl_mld_configure_trans(op_mode);
+
+	return op_mode;
+}
+
+static void
+iwl_op_mode_mld_stop(struct iwl_op_mode *op_mode)
+{
+	struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode);
+
+	iwl_fw_runtime_free(&mld->fwrt);
+
+	iwl_trans_op_mode_leave(mld->trans);
+
+	kfree(op_mode);
+}
+
+static void
+iwl_mld_rx(struct iwl_op_mode *op_mode, struct napi_struct *napi,
+	   struct iwl_rx_cmd_buffer *rxb)
+{
+	/* TODO: add RX path :-) */
+	WARN_ONCE(1, "RX is not supported yet\n");
+}
+
+static void
+iwl_mld_rx_rss(struct iwl_op_mode *op_mode, struct napi_struct *napi,
+	       struct iwl_rx_cmd_buffer *rxb, unsigned int queue)
+{
+	/* TODO: add RX path :-) */
+	WARN_ONCE(1, "RX is not supported yet\n");
+}
+
+static void
+iwl_mld_queue_full(struct iwl_op_mode *op_mode, int hw_queue)
+{
+	/* TODO */
+	WARN_ONCE(1, "Not supported yet\n");
+}
+
+static void
+iwl_mld_queue_not_full(struct iwl_op_mode *op_mode, int hw_queue)
+{
+	/* TODO */
+	WARN_ONCE(1, "Not supported yet\n");
+}
+
+static bool
+iwl_mld_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state)
+{
+	/* TODO */
+	WARN_ONCE(1, "Not supported yet\n");
+	return false;
+}
+
+static void
+iwl_mld_free_skb(struct iwl_op_mode *op_mode, struct sk_buff *skb)
+{
+	/* TODO */
+	WARN_ONCE(1, "Not supported yet\n");
+}
+
+static void
+iwl_mld_nic_error(struct iwl_op_mode *op_mode, bool sync)
+{
+	/* TODO */
+	WARN_ONCE(1, "Not supported yet\n");
+}
+
+static void
+iwl_mld_time_point(struct iwl_op_mode *op_mode,
+		   enum iwl_fw_ini_time_point tp_id,
+		   union iwl_dbg_tlv_tp_data *tp_data)
+{
+	/* TODO: debug support */
+	WARN_ONCE(1, "Not supported yet\n");
+}
+
+static const struct iwl_op_mode_ops iwl_mld_ops = {
+	.start = iwl_op_mode_mld_start,
+	.stop = iwl_op_mode_mld_stop,
+	.rx = iwl_mld_rx,
+	.rx_rss = iwl_mld_rx_rss,
+	.queue_full = iwl_mld_queue_full,
+	.queue_not_full = iwl_mld_queue_not_full,
+	.hw_rf_kill = iwl_mld_set_hw_rfkill_state,
+	.free_skb = iwl_mld_free_skb,
+	.nic_error = iwl_mld_nic_error,
+	.time_point = iwl_mld_time_point,
+};
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mld.h b/drivers/net/wireless/intel/iwlwifi/mld/mld.h
new file mode 100644
index 0000000..60fd8ff
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/mld.h
@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/*
+ * Copyright (C) 2024 Intel Corporation
+ */
+#ifndef __iwl_mld_h__
+#define __iwl_mld_h__
+
+#include "iwl-trans.h"
+#include "iwl-op-mode.h"
+#include "fw/runtime.h"
+
+/**
+ * struct iwl_mld - MLD op mode
+ *
+ * @dev: pointer to device struct. For printing purposes
+ * @trans: pointer to the transport layer
+ * @cfg: pointer to the device configuration
+ * @fw: a pointer to the fw object
+ * @fwrt: fw runtime data
+ * @debugfs_dir: debugfs directory
+ */
+struct iwl_mld {
+	struct device *dev;
+	struct iwl_trans *trans;
+	const struct iwl_cfg *cfg;
+	const struct iwl_fw *fw;
+	struct iwl_fw_runtime fwrt;
+	struct dentry *debugfs_dir;
+};
+
+/* Extract MLD priv from op_mode */
+#define IWL_OP_MODE_GET_MLD(_iwl_op_mode)		\
+	((struct iwl_mld *)(_iwl_op_mode)->op_mode_specific)
+
+#endif /* __iwl_mld_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c
index e7ae4c3..2604aea 100644
--- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c
+++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c
@@ -312,7 +312,8 @@
 /* Ma devices */
 	{IWL_PCI_DEVICE(0x2729, PCI_ANY_ID, iwl_ma_trans_cfg)},
 	{IWL_PCI_DEVICE(0x7E40, PCI_ANY_ID, iwl_ma_trans_cfg)},
-
+#endif /* CPTCFG_IWLMVM */
+#if IS_ENABLED(CPTCFG_IWLMVM) || IS_ENABLED(CPTCFG_IWLMLD)
 /* Bz devices */
 	{IWL_PCI_DEVICE(0x2727, PCI_ANY_ID, iwl_bz_trans_cfg)},
 	{IWL_PCI_DEVICE(0x272D, PCI_ANY_ID, iwl_bz_trans_cfg)},
@@ -326,7 +327,7 @@
 	{IWL_PCI_DEVICE(0xE340, PCI_ANY_ID, iwl_sc_trans_cfg)},
 	{IWL_PCI_DEVICE(0xD340, PCI_ANY_ID, iwl_sc_trans_cfg)},
 	{IWL_PCI_DEVICE(0x6E70, PCI_ANY_ID, iwl_sc_trans_cfg)},
-#endif /* CPTCFG_IWLMVM */
+#endif /* CPTCFG_IWLMVM || CPTCFG_IWLMLD */
 
 	{0}
 };
@@ -891,6 +892,8 @@
 		      IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB,
 		      iwlax210_2ax_cfg_so_jf_b0, iwl9462_name),
 
+#endif /* CPTCFG_IWLMVM */
+#if IS_ENABLED(CPTCFG_IWLMVM) || IS_ENABLED(CPTCFG_IWLMLD)
 /* Bz */
 /* FIXME: need to change the naming according to the actual CRF */
 	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
@@ -933,7 +936,7 @@
 		      IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY,
 		      IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY,
 		      iwl_cfg_sc2f, iwl_sc2f_name),
-#endif /* CPTCFG_IWLMVM */
+#endif /* CPTCFG_IWLMVM || CPTCFG_IWLMLD */
 };
 EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_dev_info_table);
 
diff --git a/local-symbols b/local-symbols
index a8c1692..d124a2b 100644
--- a/local-symbols
+++ b/local-symbols
@@ -92,3 +92,4 @@
 MAC80211_HWSIM=
 VIRT_WIFI=
 IWLWIFI_KUNIT_TESTS=
+IWLMLD=
diff --git a/versions b/versions
index a7eedd6..29eeb5c 100644
--- a/versions
+++ b/versions
@@ -2,4 +2,4 @@
 BACKPORTED_KERNEL_VERSION="(see git)"
 BACKPORTED_KERNEL_NAME="iwlwifi"
 BACKPORTS_BUILD_TSTAMP=__DATE__ \" \" __TIME__
-BACKPORTS_GIT_TRACKED="iwlwifi-stack-public:master:12216:aa56949c"
+BACKPORTS_GIT_TRACKED="iwlwifi-stack-public:master:12217:1c898c5e"