From 68b533249c847dc84bb9bdd8c4873aef0dcd88a5 Mon Sep 17 00:00:00 2001 From: Fabien Peix Date: Fri, 24 Feb 2012 15:02:08 +0100 Subject: [PATCH] TI shared transport: integration of wakelock/pm_runtime modifications BZ: 21477 This patch introduces power management features specific to android platform to TI shared transport. The added code is similar to what was added on previous version of shared transport. Change-Id: I6b8d0b041e56c3786ebfd187c26db10cc512c0f9 Signed-off-by: Fabien Peix Reviewed-on: http://android.intel.com:8080/36585 Reviewed-by: Suet, Nicolas Reviewed-by: Gross, Mark Tested-by: Zurmely, PierreX Reviewed-by: buildbot Tested-by: buildbot --- drivers/misc/ti-st/st_core.c | 59 +++++++++++++++++++++++++++++++++++++++++++- drivers/misc/ti-st/st_ll.c | 16 +++++++++++- include/linux/ti_wilink_st.h | 6 +++++ 3 files changed, 79 insertions(+), 2 deletions(-) diff --git a/drivers/misc/ti-st/st_core.c b/drivers/misc/ti-st/st_core.c index aeb81ca..119179c 100644 --- a/drivers/misc/ti-st/st_core.c +++ b/drivers/misc/ti-st/st_core.c @@ -29,6 +29,20 @@ #include #include +#include + +/* + * The main goal of this Inactivity Timeout wake lock is to avoid putting + * the system into Sleep while no application are running and some incoming + * data has to be processed. For instance, during a BT SDP service discovery + * session initiated by a remote with no service level connection opened yet, + * it will ensure enough CPU time remains to handle incoming request(s). + * Assuming the amount of "no-app-running" traffic is concentrated (mainly + * right before service connection level establishment), Power + * consumption impact is negligible. + */ +#define ST_PM_PROTECT_INACTIVITY_TIMEOUT (1*HZ) +#define ST_PM_PROTECT_WAKE_LOCK_NAME "st_core" /* function pointer pointing to either, * st_kim_recv during registration to receive fw download responses @@ -112,6 +126,15 @@ void st_send_frame(unsigned char chnl_id, struct st_data_s *st_gdata) kfree_skb(st_gdata->rx_skb); return; } + + /* + * Refresh inactivity timeout for Power Management protection + * mechanism. It will prevent from S3 sleeping for a while + * in order to handle the incoming data. + */ + wake_lock_timeout(&st_gdata->wake_lock, + ST_PM_PROTECT_INACTIVITY_TIMEOUT); + /* this cannot fail * this shouldn't take long * - should be just skb_queue_tail for the @@ -317,8 +340,16 @@ void st_int_recv(void *disc_data, * and assume chip awake */ spin_unlock_irqrestore(&st_gdata->lock, flags); - if (st_ll_getstate(st_gdata) == ST_LL_AWAKE) + if (st_ll_getstate(st_gdata) == ST_LL_AWAKE) { + /* pm_runtime_get has already been done + * in st_ll_sleep_state. st_wakeup_ack will + * call st_ll_sleep_state again and another + * pm_runtime_get will be done. We need to + * compensate the first one here. + */ + pm_runtime_put(st_gdata->tty_dev); st_wakeup_ack(st_gdata, LL_WAKE_UP_ACK); + } spin_lock_irqsave(&st_gdata->lock, flags); ptr++; @@ -682,6 +713,14 @@ long st_write(struct sk_buff *skb) pr_debug("%d to be written", skb->len); len = skb->len; + /* + * Refresh inactivity timeout for Power Management protection mechanism + * It will prevent from S3 sleeping for a while as it is very likely + * some incoming data will be received soon. + */ + wake_lock_timeout(&st_gdata->wake_lock, + ST_PM_PROTECT_INACTIVITY_TIMEOUT); + /* st_ll to decide where to enqueue the skb */ st_int_enqueue(st_gdata, skb); /* wake up */ @@ -708,6 +747,16 @@ static int st_tty_open(struct tty_struct *tty) st_gdata->tty = tty; tty->disc_data = st_gdata; + if (tty->dev->parent) + st_gdata->tty_dev = tty->dev->parent; + else + return -EINVAL; + + /* Asynchronous Get is enough here since we just want to avoid + * interface to be released too early + */ + pm_runtime_get(st_gdata->tty_dev); + /* don't do an wakeup for now */ clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); @@ -768,6 +817,8 @@ static void st_tty_close(struct tty_struct *tty) st_gdata->rx_skb = NULL; spin_unlock_irqrestore(&st_gdata->lock, flags); + pm_runtime_put(st_gdata->tty_dev); + pr_debug("%s: done ", __func__); } @@ -857,9 +908,14 @@ int st_core_init(struct st_data_s **core_data) /* Locking used in st_int_enqueue() to avoid multiple execution */ spin_lock_init(&st_gdata->lock); + /* Power Management protection mechanism w.r.t. RX queue activity */ + wake_lock_init(&st_gdata->wake_lock, WAKE_LOCK_SUSPEND, + ST_PM_PROTECT_WAKE_LOCK_NAME); + err = st_ll_init(st_gdata); if (err) { pr_err("error during st_ll initialization(%ld)", err); + wake_lock_destroy(&st_gdata->wake_lock); kfree(st_gdata); err = tty_unregister_ldisc(N_TI_WL); if (err) @@ -880,6 +936,7 @@ void st_core_exit(struct st_data_s *st_gdata) if (st_gdata != NULL) { /* Free ST Tx Qs and skbs */ + wake_lock_destroy(&st_gdata->wake_lock); skb_queue_purge(&st_gdata->txq); skb_queue_purge(&st_gdata->tx_waitq); kfree_skb(st_gdata->rx_skb); diff --git a/drivers/misc/ti-st/st_ll.c b/drivers/misc/ti-st/st_ll.c index 8c966fc..e6542a5 100644 --- a/drivers/misc/ti-st/st_ll.c +++ b/drivers/misc/ti-st/st_ll.c @@ -25,6 +25,8 @@ #include #include +#include + /**********************************************************************/ /* internal functions */ static void send_ll_cmd(struct st_data_s *st_data, @@ -43,10 +45,15 @@ static void ll_device_want_to_sleep(struct st_data_s *st_data) pr_debug("%s", __func__); /* sanity check */ - if (st_data->ll_state != ST_LL_AWAKE) + if (st_data->ll_state != ST_LL_AWAKE) { pr_err("ERR hcill: ST_LL_GO_TO_SLEEP_IND" "in state %ld", st_data->ll_state); + /* Since Driver is asked to go to sleep but not aware to be + * awake Runtime PM is not aware of the state change either + * Requesting the device has not been done, so we do it*/ + pm_runtime_get(st_data->tty_dev); + } send_ll_cmd(st_data, LL_SLEEP_ACK); /* update state */ st_data->ll_state = ST_LL_ASLEEP; @@ -155,16 +162,23 @@ unsigned long st_ll_sleep_state(struct st_data_s *st_data, case LL_SLEEP_IND: /* sleep ind */ pr_debug("sleep indication recvd"); ll_device_want_to_sleep(st_data); + pm_runtime_put(st_data->tty_dev); break; case LL_SLEEP_ACK: /* sleep ack */ pr_err("sleep ack rcvd: host shouldn't"); break; case LL_WAKE_UP_IND: /* wake ind */ pr_debug("wake indication recvd"); + /* Getting the Device is done to avoid power gating the + * Interface (for example UART). This can be done + * asynchronously since low level driver is getting the device + * when doing a transfert */ + pm_runtime_get(st_data->tty_dev); ll_device_want_to_wakeup(st_data); break; case LL_WAKE_UP_ACK: /* wake ack */ pr_debug("wake ack rcvd"); + pm_runtime_get(st_data->tty_dev); st_data->ll_state = ST_LL_AWAKE; break; default: diff --git a/include/linux/ti_wilink_st.h b/include/linux/ti_wilink_st.h index c677845..77111d5 100644 --- a/include/linux/ti_wilink_st.h +++ b/include/linux/ti_wilink_st.h @@ -25,6 +25,8 @@ #ifndef TI_WILINK_ST_H #define TI_WILINK_ST_H +#include + /** * enum proto-type - The protocol on WiLink chips which share a * common physical interface like UART. @@ -135,6 +137,8 @@ extern long st_unregister(struct st_proto_s *); * from waitq can be moved onto the txq. * Needs locking too. * @lock: the lock to protect skbs, queues, and ST states. + * @wake_lock: wake lock to implement an inactivity timeout to prevent + * going into S3 when incoming data is to be handled. * @protos_registered: count of the protocols registered, also when 0 the * chip enable gpio can be toggled, and when it changes to 1 the fw * needs to be downloaded to initialize chip side ST. @@ -157,10 +161,12 @@ struct st_data_s { unsigned char rx_chnl; struct sk_buff_head txq, tx_waitq; spinlock_t lock; + struct wake_lock wake_lock; unsigned char protos_registered; unsigned long ll_state; void *kim_data; struct tty_struct *tty; + struct device *tty_dev; }; /* -- 2.7.4