summaryrefslogtreecommitdiff
path: root/drivers/bluetooth
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/bluetooth')
-rw-r--r--drivers/bluetooth/Kconfig4
-rw-r--r--drivers/bluetooth/btmrvl_drv.h2
-rw-r--r--drivers/bluetooth/btmrvl_sdio.c43
-rw-r--r--drivers/bluetooth/btmtkuart.c538
-rw-r--r--drivers/bluetooth/btqca.c19
-rw-r--r--drivers/bluetooth/btqca.h8
-rw-r--r--drivers/bluetooth/btqcomsmd.c31
-rw-r--r--drivers/bluetooth/btrtl.c10
-rw-r--r--drivers/bluetooth/btusb.c75
-rw-r--r--drivers/bluetooth/h4_recv.h7
-rw-r--r--drivers/bluetooth/hci_h4.c4
-rw-r--r--drivers/bluetooth/hci_ldisc.c24
-rw-r--r--drivers/bluetooth/hci_qca.c121
13 files changed, 751 insertions, 135 deletions
diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig
index 845b0314ce3a..7b2e76e7f22f 100644
--- a/drivers/bluetooth/Kconfig
+++ b/drivers/bluetooth/Kconfig
@@ -336,7 +336,7 @@ config BT_MRVL
The core driver to support Marvell Bluetooth devices.
This driver is required if you want to support
- Marvell Bluetooth devices, such as 8688/8787/8797/8887/8897/8997.
+ Marvell Bluetooth devices, such as 8688/8787/8797/8887/8897/8977/8997.
Say Y here to compile Marvell Bluetooth driver
into the kernel or say M to compile it as module.
@@ -350,7 +350,7 @@ config BT_MRVL_SDIO
The driver for Marvell Bluetooth chipsets with SDIO interface.
This driver is required if you want to use Marvell Bluetooth
- devices with SDIO interface. Currently SD8688/SD8787/SD8797/SD8887/SD8897/SD8997
+ devices with SDIO interface. Currently SD8688/SD8787/SD8797/SD8887/SD8897/SD8977/SD8997
chipsets are supported.
Say Y here to compile support for Marvell BT-over-SDIO driver
diff --git a/drivers/bluetooth/btmrvl_drv.h b/drivers/bluetooth/btmrvl_drv.h
index f0454541e5fd..fb7729779166 100644
--- a/drivers/bluetooth/btmrvl_drv.h
+++ b/drivers/bluetooth/btmrvl_drv.h
@@ -24,11 +24,9 @@
#include <linux/slab.h>
#include <net/bluetooth/bluetooth.h>
#include <linux/err.h>
-#include <linux/gpio.h>
#include <linux/gfp.h>
#include <linux/interrupt.h>
#include <linux/io.h>
-#include <linux/of_gpio.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
diff --git a/drivers/bluetooth/btmrvl_sdio.c b/drivers/bluetooth/btmrvl_sdio.c
index fb3d03928460..047b75ce1deb 100644
--- a/drivers/bluetooth/btmrvl_sdio.c
+++ b/drivers/bluetooth/btmrvl_sdio.c
@@ -62,13 +62,14 @@ static const struct of_device_id btmrvl_sdio_of_match_table[] = {
static irqreturn_t btmrvl_wake_irq_bt(int irq, void *priv)
{
struct btmrvl_sdio_card *card = priv;
+ struct device *dev = &card->func->dev;
struct btmrvl_plt_wake_cfg *cfg = card->plt_wake_cfg;
- pr_info("%s: wake by bt\n", __func__);
+ dev_info(dev, "wake by bt\n");
cfg->wake_by_bt = true;
disable_irq_nosync(irq);
- pm_wakeup_event(&card->func->dev, 0);
+ pm_wakeup_event(dev, 0);
pm_system_wakeup();
return IRQ_HANDLED;
@@ -87,7 +88,7 @@ static int btmrvl_sdio_probe_of(struct device *dev,
if (!dev->of_node ||
!of_match_node(btmrvl_sdio_of_match_table, dev->of_node)) {
- pr_err("sdio platform data not available\n");
+ dev_info(dev, "sdio device tree data not available\n");
return -1;
}
@@ -211,6 +212,29 @@ static const struct btmrvl_sdio_card_reg btmrvl_reg_8897 = {
.fw_dump_end = 0xea,
};
+static const struct btmrvl_sdio_card_reg btmrvl_reg_8977 = {
+ .cfg = 0x00,
+ .host_int_mask = 0x08,
+ .host_intstatus = 0x0c,
+ .card_status = 0x5c,
+ .sq_read_base_addr_a0 = 0xf8,
+ .sq_read_base_addr_a1 = 0xf9,
+ .card_revision = 0xc8,
+ .card_fw_status0 = 0xe8,
+ .card_fw_status1 = 0xe9,
+ .card_rx_len = 0xea,
+ .card_rx_unit = 0xeb,
+ .io_port_0 = 0xe4,
+ .io_port_1 = 0xe5,
+ .io_port_2 = 0xe6,
+ .int_read_to_clear = true,
+ .host_int_rsr = 0x04,
+ .card_misc_cfg = 0xD8,
+ .fw_dump_ctrl = 0xf0,
+ .fw_dump_start = 0xf1,
+ .fw_dump_end = 0xf8,
+};
+
static const struct btmrvl_sdio_card_reg btmrvl_reg_8997 = {
.cfg = 0x00,
.host_int_mask = 0x08,
@@ -279,6 +303,15 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8897 = {
.supports_fw_dump = true,
};
+static const struct btmrvl_sdio_device btmrvl_sdio_sd8977 = {
+ .helper = NULL,
+ .firmware = "mrvl/sd8977_uapsta.bin",
+ .reg = &btmrvl_reg_8977,
+ .support_pscan_win_report = true,
+ .sd_blksz_fw_dl = 256,
+ .supports_fw_dump = true,
+};
+
static const struct btmrvl_sdio_device btmrvl_sdio_sd8997 = {
.helper = NULL,
.firmware = "mrvl/sd8997_uapsta.bin",
@@ -307,6 +340,9 @@ static const struct sdio_device_id btmrvl_sdio_ids[] = {
/* Marvell SD8897 Bluetooth device */
{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x912E),
.driver_data = (unsigned long)&btmrvl_sdio_sd8897 },
+ /* Marvell SD8977 Bluetooth device */
+ { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x9146),
+ .driver_data = (unsigned long)&btmrvl_sdio_sd8977 },
/* Marvell SD8997 Bluetooth device */
{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x9142),
.driver_data = (unsigned long)&btmrvl_sdio_sd8997 },
@@ -1760,4 +1796,5 @@ MODULE_FIRMWARE("mrvl/sd8787_uapsta.bin");
MODULE_FIRMWARE("mrvl/sd8797_uapsta.bin");
MODULE_FIRMWARE("mrvl/sd8887_uapsta.bin");
MODULE_FIRMWARE("mrvl/sd8897_uapsta.bin");
+MODULE_FIRMWARE("mrvl/sd8977_uapsta.bin");
MODULE_FIRMWARE("mrvl/sd8997_uapsta.bin");
diff --git a/drivers/bluetooth/btmtkuart.c b/drivers/bluetooth/btmtkuart.c
index 4593baff2bc9..b0b680dd69f4 100644
--- a/drivers/bluetooth/btmtkuart.c
+++ b/drivers/bluetooth/btmtkuart.c
@@ -12,10 +12,15 @@
#include <linux/atomic.h>
#include <linux/clk.h>
#include <linux/firmware.h>
+#include <linux/gpio/consumer.h>
+#include <linux/iopoll.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/pinctrl/consumer.h>
#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
#include <linux/serdev.h>
#include <linux/skbuff.h>
@@ -24,20 +29,38 @@
#include "h4_recv.h"
-#define VERSION "0.1"
+#define VERSION "0.2"
#define FIRMWARE_MT7622 "mediatek/mt7622pr2h.bin"
+#define FIRMWARE_MT7663 "mediatek/mt7663pr2h.bin"
+#define FIRMWARE_MT7668 "mediatek/mt7668pr2h.bin"
#define MTK_STP_TLR_SIZE 2
#define BTMTKUART_TX_STATE_ACTIVE 1
#define BTMTKUART_TX_STATE_WAKEUP 2
#define BTMTKUART_TX_WAIT_VND_EVT 3
+#define BTMTKUART_REQUIRED_WAKEUP 4
+
+#define BTMTKUART_FLAG_STANDALONE_HW BIT(0)
enum {
MTK_WMT_PATCH_DWNLD = 0x1,
+ MTK_WMT_TEST = 0x2,
+ MTK_WMT_WAKEUP = 0x3,
+ MTK_WMT_HIF = 0x4,
MTK_WMT_FUNC_CTRL = 0x6,
- MTK_WMT_RST = 0x7
+ MTK_WMT_RST = 0x7,
+ MTK_WMT_SEMAPHORE = 0x17,
+};
+
+enum {
+ BTMTK_WMT_INVALID,
+ BTMTK_WMT_PATCH_UNDONE,
+ BTMTK_WMT_PATCH_DONE,
+ BTMTK_WMT_ON_UNDONE,
+ BTMTK_WMT_ON_DONE,
+ BTMTK_WMT_ON_PROGRESS,
};
struct mtk_stp_hdr {
@@ -46,6 +69,11 @@ struct mtk_stp_hdr {
u8 cs;
} __packed;
+struct btmtkuart_data {
+ unsigned int flags;
+ const char *fwname;
+};
+
struct mtk_wmt_hdr {
u8 dir;
u8 op;
@@ -58,41 +86,85 @@ struct mtk_hci_wmt_cmd {
u8 data[256];
} __packed;
+struct btmtk_hci_wmt_evt {
+ struct hci_event_hdr hhdr;
+ struct mtk_wmt_hdr whdr;
+} __packed;
+
+struct btmtk_hci_wmt_evt_funcc {
+ struct btmtk_hci_wmt_evt hwhdr;
+ __be16 status;
+} __packed;
+
+struct btmtk_tci_sleep {
+ u8 mode;
+ __le16 duration;
+ __le16 host_duration;
+ u8 host_wakeup_pin;
+ u8 time_compensation;
+} __packed;
+
+struct btmtk_hci_wmt_params {
+ u8 op;
+ u8 flag;
+ u16 dlen;
+ const void *data;
+ u32 *status;
+};
+
struct btmtkuart_dev {
struct hci_dev *hdev;
struct serdev_device *serdev;
struct clk *clk;
+ struct regulator *vcc;
+ struct gpio_desc *reset;
+ struct pinctrl *pinctrl;
+ struct pinctrl_state *pins_runtime;
+ struct pinctrl_state *pins_boot;
+ speed_t desired_speed;
+ speed_t curr_speed;
+
struct work_struct tx_work;
unsigned long tx_state;
struct sk_buff_head txq;
struct sk_buff *rx_skb;
+ struct sk_buff *evt_skb;
u8 stp_pad[6];
u8 stp_cursor;
u16 stp_dlen;
+
+ const struct btmtkuart_data *data;
};
-static int mtk_hci_wmt_sync(struct hci_dev *hdev, u8 op, u8 flag, u16 plen,
- const void *param)
+#define btmtkuart_is_standalone(bdev) \
+ ((bdev)->data->flags & BTMTKUART_FLAG_STANDALONE_HW)
+#define btmtkuart_is_builtin_soc(bdev) \
+ !((bdev)->data->flags & BTMTKUART_FLAG_STANDALONE_HW)
+
+static int mtk_hci_wmt_sync(struct hci_dev *hdev,
+ struct btmtk_hci_wmt_params *wmt_params)
{
struct btmtkuart_dev *bdev = hci_get_drvdata(hdev);
+ struct btmtk_hci_wmt_evt_funcc *wmt_evt_funcc;
+ u32 hlen, status = BTMTK_WMT_INVALID;
+ struct btmtk_hci_wmt_evt *wmt_evt;
struct mtk_hci_wmt_cmd wc;
struct mtk_wmt_hdr *hdr;
- u32 hlen;
int err;
- hlen = sizeof(*hdr) + plen;
+ hlen = sizeof(*hdr) + wmt_params->dlen;
if (hlen > 255)
return -EINVAL;
hdr = (struct mtk_wmt_hdr *)&wc;
hdr->dir = 1;
- hdr->op = op;
- hdr->dlen = cpu_to_le16(plen + 1);
- hdr->flag = flag;
- memcpy(wc.data, param, plen);
+ hdr->op = wmt_params->op;
+ hdr->dlen = cpu_to_le16(wmt_params->dlen + 1);
+ hdr->flag = wmt_params->flag;
+ memcpy(wc.data, wmt_params->data, wmt_params->dlen);
set_bit(BTMTKUART_TX_WAIT_VND_EVT, &bdev->tx_state);
@@ -107,7 +179,7 @@ static int mtk_hci_wmt_sync(struct hci_dev *hdev, u8 op, u8 flag, u16 plen,
* Complete as with usual HCI command flow control.
*
* After sending the command, wait for BTMTKUART_TX_WAIT_VND_EVT
- * state to be cleared. The driver speicfic event receive routine
+ * state to be cleared. The driver specific event receive routine
* will clear that state and with that indicate completion of the
* WMT command.
*/
@@ -115,26 +187,63 @@ static int mtk_hci_wmt_sync(struct hci_dev *hdev, u8 op, u8 flag, u16 plen,
TASK_INTERRUPTIBLE, HCI_INIT_TIMEOUT);
if (err == -EINTR) {
bt_dev_err(hdev, "Execution of wmt command interrupted");
+ clear_bit(BTMTKUART_TX_WAIT_VND_EVT, &bdev->tx_state);
return err;
}
if (err) {
bt_dev_err(hdev, "Execution of wmt command timed out");
+ clear_bit(BTMTKUART_TX_WAIT_VND_EVT, &bdev->tx_state);
return -ETIMEDOUT;
}
- return 0;
+ /* Parse and handle the return WMT event */
+ wmt_evt = (struct btmtk_hci_wmt_evt *)bdev->evt_skb->data;
+ if (wmt_evt->whdr.op != hdr->op) {
+ bt_dev_err(hdev, "Wrong op received %d expected %d",
+ wmt_evt->whdr.op, hdr->op);
+ err = -EIO;
+ goto err_free_skb;
+ }
+
+ switch (wmt_evt->whdr.op) {
+ case MTK_WMT_SEMAPHORE:
+ if (wmt_evt->whdr.flag == 2)
+ status = BTMTK_WMT_PATCH_UNDONE;
+ else
+ status = BTMTK_WMT_PATCH_DONE;
+ break;
+ case MTK_WMT_FUNC_CTRL:
+ wmt_evt_funcc = (struct btmtk_hci_wmt_evt_funcc *)wmt_evt;
+ if (be16_to_cpu(wmt_evt_funcc->status) == 0x404)
+ status = BTMTK_WMT_ON_DONE;
+ else if (be16_to_cpu(wmt_evt_funcc->status) == 0x420)
+ status = BTMTK_WMT_ON_PROGRESS;
+ else
+ status = BTMTK_WMT_ON_UNDONE;
+ break;
+ }
+
+ if (wmt_params->status)
+ *wmt_params->status = status;
+
+err_free_skb:
+ kfree_skb(bdev->evt_skb);
+ bdev->evt_skb = NULL;
+
+ return err;
}
-static int mtk_setup_fw(struct hci_dev *hdev)
+static int mtk_setup_firmware(struct hci_dev *hdev, const char *fwname)
{
+ struct btmtk_hci_wmt_params wmt_params;
const struct firmware *fw;
const u8 *fw_ptr;
size_t fw_size;
int err, dlen;
u8 flag;
- err = request_firmware(&fw, FIRMWARE_MT7622, &hdev->dev);
+ err = request_firmware(&fw, fwname, &hdev->dev);
if (err < 0) {
bt_dev_err(hdev, "Failed to load firmware file (%d)", err);
return err;
@@ -153,6 +262,9 @@ static int mtk_setup_fw(struct hci_dev *hdev)
fw_ptr += 30;
flag = 1;
+ wmt_params.op = MTK_WMT_PATCH_DWNLD;
+ wmt_params.status = NULL;
+
while (fw_size > 0) {
dlen = min_t(int, 250, fw_size);
@@ -162,18 +274,37 @@ static int mtk_setup_fw(struct hci_dev *hdev)
else if (fw_size < fw->size - 30)
flag = 2;
- err = mtk_hci_wmt_sync(hdev, MTK_WMT_PATCH_DWNLD, flag, dlen,
- fw_ptr);
+ wmt_params.flag = flag;
+ wmt_params.dlen = dlen;
+ wmt_params.data = fw_ptr;
+
+ err = mtk_hci_wmt_sync(hdev, &wmt_params);
if (err < 0) {
bt_dev_err(hdev, "Failed to send wmt patch dwnld (%d)",
err);
- break;
+ goto free_fw;
}
fw_size -= dlen;
fw_ptr += dlen;
}
+ wmt_params.op = MTK_WMT_RST;
+ wmt_params.flag = 4;
+ wmt_params.dlen = 0;
+ wmt_params.data = NULL;
+ wmt_params.status = NULL;
+
+ /* Activate funciton the firmware providing to */
+ err = mtk_hci_wmt_sync(hdev, &wmt_params);
+ if (err < 0) {
+ bt_dev_err(hdev, "Failed to send wmt rst (%d)", err);
+ goto free_fw;
+ }
+
+ /* Wait a few moments for firmware activation done */
+ usleep_range(10000, 12000);
+
free_fw:
release_firmware(fw);
return err;
@@ -192,7 +323,20 @@ static int btmtkuart_recv_event(struct hci_dev *hdev, struct sk_buff *skb)
if (hdr->evt == 0xe4)
hdr->evt = HCI_EV_VENDOR;
+ /* When someone waits for the WMT event, the skb is being cloned
+ * and being processed the events from there then.
+ */
+ if (test_bit(BTMTKUART_TX_WAIT_VND_EVT, &bdev->tx_state)) {
+ bdev->evt_skb = skb_clone(skb, GFP_KERNEL);
+ if (!bdev->evt_skb) {
+ err = -ENOMEM;
+ goto err_out;
+ }
+ }
+
err = hci_recv_frame(hdev, skb);
+ if (err < 0)
+ goto err_free_skb;
if (hdr->evt == HCI_EV_VENDOR) {
if (test_and_clear_bit(BTMTKUART_TX_WAIT_VND_EVT,
@@ -203,6 +347,13 @@ static int btmtkuart_recv_event(struct hci_dev *hdev, struct sk_buff *skb)
}
}
+ return 0;
+
+err_free_skb:
+ kfree_skb(bdev->evt_skb);
+ bdev->evt_skb = NULL;
+
+err_out:
return err;
}
@@ -404,6 +555,23 @@ static int btmtkuart_open(struct hci_dev *hdev)
goto err_open;
}
+ if (btmtkuart_is_standalone(bdev)) {
+ if (bdev->curr_speed != bdev->desired_speed)
+ err = serdev_device_set_baudrate(bdev->serdev,
+ 115200);
+ else
+ err = serdev_device_set_baudrate(bdev->serdev,
+ bdev->desired_speed);
+
+ if (err < 0) {
+ bt_dev_err(hdev, "Unable to set baudrate UART device %s",
+ dev_name(&bdev->serdev->dev));
+ goto err_serdev_close;
+ }
+
+ serdev_device_set_flow_control(bdev->serdev, false);
+ }
+
bdev->stp_cursor = 2;
bdev->stp_dlen = 0;
@@ -427,6 +595,8 @@ err_put_rpm:
pm_runtime_put_sync(dev);
err_disable_rpm:
pm_runtime_disable(dev);
+err_serdev_close:
+ serdev_device_close(bdev->serdev);
err_open:
return err;
}
@@ -465,42 +635,222 @@ static int btmtkuart_flush(struct hci_dev *hdev)
return 0;
}
+static int btmtkuart_func_query(struct hci_dev *hdev)
+{
+ struct btmtk_hci_wmt_params wmt_params;
+ int status, err;
+ u8 param = 0;
+
+ /* Query whether the function is enabled */
+ wmt_params.op = MTK_WMT_FUNC_CTRL;
+ wmt_params.flag = 4;
+ wmt_params.dlen = sizeof(param);
+ wmt_params.data = &param;
+ wmt_params.status = &status;
+
+ err = mtk_hci_wmt_sync(hdev, &wmt_params);
+ if (err < 0) {
+ bt_dev_err(hdev, "Failed to query function status (%d)", err);
+ return err;
+ }
+
+ return status;
+}
+
+static int btmtkuart_change_baudrate(struct hci_dev *hdev)
+{
+ struct btmtkuart_dev *bdev = hci_get_drvdata(hdev);
+ struct btmtk_hci_wmt_params wmt_params;
+ u32 baudrate;
+ u8 param;
+ int err;
+
+ /* Indicate the device to enter the probe state the host is
+ * ready to change a new baudrate.
+ */
+ baudrate = cpu_to_le32(bdev->desired_speed);
+ wmt_params.op = MTK_WMT_HIF;
+ wmt_params.flag = 1;
+ wmt_params.dlen = 4;
+ wmt_params.data = &baudrate;
+ wmt_params.status = NULL;
+
+ err = mtk_hci_wmt_sync(hdev, &wmt_params);
+ if (err < 0) {
+ bt_dev_err(hdev, "Failed to device baudrate (%d)", err);
+ return err;
+ }
+
+ err = serdev_device_set_baudrate(bdev->serdev,
+ bdev->desired_speed);
+ if (err < 0) {
+ bt_dev_err(hdev, "Failed to set up host baudrate (%d)",
+ err);
+ return err;
+ }
+
+ serdev_device_set_flow_control(bdev->serdev, false);
+
+ /* Send a dummy byte 0xff to activate the new baudrate */
+ param = 0xff;
+ err = serdev_device_write(bdev->serdev, &param, sizeof(param),
+ MAX_SCHEDULE_TIMEOUT);
+ if (err < 0 || err < sizeof(param))
+ return err;
+
+ serdev_device_wait_until_sent(bdev->serdev, 0);
+
+ /* Wait some time for the device changing baudrate done */
+ usleep_range(20000, 22000);
+
+ /* Test the new baudrate */
+ wmt_params.op = MTK_WMT_TEST;
+ wmt_params.flag = 7;
+ wmt_params.dlen = 0;
+ wmt_params.data = NULL;
+ wmt_params.status = NULL;
+
+ err = mtk_hci_wmt_sync(hdev, &wmt_params);
+ if (err < 0) {
+ bt_dev_err(hdev, "Failed to test new baudrate (%d)",
+ err);
+ return err;
+ }
+
+ bdev->curr_speed = bdev->desired_speed;
+
+ return 0;
+}
+
static int btmtkuart_setup(struct hci_dev *hdev)
{
+ struct btmtkuart_dev *bdev = hci_get_drvdata(hdev);
+ struct btmtk_hci_wmt_params wmt_params;
+ ktime_t calltime, delta, rettime;
+ struct btmtk_tci_sleep tci_sleep;
+ unsigned long long duration;
+ struct sk_buff *skb;
+ int err, status;
u8 param = 0x1;
- int err = 0;
+
+ calltime = ktime_get();
+
+ /* Wakeup MCUSYS is required for certain devices before we start to
+ * do any setups.
+ */
+ if (test_bit(BTMTKUART_REQUIRED_WAKEUP, &bdev->tx_state)) {
+ wmt_params.op = MTK_WMT_WAKEUP;
+ wmt_params.flag = 3;
+ wmt_params.dlen = 0;
+ wmt_params.data = NULL;
+ wmt_params.status = NULL;
+
+ err = mtk_hci_wmt_sync(hdev, &wmt_params);
+ if (err < 0) {
+ bt_dev_err(hdev, "Failed to wakeup the chip (%d)", err);
+ return err;
+ }
+
+ clear_bit(BTMTKUART_REQUIRED_WAKEUP, &bdev->tx_state);
+ }
+
+ if (btmtkuart_is_standalone(bdev))
+ btmtkuart_change_baudrate(hdev);
+
+ /* Query whether the firmware is already download */
+ wmt_params.op = MTK_WMT_SEMAPHORE;
+ wmt_params.flag = 1;
+ wmt_params.dlen = 0;
+ wmt_params.data = NULL;
+ wmt_params.status = &status;
+
+ err = mtk_hci_wmt_sync(hdev, &wmt_params);
+ if (err < 0) {
+ bt_dev_err(hdev, "Failed to query firmware status (%d)", err);
+ return err;
+ }
+
+ if (status == BTMTK_WMT_PATCH_DONE) {
+ bt_dev_info(hdev, "Firmware already downloaded");
+ goto ignore_setup_fw;
+ }
/* Setup a firmware which the device definitely requires */
- err = mtk_setup_fw(hdev);
+ err = mtk_setup_firmware(hdev, bdev->data->fwname);
if (err < 0)
return err;
- /* Activate function the firmware providing to */
- err = mtk_hci_wmt_sync(hdev, MTK_WMT_RST, 0x4, 0, 0);
- if (err < 0) {
- bt_dev_err(hdev, "Failed to send wmt rst (%d)", err);
+ignore_setup_fw:
+ /* Query whether the device is already enabled */
+ err = readx_poll_timeout(btmtkuart_func_query, hdev, status,
+ status < 0 || status != BTMTK_WMT_ON_PROGRESS,
+ 2000, 5000000);
+ /* -ETIMEDOUT happens */
+ if (err < 0)
return err;
+
+ /* The other errors happen in btusb_mtk_func_query */
+ if (status < 0)
+ return status;
+
+ if (status == BTMTK_WMT_ON_DONE) {
+ bt_dev_info(hdev, "function already on");
+ goto ignore_func_on;
}
/* Enable Bluetooth protocol */
- err = mtk_hci_wmt_sync(hdev, MTK_WMT_FUNC_CTRL, 0x0, sizeof(param),
- &param);
+ wmt_params.op = MTK_WMT_FUNC_CTRL;
+ wmt_params.flag = 0;
+ wmt_params.dlen = sizeof(param);
+ wmt_params.data = &param;
+ wmt_params.status = NULL;
+
+ err = mtk_hci_wmt_sync(hdev, &wmt_params);
if (err < 0) {
bt_dev_err(hdev, "Failed to send wmt func ctrl (%d)", err);
return err;
}
+ignore_func_on:
+ /* Apply the low power environment setup */
+ tci_sleep.mode = 0x5;
+ tci_sleep.duration = cpu_to_le16(0x640);
+ tci_sleep.host_duration = cpu_to_le16(0x640);
+ tci_sleep.host_wakeup_pin = 0;
+ tci_sleep.time_compensation = 0;
+
+ skb = __hci_cmd_sync(hdev, 0xfc7a, sizeof(tci_sleep), &tci_sleep,
+ HCI_INIT_TIMEOUT);
+ if (IS_ERR(skb)) {
+ err = PTR_ERR(skb);
+ bt_dev_err(hdev, "Failed to apply low power setting (%d)", err);
+ return err;
+ }
+ kfree_skb(skb);
+
+ rettime = ktime_get();
+ delta = ktime_sub(rettime, calltime);
+ duration = (unsigned long long)ktime_to_ns(delta) >> 10;
+
+ bt_dev_info(hdev, "Device setup in %llu usecs", duration);
+
return 0;
}
static int btmtkuart_shutdown(struct hci_dev *hdev)
{
+ struct btmtk_hci_wmt_params wmt_params;
u8 param = 0x0;
int err;
/* Disable the device */
- err = mtk_hci_wmt_sync(hdev, MTK_WMT_FUNC_CTRL, 0x0, sizeof(param),
- &param);
+ wmt_params.op = MTK_WMT_FUNC_CTRL;
+ wmt_params.flag = 0;
+ wmt_params.dlen = sizeof(param);
+ wmt_params.data = &param;
+ wmt_params.status = NULL;
+
+ err = mtk_hci_wmt_sync(hdev, &wmt_params);
if (err < 0) {
bt_dev_err(hdev, "Failed to send wmt func ctrl (%d)", err);
return err;
@@ -543,24 +893,82 @@ static int btmtkuart_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
return 0;
}
+static int btmtkuart_parse_dt(struct serdev_device *serdev)
+{
+ struct btmtkuart_dev *bdev = serdev_device_get_drvdata(serdev);
+ struct device_node *node = serdev->dev.of_node;
+ u32 speed = 921600;
+ int err;
+
+ if (btmtkuart_is_standalone(bdev)) {
+ of_property_read_u32(node, "current-speed", &speed);
+
+ bdev->desired_speed = speed;
+
+ bdev->vcc = devm_regulator_get(&serdev->dev, "vcc");
+ if (IS_ERR(bdev->vcc)) {
+ err = PTR_ERR(bdev->vcc);
+ return err;
+ }
+
+ bdev->pinctrl = devm_pinctrl_get(&serdev->dev);
+ if (IS_ERR(bdev->pinctrl)) {
+ err = PTR_ERR(bdev->pinctrl);
+ return err;
+ }
+
+ bdev->pins_boot = pinctrl_lookup_state(bdev->pinctrl,
+ "default");
+ if (IS_ERR(bdev->pins_boot)) {
+ err = PTR_ERR(bdev->pins_boot);
+ return err;
+ }
+
+ bdev->pins_runtime = pinctrl_lookup_state(bdev->pinctrl,
+ "runtime");
+ if (IS_ERR(bdev->pins_runtime)) {
+ err = PTR_ERR(bdev->pins_runtime);
+ return err;
+ }
+
+ bdev->reset = devm_gpiod_get_optional(&serdev->dev, "reset",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(bdev->reset)) {
+ err = PTR_ERR(bdev->reset);
+ return err;
+ }
+ } else if (btmtkuart_is_builtin_soc(bdev)) {
+ bdev->clk = devm_clk_get(&serdev->dev, "ref");
+ if (IS_ERR(bdev->clk))
+ return PTR_ERR(bdev->clk);
+ }
+
+ return 0;
+}
+
static int btmtkuart_probe(struct serdev_device *serdev)
{
struct btmtkuart_dev *bdev;
struct hci_dev *hdev;
+ int err;
bdev = devm_kzalloc(&serdev->dev, sizeof(*bdev), GFP_KERNEL);
if (!bdev)
return -ENOMEM;
- bdev->clk = devm_clk_get(&serdev->dev, "ref");
- if (IS_ERR(bdev->clk))
- return PTR_ERR(bdev->clk);
+ bdev->data = of_device_get_match_data(&serdev->dev);
+ if (!bdev->data)
+ return -ENODEV;
bdev->serdev = serdev;
serdev_device_set_drvdata(serdev, bdev);
serdev_device_set_client_ops(serdev, &btmtkuart_client_ops);
+ err = btmtkuart_parse_dt(serdev);
+ if (err < 0)
+ return err;
+
INIT_WORK(&bdev->tx_work, btmtkuart_tx_work);
skb_queue_head_init(&bdev->txq);
@@ -587,13 +995,54 @@ static int btmtkuart_probe(struct serdev_device *serdev)
hdev->manufacturer = 70;
set_bit(HCI_QUIRK_NON_PERSISTENT_SETUP, &hdev->quirks);
- if (hci_register_dev(hdev) < 0) {
+ if (btmtkuart_is_standalone(bdev)) {
+ /* Switch to the specific pin state for the booting requires */
+ pinctrl_select_state(bdev->pinctrl, bdev->pins_boot);
+
+ /* Power on */
+ err = regulator_enable(bdev->vcc);
+ if (err < 0)
+ return err;
+
+ /* Reset if the reset-gpios is available otherwise the board
+ * -level design should be guaranteed.
+ */
+ if (bdev->reset) {
+ gpiod_set_value_cansleep(bdev->reset, 1);
+ usleep_range(1000, 2000);
+ gpiod_set_value_cansleep(bdev->reset, 0);
+ }
+
+ /* Wait some time until device got ready and switch to the pin
+ * mode the device requires for UART transfers.
+ */
+ msleep(50);
+ pinctrl_select_state(bdev->pinctrl, bdev->pins_runtime);
+
+ /* A standalone device doesn't depends on power domain on SoC,
+ * so mark it as no callbacks.
+ */
+ pm_runtime_no_callbacks(&serdev->dev);
+
+ set_bit(BTMTKUART_REQUIRED_WAKEUP, &bdev->tx_state);
+ }
+
+ err = hci_register_dev(hdev);
+ if (err < 0) {
dev_err(&serdev->dev, "Can't register HCI device\n");
hci_free_dev(hdev);
- return -ENODEV;
+ goto err_regulator_disable;
}
return 0;
+
+err_regulator_disable:
+ if (btmtkuart_is_standalone(bdev)) {
+ pinctrl_select_state(bdev->pinctrl, bdev->pins_boot);
+ regulator_disable(bdev->vcc);
+ }
+
+ return err;
}
static void btmtkuart_remove(struct serdev_device *serdev)
@@ -601,13 +1050,34 @@ static void btmtkuart_remove(struct serdev_device *serdev)
struct btmtkuart_dev *bdev = serdev_device_get_drvdata(serdev);
struct hci_dev *hdev = bdev->hdev;
+ if (btmtkuart_is_standalone(bdev)) {
+ pinctrl_select_state(bdev->pinctrl, bdev->pins_boot);
+ regulator_disable(bdev->vcc);
+ }
+
hci_unregister_dev(hdev);
hci_free_dev(hdev);
}
+static const struct btmtkuart_data mt7622_data = {
+ .fwname = FIRMWARE_MT7622,
+};
+
+static const struct btmtkuart_data mt7663_data = {
+ .flags = BTMTKUART_FLAG_STANDALONE_HW,
+ .fwname = FIRMWARE_MT7663,
+};
+
+static const struct btmtkuart_data mt7668_data = {
+ .flags = BTMTKUART_FLAG_STANDALONE_HW,
+ .fwname = FIRMWARE_MT7668,
+};
+
#ifdef CONFIG_OF
static const struct of_device_id mtk_of_match_table[] = {
- { .compatible = "mediatek,mt7622-bluetooth"},
+ { .compatible = "mediatek,mt7622-bluetooth", .data = &mt7622_data},
+ { .compatible = "mediatek,mt7663u-bluetooth", .data = &mt7663_data},
+ { .compatible = "mediatek,mt7668u-bluetooth", .data = &mt7668_data},
{ }
};
MODULE_DEVICE_TABLE(of, mtk_of_match_table);
@@ -629,3 +1099,5 @@ MODULE_DESCRIPTION("MediaTek Bluetooth Serial driver ver " VERSION);
MODULE_VERSION(VERSION);
MODULE_LICENSE("GPL");
MODULE_FIRMWARE(FIRMWARE_MT7622);
+MODULE_FIRMWARE(FIRMWARE_MT7663);
+MODULE_FIRMWARE(FIRMWARE_MT7668);
diff --git a/drivers/bluetooth/btqca.c b/drivers/bluetooth/btqca.c
index ec9e03a6b778..612268574fc7 100644
--- a/drivers/bluetooth/btqca.c
+++ b/drivers/bluetooth/btqca.c
@@ -391,6 +391,25 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
}
EXPORT_SYMBOL_GPL(qca_uart_setup);
+int qca_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr)
+{
+ struct sk_buff *skb;
+ int err;
+
+ skb = __hci_cmd_sync_ev(hdev, EDL_WRITE_BD_ADDR_OPCODE, 6, bdaddr,
+ HCI_EV_VENDOR, HCI_INIT_TIMEOUT);
+ if (IS_ERR(skb)) {
+ err = PTR_ERR(skb);
+ bt_dev_err(hdev, "QCA Change address cmd failed (%d)", err);
+ return err;
+ }
+
+ kfree_skb(skb);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(qca_set_bdaddr);
+
MODULE_AUTHOR("Ben Young Tae Kim <ytkim@qca.qualcomm.com>");
MODULE_DESCRIPTION("Bluetooth support for Qualcomm Atheros family ver " VERSION);
MODULE_VERSION(VERSION);
diff --git a/drivers/bluetooth/btqca.h b/drivers/bluetooth/btqca.h
index 0c01f375fe83..c72c56ea7480 100644
--- a/drivers/bluetooth/btqca.h
+++ b/drivers/bluetooth/btqca.h
@@ -20,6 +20,7 @@
#define EDL_PATCH_CMD_OPCODE (0xFC00)
#define EDL_NVM_ACCESS_OPCODE (0xFC0B)
+#define EDL_WRITE_BD_ADDR_OPCODE (0xFC14)
#define EDL_PATCH_CMD_LEN (1)
#define EDL_PATCH_VER_REQ_CMD (0x19)
#define EDL_PATCH_TLV_REQ_CMD (0x1E)
@@ -140,7 +141,7 @@ int qca_set_bdaddr_rome(struct hci_dev *hdev, const bdaddr_t *bdaddr);
int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
enum qca_btsoc_type soc_type, u32 soc_ver);
int qca_read_soc_version(struct hci_dev *hdev, u32 *soc_version);
-
+int qca_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr);
#else
static inline int qca_set_bdaddr_rome(struct hci_dev *hdev, const bdaddr_t *bdaddr)
@@ -159,4 +160,9 @@ static inline int qca_read_soc_version(struct hci_dev *hdev, u32 *soc_version)
return -EOPNOTSUPP;
}
+static inline int qca_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr)
+{
+ return -EOPNOTSUPP;
+}
+
#endif
diff --git a/drivers/bluetooth/btqcomsmd.c b/drivers/bluetooth/btqcomsmd.c
index 7df3eed1ef5e..e0d4c6f1d3ab 100644
--- a/drivers/bluetooth/btqcomsmd.c
+++ b/drivers/bluetooth/btqcomsmd.c
@@ -28,7 +28,6 @@
struct btqcomsmd {
struct hci_dev *hdev;
- bdaddr_t bdaddr;
struct rpmsg_endpoint *acl_channel;
struct rpmsg_endpoint *cmd_channel;
};
@@ -116,32 +115,17 @@ static int btqcomsmd_close(struct hci_dev *hdev)
static int btqcomsmd_setup(struct hci_dev *hdev)
{
- struct btqcomsmd *btq = hci_get_drvdata(hdev);
struct sk_buff *skb;
- int err;
skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT);
if (IS_ERR(skb))
return PTR_ERR(skb);
kfree_skb(skb);
- /* Devices do not have persistent storage for BD address. If no
- * BD address has been retrieved during probe, mark the device
- * as having an invalid BD address.
- */
- if (!bacmp(&btq->bdaddr, BDADDR_ANY)) {
- set_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks);
- return 0;
- }
-
- /* When setting a configured BD address fails, mark the device
- * as having an invalid BD address.
+ /* Devices do not have persistent storage for BD address. Retrieve
+ * it from the firmware node property.
*/
- err = qca_set_bdaddr_rome(hdev, &btq->bdaddr);
- if (err) {
- set_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks);
- return 0;
- }
+ set_bit(HCI_QUIRK_USE_BDADDR_PROPERTY, &hdev->quirks);
return 0;
}
@@ -169,15 +153,6 @@ static int btqcomsmd_probe(struct platform_device *pdev)
if (IS_ERR(btq->cmd_channel))
return PTR_ERR(btq->cmd_channel);
- /* The local-bd-address property is usually injected by the
- * bootloader which has access to the allocated BD address.
- */
- if (!of_property_read_u8_array(pdev->dev.of_node, "local-bd-address",
- (u8 *)&btq->bdaddr, sizeof(bdaddr_t))) {
- dev_info(&pdev->dev, "BD address %pMR retrieved from device-tree",
- &btq->bdaddr);
- }
-
hdev = hci_alloc_dev();
if (!hdev)
return -ENOMEM;
diff --git a/drivers/bluetooth/btrtl.c b/drivers/bluetooth/btrtl.c
index 41405de27d66..c91bba00df4e 100644
--- a/drivers/bluetooth/btrtl.c
+++ b/drivers/bluetooth/btrtl.c
@@ -552,10 +552,9 @@ struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev,
hdev->bus);
if (!btrtl_dev->ic_info) {
- rtl_dev_err(hdev, "rtl: unknown IC info, lmp subver %04x, hci rev %04x, hci ver %04x",
+ rtl_dev_info(hdev, "rtl: unknown IC info, lmp subver %04x, hci rev %04x, hci ver %04x",
lmp_subver, hci_rev, hci_ver);
- ret = -EINVAL;
- goto err_free;
+ return btrtl_dev;
}
if (btrtl_dev->ic_info->has_rom_version) {
@@ -610,6 +609,11 @@ int btrtl_download_firmware(struct hci_dev *hdev,
* standard btusb. Once that firmware is uploaded, the subver changes
* to a different value.
*/
+ if (!btrtl_dev->ic_info) {
+ rtl_dev_info(hdev, "rtl: assuming no firmware upload needed\n");
+ return 0;
+ }
+
switch (btrtl_dev->ic_info->lmp_subver) {
case RTL_ROM_LMP_8723A:
case RTL_ROM_LMP_3499:
diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
index 4761499db9ee..ded198328f21 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -29,6 +29,7 @@
#include <linux/of_device.h>
#include <linux/of_irq.h>
#include <linux/suspend.h>
+#include <linux/gpio/consumer.h>
#include <asm/unaligned.h>
#include <net/bluetooth/bluetooth.h>
@@ -439,6 +440,7 @@ static const struct dmi_system_id btusb_needs_reset_resume_table[] = {
#define BTUSB_BOOTING 9
#define BTUSB_DIAG_RUNNING 10
#define BTUSB_OOB_WAKE_ENABLED 11
+#define BTUSB_HW_RESET_ACTIVE 12
struct btusb_data {
struct hci_dev *hdev;
@@ -476,6 +478,8 @@ struct btusb_data {
struct usb_endpoint_descriptor *diag_tx_ep;
struct usb_endpoint_descriptor *diag_rx_ep;
+ struct gpio_desc *reset_gpio;
+
__u8 cmdreq_type;
__u8 cmdreq;
@@ -489,8 +493,41 @@ struct btusb_data {
int (*setup_on_usb)(struct hci_dev *hdev);
int oob_wake_irq; /* irq for out-of-band wake-on-bt */
+ unsigned cmd_timeout_cnt;
};
+
+static void btusb_intel_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 way to reset. Ignoring and continuing");
+ return;
+ }
+
+ /*
+ * Toggle the hard reset line if the platform provides one. The reset
+ * is going to yank the device off the USB and then replug. So doing
+ * once is enough. 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, "Initiating HW reset via gpio");
+ gpiod_set_value_cansleep(reset_gpio, 1);
+ msleep(100);
+ gpiod_set_value_cansleep(reset_gpio, 0);
+}
+
static inline void btusb_free_frags(struct btusb_data *data)
{
unsigned long flags;
@@ -2397,6 +2434,24 @@ static int btusb_shutdown_intel(struct hci_dev *hdev)
return 0;
}
+static int btusb_shutdown_intel_new(struct hci_dev *hdev)
+{
+ struct sk_buff *skb;
+
+ /* Send HCI Reset to the controller to stop any BT activity which
+ * were triggered. This will help to save power and maintain the
+ * sync b/w Host and controller
+ */
+ skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT);
+ if (IS_ERR(skb)) {
+ bt_dev_err(hdev, "HCI reset during shutdown failed");
+ return PTR_ERR(skb);
+ }
+ kfree_skb(skb);
+
+ return 0;
+}
+
#ifdef CONFIG_PM
/* Configure an out-of-band gpio as wake-up pin, if specified in device tree */
static int marvell_config_oob_wake(struct hci_dev *hdev)
@@ -2862,6 +2917,8 @@ static irqreturn_t btusb_oob_wake_handler(int irq, void *priv)
static const struct of_device_id btusb_match_table[] = {
{ .compatible = "usb1286,204e" },
+ { .compatible = "usbcf3,e300" }, /* QCA6174A */
+ { .compatible = "usb4ca,301a" }, /* QCA6174A (Lite-On) */
{ }
};
MODULE_DEVICE_TABLE(of, btusb_match_table);
@@ -2915,6 +2972,7 @@ static int btusb_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
struct usb_endpoint_descriptor *ep_desc;
+ struct gpio_desc *reset_gpio;
struct btusb_data *data;
struct hci_dev *hdev;
unsigned ifnum_base;
@@ -3028,6 +3086,15 @@ static int btusb_probe(struct usb_interface *intf,
SET_HCIDEV_DEV(hdev, &intf->dev);
+ reset_gpio = gpiod_get_optional(&data->udev->dev, "reset",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(reset_gpio)) {
+ err = PTR_ERR(reset_gpio);
+ goto out_free_dev;
+ } else if (reset_gpio) {
+ data->reset_gpio = reset_gpio;
+ }
+
hdev->open = btusb_open;
hdev->close = btusb_close;
hdev->flush = btusb_flush;
@@ -3082,6 +3149,7 @@ static int btusb_probe(struct usb_interface *intf,
hdev->shutdown = btusb_shutdown_intel;
hdev->set_diag = btintel_set_diag_mfg;
hdev->set_bdaddr = btintel_set_bdaddr;
+ hdev->cmd_timeout = btusb_intel_cmd_timeout;
set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks);
set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks);
set_bit(HCI_QUIRK_NON_PERSISTENT_DIAG, &hdev->quirks);
@@ -3091,9 +3159,11 @@ static int btusb_probe(struct usb_interface *intf,
hdev->manufacturer = 2;
hdev->send = btusb_send_frame_intel;
hdev->setup = btusb_setup_intel_new;
+ hdev->shutdown = btusb_shutdown_intel_new;
hdev->hw_error = btintel_hw_error;
hdev->set_diag = btintel_set_diag;
hdev->set_bdaddr = btintel_set_bdaddr;
+ hdev->cmd_timeout = btusb_intel_cmd_timeout;
set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks);
set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks);
set_bit(HCI_QUIRK_NON_PERSISTENT_DIAG, &hdev->quirks);
@@ -3226,6 +3296,8 @@ static int btusb_probe(struct usb_interface *intf,
return 0;
out_free_dev:
+ if (data->reset_gpio)
+ gpiod_put(data->reset_gpio);
hci_free_dev(hdev);
return err;
}
@@ -3269,6 +3341,9 @@ static void btusb_disconnect(struct usb_interface *intf)
if (data->oob_wake_irq)
device_init_wakeup(&data->udev->dev, false);
+ if (data->reset_gpio)
+ gpiod_put(data->reset_gpio);
+
hci_free_dev(hdev);
}
diff --git a/drivers/bluetooth/h4_recv.h b/drivers/bluetooth/h4_recv.h
index b432651f8236..87ccaceadba7 100644
--- a/drivers/bluetooth/h4_recv.h
+++ b/drivers/bluetooth/h4_recv.h
@@ -60,12 +60,13 @@ static inline struct sk_buff *h4_recv_buf(struct hci_dev *hdev,
const struct h4_recv_pkt *pkts,
int pkts_count)
{
+ /* Check for error from previous call */
+ if (IS_ERR(skb))
+ skb = NULL;
+
while (count) {
int i, len;
- if (!count)
- break;
-
if (!skb) {
for (i = 0; i < pkts_count; i++) {
if (buffer[0] != (&pkts[i])->type)
diff --git a/drivers/bluetooth/hci_h4.c b/drivers/bluetooth/hci_h4.c
index fb97a3bf069b..5d97d77627c1 100644
--- a/drivers/bluetooth/hci_h4.c
+++ b/drivers/bluetooth/hci_h4.c
@@ -174,6 +174,10 @@ struct sk_buff *h4_recv_buf(struct hci_dev *hdev, struct sk_buff *skb,
struct hci_uart *hu = hci_get_drvdata(hdev);
u8 alignment = hu->alignment ? hu->alignment : 1;
+ /* Check for error from previous call */
+ if (IS_ERR(skb))
+ skb = NULL;
+
while (count) {
int i, len;
diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c
index fbf7b4df23ab..9562e72c1ae5 100644
--- a/drivers/bluetooth/hci_ldisc.c
+++ b/drivers/bluetooth/hci_ldisc.c
@@ -207,11 +207,11 @@ void hci_uart_init_work(struct work_struct *work)
err = hci_register_dev(hu->hdev);
if (err < 0) {
BT_ERR("Can't register HCI device");
+ clear_bit(HCI_UART_PROTO_READY, &hu->flags);
+ hu->proto->close(hu);
hdev = hu->hdev;
hu->hdev = NULL;
hci_free_dev(hdev);
- clear_bit(HCI_UART_PROTO_READY, &hu->flags);
- hu->proto->close(hu);
return;
}
@@ -616,6 +616,7 @@ static void hci_uart_tty_receive(struct tty_struct *tty, const u8 *data,
static int hci_uart_register_dev(struct hci_uart *hu)
{
struct hci_dev *hdev;
+ int err;
BT_DBG("");
@@ -659,11 +660,22 @@ static int hci_uart_register_dev(struct hci_uart *hu)
else
hdev->dev_type = HCI_PRIMARY;
+ /* Only call open() for the protocol after hdev is fully initialized as
+ * open() (or a timer/workqueue it starts) may attempt to reference it.
+ */
+ err = hu->proto->open(hu);
+ if (err) {
+ hu->hdev = NULL;
+ hci_free_dev(hdev);
+ return err;
+ }
+
if (test_bit(HCI_UART_INIT_PENDING, &hu->hdev_flags))
return 0;
if (hci_register_dev(hdev) < 0) {
BT_ERR("Can't register HCI device");
+ hu->proto->close(hu);
hu->hdev = NULL;
hci_free_dev(hdev);
return -ENODEV;
@@ -683,20 +695,14 @@ static int hci_uart_set_proto(struct hci_uart *hu, int id)
if (!p)
return -EPROTONOSUPPORT;
- err = p->open(hu);
- if (err)
- return err;
-
hu->proto = p;
- set_bit(HCI_UART_PROTO_READY, &hu->flags);
err = hci_uart_register_dev(hu);
if (err) {
- clear_bit(HCI_UART_PROTO_READY, &hu->flags);
- p->close(hu);
return err;
}
+ set_bit(HCI_UART_PROTO_READY, &hu->flags);
return 0;
}
diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c
index f036c8f98ea3..237aea34b69f 100644
--- a/drivers/bluetooth/hci_qca.c
+++ b/drivers/bluetooth/hci_qca.c
@@ -59,7 +59,7 @@
#define IBS_WAKE_RETRANS_TIMEOUT_MS 100
#define IBS_TX_IDLE_TIMEOUT_MS 2000
-#define BAUDRATE_SETTLE_TIMEOUT_MS 300
+#define CMD_TRANS_TIMEOUT_MS 100
/* susclk rate */
#define SUSCLK_RATE_32KHZ 32768
@@ -770,16 +770,17 @@ static int qca_enqueue(struct hci_uart *hu, struct sk_buff *skb)
/* Prepend skb with frame type */
memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1);
+ spin_lock_irqsave(&qca->hci_ibs_lock, flags);
+
/* Don't go to sleep in middle of patch download or
* Out-Of-Band(GPIOs control) sleep is selected.
*/
if (!test_bit(STATE_IN_BAND_SLEEP_ENABLED, &qca->flags)) {
skb_queue_tail(&qca->txq, skb);
+ spin_unlock_irqrestore(&qca->hci_ibs_lock, flags);
return 0;
}
- spin_lock_irqsave(&qca->hci_ibs_lock, flags);
-
/* Act according to current state */
switch (qca->tx_ibs_state) {
case HCI_IBS_TX_AWAKE:
@@ -962,8 +963,8 @@ static int qca_set_baudrate(struct hci_dev *hdev, uint8_t baudrate)
{
struct hci_uart *hu = hci_get_drvdata(hdev);
struct qca_data *qca = hu->priv;
- struct sk_buff *skb;
struct qca_serdev *qcadev;
+ struct sk_buff *skb;
u8 cmd[] = { 0x01, 0x48, 0xFC, 0x01, 0x00 };
if (baudrate > QCA_BAUDRATE_3200000)
@@ -977,13 +978,6 @@ static int qca_set_baudrate(struct hci_dev *hdev, uint8_t baudrate)
return -ENOMEM;
}
- /* Disabling hardware flow control is mandatory while
- * sending change baudrate request to wcn3990 SoC.
- */
- qcadev = serdev_device_get_drvdata(hu->serdev);
- if (qcadev->btsoc_type == QCA_WCN3990)
- hci_uart_set_flow_control(hu, true);
-
/* Assign commands to change baudrate and packet type. */
skb_put_data(skb, cmd, sizeof(cmd));
hci_skb_pkt_type(skb) = HCI_COMMAND_PKT;
@@ -991,16 +985,21 @@ static int qca_set_baudrate(struct hci_dev *hdev, uint8_t baudrate)
skb_queue_tail(&qca->txq, skb);
hci_uart_tx_wakeup(hu);
- /* wait 300ms to change new baudrate on controller side
- * controller will come back after they receive this HCI command
- * then host can communicate with new baudrate to controller
- */
- set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout(msecs_to_jiffies(BAUDRATE_SETTLE_TIMEOUT_MS));
- set_current_state(TASK_RUNNING);
+ qcadev = serdev_device_get_drvdata(hu->serdev);
+
+ /* Wait for the baudrate change request to be sent */
+
+ while (!skb_queue_empty(&qca->txq))
+ usleep_range(100, 200);
+ serdev_device_wait_until_sent(hu->serdev,
+ msecs_to_jiffies(CMD_TRANS_TIMEOUT_MS));
+
+ /* Give the controller time to process the request */
if (qcadev->btsoc_type == QCA_WCN3990)
- hci_uart_set_flow_control(hu, false);
+ msleep(10);
+ else
+ msleep(300);
return 0;
}
@@ -1013,11 +1012,11 @@ static inline void host_set_baudrate(struct hci_uart *hu, unsigned int speed)
hci_uart_set_baudrate(hu, speed);
}
-static int qca_send_power_pulse(struct hci_dev *hdev, u8 cmd)
+static int qca_send_power_pulse(struct hci_uart *hu, bool on)
{
- struct hci_uart *hu = hci_get_drvdata(hdev);
- struct qca_data *qca = hu->priv;
- struct sk_buff *skb;
+ int ret;
+ int timeout = msecs_to_jiffies(CMD_TRANS_TIMEOUT_MS);
+ u8 cmd = on ? QCA_WCN3990_POWERON_PULSE : QCA_WCN3990_POWEROFF_PULSE;
/* These power pulses are single byte command which are sent
* at required baudrate to wcn3990. On wcn3990, we have an external
@@ -1029,24 +1028,25 @@ static int qca_send_power_pulse(struct hci_dev *hdev, u8 cmd)
* save power. Disabling hardware flow control is mandatory while
* sending power pulses to SoC.
*/
- bt_dev_dbg(hdev, "sending power pulse %02x to SoC", cmd);
-
- skb = bt_skb_alloc(sizeof(cmd), GFP_KERNEL);
- if (!skb)
- return -ENOMEM;
+ bt_dev_dbg(hu->hdev, "sending power pulse %02x to controller", cmd);
+ serdev_device_write_flush(hu->serdev);
hci_uart_set_flow_control(hu, true);
+ ret = serdev_device_write_buf(hu->serdev, &cmd, sizeof(cmd));
+ if (ret < 0) {
+ bt_dev_err(hu->hdev, "failed to send power pulse %02x", cmd);
+ return ret;
+ }
- skb_put_u8(skb, cmd);
- hci_skb_pkt_type(skb) = HCI_COMMAND_PKT;
-
- skb_queue_tail(&qca->txq, skb);
- hci_uart_tx_wakeup(hu);
-
- /* Wait for 100 uS for SoC to settle down */
- usleep_range(100, 200);
+ serdev_device_wait_until_sent(hu->serdev, timeout);
hci_uart_set_flow_control(hu, false);
+ /* Give to controller time to boot/shutdown */
+ if (on)
+ msleep(100);
+ else
+ msleep(10);
+
return 0;
}
@@ -1091,7 +1091,8 @@ static int qca_check_speeds(struct hci_uart *hu)
static int qca_set_speed(struct hci_uart *hu, enum qca_speed_type speed_type)
{
unsigned int speed, qca_baudrate;
- int ret;
+ struct qca_serdev *qcadev;
+ int ret = 0;
if (speed_type == QCA_INIT_SPEED) {
speed = qca_get_speed(hu, QCA_INIT_SPEED);
@@ -1102,21 +1103,31 @@ static int qca_set_speed(struct hci_uart *hu, enum qca_speed_type speed_type)
if (!speed)
return 0;
+ /* Disable flow control for wcn3990 to deassert RTS while
+ * changing the baudrate of chip and host.
+ */
+ qcadev = serdev_device_get_drvdata(hu->serdev);
+ if (qcadev->btsoc_type == QCA_WCN3990)
+ hci_uart_set_flow_control(hu, true);
+
qca_baudrate = qca_get_baudrate_value(speed);
bt_dev_dbg(hu->hdev, "Set UART speed to %d", speed);
ret = qca_set_baudrate(hu->hdev, qca_baudrate);
if (ret)
- return ret;
+ goto error;
host_set_baudrate(hu, speed);
+
+error:
+ if (qcadev->btsoc_type == QCA_WCN3990)
+ hci_uart_set_flow_control(hu, false);
}
- return 0;
+ return ret;
}
static int qca_wcn3990_init(struct hci_uart *hu)
{
- struct hci_dev *hdev = hu->hdev;
struct qca_serdev *qcadev;
int ret;
@@ -1139,18 +1150,15 @@ static int qca_wcn3990_init(struct hci_uart *hu)
/* Forcefully enable wcn3990 to enter in to boot mode. */
host_set_baudrate(hu, 2400);
- ret = qca_send_power_pulse(hdev, QCA_WCN3990_POWEROFF_PULSE);
+ ret = qca_send_power_pulse(hu, false);
if (ret)
return ret;
qca_set_speed(hu, QCA_INIT_SPEED);
- ret = qca_send_power_pulse(hdev, QCA_WCN3990_POWERON_PULSE);
+ ret = qca_send_power_pulse(hu, true);
if (ret)
return ret;
- /* Wait for 100 ms for SoC to boot */
- msleep(100);
-
/* Now the device is in ready state to communicate with host.
* To sync host with device we need to reopen port.
* Without this, we will have RTS and CTS synchronization
@@ -1193,6 +1201,7 @@ static int qca_setup(struct hci_uart *hu)
* setup for every hci up.
*/
set_bit(HCI_QUIRK_NON_PERSISTENT_SETUP, &hdev->quirks);
+ set_bit(HCI_QUIRK_USE_BDADDR_PROPERTY, &hdev->quirks);
hu->hdev->shutdown = qca_power_off;
ret = qca_wcn3990_init(hu);
if (ret)
@@ -1241,7 +1250,10 @@ static int qca_setup(struct hci_uart *hu)
}
/* Setup bdaddr */
- hu->hdev->set_bdaddr = qca_set_bdaddr_rome;
+ if (qcadev->btsoc_type == QCA_WCN3990)
+ hu->hdev->set_bdaddr = qca_set_bdaddr;
+ else
+ hu->hdev->set_bdaddr = qca_set_bdaddr_rome;
return ret;
}
@@ -1274,13 +1286,20 @@ static const struct qca_vreg_data qca_soc_data = {
static void qca_power_shutdown(struct hci_uart *hu)
{
- struct serdev_device *serdev = hu->serdev;
- unsigned char cmd = QCA_WCN3990_POWEROFF_PULSE;
+ struct qca_data *qca = hu->priv;
+ unsigned long flags;
+
+ /* From this point we go into power off state. But serial port is
+ * still open, stop queueing the IBS data and flush all the buffered
+ * data in skb's.
+ */
+ spin_lock_irqsave(&qca->hci_ibs_lock, flags);
+ clear_bit(STATE_IN_BAND_SLEEP_ENABLED, &qca->flags);
+ qca_flush(hu);
+ spin_unlock_irqrestore(&qca->hci_ibs_lock, flags);
host_set_baudrate(hu, 2400);
- hci_uart_set_flow_control(hu, true);
- serdev_device_write_buf(serdev, &cmd, sizeof(cmd));
- hci_uart_set_flow_control(hu, false);
+ qca_send_power_pulse(hu, false);
qca_power_setup(hu, false);
}