diff options
| -rw-r--r-- | drivers/misc/ti-st/st_core.c | 52 | ||||
| -rw-r--r-- | drivers/misc/ti-st/st_ll.c | 7 | ||||
| -rw-r--r-- | include/linux/ti_wilink_st.h | 9 |
3 files changed, 68 insertions, 0 deletions
diff --git a/drivers/misc/ti-st/st_core.c b/drivers/misc/ti-st/st_core.c index 7a4647bc7ea..ef6e56907c2 100644 --- a/drivers/misc/ti-st/st_core.c +++ b/drivers/misc/ti-st/st_core.c @@ -30,11 +30,20 @@ #include <linux/wakelock.h> #include <linux/ti_wilink_st.h> +#include <linux/pm_qos.h> #define ST_WAKE_LOCK_TIMEOUT_MS 150 static struct wake_lock st_wk_lock_timeout; +struct st_pm_qos_t { + struct pm_qos_request st_pm_qos_request; + u32 st_pm_qos_latency; + spinlock_t st_pm_qos_lock; /* protect internal members */ +}; + +static struct st_pm_qos_t st_pm_qos; + extern void st_kim_recv(void *, const unsigned char *, long); void st_int_recv(void *, const unsigned char *, long); /* function pointer pointing to either, @@ -62,6 +71,44 @@ static void remove_channel_from_table(struct st_data_s *st_gdata, } /* + * called when Bluetooth IC changes sleep state. + * + * This function dynamically changes pm_qos to allow + * CPU to go to deep idle state when there is no + * communication between host and Bluetooth IC. + */ +void st_pm_qos_update(struct st_data_s *st_gdata, bool awake) +{ + struct kim_data_s *kim_plat_data; + u32 latency; + unsigned long flags; + + if (unlikely(st_gdata == NULL || st_gdata->kim_data == NULL)) { + pr_err("%s: Invalid argument\n", __func__); + return; + } + + spin_lock_irqsave(&st_pm_qos.st_pm_qos_lock, flags); + + kim_plat_data = (struct kim_data_s *)st_gdata->kim_data; + if (awake) { + /* UART FIFO is 64 bytes */ + latency = (USEC_PER_SEC * 64) / (kim_plat_data->baud_rate / 8); + } else { + /* drop PM QoS constraint */ + latency = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE; + } + + if (st_pm_qos.st_pm_qos_latency != latency) { + st_pm_qos.st_pm_qos_latency = latency; + pm_qos_update_request(&st_pm_qos.st_pm_qos_request, + st_pm_qos.st_pm_qos_latency); + } + + spin_unlock_irqrestore(&st_pm_qos.st_pm_qos_lock, flags); +} + +/* * called from KIM during firmware download. * * This is a wrapper function to tty->ops->write_room. @@ -852,6 +899,10 @@ int st_core_init(struct st_data_s **core_data) long err; wake_lock_init(&st_wk_lock_timeout, WAKE_LOCK_SUSPEND, "st_wake_lock"); + spin_lock_init(&st_pm_qos.st_pm_qos_lock); + pm_qos_add_request(&st_pm_qos.st_pm_qos_request, + PM_QOS_CPU_DMA_LATENCY, + PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE); err = tty_register_ldisc(N_TI_WL, &st_ldisc_ops); if (err) { pr_err("error registering %d line discipline %ld\n", @@ -913,6 +964,7 @@ void st_core_exit(struct st_data_s *st_gdata) /* free the global data pointer */ kfree(st_gdata); } + pm_qos_remove_request(&st_pm_qos.st_pm_qos_request); wake_lock_destroy(&st_wk_lock_timeout); } diff --git a/drivers/misc/ti-st/st_ll.c b/drivers/misc/ti-st/st_ll.c index e0c110e7107..9b9ef562073 100644 --- a/drivers/misc/ti-st/st_ll.c +++ b/drivers/misc/ti-st/st_ll.c @@ -32,7 +32,14 @@ static void send_ll_cmd(struct st_data_s *st_data, { pr_debug("%s: writing %x\n", __func__, cmd); + + if (cmd == LL_WAKE_UP_IND || cmd == LL_WAKE_UP_ACK) + st_pm_qos_update(st_data, 1); + st_int_write(st_data, &cmd, 1); + + if (cmd == LL_SLEEP_ACK) + st_pm_qos_update(st_data, 0); return; } diff --git a/include/linux/ti_wilink_st.h b/include/linux/ti_wilink_st.h index 932b7639224..bf2a954c848 100644 --- a/include/linux/ti_wilink_st.h +++ b/include/linux/ti_wilink_st.h @@ -161,6 +161,15 @@ struct st_data_s { }; /* + * called when Bluetooth IC changes sleep state. + * + * This function dynamically changes pm_qos to allow + * CPU to go to deep idle state when there is no + * communication between host and Bluetooth IC. + */ +void st_pm_qos_update(struct st_data_s *st_gdata, bool awake); + +/* * wrapper around tty->ops->write_room to check * availability during firmware download */ |