Browse Source

qcacmn: Genoa: SDIO: Handle Tx Padding & Credits

In Genoa SDIO ADMA implementation Host sends packets to FW in
multiples of SDIO Block size.

If the packet/bundle is not block aligned Host adds padding at the
end of Packet/Bundle.

If the TX packet plus padding exceeds one FW TX Buffer, Padding data
will occupy the next FW TX buffer. Same applies for bundle TX packet.

For above scenario, HTC_FLAGS_PADDING_CHECK of HTC header Flags is used
to notify the FW that - Padding data follows the currentHTC packet

Since the padding data will take one extra FW Tx Buffer, host need to
handle the extra Tx credit being used by the padding data/buffer

CRs-Fixed: 2516617
Change-Id: I1eb394859689b281653bfd6d681804eb016a14cf
Visweswara Tanuku 5 years ago
parent
commit
957b63aec6
2 changed files with 224 additions and 2 deletions
  1. 4 0
      htc/htc_api.h
  2. 220 2
      htc/htc_send.c

+ 4 - 0
htc/htc_api.h

@@ -69,6 +69,8 @@ struct ol_ath_htc_stats {
 /* To resume HTT Tx queue during runtime resume */
 typedef void (*HTC_EP_RESUME_TX_QUEUE)(void *);
 
+typedef int (*HTC_EP_PADDING_CREDIT_UPDATE) (void *, int);
+
 /* per service connection send completion */
 typedef void (*HTC_EP_SEND_PKT_COMPLETE)(void *, HTC_PACKET *);
 /* per service connection callback when a plurality of packets have been sent
@@ -175,6 +177,8 @@ struct htc_ep_callbacks {
 	HTC_EP_SEND_PKT_COMP_MULTIPLE EpTxCompleteMultiple;
 
 	HTC_EP_RESUME_TX_QUEUE ep_resume_tx_queue;
+
+	HTC_EP_PADDING_CREDIT_UPDATE ep_padding_credit_update;
 	/* if EpRecvAllocThresh is non-NULL, HTC will compare the
 	 * threshold value to the current recv packet length and invoke
 	 * the EpRecvAllocThresh callback to acquire a packet buffer

+ 220 - 2
htc/htc_send.c

@@ -398,6 +398,103 @@ static QDF_STATUS htc_send_bundled_netbuf(HTC_TARGET *target,
 	return status;
 }
 
+#ifdef QCA_TX_PADDING_CREDIT_SUPPORT
+#define SDIO_BLOCK_SIZE		512
+static int htc_tx_pad_credit_avail(HTC_ENDPOINT *ep)
+{
+	int ret = 0;
+
+	if (!ep || !ep->EpCallBacks.pContext ||
+	    !ep->EpCallBacks.ep_padding_credit_update)
+		return 1;
+
+	ret = ep->EpCallBacks.ep_padding_credit_update(ep->EpCallBacks.pContext,
+						       0);
+
+	if (ret < 2)
+		AR_DEBUG_PRINTF(ATH_DEBUG_INFO, ("%s ret %d\n", __func__, ret));
+
+	return ret;
+}
+
+static bool htc_handle_extra_tx_credit(HTC_ENDPOINT *ep,
+				       HTC_PACKET *p_last_htc_pkt,
+				       unsigned char *p_last_pkt_bundle_buffer,
+				       unsigned char **p_bundle_buffer,
+				       int tot_data_len)
+{
+	bool extra_tx_credit = FALSE;
+	HTC_FRAME_HDR *p_htc_hdr;
+	int first_buf_bundled_len = 0, last_buf_len = 0;
+	int sdio_pad = 0, free_space = 0;
+	int (*update_ep_padding_credit)(void *, int);
+
+	update_ep_padding_credit = ep->EpCallBacks.ep_padding_credit_update;
+
+	AR_DEBUG_PRINTF(ATH_DEBUG_SEND,
+			("%s Tot data_len = %d\n", __func__, tot_data_len));
+
+	if (!p_last_htc_pkt)
+		return extra_tx_credit;
+
+	last_buf_len = (p_last_htc_pkt->ActualLength + HTC_HDR_LENGTH);
+	if (tot_data_len != last_buf_len) {
+		first_buf_bundled_len = tot_data_len - ep->TxCreditSize;
+		free_space = tot_data_len -
+					(first_buf_bundled_len + last_buf_len);
+	} else {
+		free_space = ep->TxCreditSize - tot_data_len;
+	}
+
+	sdio_pad = SDIO_BLOCK_SIZE - ((first_buf_bundled_len + last_buf_len) %
+			SDIO_BLOCK_SIZE);
+
+	AR_DEBUG_PRINTF(ATH_DEBUG_SEND,
+			("%s first_buf_bundled_len = %d last_buf_len = %d\n",
+			__func__, first_buf_bundled_len, last_buf_len));
+
+	AR_DEBUG_PRINTF(ATH_DEBUG_SEND,
+			("%s sdio_pad = %d free_space = %d\n", __func__,
+			sdio_pad, free_space));
+
+	if (sdio_pad <= free_space) {
+		if (p_bundle_buffer && *p_bundle_buffer) {
+			/* Align Tx bundled buf to avoid a extra Padding buf */
+			*p_bundle_buffer -= (free_space - sdio_pad);
+		}
+	} else {
+		/* Extra Padding Buffer needed, consume extra tx credit */
+		AR_DEBUG_PRINTF(ATH_DEBUG_SEND,
+				("%s Used a Tx credit for Padding Buffer\n",
+				 __func__));
+		p_htc_hdr = (HTC_FRAME_HDR *)(p_last_pkt_bundle_buffer);
+		p_htc_hdr->Flags |= HTC_FLAGS_PADDING_CHECK;
+		extra_tx_credit = TRUE;
+		if (ep->EpCallBacks.ep_padding_credit_update) {
+			/* Decrement 1 credit at host,
+			 * due to extra tx credit consumed by padding buffer
+			 */
+			update_ep_padding_credit(ep->EpCallBacks.pContext, -1);
+		}
+	}
+	return extra_tx_credit;
+}
+#else
+static int htc_tx_pad_credit_avail(HTC_ENDPOINT *ep)
+{
+	return 1;
+}
+
+static bool htc_handle_extra_tx_credit(HTC_ENDPOINT *ep,
+				       HTC_PACKET *p_last_htc_pkt,
+				       unsigned char *p_last_pkt_bundle_buffer,
+				       unsigned char **p_bundle_buffer,
+				       int tot_data_len)
+{
+	return FALSE;
+}
+#endif
+
 /**
  * htc_issue_packets_bundle() - HTC function to send bundle packets from a queue
  * @target: HTC target on which packets need to be sent
@@ -419,6 +516,8 @@ static void htc_issue_packets_bundle(HTC_TARGET *target,
 	int creditPad, creditRemainder, transferLength, bundlesSpaceRemaining =
 		0;
 	HTC_PACKET_QUEUE *pQueueSave = NULL;
+	HTC_PACKET *p_last_htc_pkt = NULL;
+	unsigned char *p_last_pkt_bundle_buffer = NULL;
 
 	bundlesSpaceRemaining =
 		target->MaxMsgsPerHTCBundle * pEndpoint->TxCreditSize;
@@ -434,6 +533,10 @@ static void htc_issue_packets_bundle(HTC_TARGET *target,
 	pBundleBuffer = qdf_nbuf_data(bundleBuf);
 	pQueueSave = (HTC_PACKET_QUEUE *) pPacketTx->pContext;
 	while (1) {
+		if (pEndpoint->EpCallBacks.ep_padding_credit_update) {
+			if (htc_tx_pad_credit_avail(pEndpoint) < 1)
+				break;
+		}
 		pPacket = htc_packet_dequeue(pPktQueue);
 		if (!pPacket)
 			break;
@@ -451,6 +554,12 @@ static void htc_issue_packets_bundle(HTC_TARGET *target,
 		}
 
 		if (bundlesSpaceRemaining < transferLength) {
+			htc_handle_extra_tx_credit(pEndpoint, p_last_htc_pkt,
+						   p_last_pkt_bundle_buffer,
+						   &pBundleBuffer,
+						   pBundleBuffer -
+						   qdf_nbuf_data(bundleBuf));
+
 			/* send out previous buffer */
 			htc_send_bundled_netbuf(target, pEndpoint,
 						pBundleBuffer - last_credit_pad,
@@ -480,6 +589,9 @@ static void htc_issue_packets_bundle(HTC_TARGET *target,
 			pQueueSave = (HTC_PACKET_QUEUE *) pPacketTx->pContext;
 		}
 
+		p_last_htc_pkt = pPacket;
+		p_last_pkt_bundle_buffer = pBundleBuffer;
+
 		bundlesSpaceRemaining -= transferLength;
 		netbuf = GET_HTC_PACKET_NET_BUF_CONTEXT(pPacket);
 
@@ -528,15 +640,41 @@ static void htc_issue_packets_bundle(HTC_TARGET *target,
 			last_credit_pad = creditPad;
 	}
 	/* send out remaining buffer */
-	if (pBundleBuffer != qdf_nbuf_data(bundleBuf))
+	if (pBundleBuffer != qdf_nbuf_data(bundleBuf)) {
+		htc_handle_extra_tx_credit(pEndpoint, p_last_htc_pkt,
+					   p_last_pkt_bundle_buffer,
+					   &pBundleBuffer,
+					   pBundleBuffer -
+					   qdf_nbuf_data(bundleBuf));
+
 		htc_send_bundled_netbuf(target, pEndpoint,
 					pBundleBuffer - last_credit_pad,
 					pPacketTx);
-	else
+	} else {
 		free_htc_bundle_packet(target, pPacketTx);
+	}
 }
 #endif /* ENABLE_BUNDLE_TX */
 #else
+static int htc_tx_pad_credit_avail(HTC_ENDPOINT *ep)
+{
+	return 1;
+}
+
+bool htc_handle_extra_tx_credit(HTC_ENDPOINT *ep,
+				HTC_PACKET *p_last_htc_pkt,
+				unsigned char *p_last_pkt_bundle_buffer,
+				unsigned char **p_bundle_buffer,
+				int tot_data_len);
+bool htc_handle_extra_tx_credit(HTC_ENDPOINT *ep,
+				HTC_PACKET *p_last_htc_pkt,
+				unsigned char *p_last_pkt_bundle_buffer,
+				unsigned char **p_bundle_buffer,
+				int tot_data_len)
+{
+	return FALSE;
+}
+
 static void htc_issue_packets_bundle(HTC_TARGET *target,
 				     HTC_ENDPOINT *pEndpoint,
 				     HTC_PACKET_QUEUE *pPktQueue)
@@ -565,6 +703,13 @@ static QDF_STATUS htc_issue_packets(HTC_TARGET *target,
 	enum qdf_bus_type bus_type;
 	QDF_STATUS ret;
 	bool rt_put = false;
+	bool used_extra_tx_credit = false;
+	uint8_t *buf = NULL;
+	int (*update_ep_padding_credit)(void *, int);
+	void *ctx = NULL;
+
+	update_ep_padding_credit =
+			pEndpoint->EpCallBacks.ep_padding_credit_update;
 
 	bus_type = hif_get_bus_type(target->hif_dev);
 
@@ -579,6 +724,11 @@ static QDF_STATUS htc_issue_packets(HTC_TARGET *target,
 			case QDF_BUS_TYPE_SDIO:
 				if (!IS_TX_CREDIT_FLOW_ENABLED(pEndpoint))
 					break;
+				if (update_ep_padding_credit) {
+					if (htc_tx_pad_credit_avail
+					    (pEndpoint) < 1)
+						break;
+				}
 			case QDF_BUS_TYPE_USB:
 				htc_issue_packets_bundle(target,
 							pEndpoint,
@@ -591,6 +741,13 @@ static QDF_STATUS htc_issue_packets(HTC_TARGET *target,
 		/* if not bundling or there was a packet that could not be
 		 * placed in a bundle, and send it by normal way
 		 */
+		if (pEndpoint->EpCallBacks.ep_padding_credit_update) {
+			if (htc_tx_pad_credit_avail(pEndpoint) < 1) {
+				status = QDF_STATUS_E_FAILURE;
+				break;
+			}
+		}
+
 		pPacket = htc_packet_dequeue(pPktQueue);
 		if (!pPacket) {
 			/* local queue is fully drained */
@@ -678,11 +835,26 @@ static QDF_STATUS htc_issue_packets(HTC_TARGET *target,
 			  pEndpoint->TxCreditSize,
 			  HTC_HDR_LENGTH + pPacket->ActualLength);
 #endif
+		buf = (uint8_t *)qdf_nbuf_get_frag_vaddr(netbuf, 0);
+		used_extra_tx_credit =
+			htc_handle_extra_tx_credit(pEndpoint, pPacket, buf,
+						   NULL, pPacket->ActualLength +
+						   HTC_HDR_LENGTH);
+
 		status = hif_send_head(target->hif_dev,
 				       pEndpoint->UL_PipeID, pEndpoint->Id,
 				       HTC_HDR_LENGTH + pPacket->ActualLength,
 				       netbuf, data_attr);
 
+		if (status != QDF_STATUS_SUCCESS) {
+			if (pEndpoint->EpCallBacks.ep_padding_credit_update) {
+				if (used_extra_tx_credit) {
+					ctx = pEndpoint->EpCallBacks.pContext;
+					update_ep_padding_credit(ctx, 1);
+				}
+			}
+		}
+
 		htc_issue_tx_bundle_stats_inc(target);
 
 		target->ce_send_cnt++;
@@ -1690,6 +1862,7 @@ QDF_STATUS htc_send_data_pkt(HTC_HANDLE HTCHandle, HTC_PACKET *pPacket,
 	int tx_resources;
 	QDF_STATUS status = QDF_STATUS_SUCCESS;
 	uint32_t data_attr = 0;
+	bool used_extra_tx_credit = false;
 
 	if (pPacket) {
 		if ((pPacket->Endpoint >= ENDPOINT_MAX) ||
@@ -1834,12 +2007,41 @@ QDF_STATUS htc_send_data_pkt(HTC_HANDLE HTCHandle, HTC_PACKET *pPacket,
 		     HTC_MIN_MSG_PER_BUNDLE) &&
 		    (hif_get_bus_type(target->hif_dev) == QDF_BUS_TYPE_SDIO ||
 		     hif_get_bus_type(target->hif_dev) == QDF_BUS_TYPE_USB)) {
+			if (pEndpoint->EpCallBacks.ep_padding_credit_update) {
+				if (htc_tx_pad_credit_avail(pEndpoint) < 1) {
+					status = QDF_STATUS_E_RESOURCES;
+					/* put the sendQueue back at the front
+					 * of pEndpoint->TxQueue
+					 */
+					LOCK_HTC_TX(target);
+					HTC_PACKET_QUEUE_TRANSFER_TO_HEAD(
+							&pEndpoint->TxQueue,
+							&sendQueue);
+					UNLOCK_HTC_TX(target);
+					break;
+				}
+			}
 			htc_issue_packets_bundle(target, pEndpoint, &sendQueue);
 		}
+		if (pEndpoint->EpCallBacks.ep_padding_credit_update) {
+			if (htc_tx_pad_credit_avail(pEndpoint) < 1) {
+				status = QDF_STATUS_E_RESOURCES;
+				/* put the sendQueue back at the front
+				 * of pEndpoint->TxQueue
+				 */
+				LOCK_HTC_TX(target);
+				HTC_PACKET_QUEUE_TRANSFER_TO_HEAD(
+							&pEndpoint->TxQueue,
+							&sendQueue);
+				UNLOCK_HTC_TX(target);
+				break;
+			}
+		}
 		pPacket = htc_packet_dequeue(&sendQueue);
 		if (!pPacket)
 			break;
 		netbuf = GET_HTC_PACKET_NET_BUF_CONTEXT(pPacket);
+		pHtcHdr = (HTC_FRAME_HDR *)qdf_nbuf_get_frag_vaddr(netbuf, 0);
 
 		LOCK_HTC_TX(target);
 		/* store in look up queue to match completions */
@@ -1848,11 +2050,27 @@ QDF_STATUS htc_send_data_pkt(HTC_HANDLE HTCHandle, HTC_PACKET *pPacket,
 		pEndpoint->ul_outstanding_cnt++;
 		UNLOCK_HTC_TX(target);
 
+		used_extra_tx_credit =
+				htc_handle_extra_tx_credit(pEndpoint, pPacket,
+							   (uint8_t *)pHtcHdr,
+							   NULL,
+							   pPacket->ActualLength
+							   + HTC_HDR_LENGTH);
+
 		status = hif_send_head(target->hif_dev,
 				       pEndpoint->UL_PipeID,
 				       pEndpoint->Id,
 				       HTC_HDR_LENGTH + pPacket->ActualLength,
 				       netbuf, data_attr);
+		if (status != QDF_STATUS_SUCCESS) {
+			if (pEndpoint->EpCallBacks.ep_padding_credit_update) {
+				if (used_extra_tx_credit) {
+					pEndpoint->EpCallBacks.
+					ep_padding_credit_update
+					(pEndpoint->EpCallBacks.pContext, 1);
+				}
+			}
+		}
 #if DEBUG_BUNDLE
 		qdf_print(" Send single EP%d buffer size:0x%x, total:0x%x.",
 			  pEndpoint->Id,