diff --git a/core/dp/htt/htt.c b/core/dp/htt/htt.c index 4bc45c879a65..686882b6aead 100644 --- a/core/dp/htt/htt.c +++ b/core/dp/htt/htt.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2014-2015 The Linux Foundation. All rights reserved. + * Copyright (c) 2011, 2014-2016 The Linux Foundation. All rights reserved. * * Previously licensed under the ISC license by Qualcomm Atheros, Inc. * @@ -43,6 +43,7 @@ #include #include +#include #include "hif.h" #define HTT_HTC_PKT_POOL_INIT_SIZE 100 /* enough for a large A-MPDU */ @@ -405,6 +406,7 @@ int htt_htc_attach(struct htt_pdev_t *pdev) connect.EpCallbacks.EpTxComplete = htt_h2t_send_complete; connect.EpCallbacks.EpTxCompleteMultiple = NULL; connect.EpCallbacks.EpRecv = htt_t2h_msg_handler; + connect.EpCallbacks.ep_resume_tx_queue = htt_tx_resume_handler; /* rx buffers currently are provided by HIF, not by EpRecvRefill */ connect.EpCallbacks.EpRecvRefill = NULL; diff --git a/core/dp/htt/htt_internal.h b/core/dp/htt/htt_internal.h index 5ab0b1a680b0..28b1e08b3da6 100644 --- a/core/dp/htt/htt_internal.h +++ b/core/dp/htt/htt_internal.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2014-2015 The Linux Foundation. All rights reserved. + * Copyright (c) 2011, 2014-2016 The Linux Foundation. All rights reserved. * * Previously licensed under the ISC license by Qualcomm Atheros, Inc. * @@ -413,6 +413,7 @@ static inline void htt_print_rx_desc(struct htt_host_rx_desc_base *rx_desc) #endif +void htt_tx_resume_handler(void *); #ifdef ATH_11AC_TXCOMPACT #define HTT_TX_SCHED htt_tx_sched #else diff --git a/core/dp/htt/htt_tx.c b/core/dp/htt/htt_tx.c index f733fc64bc9f..1aa28f9ce202 100644 --- a/core/dp/htt/htt_tx.c +++ b/core/dp/htt/htt_tx.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2014-2015 The Linux Foundation. All rights reserved. + * Copyright (c) 2011, 2014-2016 The Linux Foundation. All rights reserved. * * Previously licensed under the ISC license by Qualcomm Atheros, Inc. * @@ -625,6 +625,24 @@ int htt_tx_send_std(htt_pdev_handle pdev, cdf_nbuf_t msdu, uint16_t msdu_id) } +#ifdef FEATURE_RUNTIME_PM +/** + * htt_tx_resume_handler() - resume callback for the htt endpoint + * @context: a pointer to the htt context + * + * runs htt_tx_sched. + */ +void htt_tx_resume_handler(void *context) +{ + struct htt_pdev_t *pdev = (struct htt_pdev_t *) context; + + htt_tx_sched(pdev); +} +#else +void +htt_tx_resume_handler(void *context) { } +#endif + cdf_nbuf_t htt_tx_send_batch(htt_pdev_handle pdev, cdf_nbuf_t head_msdu, int num_msdus) { diff --git a/core/htc/htc.c b/core/htc/htc.c index 8e45efcd1721..d065193da3b7 100644 --- a/core/htc/htc.c +++ b/core/htc/htc.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2015 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. * @@ -34,6 +34,7 @@ #include "epping_main.h" #include "hif_io32.h" #include "cds_concurrency.h" +#include #ifdef DEBUG static ATH_DEBUG_MASK_DESCRIPTION g_htc_debug_description[] = { @@ -214,6 +215,7 @@ HTC_HANDLE htc_create(void *ol_sc, HTC_INIT_INFO *pInfo, cdf_device_t osdev) A_MEMZERO(target, sizeof(HTC_TARGET)); + htc_runtime_pm_init(target); cdf_spinlock_init(&target->HTCLock); cdf_spinlock_init(&target->HTCRxLock); cdf_spinlock_init(&target->HTCTxLock); @@ -681,7 +683,60 @@ void htc_stop(HTC_HANDLE HTCHandle) reset_endpoint_states(target); - AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("-htc_stop \n")); + AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("-htc_stop\n")); +} + +/** + * htc_runtime_pm_init(): runtime pm related intialization + * + * need to initialize a work item. + */ +void htc_runtime_pm_init(HTC_TARGET *target) +{ + cdf_create_work(&target->queue_kicker, htc_kick_queues, target); +} + +/** + * htc_runtime_suspend(): ensure htc is ready to suspend + * + * htc is ready to suspend if there are no pending pactets + * in the txrx queues. + * + * Return: 0 on success or -EBUSY if there are queued packets. + */ +int htc_runtime_suspend(void) +{ + ol_txrx_pdev_handle txrx_pdev = cds_get_context(CDF_MODULE_ID_TXRX); + + if (txrx_pdev == NULL) { + HTC_ERROR("%s: txrx context null", __func__); + return CDF_STATUS_E_FAULT; + } + + if (ol_txrx_get_tx_pending(txrx_pdev)) + return -EBUSY; + else + return 0; +} + +/** + * htc_runtime_resume(): resume htc + * + * The htc message queue needs to be kicked off after + * a runtime resume. Otherwise messages would get stuck. + * + * Return: 0 for success; + */ +int htc_runtime_resume(void) +{ + HTC_HANDLE htc_ctx = cds_get_context(CDF_MODULE_ID_HTC); + HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(htc_ctx); + + if (target == NULL) + return 0; + + cdf_schedule_work(&target->queue_kicker); + return 0; } void htc_dump_credit_states(HTC_HANDLE HTCHandle) diff --git a/core/htc/htc_api.h b/core/htc/htc_api.h index 7ae13b453955..94b38296a5c3 100644 --- a/core/htc/htc_api.h +++ b/core/htc/htc_api.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. * @@ -66,6 +66,9 @@ struct ol_ath_htc_stats { int htc_send_q_empty_count; }; +/* To resume HTT Tx queue during runtime resume */ +typedef void (*HTC_EP_RESUME_TX_QUEUE)(void *); + /* 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 @@ -151,6 +154,7 @@ typedef struct _HTC_EP_CALLBACKS { indications (EpTxComplete must be NULL) */ HTC_EP_RECV_PKT_MULTIPLE EpRecvPktMultiple; /* OPTIONAL completion handler for multiple recv packet indications (EpRecv must be NULL) */ + HTC_EP_RESUME_TX_QUEUE ep_resume_tx_queue; int RecvAllocThreshold; /* 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 */ @@ -694,6 +698,8 @@ void htc_dump_counter_info(HTC_HANDLE HTCHandle); void *htc_get_targetdef(HTC_HANDLE htc_handle); void htc_set_target_to_sleep(void *context); void htc_cancel_deferred_target_sleep(void *context); +int htc_runtime_suspend(void); +int htc_runtime_resume(void); /* Disable ASPM : Disable PCIe low power */ void htc_disable_aspm(void); diff --git a/core/htc/htc_internal.h b/core/htc/htc_internal.h index 8785993893b6..5b98d1368aae 100644 --- a/core/htc/htc_internal.h +++ b/core/htc/htc_internal.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2015 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. * @@ -184,6 +184,7 @@ typedef struct _HTC_TARGET { A_UINT32 ce_send_cnt; A_UINT32 TX_comp_cnt; A_UINT8 MaxMsgsPerHTCBundle; + cdf_work_t queue_kicker; } HTC_TARGET; #define HTC_ENABLE_BUNDLE(target) (target->MaxMsgsPerHTCBundle > 1) @@ -245,6 +246,8 @@ void htc_process_credit_rpt(HTC_TARGET *target, int NumEntries, HTC_ENDPOINT_ID FromEndpoint); void htc_fw_event_handler(void *context, CDF_STATUS status); void htc_send_complete_check_cleanup(void *context); +void htc_runtime_pm_init(HTC_TARGET *target); +void htc_kick_queues(void *context); void htc_credit_record(htc_credit_exchange_type type, uint32_t tx_credit, uint32_t htc_tx_queue_depth); diff --git a/core/htc/htc_send.c b/core/htc/htc_send.c index bb5c34368a00..99c626b192d3 100644 --- a/core/htc/htc_send.c +++ b/core/htc/htc_send.c @@ -1669,6 +1669,35 @@ void htc_tx_resource_avail_handler(void *context, A_UINT8 pipeID) htc_try_send(target, pEndpoint, NULL); } +/** + * htc_kick_queues(): resumes tx transactions of suspended endpoints + * @context: pointer to the htc target context + * + * Iterates throught the enpoints and provides a context to empty queues + * int the hif layer when they are stalled due to runtime suspend. + * + * Return: none + */ +void htc_kick_queues(void *context) +{ + int i; + HTC_TARGET *target = (HTC_TARGET *)context; + HTC_ENDPOINT *endpoint = NULL; + + for (i = 0; i < ENDPOINT_MAX; i++) { + endpoint = &target->endpoint[i]; + + if (endpoint->service_id == 0) + continue; + + if (endpoint->EpCallBacks.ep_resume_tx_queue) + endpoint->EpCallBacks.ep_resume_tx_queue( + endpoint->EpCallBacks.pContext); + + htc_try_send(target, endpoint, NULL); + } +} + /* flush endpoint TX queue */ void htc_flush_endpoint_tx(HTC_TARGET *target, HTC_ENDPOINT *pEndpoint, HTC_TX_TAG Tag)