diff options
Diffstat (limited to 'drivers/bluetooth/btusb.c')
| -rw-r--r-- | drivers/bluetooth/btusb.c | 65 | 
1 files changed, 61 insertions, 4 deletions
diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index ba4149054304..a9c35ebb30f8 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -438,6 +438,7 @@ static const struct dmi_system_id btusb_needs_reset_resume_table[] = {  #define BTUSB_OOB_WAKE_ENABLED	11  #define BTUSB_HW_RESET_ACTIVE	12  #define BTUSB_TX_WAIT_VND_EVT	13 +#define BTUSB_WAKEUP_DISABLE	14  struct btusb_data {  	struct hci_dev       *hdev; @@ -526,6 +527,36 @@ static void btusb_intel_cmd_timeout(struct hci_dev *hdev)  	gpiod_set_value_cansleep(reset_gpio, 0);  } +static void btusb_rtl_cmd_timeout(struct hci_dev *hdev) +{ +	struct btusb_data *data = hci_get_drvdata(hdev); +	struct gpio_desc *reset_gpio = data->reset_gpio; + +	if (++data->cmd_timeout_cnt < 5) +		return; + +	if (!reset_gpio) { +		bt_dev_err(hdev, "No gpio to reset Realtek device, ignoring"); +		return; +	} + +	/* Toggle the hard reset line. The Realtek device is going to +	 * yank itself off the USB and then replug. The cleanup is handled +	 * correctly on the way out (standard USB disconnect), and the new +	 * device is detected cleanly and bound to the driver again like +	 * it should be. +	 */ +	if (test_and_set_bit(BTUSB_HW_RESET_ACTIVE, &data->flags)) { +		bt_dev_err(hdev, "last reset failed? Not resetting again"); +		return; +	} + +	bt_dev_err(hdev, "Reset Realtek device via gpio"); +	gpiod_set_value_cansleep(reset_gpio, 0); +	msleep(200); +	gpiod_set_value_cansleep(reset_gpio, 1); +} +  static inline void btusb_free_frags(struct btusb_data *data)  {  	unsigned long flags; @@ -1174,6 +1205,13 @@ static int btusb_open(struct hci_dev *hdev)  	data->intf->needs_remote_wakeup = 1; +	/* Disable device remote wakeup when host is suspended +	 * For Realtek chips, global suspend without +	 * SET_FEATURE (DEVICE_REMOTE_WAKEUP) can save more power in device. +	 */ +	if (test_bit(BTUSB_WAKEUP_DISABLE, &data->flags)) +		device_wakeup_disable(&data->udev->dev); +  	if (test_and_set_bit(BTUSB_INTR_RUNNING, &data->flags))  		goto done; @@ -1237,6 +1275,11 @@ static int btusb_close(struct hci_dev *hdev)  		goto failed;  	data->intf->needs_remote_wakeup = 0; + +	/* Enable remote wake up for auto-suspend */ +	if (test_bit(BTUSB_WAKEUP_DISABLE, &data->flags)) +		data->intf->needs_remote_wakeup = 1; +  	usb_autopm_put_interface(data->intf);  failed: @@ -3768,12 +3811,13 @@ static int btusb_probe(struct usb_interface *intf,  	if (id->driver_info & BTUSB_REALTEK) {  		hdev->setup = btrtl_setup_realtek;  		hdev->shutdown = btrtl_shutdown_realtek; +		hdev->cmd_timeout = btusb_rtl_cmd_timeout; -		/* Realtek devices lose their updated firmware over suspend, -		 * but the USB hub doesn't notice any status change. -		 * Explicitly request a device reset on resume. +		/* Realtek devices lose their updated firmware over global +		 * suspend that means host doesn't send SET_FEATURE +		 * (DEVICE_REMOTE_WAKEUP)  		 */ -		interface_to_usbdev(intf)->quirks |= USB_QUIRK_RESET_RESUME; +		set_bit(BTUSB_WAKEUP_DISABLE, &data->flags);  	}  #endif @@ -3947,6 +3991,19 @@ static int btusb_suspend(struct usb_interface *intf, pm_message_t message)  		enable_irq(data->oob_wake_irq);  	} +	/* For global suspend, Realtek devices lose the loaded fw +	 * in them. But for autosuspend, firmware should remain. +	 * Actually, it depends on whether the usb host sends +	 * set feature (enable wakeup) or not. +	 */ +	if (test_bit(BTUSB_WAKEUP_DISABLE, &data->flags)) { +		if (PMSG_IS_AUTO(message) && +		    device_can_wakeup(&data->udev->dev)) +			data->udev->do_remote_wakeup = 1; +		else if (!PMSG_IS_AUTO(message)) +			data->udev->reset_resume = 1; +	} +  	return 0;  }  |