summaryrefslogtreecommitdiff
path: root/drivers/bluetooth/hci_intel.c
diff options
context:
space:
mode:
authorLoic Poulain <loic.poulain@intel.com>2015-08-29 13:38:18 +0200
committerMarcel Holtmann <marcel@holtmann.org>2015-09-17 13:20:00 +0200
commit765ea3abd116fc6dc5f67d73fcd7010a7a88f3cc (patch)
treeeb7636808e91ad0f4998d0ef84b7de06f4941aa0 /drivers/bluetooth/hci_intel.c
parent6f558b70fb39fc8272fc513ecf191b3ad87d867b (diff)
Bluetooth: hci_intel: Retrieve host-wake IRQ
An IRQ can be retrieved from the pdev resources. This irq will be used in case of LPM suspend mode to wake-up the host and resume the link. This resource can be declared as a GPIO-Interrupt which requires to be converted into IRQ. Signed-off-by: Loic Poulain <loic.poulain@intel.com> Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
Diffstat (limited to 'drivers/bluetooth/hci_intel.c')
-rw-r--r--drivers/bluetooth/hci_intel.c62
1 files changed, 61 insertions, 1 deletions
diff --git a/drivers/bluetooth/hci_intel.c b/drivers/bluetooth/hci_intel.c
index cf07d1121956..92bd17eb0acf 100644
--- a/drivers/bluetooth/hci_intel.c
+++ b/drivers/bluetooth/hci_intel.c
@@ -31,6 +31,7 @@
#include <linux/platform_device.h>
#include <linux/gpio/consumer.h>
#include <linux/acpi.h>
+#include <linux/interrupt.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
@@ -48,6 +49,7 @@ struct intel_device {
struct list_head list;
struct platform_device *pdev;
struct gpio_desc *reset;
+ int irq;
};
static LIST_HEAD(intel_device_list);
@@ -113,6 +115,15 @@ static int intel_wait_booting(struct hci_uart *hu)
return err;
}
+static irqreturn_t intel_irq(int irq, void *dev_id)
+{
+ struct intel_device *idev = dev_id;
+
+ dev_info(&idev->pdev->dev, "hci_intel irq\n");
+
+ return IRQ_HANDLED;
+}
+
static int intel_set_power(struct hci_uart *hu, bool powered)
{
struct list_head *p;
@@ -139,6 +150,27 @@ static int intel_set_power(struct hci_uart *hu, bool powered)
hu, dev_name(&idev->pdev->dev), powered);
gpiod_set_value(idev->reset, powered);
+
+ if (idev->irq < 0)
+ break;
+
+ if (powered && device_can_wakeup(&idev->pdev->dev)) {
+ err = devm_request_threaded_irq(&idev->pdev->dev,
+ idev->irq, NULL,
+ intel_irq,
+ IRQF_ONESHOT,
+ "bt-host-wake", idev);
+ if (err) {
+ BT_ERR("hu %p, unable to allocate irq-%d",
+ hu, idev->irq);
+ break;
+ }
+
+ device_wakeup_enable(&idev->pdev->dev);
+ } else if (!powered && device_may_wakeup(&idev->pdev->dev)) {
+ devm_free_irq(&idev->pdev->dev, idev->irq, idev);
+ device_wakeup_disable(&idev->pdev->dev);
+ }
}
spin_unlock(&intel_device_list_lock);
@@ -838,6 +870,31 @@ static int intel_probe(struct platform_device *pdev)
return PTR_ERR(idev->reset);
}
+ idev->irq = platform_get_irq(pdev, 0);
+ if (idev->irq < 0) {
+ struct gpio_desc *host_wake;
+
+ dev_err(&pdev->dev, "No IRQ, falling back to gpio-irq\n");
+
+ host_wake = devm_gpiod_get_optional(&pdev->dev, "host-wake",
+ GPIOD_IN);
+ if (IS_ERR(host_wake)) {
+ dev_err(&pdev->dev, "Unable to retrieve IRQ\n");
+ goto no_irq;
+ }
+
+ idev->irq = gpiod_to_irq(host_wake);
+ if (idev->irq < 0) {
+ dev_err(&pdev->dev, "No corresponding irq for gpio\n");
+ goto no_irq;
+ }
+ }
+
+ /* Only enable wake-up/irq when controller is powered */
+ device_set_wakeup_capable(&pdev->dev, true);
+ device_wakeup_disable(&pdev->dev);
+
+no_irq:
platform_set_drvdata(pdev, idev);
/* Place this instance on the device list */
@@ -845,7 +902,8 @@ static int intel_probe(struct platform_device *pdev)
list_add_tail(&idev->list, &intel_device_list);
spin_unlock(&intel_device_list_lock);
- dev_info(&pdev->dev, "registered.\n");
+ dev_info(&pdev->dev, "registered, gpio(%d)/irq(%d).\n",
+ desc_to_gpio(idev->reset), idev->irq);
return 0;
}
@@ -854,6 +912,8 @@ static int intel_remove(struct platform_device *pdev)
{
struct intel_device *idev = platform_get_drvdata(pdev);
+ device_wakeup_disable(&pdev->dev);
+
spin_lock(&intel_device_list_lock);
list_del(&idev->list);
spin_unlock(&intel_device_list_lock);