summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/staging/cg2900/bluetooth/cg2900_uart.c37
1 files changed, 37 insertions, 0 deletions
diff --git a/drivers/staging/cg2900/bluetooth/cg2900_uart.c b/drivers/staging/cg2900/bluetooth/cg2900_uart.c
index 5b6cd0feeae..3987c34f4e7 100644
--- a/drivers/staging/cg2900/bluetooth/cg2900_uart.c
+++ b/drivers/staging/cg2900/bluetooth/cg2900_uart.c
@@ -24,6 +24,7 @@
#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
+#include <linux/pm_qos_params.h>
#include <linux/poll.h>
#include <linux/sched.h>
#include <linux/skbuff.h>
@@ -59,6 +60,9 @@
#define UART_RESP_TIMEOUT 1000
#define UART_RESUME_TIMEOUT 20
+/* Max latency in microseconds for PM QoS to achieve max throughput */
+#define CG2900_PM_QOS_LATENCY 30
+
/* Number of bytes to reserve at start of sk_buffer when receiving packet */
#define RX_SKB_RESERVE 8
/* Max size of received packet (not including reserved bytes) */
@@ -286,6 +290,8 @@ struct uart_delayed_work_struct {
* @chip_dev: Chip device for current UART transport.
* @cts_irq: CTS interrupt for this UART.
* @cts_gpio: CTS GPIO for this UART.
+ * @suspend_blocked: True if suspend operation is blocked in the framework.
+ * @pm_qos_latency: PM QoS structure.
*/
struct uart_info {
enum uart_rx_state rx_state;
@@ -313,6 +319,8 @@ struct uart_info {
struct cg2900_chip_dev chip_dev;
int cts_irq;
int cts_gpio;
+ bool suspend_blocked;
+ struct pm_qos_request_list pm_qos_latency;
};
/* Module parameters */
@@ -396,6 +404,9 @@ static void handle_cts_irq(struct work_struct *work)
container_of(work, struct uart_work_struct, work);
struct uart_info *uart_info = (struct uart_info *)current_work->data;
+ pm_qos_update_request(&uart_info->pm_qos_latency,
+ CG2900_PM_QOS_LATENCY);
+
spin_lock_bh(&(uart_info->transmission_lock));
/* Mark that there is an ongoing transfer. */
uart_info->rx_in_progress = true;
@@ -593,6 +604,12 @@ static void wake_up_chip(struct uart_info *uart_info)
if (CHIP_POWERED_DOWN == uart_info->sleep_state)
goto finished;
+ if (!uart_info->suspend_blocked) {
+ uart_info->suspend_blocked = true;
+ pm_qos_update_request(&uart_info->pm_qos_latency,
+ CG2900_PM_QOS_LATENCY);
+ }
+
/*
* This function indicates data is transmitted.
* Therefore see to that the chip is awake.
@@ -709,6 +726,11 @@ static void set_chip_sleep_mode(struct work_struct *work)
dev_dbg(MAIN_DEV, "New sleep_state: CHIP_ASLEEP\n");
uart_info->sleep_state = CHIP_ASLEEP;
+ if (uart_info->suspend_blocked) {
+ uart_info->suspend_blocked = false;
+ pm_qos_update_request(&uart_info->pm_qos_latency,
+ PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE);
+ }
break;
case CHIP_AWAKE:
chars_in_buffer = hci_uart_chars_in_buffer(uart_info->hu);
@@ -1353,6 +1375,11 @@ static void uart_set_chip_power(struct cg2900_chip_dev *dev, bool chip_on)
}
if (chip_on) {
+ if (!uart_info->suspend_blocked) {
+ uart_info->suspend_blocked = true;
+ pm_qos_update_request(&uart_info->pm_qos_latency,
+ CG2900_PM_QOS_LATENCY);
+ }
if (uart_info->sleep_state != CHIP_POWERED_DOWN) {
dev_err(MAIN_DEV, "Chip is already powered up (%d)\n",
uart_info->sleep_state);
@@ -1389,6 +1416,12 @@ static void uart_set_chip_power(struct cg2900_chip_dev *dev, bool chip_on)
break;
}
+ if (uart_info->suspend_blocked) {
+ uart_info->suspend_blocked = false;
+ pm_qos_update_request(&uart_info->pm_qos_latency,
+ PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE);
+ }
+
if (pf_data->disable_chip) {
pf_data->disable_chip(dev);
dev_dbg(MAIN_DEV,
@@ -1951,6 +1984,9 @@ static int __devinit cg2900_uart_probe(struct platform_device *pdev)
uart_info->chip_dev.dev = &pdev->dev;
uart_info->chip_dev.t_data = uart_info;
+ pm_qos_add_request(&uart_info->pm_qos_latency, PM_QOS_CPU_DMA_LATENCY,
+ PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE);
+
resource = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
"cts_irq");
if (!resource) {
@@ -2046,6 +2082,7 @@ static int __devexit cg2900_uart_remove(struct platform_device *pdev)
if (uart_info->hu)
hci_uart_unregister_proto(uart_info->hu->proto);
+ pm_qos_remove_request(&uart_info->pm_qos_latency);
destroy_workqueue(uart_info->wq);
dev_info(MAIN_DEV, "CG2900 UART removed\n");