wl1271: Improvements to the TX path
- Fix a TX result overflow problem that was present in the TX path and visible with at least linksys AP's (probably any AP with high throughput capability.) - Optimize TX by writing FW trigger for a group of TX frames instead of each and every frame. - Slightly optimize the TX path code. Signed-off-by: Juuso Oikarinen <juuso.oikarinen@nokia.com> Reviewed-by: Kalle Valo <kalle.valo@nokia.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
parent
93c5bb68c8
commit
ffb591cd0e
@ -396,10 +396,10 @@ struct wl1271 {
|
|||||||
/* Accounting for allocated / available TX blocks on HW */
|
/* Accounting for allocated / available TX blocks on HW */
|
||||||
u32 tx_blocks_freed[NUM_TX_QUEUES];
|
u32 tx_blocks_freed[NUM_TX_QUEUES];
|
||||||
u32 tx_blocks_available;
|
u32 tx_blocks_available;
|
||||||
u8 tx_results_count;
|
u32 tx_results_count;
|
||||||
|
|
||||||
/* Transmitted TX packets counter for chipset interface */
|
/* Transmitted TX packets counter for chipset interface */
|
||||||
int tx_packets_count;
|
u32 tx_packets_count;
|
||||||
|
|
||||||
/* Time-offset between host and chipset clocks */
|
/* Time-offset between host and chipset clocks */
|
||||||
int time_offset;
|
int time_offset;
|
||||||
|
@ -453,14 +453,12 @@ static void wl1271_irq_work(struct work_struct *work)
|
|||||||
wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_HW_AVAILABLE");
|
wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_HW_AVAILABLE");
|
||||||
|
|
||||||
if (intr & WL1271_ACX_INTR_DATA) {
|
if (intr & WL1271_ACX_INTR_DATA) {
|
||||||
u8 tx_res_cnt = wl->fw_status->tx_results_counter -
|
|
||||||
wl->tx_results_count;
|
|
||||||
|
|
||||||
wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_DATA");
|
wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_DATA");
|
||||||
|
|
||||||
/* check for tx results */
|
/* check for tx results */
|
||||||
if (tx_res_cnt)
|
if (wl->fw_status->tx_results_counter !=
|
||||||
wl1271_tx_complete(wl, tx_res_cnt);
|
(wl->tx_results_count & 0xff))
|
||||||
|
wl1271_tx_complete(wl);
|
||||||
|
|
||||||
wl1271_rx(wl, wl->fw_status);
|
wl1271_rx(wl, wl->fw_status);
|
||||||
}
|
}
|
||||||
|
@ -169,7 +169,6 @@ static int wl1271_tx_send_packet(struct wl1271 *wl, struct sk_buff *skb,
|
|||||||
|
|
||||||
/* write packet new counter into the write access register */
|
/* write packet new counter into the write access register */
|
||||||
wl->tx_packets_count++;
|
wl->tx_packets_count++;
|
||||||
wl1271_write32(wl, WL1271_HOST_WR_ACCESS, wl->tx_packets_count);
|
|
||||||
|
|
||||||
desc = (struct wl1271_tx_hw_descr *) skb->data;
|
desc = (struct wl1271_tx_hw_descr *) skb->data;
|
||||||
wl1271_debug(DEBUG_TX, "tx id %u skb 0x%p payload %u (%u words)",
|
wl1271_debug(DEBUG_TX, "tx id %u skb 0x%p payload %u (%u words)",
|
||||||
@ -244,6 +243,7 @@ void wl1271_tx_work(struct work_struct *work)
|
|||||||
struct sk_buff *skb;
|
struct sk_buff *skb;
|
||||||
bool woken_up = false;
|
bool woken_up = false;
|
||||||
u32 sta_rates = 0;
|
u32 sta_rates = 0;
|
||||||
|
u32 prev_tx_packets_count;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
/* check if the rates supported by the AP have changed */
|
/* check if the rates supported by the AP have changed */
|
||||||
@ -260,6 +260,8 @@ void wl1271_tx_work(struct work_struct *work)
|
|||||||
if (unlikely(wl->state == WL1271_STATE_OFF))
|
if (unlikely(wl->state == WL1271_STATE_OFF))
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
prev_tx_packets_count = wl->tx_packets_count;
|
||||||
|
|
||||||
/* if rates have changed, re-configure the rate policy */
|
/* if rates have changed, re-configure the rate policy */
|
||||||
if (unlikely(sta_rates)) {
|
if (unlikely(sta_rates)) {
|
||||||
wl->rate_set = wl1271_tx_enabled_rates_get(wl, sta_rates);
|
wl->rate_set = wl1271_tx_enabled_rates_get(wl, sta_rates);
|
||||||
@ -270,7 +272,7 @@ void wl1271_tx_work(struct work_struct *work)
|
|||||||
if (!woken_up) {
|
if (!woken_up) {
|
||||||
ret = wl1271_ps_elp_wakeup(wl, false);
|
ret = wl1271_ps_elp_wakeup(wl, false);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto out;
|
goto out_ack;
|
||||||
woken_up = true;
|
woken_up = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -282,10 +284,10 @@ void wl1271_tx_work(struct work_struct *work)
|
|||||||
ieee80211_stop_queues(wl->hw);
|
ieee80211_stop_queues(wl->hw);
|
||||||
set_bit(WL1271_FLAG_TX_QUEUE_STOPPED, &wl->flags);
|
set_bit(WL1271_FLAG_TX_QUEUE_STOPPED, &wl->flags);
|
||||||
skb_queue_head(&wl->tx_queue, skb);
|
skb_queue_head(&wl->tx_queue, skb);
|
||||||
goto out;
|
goto out_ack;
|
||||||
} else if (ret < 0) {
|
} else if (ret < 0) {
|
||||||
dev_kfree_skb(skb);
|
dev_kfree_skb(skb);
|
||||||
goto out;
|
goto out_ack;
|
||||||
} else if (test_and_clear_bit(WL1271_FLAG_TX_QUEUE_STOPPED,
|
} else if (test_and_clear_bit(WL1271_FLAG_TX_QUEUE_STOPPED,
|
||||||
&wl->flags)) {
|
&wl->flags)) {
|
||||||
/* firmware buffer has space, restart queues */
|
/* firmware buffer has space, restart queues */
|
||||||
@ -295,6 +297,11 @@ void wl1271_tx_work(struct work_struct *work)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
out_ack:
|
||||||
|
/* interrupt the firmware with the new packets */
|
||||||
|
if (prev_tx_packets_count != wl->tx_packets_count)
|
||||||
|
wl1271_write32(wl, WL1271_HOST_WR_ACCESS, wl->tx_packets_count);
|
||||||
|
|
||||||
out:
|
out:
|
||||||
if (woken_up)
|
if (woken_up)
|
||||||
wl1271_ps_elp_sleep(wl);
|
wl1271_ps_elp_sleep(wl);
|
||||||
@ -311,7 +318,7 @@ static void wl1271_tx_complete_packet(struct wl1271 *wl,
|
|||||||
int id = result->id;
|
int id = result->id;
|
||||||
|
|
||||||
/* check for id legality */
|
/* check for id legality */
|
||||||
if (id >= ACX_TX_DESCRIPTORS || wl->tx_frames[id] == NULL) {
|
if (unlikely(id >= ACX_TX_DESCRIPTORS || wl->tx_frames[id] == NULL)) {
|
||||||
wl1271_warning("TX result illegal id: %d", id);
|
wl1271_warning("TX result illegal id: %d", id);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -366,10 +373,11 @@ static void wl1271_tx_complete_packet(struct wl1271 *wl,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Called upon reception of a TX complete interrupt */
|
/* Called upon reception of a TX complete interrupt */
|
||||||
void wl1271_tx_complete(struct wl1271 *wl, u32 count)
|
void wl1271_tx_complete(struct wl1271 *wl)
|
||||||
{
|
{
|
||||||
struct wl1271_acx_mem_map *memmap =
|
struct wl1271_acx_mem_map *memmap =
|
||||||
(struct wl1271_acx_mem_map *)wl->target_mem_map;
|
(struct wl1271_acx_mem_map *)wl->target_mem_map;
|
||||||
|
u32 count, fw_counter;
|
||||||
u32 i;
|
u32 i;
|
||||||
|
|
||||||
wl1271_debug(DEBUG_TX, "tx_complete received, packets: %d", count);
|
wl1271_debug(DEBUG_TX, "tx_complete received, packets: %d", count);
|
||||||
@ -377,12 +385,18 @@ void wl1271_tx_complete(struct wl1271 *wl, u32 count)
|
|||||||
/* read the tx results from the chipset */
|
/* read the tx results from the chipset */
|
||||||
wl1271_read(wl, le32_to_cpu(memmap->tx_result),
|
wl1271_read(wl, le32_to_cpu(memmap->tx_result),
|
||||||
wl->tx_res_if, sizeof(*wl->tx_res_if), false);
|
wl->tx_res_if, sizeof(*wl->tx_res_if), false);
|
||||||
|
fw_counter = le32_to_cpu(wl->tx_res_if->tx_result_fw_counter);
|
||||||
|
|
||||||
|
/* write host counter to chipset (to ack) */
|
||||||
|
wl1271_write32(wl, le32_to_cpu(memmap->tx_result) +
|
||||||
|
offsetof(struct wl1271_tx_hw_res_if,
|
||||||
|
tx_result_host_counter), fw_counter);
|
||||||
|
|
||||||
|
count = fw_counter - wl->tx_results_count;
|
||||||
|
|
||||||
/* verify that the result buffer is not getting overrun */
|
/* verify that the result buffer is not getting overrun */
|
||||||
if (count > TX_HW_RESULT_QUEUE_LEN) {
|
if (unlikely(count > TX_HW_RESULT_QUEUE_LEN))
|
||||||
wl1271_warning("TX result overflow from chipset: %d", count);
|
wl1271_warning("TX result overflow from chipset: %d", count);
|
||||||
count = TX_HW_RESULT_QUEUE_LEN;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* process the results */
|
/* process the results */
|
||||||
for (i = 0; i < count; i++) {
|
for (i = 0; i < count; i++) {
|
||||||
@ -395,12 +409,6 @@ void wl1271_tx_complete(struct wl1271 *wl, u32 count)
|
|||||||
|
|
||||||
wl->tx_results_count++;
|
wl->tx_results_count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* write host counter to chipset (to ack) */
|
|
||||||
wl1271_write32(wl, le32_to_cpu(memmap->tx_result) +
|
|
||||||
offsetof(struct wl1271_tx_hw_res_if,
|
|
||||||
tx_result_host_counter),
|
|
||||||
le32_to_cpu(wl->tx_res_if->tx_result_fw_counter));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* caller must hold wl->mutex */
|
/* caller must hold wl->mutex */
|
||||||
|
@ -160,7 +160,7 @@ static inline int wl1271_tx_ac_to_tid(int ac)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void wl1271_tx_work(struct work_struct *work);
|
void wl1271_tx_work(struct work_struct *work);
|
||||||
void wl1271_tx_complete(struct wl1271 *wl, u32 count);
|
void wl1271_tx_complete(struct wl1271 *wl);
|
||||||
void wl1271_tx_flush(struct wl1271 *wl);
|
void wl1271_tx_flush(struct wl1271 *wl);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
Loading…
Reference in New Issue
Block a user