All of lore.kernel.org
 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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.