From 0207f77ece3d07b964d5723c501adc3f3a5a3c6d Mon Sep 17 00:00:00 2001
From: Dan Johansen <strit@manjaro.org>
Date: Mon, 1 Jun 2020 17:14:50 +0200
Subject: [PATCH] fix wonky wifi/bt on PBP

---
 drivers/bluetooth/hci_bcm.c      | 17 +++++++++++++++++
 drivers/bluetooth/hci_serdev.c   |  2 ++
 drivers/mmc/core/pwrseq_simple.c | 19 ++++++++++++++++---
 drivers/tty/serdev/core.c        | 11 +++++++++++
 include/linux/serdev.h           |  1 +
 5 files changed, 47 insertions(+), 3 deletions(-)

diff --git a/drivers/bluetooth/hci_bcm.c b/drivers/bluetooth/hci_bcm.c
index b236cb11c0dc..bfd37fb9eeb0 100644
--- a/drivers/bluetooth/hci_bcm.c
+++ b/drivers/bluetooth/hci_bcm.c
@@ -1472,6 +1472,22 @@ static void bcm_serdev_remove(struct serdev_device *serdev)
 	hci_uart_unregister_device(&bcmdev->serdev_hu);
 }
 
+static void bcm_serdev_shutdown(struct serdev_device *serdev)
+{
+	struct bcm_device *bcmdev = serdev_device_get_drvdata(serdev);
+
+/*
+	if (test_bit(HCI_UART_REGISTERED, &bcmdev->hu->flags)) {
+		hci_uart_unregister_device(&bcmdev->serdev_hu);
+	}
+*/
+	dev_info(bcmdev->dev, "Cutting power to bluetooth module\n");
+	if (bcm_gpio_set_power(bcmdev, false)) {
+		dev_err(bcmdev->dev, "Failed to power down\n");
+	}
+	usleep_range(500000, 1000000);
+}
+
 #ifdef CONFIG_OF
 static struct bcm_device_data bcm4354_device_data = {
 	.no_early_set_baudrate = true,
@@ -1497,6 +1513,7 @@ MODULE_DEVICE_TABLE(of, bcm_bluetooth_of_match);
 static struct serdev_device_driver bcm_serdev_driver = {
 	.probe = bcm_serdev_probe,
 	.remove = bcm_serdev_remove,
+    .shutdown = bcm_serdev_shutdown,
 	.driver = {
 		.name = "hci_uart_bcm",
 		.of_match_table = of_match_ptr(bcm_bluetooth_of_match),
diff --git a/drivers/bluetooth/hci_serdev.c b/drivers/bluetooth/hci_serdev.c
index 4652896d4990..043c585b34a7 100644
--- a/drivers/bluetooth/hci_serdev.c
+++ b/drivers/bluetooth/hci_serdev.c
@@ -395,5 +395,7 @@ void hci_uart_unregister_device(struct hci_uart *hu)
 		clear_bit(HCI_UART_PROTO_READY, &hu->flags);
 		serdev_device_close(hu->serdev);
 	}
+
+clear_bit(HCI_UART_REGISTERED, &hu->flags);
 }
 EXPORT_SYMBOL_GPL(hci_uart_unregister_device);
diff --git a/drivers/mmc/core/pwrseq_simple.c b/drivers/mmc/core/pwrseq_simple.c
index ea4d3670560e..b52c3f5b4f13 100644
--- a/drivers/mmc/core/pwrseq_simple.c
+++ b/drivers/mmc/core/pwrseq_simple.c
@@ -80,10 +80,8 @@ static void mmc_pwrseq_simple_post_power_on(struct mmc_host *host)
 		msleep(pwrseq->post_power_on_delay_ms);
 }
 
-static void mmc_pwrseq_simple_power_off(struct mmc_host *host)
+static void __mmc_pwrseq_simple_power_off(struct mmc_pwrseq_simple *pwrseq)
 {
-	struct mmc_pwrseq_simple *pwrseq = to_pwrseq_simple(host->pwrseq);
-
 	mmc_pwrseq_simple_set_gpios_value(pwrseq, 1);
 
 	if (pwrseq->power_off_delay_us)
@@ -96,6 +94,12 @@ static void mmc_pwrseq_simple_power_off(struct mmc_host *host)
 	}
 }
 
+static void mmc_pwrseq_simple_power_off(struct mmc_host *host)
+{
+	struct mmc_pwrseq_simple *pwrseq = to_pwrseq_simple(host->pwrseq);
+	__mmc_pwrseq_simple_power_off(pwrseq);
+}
+
 static const struct mmc_pwrseq_ops mmc_pwrseq_simple_ops = {
 	.pre_power_on = mmc_pwrseq_simple_pre_power_on,
 	.post_power_on = mmc_pwrseq_simple_post_power_on,
@@ -151,9 +155,18 @@ static int mmc_pwrseq_simple_remove(struct platform_device *pdev)
 	return 0;
 }
 
+static void mmc_pwrseq_simple_shutdown(struct platform_device *pdev)
+{
+	struct mmc_pwrseq_simple *pwrseq = platform_get_drvdata(pdev);
+
+	dev_info(&pdev->dev, "Turning off mmc\n");
+	__mmc_pwrseq_simple_power_off(pwrseq);
+}
+
 static struct platform_driver mmc_pwrseq_simple_driver = {
 	.probe = mmc_pwrseq_simple_probe,
 	.remove = mmc_pwrseq_simple_remove,
+    .shutdown = mmc_pwrseq_simple_shutdown,
 	.driver = {
 		.name = "pwrseq_simple",
 		.of_match_table = mmc_pwrseq_simple_of_match,
diff --git a/drivers/tty/serdev/core.c b/drivers/tty/serdev/core.c
index c5f0d936b003..54bcb38f0c05 100644
--- a/drivers/tty/serdev/core.c
+++ b/drivers/tty/serdev/core.c
@@ -432,11 +432,22 @@ static int serdev_drv_remove(struct device *dev)
 	return 0;
 }
 
+static void serdev_drv_shutdown(struct device *dev)
+{
+	const struct serdev_device_driver *sdrv;
+	if (dev->driver) {
+		sdrv = to_serdev_device_driver(dev->driver);
+		if (sdrv->shutdown)
+			sdrv->shutdown(to_serdev_device(dev));
+	}
+}
+
 static struct bus_type serdev_bus_type = {
 	.name		= "serial",
 	.match		= serdev_device_match,
 	.probe		= serdev_drv_probe,
 	.remove		= serdev_drv_remove,
+    .shutdown	= serdev_drv_shutdown,
 };
 
 /**
diff --git a/include/linux/serdev.h b/include/linux/serdev.h
index 9f14f9c12ec4..c3d5dccd6115 100644
--- a/include/linux/serdev.h
+++ b/include/linux/serdev.h
@@ -63,6 +63,7 @@ struct serdev_device_driver {
 	struct device_driver driver;
 	int	(*probe)(struct serdev_device *);
 	void	(*remove)(struct serdev_device *);
+    void	(*shutdown)(struct serdev_device *);
 };
 
 static inline struct serdev_device_driver *to_serdev_device_driver(struct device_driver *d)
-- 
2.26.2

