On Sat, 2018-10-27 at 13:33 +0200, Hans de Goede wrote: > On many devices the RTL8723BS device gets reset during > suspend/resume, > causing it to loose its firmware and all state. "lose" (and in other places) > 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 of "off". > the > device on suspend to save power while suspended. > > Signed-off-by: Hans de Goede <hdegoede@xxxxxxxxxx> > --- > drivers/bluetooth/hci_h5.c | 52 > ++++++++++++++++++++++++++++++++++++++ > 1 file changed, 52 insertions(+) > > diff --git a/drivers/bluetooth/hci_h5.c b/drivers/bluetooth/hci_h5.c > index beddb89a00f2..654987b47051 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 looses > power during > + * suspend/resume, causing it to loose 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