diff options
author | Hans de Goede | 2018-10-30 14:17:23 +0100 |
---|---|---|
committer | Marcel Holtmann | 2018-12-19 00:49:33 +0100 |
commit | 8589086f4efd5756d20cedd844b865e5d20164ec (patch) | |
tree | 38ee30a7d66ade09b265696abfa55c89f6f372bf /drivers/bluetooth | |
parent | 28a75e4c813c7ae7de5b4baf4c29369769247cc6 (diff) |
Bluetooth: hci_h5: Turn off RTL8723BS on suspend, reprobe on resume
On many devices the RTL8723BS device gets reset during suspend/resume,
causing it to lose its firmware and all state.
Testing has shown it drops back to communicating at 115200 bps and sends
sync-request packages, indicating it has been fully reset.
This commit fixes this by queueing a reprobe on resume.
This mirrors how USB RTL BT devices, which have the same problem, are
handled in the btusb driver, there we set the USB_QUIRK_RESET_RESUME for
all RTL devices, which also causes a reprobe on resume. The only difference
is that here we need to do the reprobe ourselves.
Since we are doing a full reprobe on resume now, we can also turn off the
device on suspend to save power while suspended.
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
Diffstat (limited to 'drivers/bluetooth')
-rw-r--r-- | drivers/bluetooth/hci_h5.c | 52 |
1 files changed, 52 insertions, 0 deletions
diff --git a/drivers/bluetooth/hci_h5.c b/drivers/bluetooth/hci_h5.c index beddb89a00f2..069d1c8fde73 100644 --- a/drivers/bluetooth/hci_h5.c +++ b/drivers/bluetooth/hci_h5.c @@ -931,6 +931,56 @@ static void h5_btrtl_close(struct h5 *h5) gpiod_set_value_cansleep(h5->enable_gpio, 0); } +/* Suspend/resume support. On many devices the RTL BT device loses power during + * suspend/resume, causing it to lose its firmware and all state. So we simply + * turn it off on suspend and reprobe on resume. This mirrors how RTL devices + * are handled in the USB driver, where the USB_QUIRK_RESET_RESUME is used which + * also causes a reprobe on resume. + */ +static int h5_btrtl_suspend(struct h5 *h5) +{ + serdev_device_set_flow_control(h5->hu->serdev, false); + gpiod_set_value_cansleep(h5->device_wake_gpio, 0); + gpiod_set_value_cansleep(h5->enable_gpio, 0); + return 0; +} + +struct h5_btrtl_reprobe { + struct device *dev; + struct work_struct work; +}; + +static void h5_btrtl_reprobe_worker(struct work_struct *work) +{ + struct h5_btrtl_reprobe *reprobe = + container_of(work, struct h5_btrtl_reprobe, work); + int ret; + + ret = device_reprobe(reprobe->dev); + if (ret && ret != -EPROBE_DEFER) + dev_err(reprobe->dev, "Reprobe error %d\n", ret); + + put_device(reprobe->dev); + kfree(reprobe); + module_put(THIS_MODULE); +} + +static int h5_btrtl_resume(struct h5 *h5) +{ + struct h5_btrtl_reprobe *reprobe; + + reprobe = kzalloc(sizeof(*reprobe), GFP_KERNEL); + if (!reprobe) + return -ENOMEM; + + __module_get(THIS_MODULE); + + INIT_WORK(&reprobe->work, h5_btrtl_reprobe_worker); + reprobe->dev = get_device(&h5->hu->serdev->dev); + queue_work(system_long_wq, &reprobe->work); + return 0; +} + static const struct acpi_gpio_params btrtl_device_wake_gpios = { 0, 0, false }; static const struct acpi_gpio_params btrtl_enable_gpios = { 1, 0, false }; static const struct acpi_gpio_params btrtl_host_wake_gpios = { 2, 0, false }; @@ -945,6 +995,8 @@ static struct h5_vnd rtl_vnd = { .setup = h5_btrtl_setup, .open = h5_btrtl_open, .close = h5_btrtl_close, + .suspend = h5_btrtl_suspend, + .resume = h5_btrtl_resume, .acpi_gpio_map = acpi_btrtl_gpios, }; #endif |