Skip to content

Commit 8589086

Browse files
jwrdegoedeholtmann
authored andcommitted
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 <[email protected]> Signed-off-by: Marcel Holtmann <[email protected]>
1 parent 28a75e4 commit 8589086

File tree

1 file changed

+52
-0
lines changed

1 file changed

+52
-0
lines changed

drivers/bluetooth/hci_h5.c

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -931,6 +931,56 @@ static void h5_btrtl_close(struct h5 *h5)
931931
gpiod_set_value_cansleep(h5->enable_gpio, 0);
932932
}
933933

934+
/* Suspend/resume support. On many devices the RTL BT device loses power during
935+
* suspend/resume, causing it to lose its firmware and all state. So we simply
936+
* turn it off on suspend and reprobe on resume. This mirrors how RTL devices
937+
* are handled in the USB driver, where the USB_QUIRK_RESET_RESUME is used which
938+
* also causes a reprobe on resume.
939+
*/
940+
static int h5_btrtl_suspend(struct h5 *h5)
941+
{
942+
serdev_device_set_flow_control(h5->hu->serdev, false);
943+
gpiod_set_value_cansleep(h5->device_wake_gpio, 0);
944+
gpiod_set_value_cansleep(h5->enable_gpio, 0);
945+
return 0;
946+
}
947+
948+
struct h5_btrtl_reprobe {
949+
struct device *dev;
950+
struct work_struct work;
951+
};
952+
953+
static void h5_btrtl_reprobe_worker(struct work_struct *work)
954+
{
955+
struct h5_btrtl_reprobe *reprobe =
956+
container_of(work, struct h5_btrtl_reprobe, work);
957+
int ret;
958+
959+
ret = device_reprobe(reprobe->dev);
960+
if (ret && ret != -EPROBE_DEFER)
961+
dev_err(reprobe->dev, "Reprobe error %d\n", ret);
962+
963+
put_device(reprobe->dev);
964+
kfree(reprobe);
965+
module_put(THIS_MODULE);
966+
}
967+
968+
static int h5_btrtl_resume(struct h5 *h5)
969+
{
970+
struct h5_btrtl_reprobe *reprobe;
971+
972+
reprobe = kzalloc(sizeof(*reprobe), GFP_KERNEL);
973+
if (!reprobe)
974+
return -ENOMEM;
975+
976+
__module_get(THIS_MODULE);
977+
978+
INIT_WORK(&reprobe->work, h5_btrtl_reprobe_worker);
979+
reprobe->dev = get_device(&h5->hu->serdev->dev);
980+
queue_work(system_long_wq, &reprobe->work);
981+
return 0;
982+
}
983+
934984
static const struct acpi_gpio_params btrtl_device_wake_gpios = { 0, 0, false };
935985
static const struct acpi_gpio_params btrtl_enable_gpios = { 1, 0, false };
936986
static const struct acpi_gpio_params btrtl_host_wake_gpios = { 2, 0, false };
@@ -945,6 +995,8 @@ static struct h5_vnd rtl_vnd = {
945995
.setup = h5_btrtl_setup,
946996
.open = h5_btrtl_open,
947997
.close = h5_btrtl_close,
998+
.suspend = h5_btrtl_suspend,
999+
.resume = h5_btrtl_resume,
9481000
.acpi_gpio_map = acpi_btrtl_gpios,
9491001
};
9501002
#endif

0 commit comments

Comments
 (0)