Merge tag 'for-linus-5.11-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rw...
[linux-2.6-microblaze.git] / drivers / bluetooth / hci_serdev.c
index 7b23331..ef96ad0 100644 (file)
@@ -113,8 +113,22 @@ static int hci_uart_flush(struct hci_dev *hdev)
 /* Initialize device */
 static int hci_uart_open(struct hci_dev *hdev)
 {
+       struct hci_uart *hu = hci_get_drvdata(hdev);
+       int err;
+
        BT_DBG("%s %p", hdev->name, hdev);
 
+       /* When Quirk HCI_QUIRK_NON_PERSISTENT_SETUP is set by
+        * driver, BT SoC is completely turned OFF during
+        * BT OFF. Upon next BT ON UART port should be opened.
+        */
+       if (!test_bit(HCI_UART_PROTO_READY, &hu->flags)) {
+               err = serdev_device_open(hu->serdev);
+               if (err)
+                       return err;
+               set_bit(HCI_UART_PROTO_READY, &hu->flags);
+       }
+
        /* Undo clearing this from hci_uart_close() */
        hdev->flush = hci_uart_flush;
 
@@ -124,11 +138,25 @@ static int hci_uart_open(struct hci_dev *hdev)
 /* Close device */
 static int hci_uart_close(struct hci_dev *hdev)
 {
+       struct hci_uart *hu = hci_get_drvdata(hdev);
+
        BT_DBG("hdev %p", hdev);
 
+       if (!test_bit(HCI_UART_PROTO_READY, &hu->flags))
+               return 0;
+
        hci_uart_flush(hdev);
        hdev->flush = NULL;
 
+       /* When QUIRK HCI_QUIRK_NON_PERSISTENT_SETUP is set by driver,
+        * BT SOC is completely powered OFF during BT OFF, holding port
+        * open may drain the battery.
+        */
+       if (test_bit(HCI_QUIRK_NON_PERSISTENT_SETUP, &hdev->quirks)) {
+               clear_bit(HCI_UART_PROTO_READY, &hu->flags);
+               serdev_device_close(hu->serdev);
+       }
+
        return 0;
 }
 
@@ -354,7 +382,7 @@ void hci_uart_unregister_device(struct hci_uart *hu)
 {
        struct hci_dev *hdev = hu->hdev;
 
-       clear_bit(HCI_UART_PROTO_READY, &hu->flags);
+       cancel_work_sync(&hu->init_ready);
        if (test_bit(HCI_UART_REGISTERED, &hu->flags))
                hci_unregister_dev(hdev);
        hci_free_dev(hdev);
@@ -362,6 +390,10 @@ void hci_uart_unregister_device(struct hci_uart *hu)
        cancel_work_sync(&hu->write_work);
 
        hu->proto->close(hu);
-       serdev_device_close(hu->serdev);
+
+       if (test_bit(HCI_UART_PROTO_READY, &hu->flags)) {
+               clear_bit(HCI_UART_PROTO_READY, &hu->flags);
+               serdev_device_close(hu->serdev);
+       }
 }
 EXPORT_SYMBOL_GPL(hci_uart_unregister_device);