Search Linux Wireless

[RFC v1 073/256] cl8k: add enhanced_tim.c

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Viktor Barna <viktor.barna@xxxxxxxxxx>

(Part of the split. Please, take a look at the cover letter for more
details).

Signed-off-by: Viktor Barna <viktor.barna@xxxxxxxxxx>
---
 .../net/wireless/celeno/cl8k/enhanced_tim.c   | 216 ++++++++++++++++++
 1 file changed, 216 insertions(+)
 create mode 100644 drivers/net/wireless/celeno/cl8k/enhanced_tim.c

diff --git a/drivers/net/wireless/celeno/cl8k/enhanced_tim.c b/drivers/net/wireless/celeno/cl8k/enhanced_tim.c
new file mode 100644
index 000000000000..da2a0cf547fc
--- /dev/null
+++ b/drivers/net/wireless/celeno/cl8k/enhanced_tim.c
@@ -0,0 +1,216 @@
+// SPDX-License-Identifier: MIT
+/* Copyright(c) 2019-2021, Celeno Communications Ltd. */
+
+#include "enhanced_tim.h"
+#include "bus/pci/ipc.h"
+#include "utils/utils.h"
+
+/*
+ * The kernel's test_and_set_bit() gets unsigned long * as an argument, but we actually
+ * pass a pointer to u32, what cause to alignment fault in 64bit platforms.
+ * This function gets a pointer to u32 to prevent this alignment fault.
+ * Notice that the kernel's function sets the bit as an atomic operation,
+ * and our function doesn't. Vut it's not an issue since we set the bit from one context only.
+ */
+static int cl_test_and_set_bit(unsigned long nr, u32 *addr)
+{
+       u32 *new_addr, mask, old;
+
+       new_addr = ((u32 *)addr) + (nr >> 5);
+       mask = 1 << (nr & 31);
+       old = *new_addr & mask;
+       *new_addr |= mask;
+
+       return (old != 0);
+}
+
+static int CFM_TEST_AND_CLEAR_BIT(unsigned long nr, u32 *addr)
+{
+       u32 *new_addr, mask, old;
+
+       new_addr = ((u32 *)addr) + (nr >> 5);
+       mask = 1 << (nr & 31);
+       old = *new_addr & mask;
+       *new_addr &= ~mask;
+
+       return (old != 0);
+}
+
+void cl_enhanced_tim_reset(struct cl_hw *cl_hw)
+{
+       /*
+        * There is no need to reset cl_hw->ipc_env->shared->enhanced_tim.
+        * It is done as part of ipc_shared_env_init()
+        */
+       memset(&cl_hw->ipc_env->enhanced_tim, 0, sizeof(struct cl_ipc_enhanced_tim));
+}
+
+/*
+ * NOTE: the UMAC DRAM starts with the enhanced TIM elements stractures.
+ * This is hard coded in the FW, this memory allocation should be changed in
+ * the driver module .ELF file.
+ */
+
+void cl_enhanced_tim_clear_tx_agg(struct cl_hw *cl_hw, u32 ipc_queue_idx,
+                                 u8 ac, struct cl_sta *cl_sta, u8 tid)
+{
+       /* Pointer to HOST enhanced TIM */
+       u32 *source = cl_hw->ipc_env->enhanced_tim.tx_rx_agg[ac];
+       u32 ipc_queue_idx_common = IPC_TX_QUEUE_IDX_TO_COMMON_QUEUE_IDX(ipc_queue_idx);
+       /*
+        * Does the UMAC enhanced TIM need update?
+        * If the TIM element is set then clear it and update the UMAC TIM element
+        */
+       if (CFM_TEST_AND_CLEAR_BIT(ipc_queue_idx_common, source)) {
+               /* Pointer to UMAC enhanced TIM */
+               u32 *target = (u32 *)cl_hw->ipc_env->shared->enhanced_tim.tx_rx_agg[ac];
+               /* Offset to UMAC encahned TIM array position */
+               u32 agg_offset = ipc_queue_idx_common / (BITS_PER_BYTE * sizeof(u32));
+
+               /* Update tim element */
+               if (cl_sta && test_sta_flag(cl_sta->stainfo, WLAN_STA_PS_STA))
+                       ieee80211_sta_set_buffered(&cl_sta->stainfo->sta, tid,
+                                                  false);
+
+               target[agg_offset] = cpu_to_le32(source[agg_offset]);
+       }
+}
+
+void cl_enhanced_tim_clear_tx_single(struct cl_hw *cl_hw, u32 ipc_queue_idx, u8 ac,
+                                    bool no_ps_buffer, struct cl_sta *cl_sta, u8 tid)
+{
+       /* Pointer to HOST enhanced TIM */
+       u32 *source = cl_hw->ipc_env->enhanced_tim.tx_single[ac];
+       /* Staton index: 0 - 128 (do not use cl_sta->sta_idx which is 0 -127) */
+       u32 sta_idx = ipc_queue_idx % FW_MAX_NUM_STA;
+
+       /*
+        * Does the UMAC enhanced TIM need update?
+        * If the TIM element is set then clear it and update the UMAC TIM element
+        */
+       if (CFM_TEST_AND_CLEAR_BIT(sta_idx, source)) {
+               /* Pointer to UMAC enhanced TIM for singles or aggregation */
+               u32 *target = (u32 *)cl_hw->ipc_env->shared->enhanced_tim.tx_single[ac];
+               /* Offset to UMAC encahned TIM array position */
+               u32 sta_offset = sta_idx / (BITS_PER_BYTE * sizeof(u32));
+
+               /* Update tim element */
+               if (!no_ps_buffer && cl_sta &&
+                   test_sta_flag(cl_sta->stainfo, WLAN_STA_PS_STA))
+                       ieee80211_sta_set_buffered(&cl_sta->stainfo->sta, tid,
+                                                  false);
+
+               target[sta_offset] = cpu_to_le32(source[sta_offset]);
+       }
+}
+
+void cl_enhanced_tim_set_tx_agg(struct cl_hw *cl_hw, u32 ipc_queue_idx, u8 ac,
+                               bool no_ps_buffer, struct cl_sta *cl_sta, u8 tid)
+{
+       /* Pointer to HOST enhanced TIM */
+       u32 *source = cl_hw->ipc_env->enhanced_tim.tx_rx_agg[ac];
+       u32 ipc_queue_idx_common = IPC_TX_QUEUE_IDX_TO_COMMON_QUEUE_IDX(ipc_queue_idx);
+       /*
+        * Does the UMAC enhanced TIM need update?
+        * If the TIM element is cleared then set it and update the UMAC TIM element
+        */
+       if (!cl_test_and_set_bit(ipc_queue_idx_common, source)) {
+               /* Pointer to UMAC enhanced TIM */
+               u32 *target = (u32 *)cl_hw->ipc_env->shared->enhanced_tim.tx_rx_agg[ac];
+               /* Offset to UMAC encahned TIM array position */
+               u32 agg_offset = ipc_queue_idx_common / (BITS_PER_BYTE * sizeof(u32));
+
+               /* Update tim element */
+               if (!no_ps_buffer && cl_sta &&
+                   test_sta_flag(cl_sta->stainfo, WLAN_STA_PS_STA))
+                       ieee80211_sta_set_buffered(&cl_sta->stainfo->sta, tid,
+                                                  true);
+
+               target[agg_offset] = cpu_to_le32(source[agg_offset]);
+       }
+}
+
+void cl_enhanced_tim_set_tx_single(struct cl_hw *cl_hw, u32 ipc_queue_idx, u8 ac,
+                                  bool no_ps_buffer, struct cl_sta *cl_sta, u8 tid)
+{
+       /* Pointer to HOST enhanced TIM */
+       u32 *source = cl_hw->ipc_env->enhanced_tim.tx_single[ac];
+       /* Staton index: 0 - 128 (do not use cl_sta->sta_idx which is 0 -127) */
+       u32 sta_idx = ipc_queue_idx % FW_MAX_NUM_STA;
+
+       /*
+        * Does the UMAC enhanced TIM need update?
+        * If the TIM element is cleared then set it and update the UMAC TIM element
+        */
+       if (!cl_test_and_set_bit(sta_idx, source)) {
+               /* Pointer to UMAC enhanced TIM */
+               u32 *target = (u32 *)cl_hw->ipc_env->shared->enhanced_tim.tx_single[ac];
+               /* Offset to UMAC encahned TIM array position */
+               u32 sta_offset = sta_idx / (BITS_PER_BYTE * sizeof(u32));
+
+               /* Update tim element */
+               if (!no_ps_buffer && cl_sta &&
+                   test_sta_flag(cl_sta->stainfo, WLAN_STA_PS_STA))
+                       ieee80211_sta_set_buffered(&cl_sta->stainfo->sta, tid,
+                                                  true);
+
+               target[sta_offset] = cpu_to_le32(source[sta_offset]);
+       }
+}
+
+void cl_enhanced_tim_clear_rx(struct cl_hw *cl_hw, u8 ac, u8 sta_idx)
+{
+       /* Pointer to HOST enhanced TIM */
+       u32 *source = cl_hw->ipc_env->enhanced_tim.tx_rx_agg[ac];
+       u32 ipc_queue_idx_common = IPC_RX_QUEUE_IDX_TO_COMMON_QUEUE_IDX(sta_idx);
+       /*
+        * Does the UMAC enhanced TIM need update?
+        * If the TIM element is set then clear it and update the UMAC TIM element
+        */
+       if (CFM_TEST_AND_CLEAR_BIT(ipc_queue_idx_common, source)) {
+               /* Pointer to UMAC enhanced TIM for singles or aggregation */
+               u32 *target = (u32 *)cl_hw->ipc_env->shared->enhanced_tim.tx_rx_agg[ac];
+               /* Offset to UMAC encahned TIM array position */
+               u32 sta_offset = ipc_queue_idx_common / (BITS_PER_BYTE * sizeof(u32));
+
+               target[sta_offset] = cpu_to_le32(source[sta_offset]);
+       }
+}
+
+void cl_enhanced_tim_set_rx(struct cl_hw *cl_hw, u8 ac, u8 sta_idx)
+{
+       /* Pointer to HOST enhanced TIM */
+       u32 *source = cl_hw->ipc_env->enhanced_tim.tx_rx_agg[ac];
+       u32 ipc_queue_idx_common = IPC_RX_QUEUE_IDX_TO_COMMON_QUEUE_IDX(sta_idx);
+       /*
+        * Does the UMAC enhanced TIM need update?
+        * If the TIM element is cleared then set it and update the UMAC TIM element
+        */
+       if (!cl_test_and_set_bit(ipc_queue_idx_common, source)) {
+               /* Pointer to UMAC enhanced TIM */
+               u32 *target = (u32 *)cl_hw->ipc_env->shared->enhanced_tim.tx_rx_agg[ac];
+               /* Offset to UMAC encahned TIM array position */
+               u32 sta_offset = ipc_queue_idx_common / (BITS_PER_BYTE * sizeof(u32));
+
+               target[sta_offset] = cpu_to_le32(source[sta_offset]);
+
+               cl_hw->ipc_host2xmac_trigger_set(cl_hw->chip, BIT(IPC_IRQ_A2E_RX_STA_MAP(ac)));
+       }
+}
+
+void cl_enhanced_tim_clear_rx_sta(struct cl_hw *cl_hw, u8 sta_idx)
+{
+       u8 ac;
+
+       for (ac = 0; ac < AC_MAX; ac++)
+               cl_enhanced_tim_clear_rx(cl_hw, ac, sta_idx);
+}
+
+void cl_enhanced_tim_set_rx_sta(struct cl_hw *cl_hw, u8 sta_idx)
+{
+       u8 ac;
+
+       for (ac = 0; ac < AC_MAX; ac++)
+               cl_enhanced_tim_set_rx(cl_hw, ac, sta_idx);
+}
+
--
2.30.0

________________________________
The information transmitted is intended only for the person or entity to which it is addressed and may contain confidential and/or privileged material. Any retransmission, dissemination, copying or other use of, or taking of any action in reliance upon this information is prohibited. If you received this in error, please contact the sender and delete the material from any computer. Nothing contained herein shall be deemed as a representation, warranty or a commitment by Celeno. No warranties are expressed or implied, including, but not limited to, any implied warranties of non-infringement, merchantability and fitness for a particular purpose.
________________________________





[Index of Archives]     [Linux Host AP]     [ATH6KL]     [Linux Wireless Personal Area Network]     [Linux Bluetooth]     [Wireless Regulations]     [Linux Netdev]     [Kernel Newbies]     [Linux Kernel]     [IDE]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite Hiking]     [MIPS Linux]     [ARM Linux]     [Linux RAID]

  Powered by Linux