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 | 173 ++++++++++++++++++ 1 file changed, 173 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..893bddb2b25b --- /dev/null +++ b/drivers/net/wireless/celeno/cl8k/enhanced_tim.c @@ -0,0 +1,173 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause +/* Copyright(c) 2019-2022, Celeno Communications Ltd. */ + +#include "def.h" +#include "hw.h" +#include "sta.h" +#include "enhanced_tim.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 __iomem *target = + (u32 __iomem *)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) { + struct sta_info *stainfo = IEEE80211_STA_TO_STAINFO(cl_sta->sta); + + if (test_sta_flag(stainfo, WLAN_STA_PS_STA)) + ieee80211_sta_set_buffered(cl_sta->sta, tid, false); + } + + iowrite32(source[agg_offset], (void __iomem *)&target[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 __iomem *target = + (u32 __iomem *)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) { + struct sta_info *stainfo = IEEE80211_STA_TO_STAINFO(cl_sta->sta); + + if (test_sta_flag(stainfo, WLAN_STA_PS_STA)) + ieee80211_sta_set_buffered(cl_sta->sta, tid, false); + } + + iowrite32(source[sta_offset], (void __iomem *)&target[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 __iomem *target = + (u32 __iomem *)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) { + struct sta_info *stainfo = IEEE80211_STA_TO_STAINFO(cl_sta->sta); + + if (test_sta_flag(stainfo, WLAN_STA_PS_STA)) + ieee80211_sta_set_buffered(cl_sta->sta, tid, true); + } + + iowrite32(source[agg_offset], (void __iomem *)&target[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 __iomem *target = + (u32 __iomem *)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) { + struct sta_info *stainfo = IEEE80211_STA_TO_STAINFO(cl_sta->sta); + + if (test_sta_flag(stainfo, WLAN_STA_PS_STA)) + ieee80211_sta_set_buffered(cl_sta->sta, tid, true); + } + + iowrite32(source[sta_offset], (void __iomem *)&target[sta_offset]); + } +} -- 2.36.1