linux-bluetooth.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 1/2] Bluetooth: hci_intel: Retrieve host-wake IRQ
@ 2015-08-28 16:20 Loic Poulain
  2015-08-28 16:20 ` [PATCH v2 2/2] Bluetooth: hci_intel: Introduce LPM support Loic Poulain
  2015-08-28 16:22 ` [PATCH v2 1/2] Bluetooth: hci_intel: Retrieve host-wake IRQ Loic Poulain
  0 siblings, 2 replies; 4+ messages in thread
From: Loic Poulain @ 2015-08-28 16:20 UTC (permalink / raw)
  To: marcel; +Cc: linux-bluetooth, Loic Poulain

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>
---
 drivers/bluetooth/hci_intel.c | 62 ++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 61 insertions(+), 1 deletion(-)

diff --git a/drivers/bluetooth/hci_intel.c b/drivers/bluetooth/hci_intel.c
index 46426ff..1ec2205 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);
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH v2 2/2] Bluetooth: hci_intel: Introduce LPM support
  2015-08-28 16:20 [PATCH v2 1/2] Bluetooth: hci_intel: Retrieve host-wake IRQ Loic Poulain
@ 2015-08-28 16:20 ` Loic Poulain
  2015-08-28 16:25   ` Loic Poulain
  2015-08-28 16:22 ` [PATCH v2 1/2] Bluetooth: hci_intel: Retrieve host-wake IRQ Loic Poulain
  1 sibling, 1 reply; 4+ messages in thread
From: Loic Poulain @ 2015-08-28 16:20 UTC (permalink / raw)
  To: marcel; +Cc: linux-bluetooth, Loic Poulain

Enable controller Low-Power-Mode if we have a pdev to manage host
wake-up. Once LPM is enabled, controller notifies its TX status via
a vendor specific packet (tx_idle/tx_active).
tx_active means that there is more data upcoming from controller.
tx_idle means that controller can be put in suspended state.

Signed-off-by: Loic Poulain <loic.poulain@intel.com>
---
 drivers/bluetooth/hci_intel.c | 95 +++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 92 insertions(+), 3 deletions(-)

diff --git a/drivers/bluetooth/hci_intel.c b/drivers/bluetooth/hci_intel.c
index 1ec2205..30dce61 100644
--- a/drivers/bluetooth/hci_intel.c
+++ b/drivers/bluetooth/hci_intel.c
@@ -44,6 +44,20 @@
 #define STATE_FIRMWARE_LOADED	2
 #define STATE_FIRMWARE_FAILED	3
 #define STATE_BOOTING		4
+#define STATE_LPM_ENABLED	5
+#define STATE_TX_ACTIVE		6
+
+#define HCI_LPM_PKT 0xf1
+#define HCI_LPM_MAX_SIZE 10
+#define HCI_LPM_HDR_SIZE HCI_EVENT_HDR_SIZE
+
+#define LPM_OP_TX_NOTIFY 0x00
+
+struct hci_lpm_pkt {
+	__u8 opcode;
+	__u8 dlen;
+	__u8 data[0];
+} __packed;
 
 struct intel_device {
 	struct list_head list;
@@ -316,11 +330,14 @@ static int intel_setup(struct hci_uart *hu)
 {
 	static const u8 reset_param[] = { 0x00, 0x01, 0x00, 0x01,
 					  0x00, 0x08, 0x04, 0x00 };
+	static const u8 lpm_param[] = { 0x03, 0x07, 0x01, 0x0b };
 	struct intel_data *intel = hu->priv;
+	struct intel_device *idev = NULL;
 	struct hci_dev *hdev = hu->hdev;
 	struct sk_buff *skb;
 	struct intel_version *ver;
 	struct intel_boot_params *params;
+	struct list_head *p;
 	const struct firmware *fw;
 	const u8 *fw_ptr;
 	char fwname[64];
@@ -680,6 +697,37 @@ done:
 
 	BT_INFO("%s: Device booted in %llu usecs", hdev->name, duration);
 
+	/* Enable LPM if matching pdev with wakeup enabled */
+	spin_lock(&intel_device_list_lock);
+	list_for_each(p, &intel_device_list) {
+		struct intel_device *dev = list_entry(p, struct intel_device,
+						      list);
+		if (hu->tty->dev->parent != dev->pdev->dev.parent)
+			continue;
+
+		if (device_may_wakeup(&dev->pdev->dev))
+			idev = dev;
+
+		break;
+	}
+	spin_unlock(&intel_device_list_lock);
+
+	if (!idev)
+		goto no_lpm;
+
+	BT_INFO("%s: Enabling LPM", hdev->name);
+
+	skb = __hci_cmd_sync(hdev, 0xfc8b, sizeof(lpm_param), lpm_param,
+			     HCI_CMD_TIMEOUT);
+	if (IS_ERR(skb)) {
+		BT_ERR("%s: Failed to enable LPM", hdev->name);
+		goto no_lpm;
+	}
+	kfree_skb(skb);
+
+	set_bit(STATE_LPM_ENABLED, &intel->flags);
+
+no_lpm:
 	skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_CMD_TIMEOUT);
 	if (IS_ERR(skb))
 		return PTR_ERR(skb);
@@ -740,10 +788,51 @@ recv:
 	return hci_recv_frame(hdev, skb);
 }
 
+static void intel_recv_lpm_notify(struct hci_dev *hdev, int value)
+{
+	struct hci_uart *hu = hci_get_drvdata(hdev);
+	struct intel_data *intel = hu->priv;
+
+	BT_DBG("%s: TX idle notification (%d)", hdev->name, value);
+
+	if (value)
+		set_bit(STATE_TX_ACTIVE, &intel->flags);
+	else
+		clear_bit(STATE_TX_ACTIVE, &intel->flags);
+}
+
+static int intel_recv_lpm(struct hci_dev *hdev, struct sk_buff *skb)
+{
+	struct hci_lpm_pkt *lpm = (void *)skb->data;
+
+	switch (lpm->opcode) {
+	case LPM_OP_TX_NOTIFY:
+		if (lpm->dlen)
+			intel_recv_lpm_notify(hdev, lpm->data[0]);
+		break;
+	default:
+		BT_ERR("%s: unknown LPM opcode (%02x)", hdev->name,
+		       lpm->opcode);
+		break;
+	}
+
+	kfree_skb(skb);
+
+	return 0;
+}
+
+#define INTEL_RECV_LPM \
+	.type = HCI_LPM_PKT, \
+	.hlen = HCI_LPM_HDR_SIZE, \
+	.loff = 1, \
+	.lsize = 1, \
+	.maxlen = HCI_LPM_MAX_SIZE
+
 static const struct h4_recv_pkt intel_recv_pkts[] = {
-	{ H4_RECV_ACL,   .recv = hci_recv_frame },
-	{ H4_RECV_SCO,   .recv = hci_recv_frame },
-	{ H4_RECV_EVENT, .recv = intel_recv_event },
+	{ H4_RECV_ACL,    .recv = hci_recv_frame },
+	{ H4_RECV_SCO,    .recv = hci_recv_frame },
+	{ H4_RECV_EVENT,  .recv = intel_recv_event },
+	{ INTEL_RECV_LPM, .recv = intel_recv_lpm },
 };
 
 static int intel_recv(struct hci_uart *hu, const void *data, int count)
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH v2 1/2] Bluetooth: hci_intel: Retrieve host-wake IRQ
  2015-08-28 16:20 [PATCH v2 1/2] Bluetooth: hci_intel: Retrieve host-wake IRQ Loic Poulain
  2015-08-28 16:20 ` [PATCH v2 2/2] Bluetooth: hci_intel: Introduce LPM support Loic Poulain
@ 2015-08-28 16:22 ` Loic Poulain
  1 sibling, 0 replies; 4+ messages in thread
From: Loic Poulain @ 2015-08-28 16:22 UTC (permalink / raw)
  To: marcel; +Cc: linux-bluetooth



On 28/08/2015 18:20, Loic Poulain wrote:
> 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>
> ---
>   drivers/bluetooth/hci_intel.c | 62 ++++++++++++++++++++++++++++++++++++++++++-
>   1 file changed, 61 insertions(+), 1 deletion(-)
>
v2: move irq claim into set_power

-- 
Intel Open Source Technology Center
http://oss.intel.com/

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH v2 2/2] Bluetooth: hci_intel: Introduce LPM support
  2015-08-28 16:20 ` [PATCH v2 2/2] Bluetooth: hci_intel: Introduce LPM support Loic Poulain
@ 2015-08-28 16:25   ` Loic Poulain
  0 siblings, 0 replies; 4+ messages in thread
From: Loic Poulain @ 2015-08-28 16:25 UTC (permalink / raw)
  To: marcel; +Cc: linux-bluetooth


> Enable controller Low-Power-Mode if we have a pdev to manage host
> wake-up. Once LPM is enabled, controller notifies its TX status via
> a vendor specific packet (tx_idle/tx_active).
> tx_active means that there is more data upcoming from controller.
> tx_idle means that controller can be put in suspended state.
>
> Signed-off-by: Loic Poulain <loic.poulain@intel.com>
> ---
>   drivers/bluetooth/hci_intel.c | 95 +++++++++++++++++++++++++++++++++++++++++--
>   1 file changed, 92 insertions(+), 3 deletions(-)

v2: coding style, spin_lock restructuring

-- 
Intel Open Source Technology Center
http://oss.intel.com/

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2015-08-28 16:25 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-08-28 16:20 [PATCH v2 1/2] Bluetooth: hci_intel: Retrieve host-wake IRQ Loic Poulain
2015-08-28 16:20 ` [PATCH v2 2/2] Bluetooth: hci_intel: Introduce LPM support Loic Poulain
2015-08-28 16:25   ` Loic Poulain
2015-08-28 16:22 ` [PATCH v2 1/2] Bluetooth: hci_intel: Retrieve host-wake IRQ Loic Poulain

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).