diff --git a/core/dp/txrx/ol_tx_send.c b/core/dp/txrx/ol_tx_send.c index f6dc650cb837..bc762694cecf 100644 --- a/core/dp/txrx/ol_tx_send.c +++ b/core/dp/txrx/ol_tx_send.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011-2015 The Linux Foundation. All rights reserved. + * Copyright (c) 2011-2016 The Linux Foundation. All rights reserved. * * Previously licensed under the ISC license by Qualcomm Atheros, Inc. * @@ -504,6 +504,7 @@ ol_tx_completion_handler(ol_txrx_pdev_handle pdev, tx_desc->status = status; netbuf = tx_desc->netbuf; + cdf_runtime_pm_put(); cdf_nbuf_trace_update(netbuf, trace_str); /* Per SDU update of byte count */ byte_cnt += cdf_nbuf_len(netbuf); diff --git a/core/htc/htc_packet.h b/core/htc/htc_packet.h index 34707e5c6e8a..ea0d75f0cafb 100644 --- a/core/htc/htc_packet.h +++ b/core/htc/htc_packet.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2014 The Linux Foundation. All rights reserved. + * Copyright (c) 2013-2014, 2016 The Linux Foundation. All rights reserved. * * Previously licensed under the ISC license by Qualcomm Atheros, Inc. * @@ -61,10 +61,19 @@ typedef struct _HTC_TX_PACKET_INFO { A_UINT32 Flags; /* internal use */ } HTC_TX_PACKET_INFO; -#define HTC_TX_PACKET_TAG_ALL 0 /* a tag of zero is reserved and used to flush ALL packets */ -#define HTC_TX_PACKET_TAG_INTERNAL 1 /* internal tags start here */ -#define HTC_TX_PACKET_TAG_USER_DEFINED (HTC_TX_PACKET_TAG_INTERNAL + 9) /* user-defined tags start here */ -#define HTC_TX_PACKET_TAG_BUNDLED HTC_TX_PACKET_TAG_USER_DEFINED + 1 /* indicate this is bundled TX packet */ +/** + * HTC_TX_PACKET_TAG_XXX - #defines for tagging packets for special handling + * HTC_TX_PACKET_TAG_ALL: zero is reserved and used to flush ALL packets + * HTC_TX_PACKET_TAG_INTERNAL: internal tags start here + * HTC_TX_PACKET_TAG_USER_DEFINED: user-defined tags start here + * HTC_TX_PACKET_TAG_BUNDLED: indicate this is a bundled tx packet + * HTC_TX_PACKET_TAG_AUTO_PM: indicate a power management wmi command + */ +#define HTC_TX_PACKET_TAG_ALL 0 +#define HTC_TX_PACKET_TAG_INTERNAL 1 +#define HTC_TX_PACKET_TAG_USER_DEFINED (HTC_TX_PACKET_TAG_INTERNAL + 9) +#define HTC_TX_PACKET_TAG_BUNDLED (HTC_TX_PACKET_TAG_USER_DEFINED + 1) +#define HTC_TX_PACKET_TAG_AUTO_PM (HTC_TX_PACKET_TAG_USER_DEFINED + 2) #define HTC_TX_PACKET_FLAG_FIXUP_NETBUF (1 << 0) diff --git a/core/htc/htc_send.c b/core/htc/htc_send.c index 99c626b192d3..3b3d3c7265f9 100644 --- a/core/htc/htc_send.c +++ b/core/htc/htc_send.c @@ -597,6 +597,60 @@ static A_STATUS htc_issue_packets(HTC_TARGET *target, return status; } +#ifdef FEATURE_RUNTIME_PM +/** + * extract_htc_pm_packtes(): move pm packets from endpoint into queue + * @endpoint: which enpoint to extract packets from + * @queue: a queue to store extracted packets in. + * + * remove pm packets from the endpoint's tx queue. + * queue them into a queue + */ +static void extract_htc_pm_packets(HTC_ENDPOINT *endpoint, + HTC_PACKET_QUEUE *queue) +{ + HTC_PACKET *packet; + + /* only WMI endpoint has power management packets */ + if (endpoint->service_id != WMI_CONTROL_SVC) + return; + + ITERATE_OVER_LIST_ALLOW_REMOVE(&endpoint->TxQueue.QueueHead, packet, + HTC_PACKET, ListLink) { + if (packet->PktInfo.AsTx.Tag == HTC_TX_PACKET_TAG_AUTO_PM) { + HTC_PACKET_REMOVE(&endpoint->TxQueue, packet); + HTC_PACKET_ENQUEUE(queue, packet); + } + } ITERATE_END +} + +/** + * queue_htc_pm_packets(): queue pm packets with priority + * @endpoint: enpoint to queue packets to + * @queue: queue of pm packets to enque + * + * suspend resume packets get special treatment & priority. + * need to queue them at the front of the queue. + */ +static void queue_htc_pm_packets(HTC_ENDPOINT *endpoint, + HTC_PACKET_QUEUE *queue) +{ + if (endpoint->service_id != WMI_CONTROL_SVC) + return; + + HTC_PACKET_QUEUE_TRANSFER_TO_HEAD(&endpoint->TxQueue, queue); +} +#else +static void extract_htc_pm_packets(HTC_ENDPOINT *endpoint, + HTC_PACKET_QUEUE *queue) +{} + +static void queue_htc_pm_packets(HTC_ENDPOINT *endpoint, + HTC_PACKET_QUEUE *queue) +{} +#endif + + /* get HTC send packets from the TX queue on an endpoint, based on available credits */ void get_htc_send_packets_credit_based(HTC_TARGET *target, HTC_ENDPOINT *pEndpoint, @@ -607,24 +661,43 @@ void get_htc_send_packets_credit_based(HTC_TARGET *target, A_UINT8 sendFlags; HTC_PACKET *pPacket; unsigned int transferLength; + HTC_PACKET_QUEUE *tx_queue; + HTC_PACKET_QUEUE pm_queue; + bool do_pm_get = false; /****** NOTE : the TX lock is held when this function is called *****************/ - AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("+get_htc_send_packets_credit_based \n")); + AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("+get_htc_send_packets_credit_based\n")); + + INIT_HTC_PACKET_QUEUE(&pm_queue); + extract_htc_pm_packets(pEndpoint, &pm_queue); + if (HTC_QUEUE_EMPTY(&pm_queue)) { + tx_queue = &pEndpoint->TxQueue; + do_pm_get = true; + } else { + tx_queue = &pm_queue; + } /* loop until we can grab as many packets out of the queue as we can */ while (true) { + if (do_pm_get && hif_pm_runtime_get(target->hif_dev)) { + /* bus suspended, runtime resume issued */ + CDF_ASSERT(HTC_PACKET_QUEUE_DEPTH(pQueue) == 0); + break; + } sendFlags = 0; /* get packet at head, but don't remove it */ - pPacket = htc_get_pkt_at_head(&pEndpoint->TxQueue); + pPacket = htc_get_pkt_at_head(tx_queue); if (pPacket == NULL) { + if (do_pm_get) + hif_pm_runtime_put(target->hif_dev); break; } AR_DEBUG_PRINTF(ATH_DEBUG_SEND, (" Got head packet:%p , Queue Depth: %d\n", pPacket, - HTC_PACKET_QUEUE_DEPTH(&pEndpoint->TxQueue))); + HTC_PACKET_QUEUE_DEPTH(tx_queue))); transferLength = pPacket->ActualLength + HTC_HDR_LENGTH; @@ -676,6 +749,8 @@ void get_htc_send_packets_credit_based(HTC_TARGET *target, pEndpoint->TxCredits, creditsRequired)); #endif + if (do_pm_get) + hif_pm_runtime_put(target->hif_dev); break; } @@ -694,8 +769,7 @@ void get_htc_send_packets_credit_based(HTC_TARGET *target, htc_credit_record(HTC_REQUEST_CREDIT, pEndpoint->TxCredits, HTC_PACKET_QUEUE_DEPTH - (&pEndpoint-> - TxQueue)); + (tx_queue)); UNLOCK_HTC_CREDIT(target); } @@ -710,7 +784,7 @@ void get_htc_send_packets_credit_based(HTC_TARGET *target, } /* now we can fully dequeue */ - pPacket = htc_packet_dequeue(&pEndpoint->TxQueue); + pPacket = htc_packet_dequeue(tx_queue); if (pPacket) { /* save the number of credits this packet consumed */ pPacket->PktInfo.AsTx.CreditsUsed = creditsRequired; @@ -722,7 +796,11 @@ void get_htc_send_packets_credit_based(HTC_TARGET *target, } } - AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("-get_htc_send_packets_credit_based \n")); + if (!HTC_QUEUE_EMPTY(&pm_queue)) + queue_htc_pm_packets(pEndpoint, &pm_queue); + + AR_DEBUG_PRINTF(ATH_DEBUG_SEND, + ("-get_htc_send_packets_credit_based\n")); } @@ -732,23 +810,43 @@ void get_htc_send_packets(HTC_TARGET *target, { HTC_PACKET *pPacket; + HTC_PACKET_QUEUE *tx_queue; + HTC_PACKET_QUEUE pm_queue; + bool do_pm_get; /****** NOTE : the TX lock is held when this function is called *****************/ AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("+get_htc_send_packets %d resources\n", Resources)); + INIT_HTC_PACKET_QUEUE(&pm_queue); + extract_htc_pm_packets(pEndpoint, &pm_queue); + if (HTC_QUEUE_EMPTY(&pm_queue)) { + tx_queue = &pEndpoint->TxQueue; + do_pm_get = true; + } else { + tx_queue = &pm_queue; + } + /* loop until we can grab as many packets out of the queue as we can */ while (Resources > 0) { int num_frags; - pPacket = htc_packet_dequeue(&pEndpoint->TxQueue); + if (do_pm_get && hif_pm_runtime_get(target->hif_dev)) { + /* bus suspended, runtime resume issued */ + CDF_ASSERT(HTC_PACKET_QUEUE_DEPTH(pQueue) == 0); + break; + } + + pPacket = htc_packet_dequeue(tx_queue); if (pPacket == NULL) { + if (do_pm_get) + hif_pm_runtime_put(target->hif_dev); break; } AR_DEBUG_PRINTF(ATH_DEBUG_SEND, (" Got packet:%p , New Queue Depth: %d\n", pPacket, - HTC_PACKET_QUEUE_DEPTH(&pEndpoint->TxQueue))); + HTC_PACKET_QUEUE_DEPTH(tx_queue))); /* For non-credit path the sequence number is already embedded * in the constructed HTC header */ @@ -779,7 +877,10 @@ void get_htc_send_packets(HTC_TARGET *target, Resources -= num_frags; } - AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("-get_htc_send_packets \n")); + if (!HTC_QUEUE_EMPTY(&pm_queue)) + queue_htc_pm_packets(pEndpoint, &pm_queue); + + AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("-get_htc_send_packets\n")); } @@ -991,9 +1092,14 @@ static HTC_SEND_QUEUE_RESULT htc_try_send(HTC_TARGET *target, /* send what we can */ result = htc_issue_packets(target, pEndpoint, &sendQueue); if (result) { + int i; AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("htc_issue_packets, failed status:%d put it back to head of callersSendQueue", result)); + + for (i = HTC_PACKET_QUEUE_DEPTH(&sendQueue); i > 0; i--) + hif_pm_runtime_put(target->hif_dev); + HTC_PACKET_QUEUE_TRANSFER_TO_HEAD(&pEndpoint->TxQueue, &sendQueue); LOCK_HTC_TX(target); @@ -1264,6 +1370,9 @@ A_STATUS htc_send_data_pkt(HTC_HANDLE HTCHandle, cdf_nbuf_t netbuf, int Epid, } } + if (hif_pm_runtime_get(target->hif_dev)) + return A_ERROR; + pHtcHdr = (HTC_FRAME_HDR *) cdf_nbuf_get_frag_vaddr(netbuf, 0); AR_DEBUG_ASSERT(pHtcHdr); @@ -1609,6 +1718,9 @@ CDF_STATUS htc_tx_completion_handler(void *Context, netbuf = NULL; break; } + if (pPacket->PktInfo.AsTx.Tag != HTC_TX_PACKET_TAG_AUTO_PM) + hif_pm_runtime_put(target->hif_dev); + if (pPacket->PktInfo.AsTx.Tag == HTC_TX_PACKET_TAG_BUNDLED) { HTC_PACKET *pPacketTemp; HTC_PACKET_QUEUE *pQueueSave = diff --git a/core/wma/src/wma_features.c b/core/wma/src/wma_features.c index c97fccc7dcd1..5bd515e647fe 100644 --- a/core/wma/src/wma_features.c +++ b/core/wma/src/wma_features.c @@ -3654,6 +3654,8 @@ CDF_STATUS wma_resume_req(tp_wma_handle wma, enum cdf_suspend_type type) /* unpause the vdev if left paused and hif_pci_suspend fails */ wma_unpause_vdev(wma); + wmi_set_runtime_pm_inprogress(wma->wmi_handle, false); + return CDF_STATUS_SUCCESS; } @@ -4172,6 +4174,9 @@ static void wma_notify_suspend_req_procesed(tp_wma_handle wma, */ CDF_STATUS wma_suspend_req(tp_wma_handle wma, enum cdf_suspend_type type) { + if (type == CDF_RUNTIME_SUSPEND) + wmi_set_runtime_pm_inprogress(wma->wmi_handle, true); + if (wma_is_wow_applicable(wma)) { WMA_LOGE("WOW Suspend"); wma_apply_lphb(wma); diff --git a/core/wmi/wmi_unified.c b/core/wmi/wmi_unified.c index 78bc9f345961..2270e602f3d4 100644 --- a/core/wmi/wmi_unified.c +++ b/core/wmi/wmi_unified.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015 The Linux Foundation. All rights reserved. + * Copyright (c) 2015-2016 The Linux Foundation. All rights reserved. * * Previously licensed under the ISC license by Qualcomm Atheros, Inc. * @@ -766,6 +766,30 @@ static inline void wma_log_cmd_id(WMI_CMD_ID cmd_id) } #endif +/** + * wmi_is_runtime_pm_cmd() - check if a cmd is part of the suspend resume sequence + * @cmd: command to check + * + * Return: true if the command is part of the suspend resume sequence. + */ +bool wmi_is_runtime_pm_cmd(WMI_CMD_ID cmd_id) +{ + switch (cmd_id) { + case WMI_WOW_ENABLE_CMDID: + case WMI_PDEV_SUSPEND_CMDID: + case WMI_WOW_ENABLE_DISABLE_WAKE_EVENT_CMDID: + case WMI_WOW_ADD_WAKE_PATTERN_CMDID: + case WMI_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID: + case WMI_PDEV_RESUME_CMDID: + case WMI_WOW_DEL_WAKE_PATTERN_CMDID: + case WMI_D0_WOW_ENABLE_DISABLE_CMDID: + return true; + + default: + return false; + } +} + /* WMI command API */ int wmi_unified_cmd_send(wmi_unified_t wmi_handle, wmi_buf_t buf, int len, WMI_CMD_ID cmd_id) @@ -773,8 +797,12 @@ int wmi_unified_cmd_send(wmi_unified_t wmi_handle, wmi_buf_t buf, int len, HTC_PACKET *pkt; A_STATUS status; struct ol_softc *scn; + uint16_t htc_tag = 0; - if (cdf_atomic_read(&wmi_handle->is_target_suspended) && + if (wmi_get_runtime_pm_inprogress(wmi_handle)) { + if (wmi_is_runtime_pm_cmd(cmd_id)) + htc_tag = HTC_TX_PACKET_TAG_AUTO_PM; + } else if (cdf_atomic_read(&wmi_handle->is_target_suspended) && ((WMI_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID != cmd_id) && (WMI_PDEV_RESUME_CMDID != cmd_id))) { pr_err("%s: Target is suspended could not send WMI command\n", @@ -830,7 +858,7 @@ int wmi_unified_cmd_send(wmi_unified_t wmi_handle, wmi_buf_t buf, int len, NULL, cdf_nbuf_data(buf), len + sizeof(WMI_CMD_HDR), /* htt_host_data_dl_len(buf)+20 */ - wmi_handle->wmi_endpoint_id, 0 /*htc_tag */ ); + wmi_handle->wmi_endpoint_id, htc_tag); SET_HTC_PACKET_NET_BUF_CONTEXT(pkt, buf); @@ -1160,6 +1188,21 @@ void wmi_rx_event_work(struct work_struct *work) /* WMI Initialization functions */ +#ifdef FEATURE_RUNTIME_PM +/** + * wmi_runtime_pm_init() - initialize runtime pm wmi variables + * @wmi_handle: wmi context + */ +void wmi_runtime_pm_init(struct wmi_unified *wmi_handle) +{ + cdf_atomic_init(&wmi_handle->runtime_pm_inprogress); +} +#else +void wmi_runtime_pm_init(struct wmi_unified *wmi_handle) +{ +} +#endif + void *wmi_unified_attach(ol_scn_t scn_handle, wma_process_fw_event_handler_cbk func) { @@ -1176,6 +1219,7 @@ void *wmi_unified_attach(ol_scn_t scn_handle, wmi_handle->scn_handle = scn_handle; cdf_atomic_init(&wmi_handle->pending_cmds); cdf_atomic_init(&wmi_handle->is_target_suspended); + wmi_runtime_pm_init(wmi_handle); cdf_spinlock_init(&wmi_handle->eventq_lock); cdf_nbuf_queue_init(&wmi_handle->event_queue); #ifdef CONFIG_CNSS @@ -1335,3 +1379,14 @@ void wmi_set_target_suspend(wmi_unified_t wmi_handle, A_BOOL val) cdf_atomic_set(&wmi_handle->is_target_suspended, val); } +#ifdef FEATURE_RUNTIME_PM +void wmi_set_runtime_pm_inprogress(wmi_unified_t wmi_handle, A_BOOL val) +{ + cdf_atomic_set(&wmi_handle->runtime_pm_inprogress, val); +} + +inline bool wmi_get_runtime_pm_inprogress(wmi_unified_t wmi_handle) +{ + return cdf_atomic_read(&wmi_handle->runtime_pm_inprogress); +} +#endif diff --git a/core/wmi/wmi_unified_api.h b/core/wmi/wmi_unified_api.h index bcac971aa79e..f96c128aa19d 100644 --- a/core/wmi/wmi_unified_api.h +++ b/core/wmi/wmi_unified_api.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2014 The Linux Foundation. All rights reserved. + * Copyright (c) 2013-2016 The Linux Foundation. All rights reserved. * * Previously licensed under the ISC license by Qualcomm Atheros, Inc. * @@ -150,7 +150,24 @@ int wmi_get_pending_cmds(wmi_unified_t wmi_handle); WMI API to set target suspend state */ -void wmi_set_target_suspend(wmi_unified_t wmi_handle, A_BOOL val); +void wmi_set_target_suspend(wmi_unified_t wmi_handle, bool val); + +#ifdef FEATURE_RUNTIME_PM +void +wmi_set_runtime_pm_inprogress(wmi_unified_t wmi_handle, bool val); +bool wmi_get_runtime_pm_inprogress(wmi_unified_t wmi_handle); +#else +static inline void +wmi_set_runtime_pm_inprogress(wmi_unified_t wmi_handle, bool val) +{ + return; +} +static inline bool wmi_get_runtime_pm_inprogress(wmi_unified_t wmi_handle) +{ + return false; +} +#endif + /** * WMA Callback to process fw event. diff --git a/core/wmi/wmi_unified_priv.h b/core/wmi/wmi_unified_priv.h index f0f87028530b..293480dba42c 100644 --- a/core/wmi/wmi_unified_priv.h +++ b/core/wmi/wmi_unified_priv.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2014 The Linux Foundation. All rights reserved. + * Copyright (c) 2013-2016 The Linux Foundation. All rights reserved. * * Previously licensed under the ISC license by Qualcomm Atheros, Inc. * @@ -89,6 +89,11 @@ struct wmi_unified { #endif /*WMI_INTERFACE_EVENT_LOGGING */ cdf_atomic_t is_target_suspended; + +#ifdef FEATURE_RUNTIME_PM + cdf_atomic_t runtime_pm_inprogress; +#endif + int (*wma_process_fw_event_handler_cbk)(struct wmi_unified *wmi_handle, wmi_buf_t evt_buf); };