ath10k: add HTC TX credits replenishing notification
[cascardo/linux.git] / drivers / net / wireless / ath / ath10k / htc.c
index 7b5c334..49da4e5 100644 (file)
@@ -150,6 +150,9 @@ err:
        ep->tx_credits += credits;
        spin_unlock_bh(&htc->tx_lock);
 
+       if (ep->ep_ops.ep_tx_credits)
+               ep->ep_ops.ep_tx_credits(htc->ar);
+
        /* this is the simplest way to handle out-of-resources for non-credit
         * based endpoints. credit based endpoints can still get -ENOSR, but
         * this is highly unlikely as credit reservation should prevent that */
@@ -167,49 +170,6 @@ err:
        return ret;
 }
 
-static struct sk_buff *ath10k_htc_get_skb_credit_based(struct ath10k_htc *htc,
-                                                      struct ath10k_htc_ep *ep,
-                                                      u8 *credits)
-{
-       struct sk_buff *skb;
-       struct ath10k_skb_cb *skb_cb;
-       int credits_required;
-       int remainder;
-       unsigned int transfer_len;
-
-       lockdep_assert_held(&htc->tx_lock);
-
-       skb = __skb_dequeue(&ep->tx_queue);
-       if (!skb)
-               return NULL;
-
-       skb_cb = ATH10K_SKB_CB(skb);
-       transfer_len = skb->len;
-
-       if (likely(transfer_len <= htc->target_credit_size)) {
-               credits_required = 1;
-       } else {
-               /* figure out how many credits this message requires */
-               credits_required = transfer_len / htc->target_credit_size;
-               remainder = transfer_len % htc->target_credit_size;
-
-               if (remainder)
-                       credits_required++;
-       }
-
-       ath10k_dbg(ATH10K_DBG_HTC, "Credits required %d got %d\n",
-                  credits_required, ep->tx_credits);
-
-       if (ep->tx_credits < credits_required) {
-               __skb_queue_head(&ep->tx_queue, skb);
-               return NULL;
-       }
-
-       ep->tx_credits -= credits_required;
-       *credits = credits_required;
-       return skb;
-}
-
 static void ath10k_htc_send_work(struct work_struct *work)
 {
        struct ath10k_htc_ep *ep = container_of(work,
@@ -224,11 +184,16 @@ static void ath10k_htc_send_work(struct work_struct *work)
                        ath10k_htc_send_complete_check(ep, 0);
 
                spin_lock_bh(&htc->tx_lock);
-               if (ep->tx_credit_flow_enabled)
-                       skb = ath10k_htc_get_skb_credit_based(htc, ep,
-                                                             &credits);
-               else
-                       skb = __skb_dequeue(&ep->tx_queue);
+               skb = __skb_dequeue(&ep->tx_queue);
+
+               if (ep->tx_credit_flow_enabled) {
+                       credits = DIV_ROUND_UP(skb->len,
+                                              htc->target_credit_size);
+                       if (ep->tx_credits < credits) {
+                               __skb_queue_head(&ep->tx_queue, skb);
+                               skb = NULL;
+                       }
+               }
                spin_unlock_bh(&htc->tx_lock);
 
                if (!skb)
@@ -246,15 +211,22 @@ int ath10k_htc_send(struct ath10k_htc *htc,
 {
        struct ath10k_htc_ep *ep = &htc->endpoint[eid];
 
+       if (htc->ar->state == ATH10K_STATE_WEDGED)
+               return -ECOMM;
+
        if (eid >= ATH10K_HTC_EP_COUNT) {
                ath10k_warn("Invalid endpoint id: %d\n", eid);
                return -ENOENT;
        }
 
-       skb_push(skb, sizeof(struct ath10k_htc_hdr));
-
        spin_lock_bh(&htc->tx_lock);
+       if (htc->stopped) {
+               spin_unlock_bh(&htc->tx_lock);
+               return -ESHUTDOWN;
+       }
+
        __skb_queue_tail(&ep->tx_queue, skb);
+       skb_push(skb, sizeof(struct ath10k_htc_hdr));
        spin_unlock_bh(&htc->tx_lock);
 
        queue_work(htc->ar->workqueue, &ep->send_work);
@@ -265,25 +237,19 @@ static int ath10k_htc_tx_completion_handler(struct ath10k *ar,
                                            struct sk_buff *skb,
                                            unsigned int eid)
 {
-       struct ath10k_htc *htc = ar->htc;
+       struct ath10k_htc *htc = &ar->htc;
        struct ath10k_htc_ep *ep = &htc->endpoint[eid];
-       bool stopping;
 
        ath10k_htc_notify_tx_completion(ep, skb);
        /* the skb now belongs to the completion handler */
 
+       /* note: when using TX credit flow, the re-checking of queues happens
+        * when credits flow back from the target.  in the non-TX credit case,
+        * we recheck after the packet completes */
        spin_lock_bh(&htc->tx_lock);
-       stopping = htc->stopping;
-       spin_unlock_bh(&htc->tx_lock);
-
-       if (!ep->tx_credit_flow_enabled && !stopping)
-               /*
-                * note: when using TX credit flow, the re-checking of
-                * queues happens when credits flow back from the target.
-                * in the non-TX credit case, we recheck after the packet
-                * completes
-                */
+       if (!ep->tx_credit_flow_enabled && !htc->stopped)
                queue_work(ar->workqueue, &ep->send_work);
+       spin_unlock_bh(&htc->tx_lock);
 
        return 0;
 }
@@ -339,6 +305,12 @@ ath10k_htc_process_credit_report(struct ath10k_htc *htc,
                ep = &htc->endpoint[report->eid];
                ep->tx_credits += report->credits;
 
+               if (ep->ep_ops.ep_tx_credits) {
+                       spin_unlock_bh(&htc->tx_lock);
+                       ep->ep_ops.ep_tx_credits(htc->ar);
+                       spin_lock_bh(&htc->tx_lock);
+               }
+
                if (ep->tx_credits && !skb_queue_empty(&ep->tx_queue))
                        queue_work(htc->ar->workqueue, &ep->send_work);
        }
@@ -414,7 +386,7 @@ static int ath10k_htc_rx_completion_handler(struct ath10k *ar,
                                            u8 pipe_id)
 {
        int status = 0;
-       struct ath10k_htc *htc = ar->htc;
+       struct ath10k_htc *htc = &ar->htc;
        struct ath10k_htc_hdr *hdr;
        struct ath10k_htc_ep *ep;
        u16 payload_len;
@@ -751,8 +723,8 @@ int ath10k_htc_connect_service(struct ath10k_htc *htc,
        tx_alloc = ath10k_htc_get_credit_allocation(htc,
                                                    conn_req->service_id);
        if (!tx_alloc)
-               ath10k_dbg(ATH10K_DBG_HTC,
-                          "HTC Service %s does not allocate target credits\n",
+               ath10k_dbg(ATH10K_DBG_BOOT,
+                          "boot htc service %s does not allocate target credits\n",
                           htc_service_name(conn_req->service_id));
 
        skb = ath10k_htc_build_tx_ctrl_skb(htc->ar);
@@ -771,16 +743,16 @@ int ath10k_htc_connect_service(struct ath10k_htc *htc,
 
        flags |= SM(tx_alloc, ATH10K_HTC_CONN_FLAGS_RECV_ALLOC);
 
-       req_msg = &msg->connect_service;
-       req_msg->flags = __cpu_to_le16(flags);
-       req_msg->service_id = __cpu_to_le16(conn_req->service_id);
-
        /* Only enable credit flow control for WMI ctrl service */
        if (conn_req->service_id != ATH10K_HTC_SVC_ID_WMI_CONTROL) {
                flags |= ATH10K_HTC_CONN_FLAGS_DISABLE_CREDIT_FLOW_CTRL;
                disable_credit_flow_ctrl = true;
        }
 
+       req_msg = &msg->connect_service;
+       req_msg->flags = __cpu_to_le16(flags);
+       req_msg->service_id = __cpu_to_le16(conn_req->service_id);
+
        INIT_COMPLETION(htc->ctl_resp);
 
        status = ath10k_htc_send(htc, ATH10K_HTC_EP_0, skb);
@@ -872,19 +844,19 @@ setup:
        if (status)
                return status;
 
-       ath10k_dbg(ATH10K_DBG_HTC,
-                  "HTC service: %s UL pipe: %d DL pipe: %d eid: %d ready\n",
+       ath10k_dbg(ATH10K_DBG_BOOT,
+                  "boot htc service '%s' ul pipe %d dl pipe %d eid %d ready\n",
                   htc_service_name(ep->service_id), ep->ul_pipe_id,
                   ep->dl_pipe_id, ep->eid);
 
-       ath10k_dbg(ATH10K_DBG_HTC,
-                  "EP %d UL polled: %d, DL polled: %d\n",
+       ath10k_dbg(ATH10K_DBG_BOOT,
+                  "boot htc ep %d ul polled %d dl polled %d\n",
                   ep->eid, ep->ul_is_polled, ep->dl_is_polled);
 
        if (disable_credit_flow_ctrl && ep->tx_credit_flow_enabled) {
                ep->tx_credit_flow_enabled = false;
-               ath10k_dbg(ATH10K_DBG_HTC,
-                          "HTC service: %s eid: %d TX flow control disabled\n",
+               ath10k_dbg(ATH10K_DBG_BOOT,
+                          "boot htc service '%s' eid %d TX flow control disabled\n",
                           htc_service_name(ep->service_id), assigned_eid);
        }
 
@@ -948,7 +920,7 @@ void ath10k_htc_stop(struct ath10k_htc *htc)
        struct ath10k_htc_ep *ep;
 
        spin_lock_bh(&htc->tx_lock);
-       htc->stopping = true;
+       htc->stopped = true;
        spin_unlock_bh(&htc->tx_lock);
 
        for (i = ATH10K_HTC_EP_0; i < ATH10K_HTC_EP_COUNT; i++) {
@@ -957,26 +929,18 @@ void ath10k_htc_stop(struct ath10k_htc *htc)
        }
 
        ath10k_hif_stop(htc->ar);
-       ath10k_htc_reset_endpoint_states(htc);
 }
 
 /* registered target arrival callback from the HIF layer */
-struct ath10k_htc *ath10k_htc_create(struct ath10k *ar,
-                                    struct ath10k_htc_ops *htc_ops)
+int ath10k_htc_init(struct ath10k *ar)
 {
        struct ath10k_hif_cb htc_callbacks;
        struct ath10k_htc_ep *ep = NULL;
-       struct ath10k_htc *htc = NULL;
-
-       /* FIXME: use struct ath10k instead */
-       htc = kzalloc(sizeof(struct ath10k_htc), GFP_KERNEL);
-       if (!htc)
-               return ERR_PTR(-ENOMEM);
+       struct ath10k_htc *htc = &ar->htc;
 
        spin_lock_init(&htc->tx_lock);
 
-       memcpy(&htc->htc_ops, htc_ops, sizeof(struct ath10k_htc_ops));
-
+       htc->stopped = false;
        ath10k_htc_reset_endpoint_states(htc);
 
        /* setup HIF layer callbacks */
@@ -987,15 +951,10 @@ struct ath10k_htc *ath10k_htc_create(struct ath10k *ar,
        /* Get HIF default pipe for HTC message exchange */
        ep = &htc->endpoint[ATH10K_HTC_EP_0];
 
-       ath10k_hif_init(ar, &htc_callbacks);
+       ath10k_hif_set_callbacks(ar, &htc_callbacks);
        ath10k_hif_get_default_pipe(ar, &ep->ul_pipe_id, &ep->dl_pipe_id);
 
        init_completion(&htc->ctl_resp);
 
-       return htc;
-}
-
-void ath10k_htc_destroy(struct ath10k_htc *htc)
-{
-       kfree(htc);
+       return 0;
 }