/*
* drivers/misc/bluedroid_pm.c
*
- * Copyright (c) 2012, NVIDIA Corporation.
+ * Copyright (c) 2012-2013, NVIDIA Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/interrupt.h>
+#include <linux/wakelock.h>
+#include <linux/pm_qos.h>
#define PROC_DIR "bluetooth/sleep"
+
+/* 5 seconds of Min CPU configurations during resume */
+#define DEFAULT_RESUME_CPU_TIMEOUT 5000000
+
struct bluedroid_pm_data {
int gpio_reset;
int gpio_shutdown;
int host_wake;
int ext_wake;
int is_blocked;
+ int resume_min_frequency;
unsigned host_wake_irq;
struct regulator *vdd_3v3;
struct regulator *vdd_1v8;
struct rfkill *rfkill;
+ struct wake_lock wake_lock;
+ struct pm_qos_request resume_cpu_freq_req;
};
struct proc_dir_entry *proc_bt_dir, *bluetooth_sleep_dir;
regulator_disable(bluedroid_pm->vdd_3v3);
if (bluedroid_pm->vdd_1v8)
regulator_disable(bluedroid_pm->vdd_1v8);
+ if (bluedroid_pm->ext_wake)
+ wake_unlock(&bluedroid_pm->wake_lock);
+ pm_qos_remove_request(&bluedroid_pm->resume_cpu_freq_req);
} else {
if (bluedroid_pm->vdd_3v3)
regulator_enable(bluedroid_pm->vdd_3v3);
gpio_set_value(bluedroid_pm->gpio_shutdown, 1);
if (bluedroid_pm->gpio_reset)
gpio_set_value(bluedroid_pm->gpio_reset, 1);
+ pm_qos_add_request(&bluedroid_pm->resume_cpu_freq_req,
+ PM_QOS_CPU_FREQ_MIN, PM_QOS_DEFAULT_VALUE);
}
bluedroid_pm->is_blocked = blocked;
return 0;
pr_err("%s: Failed to create proc interface", __func__);
goto free_res;
}
+ /* initialize wake lock */
+ wake_lock_init(&bluedroid_pm->wake_lock, WAKE_LOCK_SUSPEND,
+ "bluedroid_pm");
} else {
pr_debug("%s: gpio_ext_wake not registered\n", __func__);
bluedroid_pm->ext_wake = 0;
}
+ res = platform_get_resource_byname(pdev, IORESOURCE_IO,
+ "min_cpu_freq");
+ if (res)
+ bluedroid_pm->resume_min_frequency = res->start;
+
platform_set_drvdata(pdev, bluedroid_pm);
pr_debug("RFKILL BT driver successfully registered");
return 0;
if (bluedroid_pm->host_wake_irq)
free_irq(bluedroid_pm->host_wake_irq, NULL);
if (bluedroid_pm->ext_wake) {
+ wake_lock_destroy(&bluedroid_pm->wake_lock);
gpio_free(bluedroid_pm->ext_wake);
remove_bt_proc_interface();
}
{
struct bluedroid_pm_data *bluedroid_pm = platform_get_drvdata(pdev);
if (bluedroid_pm->host_wake)
- if (!bluedroid_pm->is_blocked || !bluedroid_pm_blocked)
+ if (!bluedroid_pm->is_blocked || !bluedroid_pm_blocked) {
disable_irq_wake(bluedroid_pm->host_wake_irq);
+ if (bluedroid_pm->resume_min_frequency)
+ pm_qos_update_request_timeout(
+ &bluedroid_pm->resume_cpu_freq_req,
+ bluedroid_pm->resume_min_frequency,
+ DEFAULT_RESUME_CPU_TIMEOUT);
+ }
return 0;
}
}
if (!bluedroid_pm->is_blocked) {
- if (buf[0] == '0')
+ if (buf[0] == '0') {
gpio_set_value(bluedroid_pm->ext_wake, 0);
- else if (buf[0] == '1')
+ wake_unlock(&bluedroid_pm->wake_lock);
+ } else if (buf[0] == '1') {
gpio_set_value(bluedroid_pm->ext_wake, 1);
- else {
+ wake_lock(&bluedroid_pm->wake_lock);
+ } else {
kfree(buf);
return -EINVAL;
}