diff options
Diffstat (limited to 'drivers/net/wireless/ath/ath11k/dp.c')
-rw-r--r-- | drivers/net/wireless/ath/ath11k/dp.c | 316 |
1 files changed, 272 insertions, 44 deletions
diff --git a/drivers/net/wireless/ath/ath11k/dp.c b/drivers/net/wireless/ath/ath11k/dp.c index 1d64c3c51ac9..59dd185a0cfc 100644 --- a/drivers/net/wireless/ath/ath11k/dp.c +++ b/drivers/net/wireless/ath/ath11k/dp.c @@ -7,6 +7,7 @@ #include "core.h" #include "dp_tx.h" #include "hal_tx.h" +#include "hif.h" #include "debug.h" #include "dp_rx.h" #include "peer.h" @@ -106,13 +107,120 @@ void ath11k_dp_srng_cleanup(struct ath11k_base *ab, struct dp_srng *ring) ring->vaddr_unaligned = NULL; } +static int ath11k_dp_srng_find_ring_in_mask(int ring_num, const u8 *grp_mask) +{ + int ext_group_num; + u8 mask = 1 << ring_num; + + for (ext_group_num = 0; ext_group_num < ATH11K_EXT_IRQ_GRP_NUM_MAX; + ext_group_num++) { + if (mask & grp_mask[ext_group_num]) + return ext_group_num; + } + + return -ENOENT; +} + +static int ath11k_dp_srng_calculate_msi_group(struct ath11k_base *ab, + enum hal_ring_type type, int ring_num) +{ + const u8 *grp_mask; + + switch (type) { + case HAL_WBM2SW_RELEASE: + if (ring_num < 3) { + grp_mask = &ab->hw_params.ring_mask->tx[0]; + } else if (ring_num == 3) { + grp_mask = &ab->hw_params.ring_mask->rx_wbm_rel[0]; + ring_num = 0; + } else { + return -ENOENT; + } + break; + case HAL_REO_EXCEPTION: + grp_mask = &ab->hw_params.ring_mask->rx_err[0]; + break; + case HAL_REO_DST: + grp_mask = &ab->hw_params.ring_mask->rx[0]; + break; + case HAL_REO_STATUS: + grp_mask = &ab->hw_params.ring_mask->reo_status[0]; + break; + case HAL_RXDMA_MONITOR_STATUS: + case HAL_RXDMA_MONITOR_DST: + grp_mask = &ab->hw_params.ring_mask->rx_mon_status[0]; + break; + case HAL_RXDMA_DST: + grp_mask = &ab->hw_params.ring_mask->rxdma2host[0]; + break; + case HAL_RXDMA_BUF: + grp_mask = &ab->hw_params.ring_mask->host2rxdma[0]; + break; + case HAL_RXDMA_MONITOR_BUF: + case HAL_TCL_DATA: + case HAL_TCL_CMD: + case HAL_REO_CMD: + case HAL_SW2WBM_RELEASE: + case HAL_WBM_IDLE_LINK: + case HAL_TCL_STATUS: + case HAL_REO_REINJECT: + case HAL_CE_SRC: + case HAL_CE_DST: + case HAL_CE_DST_STATUS: + default: + return -ENOENT; + } + + return ath11k_dp_srng_find_ring_in_mask(ring_num, grp_mask); +} + +static void ath11k_dp_srng_msi_setup(struct ath11k_base *ab, + struct hal_srng_params *ring_params, + enum hal_ring_type type, int ring_num) +{ + int msi_group_number, msi_data_count; + u32 msi_data_start, msi_irq_start, addr_lo, addr_hi; + int ret; + + ret = ath11k_get_user_msi_vector(ab, "DP", + &msi_data_count, &msi_data_start, + &msi_irq_start); + if (ret) + return; + + msi_group_number = ath11k_dp_srng_calculate_msi_group(ab, type, + ring_num); + if (msi_group_number < 0) { + ath11k_dbg(ab, ATH11K_DBG_PCI, + "ring not part of an ext_group; ring_type: %d,ring_num %d", + type, ring_num); + ring_params->msi_addr = 0; + ring_params->msi_data = 0; + return; + } + + if (msi_group_number > msi_data_count) { + ath11k_dbg(ab, ATH11K_DBG_PCI, + "multiple msi_groups share one msi, msi_group_num %d", + msi_group_number); + } + + ath11k_get_msi_address(ab, &addr_lo, &addr_hi); + + ring_params->msi_addr = addr_lo; + ring_params->msi_addr |= (dma_addr_t)(((uint64_t)addr_hi) << 32); + ring_params->msi_data = (msi_group_number % msi_data_count) + + msi_data_start; + ring_params->flags |= HAL_SRNG_FLAGS_MSI_INTR; +} + int ath11k_dp_srng_setup(struct ath11k_base *ab, struct dp_srng *ring, enum hal_ring_type type, int ring_num, int mac_id, int num_entries) { struct hal_srng_params params = { 0 }; - int entry_sz = ath11k_hal_srng_get_entrysize(type); - int max_entries = ath11k_hal_srng_get_max_entries(type); + int entry_sz = ath11k_hal_srng_get_entrysize(ab, type); + int max_entries = ath11k_hal_srng_get_max_entries(ab, type); int ret; if (max_entries < 0 || entry_sz < 0) @@ -135,6 +243,7 @@ int ath11k_dp_srng_setup(struct ath11k_base *ab, struct dp_srng *ring, params.ring_base_vaddr = ring->vaddr; params.ring_base_paddr = ring->paddr; params.num_entries = num_entries; + ath11k_dp_srng_msi_setup(ab, ¶ms, type, ring_num + mac_id); switch (type) { case HAL_REO_DST: @@ -159,7 +268,7 @@ int ath11k_dp_srng_setup(struct ath11k_base *ab, struct dp_srng *ring, break; } /* follow through when ring_num >= 3 */ - /* fall through */ + fallthrough; case HAL_REO_EXCEPTION: case HAL_REO_REINJECT: case HAL_REO_CMD: @@ -195,11 +304,25 @@ int ath11k_dp_srng_setup(struct ath11k_base *ab, struct dp_srng *ring, return 0; } +static void ath11k_dp_stop_shadow_timers(struct ath11k_base *ab) +{ + int i; + + if (!ab->hw_params.supports_shadow_regs) + return; + + for (i = 0; i < DP_TCL_NUM_RING_MAX; i++) + ath11k_dp_shadow_stop_timer(ab, &ab->dp.tx_ring_timer[i]); + + ath11k_dp_shadow_stop_timer(ab, &ab->dp.reo_cmd_timer); +} + static void ath11k_dp_srng_common_cleanup(struct ath11k_base *ab) { struct ath11k_dp *dp = &ab->dp; int i; + ath11k_dp_stop_shadow_timers(ab); ath11k_dp_srng_cleanup(ab, &dp->wbm_desc_rel_ring); ath11k_dp_srng_cleanup(ab, &dp->tcl_cmd_ring); ath11k_dp_srng_cleanup(ab, &dp->tcl_status_ring); @@ -265,6 +388,10 @@ static int ath11k_dp_srng_common_setup(struct ath11k_base *ab) srng = &ab->hal.srng_list[dp->tx_ring[i].tcl_data_ring.ring_id]; ath11k_hal_tx_init_data_ring(ab, srng); + + ath11k_dp_shadow_init_timer(ab, &dp->tx_ring_timer[i], + ATH11K_SHADOW_DP_TIMER_INTERVAL, + dp->tx_ring[i].tcl_data_ring.ring_id); } ret = ath11k_dp_srng_setup(ab, &dp->reo_reinject_ring, HAL_REO_REINJECT, @@ -300,6 +427,10 @@ static int ath11k_dp_srng_common_setup(struct ath11k_base *ab) srng = &ab->hal.srng_list[dp->reo_cmd_ring.ring_id]; ath11k_hal_reo_init_cmd_ring(ab, srng); + ath11k_dp_shadow_init_timer(ab, &dp->reo_cmd_timer, + ATH11K_SHADOW_CTRL_TIMER_INTERVAL, + dp->reo_cmd_ring.ring_id); + ret = ath11k_dp_srng_setup(ab, &dp->reo_status_ring, HAL_REO_STATUS, 0, 0, DP_REO_STATUS_RING_SIZE); if (ret) { @@ -367,7 +498,7 @@ static int ath11k_dp_scatter_idle_link_desc_setup(struct ath11k_base *ab, u32 end_offset; n_entries_per_buf = HAL_WBM_IDLE_SCATTER_BUF_SIZE / - ath11k_hal_srng_get_entrysize(HAL_WBM_IDLE_LINK); + ath11k_hal_srng_get_entrysize(ab, HAL_WBM_IDLE_LINK); num_scatter_buf = DIV_ROUND_UP(size, HAL_WBM_IDLE_SCATTER_BUF_SIZE); if (num_scatter_buf > DP_IDLE_SCATTER_BUFS_MAX) @@ -565,7 +696,7 @@ int ath11k_dp_link_desc_setup(struct ath11k_base *ab, return ret; /* Setup link desc idle list for HW internal usage */ - entry_sz = ath11k_hal_srng_get_entrysize(ring_type); + entry_sz = ath11k_hal_srng_get_entrysize(ab, ring_type); tot_mem_sz = entry_sz * n_link_desc; /* Setup scatter desc list when the total memory requirement is more */ @@ -622,16 +753,16 @@ int ath11k_dp_service_srng(struct ath11k_base *ab, struct napi_struct *napi = &irq_grp->napi; int grp_id = irq_grp->grp_id; int work_done = 0; - int i = 0; + int i = 0, j; int tot_work_done = 0; - while (ath11k_tx_ring_mask[grp_id] >> i) { - if (ath11k_tx_ring_mask[grp_id] & BIT(i)) + while (ab->hw_params.ring_mask->tx[grp_id] >> i) { + if (ab->hw_params.ring_mask->tx[grp_id] & BIT(i)) ath11k_dp_tx_completion_handler(ab, i); i++; } - if (ath11k_rx_err_ring_mask[grp_id]) { + if (ab->hw_params.ring_mask->rx_err[grp_id]) { work_done = ath11k_dp_process_rx_err(ab, napi, budget); budget -= work_done; tot_work_done += work_done; @@ -639,7 +770,7 @@ int ath11k_dp_service_srng(struct ath11k_base *ab, goto done; } - if (ath11k_rx_wbm_rel_ring_mask[grp_id]) { + if (ab->hw_params.ring_mask->rx_wbm_rel[grp_id]) { work_done = ath11k_dp_rx_process_wbm_err(ab, napi, budget); @@ -650,8 +781,8 @@ int ath11k_dp_service_srng(struct ath11k_base *ab, goto done; } - if (ath11k_rx_ring_mask[grp_id]) { - i = fls(ath11k_rx_ring_mask[grp_id]) - 1; + if (ab->hw_params.ring_mask->rx[grp_id]) { + i = fls(ab->hw_params.ring_mask->rx[grp_id]) - 1; work_done = ath11k_dp_process_rx(ab, i, napi, budget); budget -= work_done; @@ -660,41 +791,51 @@ int ath11k_dp_service_srng(struct ath11k_base *ab, goto done; } - if (rx_mon_status_ring_mask[grp_id]) { - for (i = 0; i < ab->num_radios; i++) { - if (rx_mon_status_ring_mask[grp_id] & BIT(i)) { - work_done = - ath11k_dp_rx_process_mon_rings(ab, - i, napi, - budget); - budget -= work_done; - tot_work_done += work_done; + if (ab->hw_params.ring_mask->rx_mon_status[grp_id]) { + for (i = 0; i < ab->num_radios; i++) { + for (j = 0; j < ab->hw_params.num_rxmda_per_pdev; j++) { + int id = i * ab->hw_params.num_rxmda_per_pdev + j; + + if (ab->hw_params.ring_mask->rx_mon_status[grp_id] & + BIT(id)) { + work_done = + ath11k_dp_rx_process_mon_rings(ab, + id, + napi, budget); + budget -= work_done; + tot_work_done += work_done; + + if (budget <= 0) + goto done; + } } - if (budget <= 0) - goto done; } } - if (ath11k_reo_status_ring_mask[grp_id]) + if (ab->hw_params.ring_mask->reo_status[grp_id]) ath11k_dp_process_reo_status(ab); for (i = 0; i < ab->num_radios; i++) { - if (ath11k_rxdma2host_ring_mask[grp_id] & BIT(i)) { - work_done = ath11k_dp_process_rxdma_err(ab, i, budget); - budget -= work_done; - tot_work_done += work_done; - } + for (j = 0; j < ab->hw_params.num_rxmda_per_pdev; j++) { + int id = i * ab->hw_params.num_rxmda_per_pdev + j; - if (budget <= 0) - goto done; + if (ab->hw_params.ring_mask->rxdma2host[grp_id] & BIT(id)) { + work_done = ath11k_dp_process_rxdma_err(ab, id, budget); + budget -= work_done; + tot_work_done += work_done; + } - if (ath11k_host2rxdma_ring_mask[grp_id] & BIT(i)) { - struct ath11k_pdev_dp *dp = &ab->pdevs[i].ar->dp; - struct dp_rxdma_ring *rx_ring = &dp->rx_refill_buf_ring; + if (budget <= 0) + goto done; - ath11k_dp_rxbufs_replenish(ab, i, rx_ring, 0, - HAL_RX_BUF_RBM_SW3_BM, - GFP_ATOMIC); + if (ab->hw_params.ring_mask->host2rxdma[grp_id] & BIT(id)) { + struct ath11k *ar = ath11k_ab_to_ar(ab, id); + struct ath11k_pdev_dp *dp = &ar->dp; + struct dp_rxdma_ring *rx_ring = &dp->rx_refill_buf_ring; + + ath11k_dp_rxbufs_replenish(ab, id, rx_ring, 0, + HAL_RX_BUF_RBM_SW3_BM); + } } } /* TODO: Implement handler for other interrupts */ @@ -709,10 +850,12 @@ void ath11k_dp_pdev_free(struct ath11k_base *ab) struct ath11k *ar; int i; + del_timer_sync(&ab->mon_reap_timer); + for (i = 0; i < ab->num_radios; i++) { ar = ab->pdevs[i].ar; ath11k_dp_rx_pdev_free(ab, i); - ath11k_debug_unregister(ar); + ath11k_debugfs_unregister(ar); ath11k_dp_rx_pdev_mon_detach(ar); } } @@ -722,6 +865,7 @@ void ath11k_dp_pdev_pre_alloc(struct ath11k_base *ab) struct ath11k *ar; struct ath11k_pdev_dp *dp; int i; + int j; for (i = 0; i < ab->num_radios; i++) { ar = ab->pdevs[i].ar; @@ -731,8 +875,10 @@ void ath11k_dp_pdev_pre_alloc(struct ath11k_base *ab) spin_lock_init(&dp->rx_refill_buf_ring.idr_lock); atomic_set(&dp->num_tx_pending, 0); init_waitqueue_head(&dp->tx_empty_waitq); - idr_init(&dp->rx_mon_status_refill_ring.bufs_idr); - spin_lock_init(&dp->rx_mon_status_refill_ring.idr_lock); + for (j = 0; j < ab->hw_params.num_rxmda_per_pdev; j++) { + idr_init(&dp->rx_mon_status_refill_ring[j].bufs_idr); + spin_lock_init(&dp->rx_mon_status_refill_ring[j].idr_lock); + } idr_init(&dp->rxdma_mon_buf_ring.bufs_idr); spin_lock_init(&dp->rxdma_mon_buf_ring.idr_lock); } @@ -797,13 +943,20 @@ int ath11k_dp_htt_connect(struct ath11k_dp *dp) static void ath11k_dp_update_vdev_search(struct ath11k_vif *arvif) { - /* For STA mode, enable address search index, - * tcl uses ast_hash value in the descriptor. + /* When v2_map_support is true:for STA mode, enable address + * search index, tcl uses ast_hash value in the descriptor. + * When v2_map_support is false: for STA mode, dont' enable + * address search index. */ switch (arvif->vdev_type) { case WMI_VDEV_TYPE_STA: - arvif->hal_addr_search_flags = HAL_TX_ADDRX_EN; - arvif->search_type = HAL_TX_ADDR_SEARCH_INDEX; + if (arvif->ar->ab->hw_params.htt_peer_map_v2) { + arvif->hal_addr_search_flags = HAL_TX_ADDRX_EN; + arvif->search_type = HAL_TX_ADDR_SEARCH_INDEX; + } else { + arvif->hal_addr_search_flags = HAL_TX_ADDRY_EN; + arvif->search_type = HAL_TX_ADDR_SEARCH_DEFAULT; + } break; case WMI_VDEV_TYPE_AP: case WMI_VDEV_TYPE_IBSS: @@ -935,3 +1088,78 @@ fail_link_desc_cleanup: return ret; } + +static void ath11k_dp_shadow_timer_handler(struct timer_list *t) +{ + struct ath11k_hp_update_timer *update_timer = from_timer(update_timer, + t, timer); + struct ath11k_base *ab = update_timer->ab; + struct hal_srng *srng = &ab->hal.srng_list[update_timer->ring_id]; + + spin_lock_bh(&srng->lock); + + /* when the timer is fired, the handler checks whether there + * are new TX happened. The handler updates HP only when there + * are no TX operations during the timeout interval, and stop + * the timer. Timer will be started again when TX happens again. + */ + if (update_timer->timer_tx_num != update_timer->tx_num) { + update_timer->timer_tx_num = update_timer->tx_num; + mod_timer(&update_timer->timer, jiffies + + msecs_to_jiffies(update_timer->interval)); + } else { + update_timer->started = false; + ath11k_hal_srng_shadow_update_hp_tp(ab, srng); + } + + spin_unlock_bh(&srng->lock); +} + +void ath11k_dp_shadow_start_timer(struct ath11k_base *ab, + struct hal_srng *srng, + struct ath11k_hp_update_timer *update_timer) +{ + lockdep_assert_held(&srng->lock); + + if (!ab->hw_params.supports_shadow_regs) + return; + + update_timer->tx_num++; + + if (update_timer->started) + return; + + update_timer->started = true; + update_timer->timer_tx_num = update_timer->tx_num; + mod_timer(&update_timer->timer, jiffies + + msecs_to_jiffies(update_timer->interval)); +} + +void ath11k_dp_shadow_stop_timer(struct ath11k_base *ab, + struct ath11k_hp_update_timer *update_timer) +{ + if (!ab->hw_params.supports_shadow_regs) + return; + + if (!update_timer->init) + return; + + del_timer_sync(&update_timer->timer); +} + +void ath11k_dp_shadow_init_timer(struct ath11k_base *ab, + struct ath11k_hp_update_timer *update_timer, + u32 interval, u32 ring_id) +{ + if (!ab->hw_params.supports_shadow_regs) + return; + + update_timer->tx_num = 0; + update_timer->timer_tx_num = 0; + update_timer->ab = ab; + update_timer->ring_id = ring_id; + update_timer->interval = interval; + update_timer->init = true; + timer_setup(&update_timer->timer, + ath11k_dp_shadow_timer_handler, 0); +} |