Commit 0a4254be authored by Hante Meuleman's avatar Hante Meuleman Committed by John W. Linville

brcmfmac: always use worker thread for tx data.

When fw signalling is disabled tx is sent immediately. Using
queues and worker thread allows usb to do synchronous autopm. This
patch makes fws use queues and worker thread even if signalling is
not supported by FW or not enabled.
Reviewed-by: default avatarArend Van Spriel <arend@broadcom.com>
Reviewed-by: default avatarPieter-Paul Giesberts <pieterpg@broadcom.com>
Signed-off-by: default avatarHante Meuleman <meuleman@broadcom.com>
Signed-off-by: default avatarArend van Spriel <arend@broadcom.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 87edd891
...@@ -557,7 +557,6 @@ struct brcmf_pub { ...@@ -557,7 +557,6 @@ struct brcmf_pub {
struct brcmf_fweh_info fweh; struct brcmf_fweh_info fweh;
bool fw_signals;
struct brcmf_fws_info *fws; struct brcmf_fws_info *fws;
spinlock_t fws_spinlock; spinlock_t fws_spinlock;
......
...@@ -278,18 +278,10 @@ void brcmf_txflowblock(struct device *dev, bool state) ...@@ -278,18 +278,10 @@ void brcmf_txflowblock(struct device *dev, bool state)
{ {
struct brcmf_bus *bus_if = dev_get_drvdata(dev); struct brcmf_bus *bus_if = dev_get_drvdata(dev);
struct brcmf_pub *drvr = bus_if->drvr; struct brcmf_pub *drvr = bus_if->drvr;
int i;
brcmf_dbg(TRACE, "Enter\n"); brcmf_dbg(TRACE, "Enter\n");
if (brcmf_fws_fc_active(drvr->fws)) { brcmf_fws_bus_blocked(drvr, state);
brcmf_fws_bus_blocked(drvr, state);
} else {
for (i = 0; i < BRCMF_MAX_IFS; i++)
brcmf_txflowblock_if(drvr->iflist[i],
BRCMF_NETIF_STOP_REASON_BLOCK_BUS,
state);
}
} }
static void brcmf_netif_rx(struct brcmf_if *ifp, struct sk_buff *skb) static void brcmf_netif_rx(struct brcmf_if *ifp, struct sk_buff *skb)
...@@ -534,7 +526,7 @@ void brcmf_rx_frames(struct device *dev, struct sk_buff_head *skb_list) ...@@ -534,7 +526,7 @@ void brcmf_rx_frames(struct device *dev, struct sk_buff_head *skb_list)
skb_unlink(skb, skb_list); skb_unlink(skb, skb_list);
/* process and remove protocol-specific header */ /* process and remove protocol-specific header */
ret = brcmf_proto_hdrpull(drvr, drvr->fw_signals, &ifidx, skb); ret = brcmf_proto_hdrpull(drvr, true, &ifidx, skb);
ifp = drvr->iflist[ifidx]; ifp = drvr->iflist[ifidx];
if (ret || !ifp || !ifp->ndev) { if (ret || !ifp || !ifp->ndev) {
...@@ -1109,7 +1101,6 @@ int brcmf_bus_start(struct device *dev) ...@@ -1109,7 +1101,6 @@ int brcmf_bus_start(struct device *dev)
if (ret < 0) if (ret < 0)
goto fail; goto fail;
drvr->fw_signals = true;
ret = brcmf_fws_init(drvr); ret = brcmf_fws_init(drvr);
if (ret < 0) if (ret < 0)
goto fail; goto fail;
......
...@@ -425,6 +425,7 @@ struct brcmf_fws_info { ...@@ -425,6 +425,7 @@ struct brcmf_fws_info {
struct brcmf_fws_stats stats; struct brcmf_fws_stats stats;
struct brcmf_fws_hanger hanger; struct brcmf_fws_hanger hanger;
enum brcmf_fws_fcmode fcmode; enum brcmf_fws_fcmode fcmode;
bool fw_signals;
bool bcmc_credit_check; bool bcmc_credit_check;
struct brcmf_fws_macdesc_table desc; struct brcmf_fws_macdesc_table desc;
struct workqueue_struct *fws_wq; struct workqueue_struct *fws_wq;
...@@ -1160,7 +1161,8 @@ static void brcmf_fws_return_credits(struct brcmf_fws_info *fws, ...@@ -1160,7 +1161,8 @@ static void brcmf_fws_return_credits(struct brcmf_fws_info *fws,
static void brcmf_fws_schedule_deq(struct brcmf_fws_info *fws) static void brcmf_fws_schedule_deq(struct brcmf_fws_info *fws)
{ {
/* only schedule dequeue when there are credits for delayed traffic */ /* only schedule dequeue when there are credits for delayed traffic */
if (fws->fifo_credit_map & fws->fifo_delay_map) if ((fws->fifo_credit_map & fws->fifo_delay_map) ||
(!brcmf_fws_fc_active(fws) && fws->fifo_delay_map))
queue_work(fws->fws_wq, &fws->fws_dequeue_work); queue_work(fws->fws_wq, &fws->fws_dequeue_work);
} }
...@@ -1498,8 +1500,10 @@ int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len, ...@@ -1498,8 +1500,10 @@ int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len,
WARN_ON(signal_len > skb->len); WARN_ON(signal_len > skb->len);
if (!signal_len)
return 0;
/* if flow control disabled, skip to packet data and leave */ /* if flow control disabled, skip to packet data and leave */
if (!signal_len || !drvr->fw_signals) { if (!fws->fw_signals) {
skb_pull(skb, signal_len); skb_pull(skb, signal_len);
return 0; return 0;
} }
...@@ -1749,7 +1753,6 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb) ...@@ -1749,7 +1753,6 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb)
int fifo = BRCMF_FWS_FIFO_BCMC; int fifo = BRCMF_FWS_FIFO_BCMC;
bool multicast = is_multicast_ether_addr(eh->h_dest); bool multicast = is_multicast_ether_addr(eh->h_dest);
bool pae = eh->h_proto == htons(ETH_P_PAE); bool pae = eh->h_proto == htons(ETH_P_PAE);
int ret;
brcmf_dbg(DATA, "tx proto=0x%X\n", ntohs(eh->h_proto)); brcmf_dbg(DATA, "tx proto=0x%X\n", ntohs(eh->h_proto));
/* determine the priority */ /* determine the priority */
...@@ -1760,17 +1763,6 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb) ...@@ -1760,17 +1763,6 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb)
if (pae) if (pae)
atomic_inc(&ifp->pend_8021x_cnt); atomic_inc(&ifp->pend_8021x_cnt);
if (!brcmf_fws_fc_active(fws)) {
/* If the protocol uses a data header, apply it */
brcmf_proto_hdrpush(drvr, ifp->ifidx, 0, skb);
/* Use bus module to send data frame */
ret = brcmf_bus_txdata(drvr->bus_if, skb);
if (ret < 0)
brcmf_txfinalize(drvr, skb, false);
return ret;
}
/* set control buffer information */ /* set control buffer information */
skcb->if_flags = 0; skcb->if_flags = 0;
skcb->state = BRCMF_FWS_SKBSTATE_NEW; skcb->state = BRCMF_FWS_SKBSTATE_NEW;
...@@ -1818,7 +1810,7 @@ void brcmf_fws_add_interface(struct brcmf_if *ifp) ...@@ -1818,7 +1810,7 @@ void brcmf_fws_add_interface(struct brcmf_if *ifp)
struct brcmf_fws_info *fws = ifp->drvr->fws; struct brcmf_fws_info *fws = ifp->drvr->fws;
struct brcmf_fws_mac_descriptor *entry; struct brcmf_fws_mac_descriptor *entry;
if (!ifp->ndev || !ifp->drvr->fw_signals) if (!ifp->ndev)
return; return;
entry = &fws->desc.iface[ifp->ifidx]; entry = &fws->desc.iface[ifp->ifidx];
...@@ -1849,15 +1841,38 @@ void brcmf_fws_del_interface(struct brcmf_if *ifp) ...@@ -1849,15 +1841,38 @@ void brcmf_fws_del_interface(struct brcmf_if *ifp)
static void brcmf_fws_dequeue_worker(struct work_struct *worker) static void brcmf_fws_dequeue_worker(struct work_struct *worker)
{ {
struct brcmf_fws_info *fws; struct brcmf_fws_info *fws;
struct brcmf_pub *drvr;
struct sk_buff *skb; struct sk_buff *skb;
ulong flags; ulong flags;
int fifo; int fifo;
u32 hslot;
u32 ifidx;
int ret;
fws = container_of(worker, struct brcmf_fws_info, fws_dequeue_work); fws = container_of(worker, struct brcmf_fws_info, fws_dequeue_work);
drvr = fws->drvr;
brcmf_fws_lock(fws->drvr, flags); brcmf_fws_lock(drvr, flags);
for (fifo = BRCMF_FWS_FIFO_BCMC; fifo >= 0 && !fws->bus_flow_blocked; for (fifo = BRCMF_FWS_FIFO_BCMC; fifo >= 0 && !fws->bus_flow_blocked;
fifo--) { fifo--) {
if (!brcmf_fws_fc_active(fws)) {
while ((skb = brcmf_fws_deq(fws, fifo)) != NULL) {
hslot = brcmf_skb_htod_tag_get_field(skb,
HSLOT);
brcmf_fws_hanger_poppkt(&fws->hanger, hslot,
&skb, true);
ifidx = brcmf_skb_if_flags_get_field(skb,
INDEX);
brcmf_proto_hdrpush(drvr, ifidx, 0, skb);
/* Use bus module to send data frame */
ret = brcmf_bus_txdata(drvr->bus_if, skb);
if (ret < 0)
brcmf_txfinalize(drvr, skb, false);
if (fws->bus_flow_blocked)
break;
}
continue;
}
while ((fws->fifo_credit[fifo]) || ((!fws->bcmc_credit_check) && while ((fws->fifo_credit[fifo]) || ((!fws->bcmc_credit_check) &&
(fifo == BRCMF_FWS_FIFO_BCMC))) { (fifo == BRCMF_FWS_FIFO_BCMC))) {
skb = brcmf_fws_deq(fws, fifo); skb = brcmf_fws_deq(fws, fifo);
...@@ -1885,17 +1900,15 @@ static void brcmf_fws_dequeue_worker(struct work_struct *worker) ...@@ -1885,17 +1900,15 @@ static void brcmf_fws_dequeue_worker(struct work_struct *worker)
} }
} }
} }
brcmf_fws_unlock(fws->drvr, flags); brcmf_fws_unlock(drvr, flags);
} }
int brcmf_fws_init(struct brcmf_pub *drvr) int brcmf_fws_init(struct brcmf_pub *drvr)
{ {
struct brcmf_fws_info *fws;
u32 tlv = BRCMF_FWS_FLAGS_RSSI_SIGNALS; u32 tlv = BRCMF_FWS_FLAGS_RSSI_SIGNALS;
int rc; int rc;
if (!drvr->fw_signals)
return 0;
spin_lock_init(&drvr->fws_spinlock); spin_lock_init(&drvr->fws_spinlock);
drvr->fws = kzalloc(sizeof(*(drvr->fws)), GFP_KERNEL); drvr->fws = kzalloc(sizeof(*(drvr->fws)), GFP_KERNEL);
...@@ -1904,20 +1917,21 @@ int brcmf_fws_init(struct brcmf_pub *drvr) ...@@ -1904,20 +1917,21 @@ int brcmf_fws_init(struct brcmf_pub *drvr)
goto fail; goto fail;
} }
fws = drvr->fws;
/* set linkage back */ /* set linkage back */
drvr->fws->drvr = drvr; fws->drvr = drvr;
drvr->fws->fcmode = fcmode; fws->fcmode = fcmode;
drvr->fws->fws_wq = create_singlethread_workqueue("brcmf_fws_wq"); fws->fws_wq = create_singlethread_workqueue("brcmf_fws_wq");
if (drvr->fws->fws_wq == NULL) { if (fws->fws_wq == NULL) {
brcmf_err("workqueue creation failed\n"); brcmf_err("workqueue creation failed\n");
rc = -EBADF; rc = -EBADF;
goto fail; goto fail;
} }
INIT_WORK(&drvr->fws->fws_dequeue_work, brcmf_fws_dequeue_worker); INIT_WORK(&fws->fws_dequeue_work, brcmf_fws_dequeue_worker);
/* enable firmware signalling if fcmode active */ /* enable firmware signalling if fcmode active */
if (drvr->fws->fcmode != BRCMF_FWS_FCMODE_NONE) if (fws->fcmode != BRCMF_FWS_FCMODE_NONE)
tlv |= BRCMF_FWS_FLAGS_XONXOFF_SIGNALS | tlv |= BRCMF_FWS_FLAGS_XONXOFF_SIGNALS |
BRCMF_FWS_FLAGS_CREDIT_STATUS_SIGNALS | BRCMF_FWS_FLAGS_CREDIT_STATUS_SIGNALS |
BRCMF_FWS_FLAGS_HOST_PROPTXSTATUS_ACTIVE | BRCMF_FWS_FLAGS_HOST_PROPTXSTATUS_ACTIVE |
...@@ -1937,34 +1951,33 @@ int brcmf_fws_init(struct brcmf_pub *drvr) ...@@ -1937,34 +1951,33 @@ int brcmf_fws_init(struct brcmf_pub *drvr)
goto fail; goto fail;
} }
/* setting the iovar may fail if feature is unsupported /* Setting the iovar may fail if feature is unsupported
* so leave the rc as is so driver initialization can * so leave the rc as is so driver initialization can
* continue. * continue. Set mode back to none indicating not enabled.
*/ */
fws->fw_signals = true;
if (brcmf_fil_iovar_int_set(drvr->iflist[0], "tlv", tlv)) { if (brcmf_fil_iovar_int_set(drvr->iflist[0], "tlv", tlv)) {
brcmf_err("failed to set bdcv2 tlv signaling\n"); brcmf_err("failed to set bdcv2 tlv signaling\n");
goto fail_event; fws->fcmode = BRCMF_FWS_FCMODE_NONE;
fws->fw_signals = false;
} }
if (brcmf_fil_iovar_int_set(drvr->iflist[0], "ampdu_hostreorder", 1)) if (brcmf_fil_iovar_int_set(drvr->iflist[0], "ampdu_hostreorder", 1))
brcmf_dbg(INFO, "enabling AMPDU host-reorder failed\n"); brcmf_dbg(INFO, "enabling AMPDU host-reorder failed\n");
brcmf_fws_hanger_init(&drvr->fws->hanger); brcmf_fws_hanger_init(&fws->hanger);
brcmf_fws_macdesc_init(&drvr->fws->desc.other, NULL, 0); brcmf_fws_macdesc_init(&fws->desc.other, NULL, 0);
brcmf_fws_macdesc_set_name(drvr->fws, &drvr->fws->desc.other); brcmf_fws_macdesc_set_name(fws, &fws->desc.other);
brcmu_pktq_init(&drvr->fws->desc.other.psq, BRCMF_FWS_PSQ_PREC_COUNT, brcmu_pktq_init(&fws->desc.other.psq, BRCMF_FWS_PSQ_PREC_COUNT,
BRCMF_FWS_PSQ_LEN); BRCMF_FWS_PSQ_LEN);
/* create debugfs file for statistics */ /* create debugfs file for statistics */
brcmf_debugfs_create_fws_stats(drvr, &drvr->fws->stats); brcmf_debugfs_create_fws_stats(drvr, &fws->stats);
brcmf_dbg(INFO, "%s bdcv2 tlv signaling [%x]\n", brcmf_dbg(INFO, "%s bdcv2 tlv signaling [%x]\n",
drvr->fw_signals ? "enabled" : "disabled", tlv); fws->fw_signals ? "enabled" : "disabled", tlv);
return 0; return 0;
fail_event:
brcmf_fweh_unregister(drvr, BRCMF_E_BCMC_CREDIT_SUPPORT);
brcmf_fweh_unregister(drvr, BRCMF_E_FIFO_CREDIT_MAP);
fail: fail:
brcmf_fws_deinit(drvr); brcmf_fws_deinit(drvr);
return rc; return rc;
...@@ -1978,11 +1991,6 @@ void brcmf_fws_deinit(struct brcmf_pub *drvr) ...@@ -1978,11 +1991,6 @@ void brcmf_fws_deinit(struct brcmf_pub *drvr)
if (!fws) if (!fws)
return; return;
/* disable firmware signalling entirely
* to avoid using the workqueue.
*/
drvr->fw_signals = false;
if (drvr->fws->fws_wq) if (drvr->fws->fws_wq)
destroy_workqueue(drvr->fws->fws_wq); destroy_workqueue(drvr->fws->fws_wq);
...@@ -1998,7 +2006,7 @@ void brcmf_fws_deinit(struct brcmf_pub *drvr) ...@@ -1998,7 +2006,7 @@ void brcmf_fws_deinit(struct brcmf_pub *drvr)
bool brcmf_fws_fc_active(struct brcmf_fws_info *fws) bool brcmf_fws_fc_active(struct brcmf_fws_info *fws)
{ {
if (!fws) if (!fws->creditmap_received)
return false; return false;
return fws->fcmode != BRCMF_FWS_FCMODE_NONE; return fws->fcmode != BRCMF_FWS_FCMODE_NONE;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment