Skip to content

Commit fd913ef

Browse files
Rajat Jainholtmann
authored andcommitted
Bluetooth: btusb: Add out-of-band wakeup support
Some onboard BT chips (e.g. Marvell 8997) contain a wakeup pin that can be connected to a gpio on the CPU side, and can be used to wakeup the host out-of-band. This can be useful in situations where the in-band wakeup is not possible or not preferable (e.g. the in-band wakeup may require the USB host controller to remain active, and hence consuming more system power during system sleep). The oob gpio interrupt to be used for wakeup on the CPU side, is read from the device tree node, (using standard interrupt descriptors). A devcie tree binding document is also added for the driver. The compatible string is in compliance with Documentation/devicetree/bindings/usb/usb-device.txt Signed-off-by: Rajat Jain <rajatja@google.com> Reviewed-by: Brian Norris <briannorris@chromium.org> Acked-by: Rob Herring <robh@kernel.org> Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
1 parent 10ab133 commit fd913ef

File tree

2 files changed

+125
-0
lines changed

2 files changed

+125
-0
lines changed
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
Generic Bluetooth controller over USB (btusb driver)
2+
---------------------------------------------------
3+
4+
Required properties:
5+
6+
- compatible : should comply with the format "usbVID,PID" specified in
7+
Documentation/devicetree/bindings/usb/usb-device.txt
8+
At the time of writing, the only OF supported devices
9+
(more may be added later) are:
10+
11+
"usb1286,204e" (Marvell 8997)
12+
13+
Optional properties:
14+
15+
- interrupt-parent: phandle of the parent interrupt controller
16+
- interrupt-names: (see below)
17+
- interrupts : The interrupt specified by the name "wakeup" is the interrupt
18+
that shall be used for out-of-band wake-on-bt. Driver will
19+
request this interrupt for wakeup. During system suspend, the
20+
irq will be enabled so that the bluetooth chip can wakeup host
21+
platform out of band. During system resume, the irq will be
22+
disabled to make sure unnecessary interrupt is not received.
23+
24+
Example:
25+
26+
Following example uses irq pin number 3 of gpio0 for out of band wake-on-bt:
27+
28+
&usb_host1_ehci {
29+
status = "okay";
30+
#address-cells = <1>;
31+
#size-cells = <0>;
32+
33+
mvl_bt1: bt@1 {
34+
compatible = "usb1286,204e";
35+
reg = <1>;
36+
interrupt-parent = <&gpio0>;
37+
interrupt-name = "wakeup";
38+
interrupts = <3 IRQ_TYPE_LEVEL_LOW>;
39+
};
40+
};

drivers/bluetooth/btusb.c

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
#include <linux/module.h>
2525
#include <linux/usb.h>
2626
#include <linux/firmware.h>
27+
#include <linux/of_device.h>
28+
#include <linux/of_irq.h>
2729
#include <asm/unaligned.h>
2830

2931
#include <net/bluetooth/bluetooth.h>
@@ -373,6 +375,7 @@ static const struct usb_device_id blacklist_table[] = {
373375
#define BTUSB_BOOTING 9
374376
#define BTUSB_RESET_RESUME 10
375377
#define BTUSB_DIAG_RUNNING 11
378+
#define BTUSB_OOB_WAKE_ENABLED 12
376379

377380
struct btusb_data {
378381
struct hci_dev *hdev;
@@ -420,6 +423,8 @@ struct btusb_data {
420423
int (*recv_bulk)(struct btusb_data *data, void *buffer, int count);
421424

422425
int (*setup_on_usb)(struct hci_dev *hdev);
426+
427+
int oob_wake_irq; /* irq for out-of-band wake-on-bt */
423428
};
424429

425430
static inline void btusb_free_frags(struct btusb_data *data)
@@ -2732,6 +2737,66 @@ static int btusb_bcm_set_diag(struct hci_dev *hdev, bool enable)
27322737
}
27332738
#endif
27342739

2740+
#ifdef CONFIG_PM
2741+
static irqreturn_t btusb_oob_wake_handler(int irq, void *priv)
2742+
{
2743+
struct btusb_data *data = priv;
2744+
2745+
pm_wakeup_event(&data->udev->dev, 0);
2746+
2747+
/* Disable only if not already disabled (keep it balanced) */
2748+
if (test_and_clear_bit(BTUSB_OOB_WAKE_ENABLED, &data->flags)) {
2749+
disable_irq_nosync(irq);
2750+
disable_irq_wake(irq);
2751+
}
2752+
return IRQ_HANDLED;
2753+
}
2754+
2755+
static const struct of_device_id btusb_match_table[] = {
2756+
{ .compatible = "usb1286,204e" },
2757+
{ }
2758+
};
2759+
MODULE_DEVICE_TABLE(of, btusb_match_table);
2760+
2761+
/* Use an oob wakeup pin? */
2762+
static int btusb_config_oob_wake(struct hci_dev *hdev)
2763+
{
2764+
struct btusb_data *data = hci_get_drvdata(hdev);
2765+
struct device *dev = &data->udev->dev;
2766+
int irq, ret;
2767+
2768+
clear_bit(BTUSB_OOB_WAKE_ENABLED, &data->flags);
2769+
2770+
if (!of_match_device(btusb_match_table, dev))
2771+
return 0;
2772+
2773+
/* Move on if no IRQ specified */
2774+
irq = of_irq_get_byname(dev->of_node, "wakeup");
2775+
if (irq <= 0) {
2776+
bt_dev_dbg(hdev, "%s: no OOB Wakeup IRQ in DT", __func__);
2777+
return 0;
2778+
}
2779+
2780+
ret = devm_request_irq(&hdev->dev, irq, btusb_oob_wake_handler,
2781+
0, "OOB Wake-on-BT", data);
2782+
if (ret) {
2783+
bt_dev_err(hdev, "%s: IRQ request failed", __func__);
2784+
return ret;
2785+
}
2786+
2787+
ret = device_init_wakeup(dev, true);
2788+
if (ret) {
2789+
bt_dev_err(hdev, "%s: failed to init_wakeup", __func__);
2790+
return ret;
2791+
}
2792+
2793+
data->oob_wake_irq = irq;
2794+
disable_irq(irq);
2795+
bt_dev_info(hdev, "OOB Wake-on-BT configured at IRQ %u", irq);
2796+
return 0;
2797+
}
2798+
#endif
2799+
27352800
static int btusb_probe(struct usb_interface *intf,
27362801
const struct usb_device_id *id)
27372802
{
@@ -2853,6 +2918,11 @@ static int btusb_probe(struct usb_interface *intf,
28532918
hdev->send = btusb_send_frame;
28542919
hdev->notify = btusb_notify;
28552920

2921+
#ifdef CONFIG_PM
2922+
err = btusb_config_oob_wake(hdev);
2923+
if (err)
2924+
goto out_free_dev;
2925+
#endif
28562926
if (id->driver_info & BTUSB_CW6622)
28572927
set_bit(HCI_QUIRK_BROKEN_STORED_LINK_KEY, &hdev->quirks);
28582928

@@ -3065,6 +3135,9 @@ static void btusb_disconnect(struct usb_interface *intf)
30653135
usb_driver_release_interface(&btusb_driver, data->isoc);
30663136
}
30673137

3138+
if (data->oob_wake_irq)
3139+
device_init_wakeup(&data->udev->dev, false);
3140+
30683141
hci_free_dev(hdev);
30693142
}
30703143

@@ -3093,6 +3166,12 @@ static int btusb_suspend(struct usb_interface *intf, pm_message_t message)
30933166
btusb_stop_traffic(data);
30943167
usb_kill_anchored_urbs(&data->tx_anchor);
30953168

3169+
if (data->oob_wake_irq && device_may_wakeup(&data->udev->dev)) {
3170+
set_bit(BTUSB_OOB_WAKE_ENABLED, &data->flags);
3171+
enable_irq_wake(data->oob_wake_irq);
3172+
enable_irq(data->oob_wake_irq);
3173+
}
3174+
30963175
/* Optionally request a device reset on resume, but only when
30973176
* wakeups are disabled. If wakeups are enabled we assume the
30983177
* device will stay powered up throughout suspend.
@@ -3130,6 +3209,12 @@ static int btusb_resume(struct usb_interface *intf)
31303209
if (--data->suspend_count)
31313210
return 0;
31323211

3212+
/* Disable only if not already disabled (keep it balanced) */
3213+
if (test_and_clear_bit(BTUSB_OOB_WAKE_ENABLED, &data->flags)) {
3214+
disable_irq(data->oob_wake_irq);
3215+
disable_irq_wake(data->oob_wake_irq);
3216+
}
3217+
31333218
if (!test_bit(HCI_RUNNING, &hdev->flags))
31343219
goto done;
31353220

0 commit comments

Comments
 (0)