From 34682110abc50ffea7e002b0c2fd7ea9e0000ccc Mon Sep 17 00:00:00 2001 From: Max Chou Date: Wed, 27 Nov 2019 11:01:07 +0800 Subject: Bluetooth: btusb: Edit the logical value for Realtek Bluetooth reset It should be pull low and pull high on the physical line for the Realtek Bluetooth reset. gpiod_set_value_cansleep() takes ACTIVE_LOW status for the logical value settings, so the original commit should be corrected. Signed-off-by: Max Chou Signed-off-by: Marcel Holtmann --- drivers/bluetooth/btusb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/bluetooth') diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 70e385987d41..82fb2e7b2892 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -552,9 +552,9 @@ static void btusb_rtl_cmd_timeout(struct hci_dev *hdev) } 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); + msleep(200); + gpiod_set_value_cansleep(reset_gpio, 0); } static inline void btusb_free_frags(struct btusb_data *data) -- cgit v1.2.3 From 5d6f391073d5c1c903ac12be72c66b96b2ae93f4 Mon Sep 17 00:00:00 2001 From: Abhishek Pandit-Subedi Date: Tue, 26 Nov 2019 08:17:29 +0100 Subject: Bluetooth: hci_bcm: Disallow set_baudrate for BCM4354 Without updating the patchram, the BCM4354 does not support a higher operating speed. The normal bcm_setup follows the correct order (init_speed, patchram and then oper_speed) but the serdev driver will set the operating speed before calling the hu->setup function. Thus, for the BCM4354, don't set the operating speed before patchram. Signed-off-by: Abhishek Pandit-Subedi Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- drivers/bluetooth/hci_bcm.c | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) (limited to 'drivers/bluetooth') diff --git a/drivers/bluetooth/hci_bcm.c b/drivers/bluetooth/hci_bcm.c index d2a6a4afdbbb..0200eff12bc9 100644 --- a/drivers/bluetooth/hci_bcm.c +++ b/drivers/bluetooth/hci_bcm.c @@ -47,6 +47,14 @@ #define BCM_NUM_SUPPLIES 2 +/** + * struct bcm_device_data - device specific data + * @no_early_set_baudrate: Disallow set baudrate before driver setup() + */ +struct bcm_device_data { + bool no_early_set_baudrate; +}; + /** * struct bcm_device - device driver resources * @serdev_hu: HCI UART controller struct @@ -79,6 +87,7 @@ * @hu: pointer to HCI UART controller struct, * used to disable flow control during runtime suspend and system sleep * @is_suspended: whether flow control is currently disabled + * @no_early_set_baudrate: don't set_baudrate before setup() */ struct bcm_device { /* Must be the first member, hci_serdev.c expects this. */ @@ -112,6 +121,7 @@ struct bcm_device { struct hci_uart *hu; bool is_suspended; #endif + bool no_early_set_baudrate; }; /* generic bcm uart resources */ @@ -447,7 +457,13 @@ out: if (bcm->dev) { hci_uart_set_flow_control(hu, true); hu->init_speed = bcm->dev->init_speed; - hu->oper_speed = bcm->dev->oper_speed; + + /* If oper_speed is set, ldisc/serdev will set the baudrate + * before calling setup() + */ + if (!bcm->dev->no_early_set_baudrate) + hu->oper_speed = bcm->dev->oper_speed; + err = bcm_gpio_set_power(bcm->dev, true); hci_uart_set_flow_control(hu, false); if (err) @@ -565,6 +581,8 @@ static int bcm_setup(struct hci_uart *hu) /* Operational speed if any */ if (hu->oper_speed) speed = hu->oper_speed; + else if (bcm->dev && bcm->dev->oper_speed) + speed = bcm->dev->oper_speed; else if (hu->proto->oper_speed) speed = hu->proto->oper_speed; else @@ -1374,6 +1392,7 @@ static struct platform_driver bcm_driver = { static int bcm_serdev_probe(struct serdev_device *serdev) { struct bcm_device *bcmdev; + const struct bcm_device_data *data; int err; bcmdev = devm_kzalloc(&serdev->dev, sizeof(*bcmdev), GFP_KERNEL); @@ -1408,6 +1427,10 @@ static int bcm_serdev_probe(struct serdev_device *serdev) if (err) dev_err(&serdev->dev, "Failed to power down\n"); + data = device_get_match_data(bcmdev->dev); + if (data) + bcmdev->no_early_set_baudrate = data->no_early_set_baudrate; + return hci_uart_register_device(&bcmdev->serdev_hu, &bcm_proto); } @@ -1419,12 +1442,16 @@ static void bcm_serdev_remove(struct serdev_device *serdev) } #ifdef CONFIG_OF +static struct bcm_device_data bcm4354_device_data = { + .no_early_set_baudrate = true, +}; + static const struct of_device_id bcm_bluetooth_of_match[] = { { .compatible = "brcm,bcm20702a1" }, { .compatible = "brcm,bcm4345c5" }, { .compatible = "brcm,bcm4330-bt" }, { .compatible = "brcm,bcm43438-bt" }, - { .compatible = "brcm,bcm43540-bt" }, + { .compatible = "brcm,bcm43540-bt", .data = &bcm4354_device_data }, { .compatible = "brcm,bcm4335a0" }, { }, }; -- cgit v1.2.3 From 528379902337102b0264fe5343eafb3d6c59fa45 Mon Sep 17 00:00:00 2001 From: Abhishek Pandit-Subedi Date: Tue, 26 Nov 2019 08:17:30 +0100 Subject: Bluetooth: btbcm: Support pcm configuration Add BCM vendor specific command to configure PCM parameters. The new vendor opcode allows us to set the sco routing, the pcm interface rate, and a few other pcm specific options (frame sync, sync mode, and clock mode). See broadcom-bluetooth.txt in Documentation for more information about valid values for those settings. Here is an example trace where this opcode was used to configure a BCM4354: < HCI Command: Vendor (0x3f|0x001c) plen 5 01 02 00 01 01 > HCI Event: Command Complete (0x0e) plen 4 Vendor (0x3f|0x001c) ncmd 1 Status: Success (0x00) We can read back the values as well with ocf 0x001d to confirm the values that were set: $ hcitool cmd 0x3f 0x001d < HCI Command: ogf 0x3f, ocf 0x001d, plen 0 > HCI Event: 0x0e plen 9 01 1D FC 00 01 02 00 01 01 Signed-off-by: Abhishek Pandit-Subedi Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- drivers/bluetooth/btbcm.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/bluetooth/btbcm.h | 16 ++++++++++++++++ 2 files changed, 62 insertions(+) (limited to 'drivers/bluetooth') diff --git a/drivers/bluetooth/btbcm.c b/drivers/bluetooth/btbcm.c index 8e05706fe5d9..0795a49edfae 100644 --- a/drivers/bluetooth/btbcm.c +++ b/drivers/bluetooth/btbcm.c @@ -107,6 +107,52 @@ int btbcm_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr) } EXPORT_SYMBOL_GPL(btbcm_set_bdaddr); +int btbcm_read_pcm_int_params(struct hci_dev *hdev, + struct bcm_set_pcm_int_params *params) +{ + struct sk_buff *skb; + int err = 0; + + skb = __hci_cmd_sync(hdev, 0xfc1d, 0, NULL, HCI_INIT_TIMEOUT); + if (IS_ERR(skb)) { + err = PTR_ERR(skb); + bt_dev_err(hdev, "BCM: Read PCM int params failed (%d)", err); + return err; + } + + if (skb->len != 6 || skb->data[0]) { + bt_dev_err(hdev, "BCM: Read PCM int params length mismatch"); + kfree_skb(skb); + return -EIO; + } + + if (params) + memcpy(params, skb->data + 1, 5); + + kfree_skb(skb); + + return 0; +} +EXPORT_SYMBOL_GPL(btbcm_read_pcm_int_params); + +int btbcm_write_pcm_int_params(struct hci_dev *hdev, + const struct bcm_set_pcm_int_params *params) +{ + struct sk_buff *skb; + int err; + + skb = __hci_cmd_sync(hdev, 0xfc1c, 5, params, HCI_INIT_TIMEOUT); + if (IS_ERR(skb)) { + err = PTR_ERR(skb); + bt_dev_err(hdev, "BCM: Write PCM int params failed (%d)", err); + return err; + } + kfree_skb(skb); + + return 0; +} +EXPORT_SYMBOL_GPL(btbcm_write_pcm_int_params); + int btbcm_patchram(struct hci_dev *hdev, const struct firmware *fw) { const struct hci_command_hdr *cmd; diff --git a/drivers/bluetooth/btbcm.h b/drivers/bluetooth/btbcm.h index d204be8a84bf..3c7dd0765837 100644 --- a/drivers/bluetooth/btbcm.h +++ b/drivers/bluetooth/btbcm.h @@ -54,6 +54,10 @@ struct bcm_set_pcm_format_params { int btbcm_check_bdaddr(struct hci_dev *hdev); int btbcm_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr); int btbcm_patchram(struct hci_dev *hdev, const struct firmware *fw); +int btbcm_read_pcm_int_params(struct hci_dev *hdev, + struct bcm_set_pcm_int_params *params); +int btbcm_write_pcm_int_params(struct hci_dev *hdev, + const struct bcm_set_pcm_int_params *params); int btbcm_setup_patchram(struct hci_dev *hdev); int btbcm_setup_apple(struct hci_dev *hdev); @@ -74,6 +78,18 @@ static inline int btbcm_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr) return -EOPNOTSUPP; } +int btbcm_read_pcm_int_params(struct hci_dev *hdev, + struct bcm_set_pcm_int_params *params) +{ + return -EOPNOTSUPP; +} + +int btbcm_write_pcm_int_params(struct hci_dev *hdev, + const struct bcm_set_pcm_int_params *params) +{ + return -EOPNOTSUPP; +} + static inline int btbcm_patchram(struct hci_dev *hdev, const struct firmware *fw) { return -EOPNOTSUPP; -- cgit v1.2.3 From eb762b94111b646b4f116ebfdbfcadbad14e12b3 Mon Sep 17 00:00:00 2001 From: Abhishek Pandit-Subedi Date: Tue, 26 Nov 2019 08:17:32 +0100 Subject: Bluetooth: hci_bcm: Support pcm params in dts BCM chips may require configuration of PCM to operate correctly and there is a vendor specific HCI command to do this. Add support in the hci_bcm driver to parse this from devicetree and configure the chip. Signed-off-by: Abhishek Pandit-Subedi Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- drivers/bluetooth/hci_bcm.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'drivers/bluetooth') diff --git a/drivers/bluetooth/hci_bcm.c b/drivers/bluetooth/hci_bcm.c index 0200eff12bc9..f8f5c593a05c 100644 --- a/drivers/bluetooth/hci_bcm.c +++ b/drivers/bluetooth/hci_bcm.c @@ -122,6 +122,7 @@ struct bcm_device { bool is_suspended; #endif bool no_early_set_baudrate; + u8 pcm_int_params[5]; }; /* generic bcm uart resources */ @@ -594,6 +595,16 @@ static int bcm_setup(struct hci_uart *hu) host_set_baudrate(hu, speed); } + /* PCM parameters if provided */ + if (bcm->dev && bcm->dev->pcm_int_params[0] != 0xff) { + struct bcm_set_pcm_int_params params; + + btbcm_read_pcm_int_params(hu->hdev, ¶ms); + + memcpy(¶ms, bcm->dev->pcm_int_params, 5); + btbcm_write_pcm_int_params(hu->hdev, ¶ms); + } + finalize: release_firmware(fw); @@ -1131,6 +1142,8 @@ static int bcm_acpi_probe(struct bcm_device *dev) static int bcm_of_probe(struct bcm_device *bdev) { device_property_read_u32(bdev->dev, "max-speed", &bdev->oper_speed); + device_property_read_u8_array(bdev->dev, "brcm,bt-pcm-int-params", + bdev->pcm_int_params, 5); return 0; } @@ -1146,6 +1159,9 @@ static int bcm_probe(struct platform_device *pdev) dev->dev = &pdev->dev; dev->irq = platform_get_irq(pdev, 0); + /* Initialize routing field to an unused value */ + dev->pcm_int_params[0] = 0xff; + if (has_acpi_companion(&pdev->dev)) { ret = bcm_acpi_probe(dev); if (ret) @@ -1406,6 +1422,9 @@ static int bcm_serdev_probe(struct serdev_device *serdev) bcmdev->serdev_hu.serdev = serdev; serdev_device_set_drvdata(serdev, bcmdev); + /* Initialize routing field to an unused value */ + bcmdev->pcm_int_params[0] = 0xff; + if (has_acpi_companion(&serdev->dev)) err = bcm_acpi_probe(bcmdev); else -- cgit v1.2.3 From 22cc6b7a1dbb58da4afc539d9b7d470b23a25eea Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 28 Nov 2019 19:24:27 +0100 Subject: Bluetooth: btusb: fix non-atomic allocation in completion handler USB completion handlers are called in atomic context and must specifically not allocate memory using GFP_KERNEL. Fixes: a1c49c434e15 ("Bluetooth: btusb: Add protocol support for MediaTek MT7668U USB devices") Cc: stable # 5.3 Cc: Sean Wang Signed-off-by: Johan Hovold Signed-off-by: Marcel Holtmann --- drivers/bluetooth/btusb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/bluetooth') diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 82fb2e7b2892..9b587e662b48 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -2602,7 +2602,7 @@ static void btusb_mtk_wmt_recv(struct urb *urb) * and being processed the events from there then. */ if (test_bit(BTUSB_TX_WAIT_VND_EVT, &data->flags)) { - data->evt_skb = skb_clone(skb, GFP_KERNEL); + data->evt_skb = skb_clone(skb, GFP_ATOMIC); if (!data->evt_skb) goto err_out; } -- cgit v1.2.3 From 3168c19d7eb17a0108a3b60ad8e8c1b18ea05c63 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 29 Nov 2019 17:36:35 +0000 Subject: Bluetooth: btusb: fix memory leak on fw Currently the error return path when the call to btusb_mtk_hci_wmt_sync fails does not free fw. Fix this by returning via the error_release_fw label that performs the free'ing. Addresses-Coverity: ("Resource leak") Fixes: a1c49c434e15 ("Bluetooth: btusb: Add protocol support for MediaTek MT7668U USB devices") Signed-off-by: Colin Ian King Signed-off-by: Marcel Holtmann --- drivers/bluetooth/btusb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/bluetooth') diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 9b587e662b48..d9cd0677d41c 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -2867,7 +2867,7 @@ static int btusb_mtk_setup_firmware(struct hci_dev *hdev, const char *fwname) err = btusb_mtk_hci_wmt_sync(hdev, &wmt_params); if (err < 0) { bt_dev_err(hdev, "Failed to send wmt rst (%d)", err); - return err; + goto err_release_fw; } /* Wait a few moments for firmware activation done */ -- cgit v1.2.3 From 7ecacafc240638148567742cca41aa7144b4fe1e Mon Sep 17 00:00:00 2001 From: Kai-Heng Feng Date: Thu, 5 Dec 2019 17:07:01 +0800 Subject: Bluetooth: btusb: Disable runtime suspend on Realtek devices After commit 9e45524a0111 ("Bluetooth: btusb: Fix suspend issue for Realtek devices") both WiFi and Bluetooth stop working after reboot: [ 34.322617] usb 1-8: reset full-speed USB device number 3 using xhci_hcd [ 34.450401] usb 1-8: device descriptor read/64, error -71 [ 34.694375] usb 1-8: device descriptor read/64, error -71 ... [ 44.599111] rtw_pci 0000:02:00.0: failed to poll offset=0x5 mask=0x3 value=0x0 [ 44.599113] rtw_pci 0000:02:00.0: mac power on failed [ 44.599114] rtw_pci 0000:02:00.0: failed to power on mac [ 44.599114] rtw_pci 0000:02:00.0: leave idle state failed [ 44.599492] rtw_pci 0000:02:00.0: failed to leave ips state [ 44.599493] rtw_pci 0000:02:00.0: failed to leave idle state That commit removed USB_QUIRK_RESET_RESUME, which not only resets the USB device after resume, it also prevents the device from being runtime suspended by USB core. My experiment shows if the Realtek btusb device ever runtime suspends once, the entire wireless module becomes useless after reboot. So let's explicitly disable runtime suspend on Realtek btusb device for now. Fixes: 9e45524a0111 ("Bluetooth: btusb: Fix suspend issue for Realtek devices") Signed-off-by: Kai-Heng Feng Signed-off-by: Marcel Holtmann --- drivers/bluetooth/btusb.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/bluetooth') diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index d9cd0677d41c..0eaeca0a64fb 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -3832,6 +3832,10 @@ static int btusb_probe(struct usb_interface *intf, * (DEVICE_REMOTE_WAKEUP) */ set_bit(BTUSB_WAKEUP_DISABLE, &data->flags); + + err = usb_autopm_get_interface(intf); + if (err < 0) + goto out_free_dev; } if (id->driver_info & BTUSB_AMP) { -- cgit v1.2.3 From 88d1cc96c4660f59d32a45810f49943e84c4bd54 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Sun, 15 Dec 2019 21:52:52 +0300 Subject: Bluetooth: hci_bcm: Add device-tree compatible for BCM4329 Driver supports BCM4329, but there is no device-tree compatible for that chip. Let's add it in order to allow boards to specify Bluetooth in theirs device-trees, in particular this is useful for NVIDIA Tegra20 boards. Signed-off-by: Dmitry Osipenko Signed-off-by: Marcel Holtmann --- drivers/bluetooth/hci_bcm.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/bluetooth') diff --git a/drivers/bluetooth/hci_bcm.c b/drivers/bluetooth/hci_bcm.c index f8f5c593a05c..bbfaf0c1529d 100644 --- a/drivers/bluetooth/hci_bcm.c +++ b/drivers/bluetooth/hci_bcm.c @@ -1467,6 +1467,7 @@ static struct bcm_device_data bcm4354_device_data = { static const struct of_device_id bcm_bluetooth_of_match[] = { { .compatible = "brcm,bcm20702a1" }, + { .compatible = "brcm,bcm4329-bt" }, { .compatible = "brcm,bcm4345c5" }, { .compatible = "brcm,bcm4330-bt" }, { .compatible = "brcm,bcm43438-bt" }, -- cgit v1.2.3 From 9f3565b89c43d2a0082bccbc3918f2ebae739533 Mon Sep 17 00:00:00 2001 From: Rocky Liao Date: Fri, 13 Dec 2019 16:50:45 +0800 Subject: Bluetooth: hci_qca: Replace of_device_get_match_data with device_get_match_data Replace of_device_get_match_data with device_get_match_data to make driver work across platforms. Signed-off-by: Rocky Liao Signed-off-by: Marcel Holtmann --- drivers/bluetooth/hci_qca.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/bluetooth') diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c index f10bdf8e1fc5..b602ed01505b 100644 --- a/drivers/bluetooth/hci_qca.c +++ b/drivers/bluetooth/hci_qca.c @@ -1501,7 +1501,7 @@ static int qca_serdev_probe(struct serdev_device *serdev) return -ENOMEM; qcadev->serdev_hu.serdev = serdev; - data = of_device_get_match_data(&serdev->dev); + data = device_get_match_data(&serdev->dev); serdev_device_set_drvdata(serdev, qcadev); device_property_read_string(&serdev->dev, "firmware-name", &qcadev->firmware_name); -- cgit v1.2.3 From 5bd64c645fc660734f172be4bba449952ed84ce2 Mon Sep 17 00:00:00 2001 From: Abhishek Pandit-Subedi Date: Thu, 26 Dec 2019 11:03:40 -0800 Subject: Bluetooth: btbcm: Add missing static inline in header This fixes a double definition error when CONFIG_BT_BCM is not set. Fixes: 528379902337 ("Bluetooth: btbcm: Support pcm configuration") Signed-off-by: Abhishek Pandit-Subedi Signed-off-by: Marcel Holtmann --- drivers/bluetooth/btbcm.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/bluetooth') diff --git a/drivers/bluetooth/btbcm.h b/drivers/bluetooth/btbcm.h index 3c7dd0765837..014ef847a486 100644 --- a/drivers/bluetooth/btbcm.h +++ b/drivers/bluetooth/btbcm.h @@ -78,13 +78,13 @@ static inline int btbcm_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr) return -EOPNOTSUPP; } -int btbcm_read_pcm_int_params(struct hci_dev *hdev, +static inline int btbcm_read_pcm_int_params(struct hci_dev *hdev, struct bcm_set_pcm_int_params *params) { return -EOPNOTSUPP; } -int btbcm_write_pcm_int_params(struct hci_dev *hdev, +static inline int btbcm_write_pcm_int_params(struct hci_dev *hdev, const struct bcm_set_pcm_int_params *params) { return -EOPNOTSUPP; -- cgit v1.2.3 From e601daed271e9eb1b923972a0a1af65f8c7bb77b Mon Sep 17 00:00:00 2001 From: Stefan Wahren Date: Wed, 1 Jan 2020 15:01:34 +0100 Subject: Bluetooth: hci_bcm: Drive RTS only for BCM43438 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The commit 3347a80965b3 ("Bluetooth: hci_bcm: Fix RTS handling during startup") is causing at least a regression for AP6256 on Orange Pi 3. So do the RTS line handing during startup only on the necessary platform. Fixes: 3347a80965b3 ("Bluetooth: hci_bcm: Fix RTS handling during startup") Reported-by: OndÅ™ej Jirman Signed-off-by: Stefan Wahren Signed-off-by: Marcel Holtmann --- drivers/bluetooth/hci_bcm.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) (limited to 'drivers/bluetooth') diff --git a/drivers/bluetooth/hci_bcm.c b/drivers/bluetooth/hci_bcm.c index bbfaf0c1529d..769bb4404bd1 100644 --- a/drivers/bluetooth/hci_bcm.c +++ b/drivers/bluetooth/hci_bcm.c @@ -53,6 +53,7 @@ */ struct bcm_device_data { bool no_early_set_baudrate; + bool drive_rts_on_open; }; /** @@ -122,6 +123,7 @@ struct bcm_device { bool is_suspended; #endif bool no_early_set_baudrate; + bool drive_rts_on_open; u8 pcm_int_params[5]; }; @@ -456,7 +458,9 @@ static int bcm_open(struct hci_uart *hu) out: if (bcm->dev) { - hci_uart_set_flow_control(hu, true); + if (bcm->dev->drive_rts_on_open) + hci_uart_set_flow_control(hu, true); + hu->init_speed = bcm->dev->init_speed; /* If oper_speed is set, ldisc/serdev will set the baudrate @@ -466,7 +470,10 @@ out: hu->oper_speed = bcm->dev->oper_speed; err = bcm_gpio_set_power(bcm->dev, true); - hci_uart_set_flow_control(hu, false); + + if (bcm->dev->drive_rts_on_open) + hci_uart_set_flow_control(hu, false); + if (err) goto err_unset_hu; } @@ -1447,8 +1454,10 @@ static int bcm_serdev_probe(struct serdev_device *serdev) dev_err(&serdev->dev, "Failed to power down\n"); data = device_get_match_data(bcmdev->dev); - if (data) + if (data) { bcmdev->no_early_set_baudrate = data->no_early_set_baudrate; + bcmdev->drive_rts_on_open = data->drive_rts_on_open; + } return hci_uart_register_device(&bcmdev->serdev_hu, &bcm_proto); } @@ -1465,12 +1474,16 @@ static struct bcm_device_data bcm4354_device_data = { .no_early_set_baudrate = true, }; +static struct bcm_device_data bcm43438_device_data = { + .drive_rts_on_open = true, +}; + static const struct of_device_id bcm_bluetooth_of_match[] = { { .compatible = "brcm,bcm20702a1" }, { .compatible = "brcm,bcm4329-bt" }, { .compatible = "brcm,bcm4345c5" }, { .compatible = "brcm,bcm4330-bt" }, - { .compatible = "brcm,bcm43438-bt" }, + { .compatible = "brcm,bcm43438-bt", .data = &bcm43438_device_data }, { .compatible = "brcm,bcm43540-bt", .data = &bcm4354_device_data }, { .compatible = "brcm,bcm4335a0" }, { }, -- cgit v1.2.3 From d841502c79e3fda2ba0e8d64f9eb00e9dd884af0 Mon Sep 17 00:00:00 2001 From: Balakrishna Godavarthi Date: Thu, 2 Jan 2020 20:19:11 +0530 Subject: Bluetooth: hci_qca: Collect controller memory dump during SSR We will collect the ramdump of BT controller when hardware error event received before rebooting the HCI layer. Before restarting a subsystem or a process running on a subsystem, it is often required to request either a subsystem or a process to perform proper cache dump and software failure reason into a memory buffer which application processor can retrieve afterwards. SW developers can often provide initial investigation by looking into that debugging information. Signed-off-by: Balakrishna Godavarthi Signed-off-by: Venkata Lakshmi Narayana Gubba Signed-off-by: Marcel Holtmann --- drivers/bluetooth/hci_qca.c | 296 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 291 insertions(+), 5 deletions(-) (limited to 'drivers/bluetooth') diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c index b602ed01505b..9392cc7f9908 100644 --- a/drivers/bluetooth/hci_qca.c +++ b/drivers/bluetooth/hci_qca.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -46,6 +47,7 @@ #define IBS_BTSOC_TX_IDLE_TIMEOUT_MS 40 #define IBS_HOST_TX_IDLE_TIMEOUT_MS 2000 #define CMD_TRANS_TIMEOUT_MS 100 +#define MEMDUMP_TIMEOUT_MS 8000 /* susclk rate */ #define SUSCLK_RATE_32KHZ 32768 @@ -53,12 +55,21 @@ /* Controller debug log header */ #define QCA_DEBUG_HANDLE 0x2EDC +/* Controller dump header */ +#define QCA_SSR_DUMP_HANDLE 0x0108 +#define QCA_DUMP_PACKET_SIZE 255 +#define QCA_LAST_SEQUENCE_NUM 0xFFFF +#define QCA_CRASHBYTE_PACKET_LEN 1096 +#define QCA_MEMDUMP_BYTE 0xFB + enum qca_flags { QCA_IBS_ENABLED, QCA_DROP_VENDOR_EVENT, QCA_SUSPENDING, + QCA_MEMDUMP_COLLECTION }; + /* HCI_IBS transmit side sleep protocol states */ enum tx_ibs_states { HCI_IBS_TX_ASLEEP, @@ -81,11 +92,40 @@ enum hci_ibs_clock_state_vote { HCI_IBS_RX_VOTE_CLOCK_OFF, }; +/* Controller memory dump states */ +enum qca_memdump_states { + QCA_MEMDUMP_IDLE, + QCA_MEMDUMP_COLLECTING, + QCA_MEMDUMP_COLLECTED, + QCA_MEMDUMP_TIMEOUT, +}; + +struct qca_memdump_data { + char *memdump_buf_head; + char *memdump_buf_tail; + u32 current_seq_no; + u32 received_dump; +}; + +struct qca_memdump_event_hdr { + __u8 evt; + __u8 plen; + __u16 opcode; + __u16 seq_no; + __u8 reserved; +} __packed; + + +struct qca_dump_size { + u32 dump_size; +} __packed; + struct qca_data { struct hci_uart *hu; struct sk_buff *rx_skb; struct sk_buff_head txq; struct sk_buff_head tx_wait_q; /* HCI_IBS wait queue */ + struct sk_buff_head rx_memdump_q; /* Memdump wait queue */ spinlock_t hci_ibs_lock; /* HCI_IBS state lock */ u8 tx_ibs_state; /* HCI_IBS transmit side power state*/ u8 rx_ibs_state; /* HCI_IBS receive side power state */ @@ -95,14 +135,18 @@ struct qca_data { u32 tx_idle_delay; struct timer_list wake_retrans_timer; u32 wake_retrans; + struct timer_list memdump_timer; struct workqueue_struct *workqueue; struct work_struct ws_awake_rx; struct work_struct ws_awake_device; struct work_struct ws_rx_vote_off; struct work_struct ws_tx_vote_off; + struct work_struct ctrl_memdump_evt; + struct qca_memdump_data *qca_memdump; unsigned long flags; struct completion drop_ev_comp; wait_queue_head_t suspend_wait_q; + enum qca_memdump_states memdump_state; /* For debugging purpose */ u64 ibs_sent_wacks; @@ -167,6 +211,7 @@ static int qca_regulator_enable(struct qca_serdev *qcadev); static void qca_regulator_disable(struct qca_serdev *qcadev); static void qca_power_shutdown(struct hci_uart *hu); static int qca_power_off(struct hci_dev *hdev); +static void qca_controller_memdump(struct work_struct *work); static enum qca_btsoc_type qca_soc_type(struct hci_uart *hu) { @@ -474,6 +519,23 @@ static void hci_ibs_wake_retrans_timeout(struct timer_list *t) hci_uart_tx_wakeup(hu); } +static void hci_memdump_timeout(struct timer_list *t) +{ + struct qca_data *qca = from_timer(qca, t, tx_idle_timer); + struct hci_uart *hu = qca->hu; + struct qca_memdump_data *qca_memdump = qca->qca_memdump; + char *memdump_buf = qca_memdump->memdump_buf_tail; + + bt_dev_err(hu->hdev, "clearing allocated memory due to memdump timeout"); + /* Inject hw error event to reset the device and driver. */ + hci_reset_dev(hu->hdev); + kfree(memdump_buf); + kfree(qca_memdump); + qca->memdump_state = QCA_MEMDUMP_TIMEOUT; + del_timer(&qca->memdump_timer); + cancel_work_sync(&qca->ctrl_memdump_evt); +} + /* Initialize protocol */ static int qca_open(struct hci_uart *hu) { @@ -492,6 +554,7 @@ static int qca_open(struct hci_uart *hu) skb_queue_head_init(&qca->txq); skb_queue_head_init(&qca->tx_wait_q); + skb_queue_head_init(&qca->rx_memdump_q); spin_lock_init(&qca->hci_ibs_lock); qca->workqueue = alloc_ordered_workqueue("qca_wq", 0); if (!qca->workqueue) { @@ -504,7 +567,7 @@ static int qca_open(struct hci_uart *hu) INIT_WORK(&qca->ws_awake_device, qca_wq_awake_device); INIT_WORK(&qca->ws_rx_vote_off, qca_wq_serial_rx_clock_vote_off); INIT_WORK(&qca->ws_tx_vote_off, qca_wq_serial_tx_clock_vote_off); - + INIT_WORK(&qca->ctrl_memdump_evt, qca_controller_memdump); init_waitqueue_head(&qca->suspend_wait_q); qca->hu = hu; @@ -544,6 +607,7 @@ static int qca_open(struct hci_uart *hu) timer_setup(&qca->tx_idle_timer, hci_ibs_tx_idle_timeout, 0); qca->tx_idle_delay = IBS_HOST_TX_IDLE_TIMEOUT_MS; + timer_setup(&qca->memdump_timer, hci_memdump_timeout, 0); BT_DBG("HCI_UART_QCA open, tx_idle_delay=%u, wake_retrans=%u", qca->tx_idle_delay, qca->wake_retrans); @@ -622,8 +686,10 @@ static int qca_close(struct hci_uart *hu) skb_queue_purge(&qca->tx_wait_q); skb_queue_purge(&qca->txq); + skb_queue_purge(&qca->rx_memdump_q); del_timer(&qca->tx_idle_timer); del_timer(&qca->wake_retrans_timer); + del_timer(&qca->memdump_timer); destroy_workqueue(qca->workqueue); qca->hu = NULL; @@ -900,6 +966,126 @@ static int qca_recv_acl_data(struct hci_dev *hdev, struct sk_buff *skb) return hci_recv_frame(hdev, skb); } +static void qca_controller_memdump(struct work_struct *work) +{ + struct qca_data *qca = container_of(work, struct qca_data, + ctrl_memdump_evt); + struct hci_uart *hu = qca->hu; + struct sk_buff *skb; + struct qca_memdump_event_hdr *cmd_hdr; + struct qca_memdump_data *qca_memdump = qca->qca_memdump; + struct qca_dump_size *dump; + char *memdump_buf; + char nullBuff[QCA_DUMP_PACKET_SIZE] = { 0 }; + u16 opcode, seq_no; + u32 dump_size; + + while ((skb = skb_dequeue(&qca->rx_memdump_q))) { + + if (!qca_memdump) { + qca_memdump = kzalloc(sizeof(struct qca_memdump_data), + GFP_ATOMIC); + if (!qca_memdump) + return; + + qca->qca_memdump = qca_memdump; + } + + qca->memdump_state = QCA_MEMDUMP_COLLECTING; + cmd_hdr = (void *) skb->data; + opcode = __le16_to_cpu(cmd_hdr->opcode); + seq_no = __le16_to_cpu(cmd_hdr->seq_no); + skb_pull(skb, sizeof(struct qca_memdump_event_hdr)); + + if (!seq_no) { + + /* This is the first frame of memdump packet from + * the controller, Disable IBS to recevie dump + * with out any interruption, ideally time required for + * the controller to send the dump is 8 seconds. let us + * start timer to handle this asynchronous activity. + */ + clear_bit(QCA_IBS_ENABLED, &qca->flags); + set_bit(QCA_MEMDUMP_COLLECTION, &qca->flags); + dump = (void *) skb->data; + dump_size = __le32_to_cpu(dump->dump_size); + if (!(dump_size)) { + bt_dev_err(hu->hdev, "Rx invalid memdump size"); + kfree_skb(skb); + return; + } + + bt_dev_info(hu->hdev, "QCA collecting dump of size:%u", + dump_size); + mod_timer(&qca->memdump_timer, (jiffies + + msecs_to_jiffies(MEMDUMP_TIMEOUT_MS))); + + skb_pull(skb, sizeof(dump_size)); + memdump_buf = vmalloc(dump_size); + qca_memdump->memdump_buf_head = memdump_buf; + qca_memdump->memdump_buf_tail = memdump_buf; + } + + memdump_buf = qca_memdump->memdump_buf_tail; + + /* If sequence no 0 is missed then there is no point in + * accepting the other sequences. + */ + if (!memdump_buf) { + bt_dev_err(hu->hdev, "QCA: Discarding other packets"); + kfree(qca_memdump); + kfree_skb(skb); + qca->qca_memdump = NULL; + return; + } + + /* There could be chance of missing some packets from + * the controller. In such cases let us store the dummy + * packets in the buffer. + */ + while ((seq_no > qca_memdump->current_seq_no + 1) && + seq_no != QCA_LAST_SEQUENCE_NUM) { + bt_dev_err(hu->hdev, "QCA controller missed packet:%d", + qca_memdump->current_seq_no); + memcpy(memdump_buf, nullBuff, QCA_DUMP_PACKET_SIZE); + memdump_buf = memdump_buf + QCA_DUMP_PACKET_SIZE; + qca_memdump->received_dump += QCA_DUMP_PACKET_SIZE; + qca_memdump->current_seq_no++; + } + + memcpy(memdump_buf, (unsigned char *) skb->data, skb->len); + memdump_buf = memdump_buf + skb->len; + qca_memdump->memdump_buf_tail = memdump_buf; + qca_memdump->current_seq_no = seq_no + 1; + qca_memdump->received_dump += skb->len; + qca->qca_memdump = qca_memdump; + kfree_skb(skb); + if (seq_no == QCA_LAST_SEQUENCE_NUM) { + bt_dev_info(hu->hdev, "QCA writing crash dump of size %d bytes", + qca_memdump->received_dump); + memdump_buf = qca_memdump->memdump_buf_head; + dev_coredumpv(&hu->serdev->dev, memdump_buf, + qca_memdump->received_dump, GFP_KERNEL); + del_timer(&qca->memdump_timer); + kfree(qca->qca_memdump); + qca->qca_memdump = NULL; + qca->memdump_state = QCA_MEMDUMP_COLLECTED; + } + } + +} + +int qca_controller_memdump_event(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct hci_uart *hu = hci_get_drvdata(hdev); + struct qca_data *qca = hu->priv; + + skb_queue_tail(&qca->rx_memdump_q, skb); + queue_work(qca->workqueue, &qca->ctrl_memdump_evt); + + return 0; +} + static int qca_recv_event(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_uart *hu = hci_get_drvdata(hdev); @@ -925,6 +1111,14 @@ static int qca_recv_event(struct hci_dev *hdev, struct sk_buff *skb) return 0; } + /* We receive chip memory dump as an event packet, With a dedicated + * handler followed by a hardware error event. When this event is + * received we store dump into a file before closing hci. This + * dump will help in triaging the issues. + */ + if ((skb->data[0] == HCI_VENDOR_PKT) && + (get_unaligned_be16(skb->data + 2) == QCA_SSR_DUMP_HANDLE)) + return qca_controller_memdump_event(hdev, skb); return hci_recv_frame(hdev, skb); } @@ -1203,6 +1397,91 @@ error: return ret; } +static int qca_send_crashbuffer(struct hci_uart *hu) +{ + struct qca_data *qca = hu->priv; + struct sk_buff *skb; + + skb = bt_skb_alloc(QCA_CRASHBYTE_PACKET_LEN, GFP_KERNEL); + if (!skb) { + bt_dev_err(hu->hdev, "Failed to allocate memory for skb packet"); + return -ENOMEM; + } + + /* We forcefully crash the controller, by sending 0xfb byte for + * 1024 times. We also might have chance of losing data, To be + * on safer side we send 1096 bytes to the SoC. + */ + memset(skb_put(skb, QCA_CRASHBYTE_PACKET_LEN), QCA_MEMDUMP_BYTE, + QCA_CRASHBYTE_PACKET_LEN); + hci_skb_pkt_type(skb) = HCI_COMMAND_PKT; + bt_dev_info(hu->hdev, "crash the soc to collect controller dump"); + skb_queue_tail(&qca->txq, skb); + hci_uart_tx_wakeup(hu); + + return 0; +} + +static void qca_wait_for_dump_collection(struct hci_dev *hdev) +{ + struct hci_uart *hu = hci_get_drvdata(hdev); + struct qca_data *qca = hu->priv; + struct qca_memdump_data *qca_memdump = qca->qca_memdump; + char *memdump_buf = NULL; + + wait_on_bit_timeout(&qca->flags, QCA_MEMDUMP_COLLECTION, + TASK_UNINTERRUPTIBLE, MEMDUMP_TIMEOUT_MS); + + clear_bit(QCA_MEMDUMP_COLLECTION, &qca->flags); + if (qca->memdump_state == QCA_MEMDUMP_IDLE) { + bt_dev_err(hu->hdev, "Clearing the buffers due to timeout"); + if (qca_memdump) + memdump_buf = qca_memdump->memdump_buf_tail; + kfree(memdump_buf); + kfree(qca_memdump); + qca->memdump_state = QCA_MEMDUMP_TIMEOUT; + del_timer(&qca->memdump_timer); + cancel_work_sync(&qca->ctrl_memdump_evt); + } +} + +static void qca_hw_error(struct hci_dev *hdev, u8 code) +{ + struct hci_uart *hu = hci_get_drvdata(hdev); + struct qca_data *qca = hu->priv; + + bt_dev_info(hdev, "mem_dump_status: %d", qca->memdump_state); + + if (qca->memdump_state == QCA_MEMDUMP_IDLE) { + /* If hardware error event received for other than QCA + * soc memory dump event, then we need to crash the SOC + * and wait here for 8 seconds to get the dump packets. + * This will block main thread to be on hold until we + * collect dump. + */ + set_bit(QCA_MEMDUMP_COLLECTION, &qca->flags); + qca_send_crashbuffer(hu); + qca_wait_for_dump_collection(hdev); + } else if (qca->memdump_state == QCA_MEMDUMP_COLLECTING) { + /* Let us wait here until memory dump collected or + * memory dump timer expired. + */ + bt_dev_info(hdev, "waiting for dump to complete"); + qca_wait_for_dump_collection(hdev); + } +} + +static void qca_cmd_timeout(struct hci_dev *hdev) +{ + struct hci_uart *hu = hci_get_drvdata(hdev); + struct qca_data *qca = hu->priv; + + if (qca->memdump_state == QCA_MEMDUMP_IDLE) + qca_send_crashbuffer(hu); + else + bt_dev_info(hdev, "Dump collection is in process"); +} + static int qca_wcn3990_init(struct hci_uart *hu) { struct qca_serdev *qcadev; @@ -1320,6 +1599,8 @@ static int qca_setup(struct hci_uart *hu) if (!ret) { set_bit(QCA_IBS_ENABLED, &qca->flags); qca_debugfs_init(hdev); + hu->hdev->hw_error = qca_hw_error; + hu->hdev->cmd_timeout = qca_cmd_timeout; } else if (ret == -ENOENT) { /* No patch/nvm-config found, run with original fw/config */ ret = 0; @@ -1408,17 +1689,22 @@ static void qca_power_shutdown(struct hci_uart *hu) host_set_baudrate(hu, 2400); qca_send_power_pulse(hu, false); qca_regulator_disable(qcadev); + hu->hdev->hw_error = NULL; + hu->hdev->cmd_timeout = NULL; } static int qca_power_off(struct hci_dev *hdev) { struct hci_uart *hu = hci_get_drvdata(hdev); + struct qca_data *qca = hu->priv; - /* Perform pre shutdown command */ - qca_send_pre_shutdown_cmd(hdev); - - usleep_range(8000, 10000); + /* Stop sending shutdown command if soc crashes. */ + if (qca->memdump_state == QCA_MEMDUMP_IDLE) { + qca_send_pre_shutdown_cmd(hdev); + usleep_range(8000, 10000); + } + qca->memdump_state = QCA_MEMDUMP_IDLE; qca_power_shutdown(hu); return 0; } -- cgit v1.2.3 From 19220f35b3708dc069135046061fbe7366d5cb6e Mon Sep 17 00:00:00 2001 From: Rocky Liao Date: Mon, 6 Jan 2020 14:07:44 +0800 Subject: Bluetooth: btusb: Add support for 04ca:3021 QCA_ROME device USB "VendorID:04ca ProductID:3021" is a new QCA ROME USB Bluetooth device, this patch will support firmware downloading for it. T: Bus=02 Lev=02 Prnt=02 Port=05 Cnt=01 Dev#= 3 Spd=12 MxCh= 0 D: Ver= 2.01 Cls=e0(wlcon) Sub=01 Prot=01 MxPS=64 #Cfgs= 1 P: Vendor=04ca ProdID=3021 Rev= 0.01 C:* #Ifs= 2 Cfg#= 1 Atr=e0 MxPwr=100mA I:* If#= 0 Alt= 0 #EPs= 3 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=81(I) Atr=03(Int.) MxPS= 16 Ivl=1ms E: Ad=82(I) Atr=02(Bulk) MxPS= 64 Ivl=0ms E: Ad=02(O) Atr=02(Bulk) MxPS= 64 Ivl=0ms I:* If#= 1 Alt= 0 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 0 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 0 Ivl=1ms I: If#= 1 Alt= 1 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 9 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 9 Ivl=1ms I: If#= 1 Alt= 2 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 17 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 17 Ivl=1ms I: If#= 1 Alt= 3 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 25 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 25 Ivl=1ms I: If#= 1 Alt= 4 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 33 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 33 Ivl=1ms I: If#= 1 Alt= 5 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 49 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 49 Ivl=1ms Signed-off-by: Rocky Liao Signed-off-by: Marcel Holtmann --- drivers/bluetooth/btusb.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/bluetooth') diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 0eaeca0a64fb..f5924f3e8b8d 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -266,6 +266,7 @@ static const struct usb_device_id blacklist_table[] = { { USB_DEVICE(0x04ca, 0x3015), .driver_info = BTUSB_QCA_ROME }, { USB_DEVICE(0x04ca, 0x3016), .driver_info = BTUSB_QCA_ROME }, { USB_DEVICE(0x04ca, 0x301a), .driver_info = BTUSB_QCA_ROME }, + { USB_DEVICE(0x04ca, 0x3021), .driver_info = BTUSB_QCA_ROME }, { USB_DEVICE(0x13d3, 0x3491), .driver_info = BTUSB_QCA_ROME }, { USB_DEVICE(0x13d3, 0x3496), .driver_info = BTUSB_QCA_ROME }, { USB_DEVICE(0x13d3, 0x3501), .driver_info = BTUSB_QCA_ROME }, -- cgit v1.2.3 From 87c2a2a7d7631d0caa956b73f9f20010b5f55681 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 8 Jan 2020 03:59:31 +0000 Subject: Bluetooth: hci_qca: Use vfree() instead of kfree() Use vfree() instead of kfree() to free vmalloc() allocated data. Fixes: d841502c79e3 ("Bluetooth: hci_qca: Collect controller memory dump during SSR") Signed-off-by: Wei Yongjun Reviewed-by: Balakrishna Godavarthi Signed-off-by: Marcel Holtmann --- drivers/bluetooth/hci_qca.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/bluetooth') diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c index 9392cc7f9908..a17260641283 100644 --- a/drivers/bluetooth/hci_qca.c +++ b/drivers/bluetooth/hci_qca.c @@ -529,7 +529,7 @@ static void hci_memdump_timeout(struct timer_list *t) bt_dev_err(hu->hdev, "clearing allocated memory due to memdump timeout"); /* Inject hw error event to reset the device and driver. */ hci_reset_dev(hu->hdev); - kfree(memdump_buf); + vfree(memdump_buf); kfree(qca_memdump); qca->memdump_state = QCA_MEMDUMP_TIMEOUT; del_timer(&qca->memdump_timer); @@ -1437,7 +1437,7 @@ static void qca_wait_for_dump_collection(struct hci_dev *hdev) bt_dev_err(hu->hdev, "Clearing the buffers due to timeout"); if (qca_memdump) memdump_buf = qca_memdump->memdump_buf_tail; - kfree(memdump_buf); + vfree(memdump_buf); kfree(qca_memdump); qca->memdump_state = QCA_MEMDUMP_TIMEOUT; del_timer(&qca->memdump_timer); -- cgit v1.2.3 From 56b084ed6eaa0a6353395deffb601dbd6570a1a5 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Wed, 8 Jan 2020 01:54:31 +0000 Subject: Bluetooth: hci_qca: Remove set but not used variable 'opcode' Fixes gcc '-Wunused-but-set-variable' warning: drivers/bluetooth/hci_qca.c: In function 'qca_controller_memdump': drivers/bluetooth/hci_qca.c:980:6: warning: variable 'opcode' set but not used [-Wunused-but-set-variable] It is never used since commit d841502c79e3 ("Bluetooth: hci_qca: Collect controller memory dump during SSR"), so remove it. Reported-by: Hulk Robot Signed-off-by: YueHaibing Signed-off-by: Marcel Holtmann --- drivers/bluetooth/hci_qca.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/bluetooth') diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c index a17260641283..82e4cd4b6663 100644 --- a/drivers/bluetooth/hci_qca.c +++ b/drivers/bluetooth/hci_qca.c @@ -977,7 +977,7 @@ static void qca_controller_memdump(struct work_struct *work) struct qca_dump_size *dump; char *memdump_buf; char nullBuff[QCA_DUMP_PACKET_SIZE] = { 0 }; - u16 opcode, seq_no; + u16 seq_no; u32 dump_size; while ((skb = skb_dequeue(&qca->rx_memdump_q))) { @@ -993,7 +993,6 @@ static void qca_controller_memdump(struct work_struct *work) qca->memdump_state = QCA_MEMDUMP_COLLECTING; cmd_hdr = (void *) skb->data; - opcode = __le16_to_cpu(cmd_hdr->opcode); seq_no = __le16_to_cpu(cmd_hdr->seq_no); skb_pull(skb, sizeof(struct qca_memdump_event_hdr)); -- cgit v1.2.3 From dde8010be0a0ed9823cc1ddf75ef8448910821a9 Mon Sep 17 00:00:00 2001 From: Changqi Du Date: Wed, 8 Jan 2020 23:40:19 -0500 Subject: Bluetooth: btbcm : Fix warning about missing blank lines after declarations This patches fixes two warnings of checkpatch.pl, both of the type WARNING: Missing a blank line after declarations Signed-off-by: Changqi Du Signed-off-by: Marcel Holtmann --- drivers/bluetooth/btbcm.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/bluetooth') diff --git a/drivers/bluetooth/btbcm.c b/drivers/bluetooth/btbcm.c index 0795a49edfae..1f498f358f60 100644 --- a/drivers/bluetooth/btbcm.c +++ b/drivers/bluetooth/btbcm.c @@ -36,6 +36,7 @@ int btbcm_check_bdaddr(struct hci_dev *hdev) HCI_INIT_TIMEOUT); if (IS_ERR(skb)) { int err = PTR_ERR(skb); + bt_dev_err(hdev, "BCM: Reading device address failed (%d)", err); return err; } @@ -223,6 +224,7 @@ static int btbcm_reset(struct hci_dev *hdev) skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT); if (IS_ERR(skb)) { int err = PTR_ERR(skb); + bt_dev_err(hdev, "BCM: Reset failed (%d)", err); return err; } -- cgit v1.2.3 From 5e6d8401ade984296ae9629cb06458c2784e1882 Mon Sep 17 00:00:00 2001 From: Rocky Liao Date: Mon, 13 Jan 2020 12:30:20 +0800 Subject: Bluetooth: hci_qca: Add qca_power_on() API to support both wcn399x and Rome power up This patch adds a unified API qca_power_on() to support both wcn399x and Rome power on. For wcn399x it calls the qca_wcn3990_init() to init the regulators, and for Rome it pulls up the bt_en GPIO to power up the btsoc. It also moves all the power up operation from hdev->open() to hdev->setup(). Signed-off-by: Rocky Liao Reviewed-by: Matthias Kaehlcke Signed-off-by: Marcel Holtmann --- drivers/bluetooth/hci_qca.c | 54 +++++++++++++++++++++++++++------------------ 1 file changed, 33 insertions(+), 21 deletions(-) (limited to 'drivers/bluetooth') diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c index 82e4cd4b6663..992622dc1263 100644 --- a/drivers/bluetooth/hci_qca.c +++ b/drivers/bluetooth/hci_qca.c @@ -541,7 +541,6 @@ static int qca_open(struct hci_uart *hu) { struct qca_serdev *qcadev; struct qca_data *qca; - int ret; BT_DBG("hu %p qca_open", hu); @@ -582,23 +581,10 @@ static int qca_open(struct hci_uart *hu) hu->priv = qca; if (hu->serdev) { - qcadev = serdev_device_get_drvdata(hu->serdev); - if (!qca_is_wcn399x(qcadev->btsoc_type)) { - gpiod_set_value_cansleep(qcadev->bt_en, 1); - /* Controller needs time to bootup. */ - msleep(150); - } else { + if (qca_is_wcn399x(qcadev->btsoc_type)) { hu->init_speed = qcadev->init_speed; hu->oper_speed = qcadev->oper_speed; - ret = qca_regulator_enable(qcadev); - if (ret) { - destroy_workqueue(qca->workqueue); - kfree_skb(qca->rx_skb); - hu->priv = NULL; - kfree(qca); - return ret; - } } } @@ -1531,6 +1517,31 @@ static int qca_wcn3990_init(struct hci_uart *hu) return 0; } +static int qca_power_on(struct hci_dev *hdev) +{ + struct hci_uart *hu = hci_get_drvdata(hdev); + enum qca_btsoc_type soc_type = qca_soc_type(hu); + struct qca_serdev *qcadev; + int ret = 0; + + /* Non-serdev device usually is powered by external power + * and don't need additional action in driver for power on + */ + if (!hu->serdev) + return 0; + + if (qca_is_wcn399x(soc_type)) { + ret = qca_wcn3990_init(hu); + } else { + qcadev = serdev_device_get_drvdata(hu->serdev); + gpiod_set_value_cansleep(qcadev->bt_en, 1); + /* Controller needs time to bootup. */ + msleep(150); + } + + return ret; +} + static int qca_setup(struct hci_uart *hu) { struct hci_dev *hdev = hu->hdev; @@ -1553,24 +1564,25 @@ static int qca_setup(struct hci_uart *hu) */ set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks); - if (qca_is_wcn399x(soc_type)) { - bt_dev_info(hdev, "setting up wcn3990"); + bt_dev_info(hdev, "setting up %s", + qca_is_wcn399x(soc_type) ? "wcn399x" : "ROME"); + ret = qca_power_on(hdev); + if (ret) + return ret; + + if (qca_is_wcn399x(soc_type)) { /* Enable NON_PERSISTENT_SETUP QUIRK to ensure to execute * 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) - return ret; ret = qca_read_soc_version(hdev, &soc_ver, soc_type); if (ret) return ret; } else { - bt_dev_info(hdev, "ROME setup"); qca_set_speed(hu, QCA_INIT_SPEED); } -- cgit v1.2.3 From f92a8cb569e39e28515e3f9c0ccaa16f874644b7 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Wed, 15 Jan 2020 13:02:16 -0800 Subject: Bluetooth: hci_vhci: Add support for ISO packets This make virtual controllers to pass ISO packets around. Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Marcel Holtmann --- drivers/bluetooth/hci_vhci.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/bluetooth') diff --git a/drivers/bluetooth/hci_vhci.c b/drivers/bluetooth/hci_vhci.c index 65e41c1d760f..8ab26dec5f6e 100644 --- a/drivers/bluetooth/hci_vhci.c +++ b/drivers/bluetooth/hci_vhci.c @@ -178,6 +178,7 @@ static inline ssize_t vhci_get_user(struct vhci_data *data, case HCI_EVENT_PKT: case HCI_ACLDATA_PKT: case HCI_SCODATA_PKT: + case HCI_ISODATA_PKT: if (!data->hdev) { kfree_skb(skb); return -ENODEV; -- cgit v1.2.3 From ef564119ba832f55337935038dc0a91baa7417d1 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Wed, 15 Jan 2020 13:02:19 -0800 Subject: Bluetooth: hci_h4: Add support for ISO packets This enables H4 driver to properly handle ISO packets. Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Marcel Holtmann --- drivers/bluetooth/hci_h4.c | 1 + drivers/bluetooth/hci_uart.h | 7 +++++++ 2 files changed, 8 insertions(+) (limited to 'drivers/bluetooth') diff --git a/drivers/bluetooth/hci_h4.c b/drivers/bluetooth/hci_h4.c index 19ba52005009..6dc1fbeb564b 100644 --- a/drivers/bluetooth/hci_h4.c +++ b/drivers/bluetooth/hci_h4.c @@ -103,6 +103,7 @@ static const struct h4_recv_pkt h4_recv_pkts[] = { { H4_RECV_ACL, .recv = hci_recv_frame }, { H4_RECV_SCO, .recv = hci_recv_frame }, { H4_RECV_EVENT, .recv = hci_recv_frame }, + { H4_RECV_ISO, .recv = hci_recv_frame }, }; /* Recv data */ diff --git a/drivers/bluetooth/hci_uart.h b/drivers/bluetooth/hci_uart.h index 6ab631101019..4e039d7a16f8 100644 --- a/drivers/bluetooth/hci_uart.h +++ b/drivers/bluetooth/hci_uart.h @@ -143,6 +143,13 @@ struct h4_recv_pkt { .lsize = 1, \ .maxlen = HCI_MAX_EVENT_SIZE +#define H4_RECV_ISO \ + .type = HCI_ISODATA_PKT, \ + .hlen = HCI_ISO_HDR_SIZE, \ + .loff = 2, \ + .lsize = 2, \ + .maxlen = HCI_MAX_FRAME_SIZE \ + struct sk_buff *h4_recv_buf(struct hci_dev *hdev, struct sk_buff *skb, const unsigned char *buffer, int count, const struct h4_recv_pkt *pkts, int pkts_count); -- cgit v1.2.3 From 1cc3c10c5aea84d4b0400423449c316eed3f27df Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Wed, 15 Jan 2020 13:02:20 -0800 Subject: Bluetooth: hci_h5: Add support for ISO packets This enables H5 driver to properly handle ISO packets. Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Marcel Holtmann --- drivers/bluetooth/hci_h5.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/bluetooth') diff --git a/drivers/bluetooth/hci_h5.c b/drivers/bluetooth/hci_h5.c index dacf297baf59..0b14547482a7 100644 --- a/drivers/bluetooth/hci_h5.c +++ b/drivers/bluetooth/hci_h5.c @@ -385,6 +385,7 @@ static void h5_complete_rx_pkt(struct hci_uart *hu) case HCI_EVENT_PKT: case HCI_ACLDATA_PKT: case HCI_SCODATA_PKT: + case HCI_ISODATA_PKT: hci_skb_pkt_type(h5->rx_skb) = H5_HDR_PKT_TYPE(hdr); /* Remove Three-wire header */ @@ -594,6 +595,7 @@ static int h5_enqueue(struct hci_uart *hu, struct sk_buff *skb) break; case HCI_SCODATA_PKT: + case HCI_ISODATA_PKT: skb_queue_tail(&h5->unrel, skb); break; @@ -636,6 +638,7 @@ static bool valid_packet_type(u8 type) case HCI_ACLDATA_PKT: case HCI_COMMAND_PKT: case HCI_SCODATA_PKT: + case HCI_ISODATA_PKT: case HCI_3WIRE_LINK_PKT: case HCI_3WIRE_ACK_PKT: return true; -- cgit v1.2.3 From 7e8aeffb112aa1d1600ce0ee44e444b63e3d41f9 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Wed, 15 Jan 2020 13:02:21 -0800 Subject: Bluetooth: btsdio: Check for valid packet type Check for valid packet type before calling hci_recv_frame which is inline with what other drivers are doing. Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Marcel Holtmann --- drivers/bluetooth/btsdio.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) (limited to 'drivers/bluetooth') diff --git a/drivers/bluetooth/btsdio.c b/drivers/bluetooth/btsdio.c index fd9571d5fdac..199e8f7d426d 100644 --- a/drivers/bluetooth/btsdio.c +++ b/drivers/bluetooth/btsdio.c @@ -145,11 +145,20 @@ static int btsdio_rx_packet(struct btsdio_data *data) data->hdev->stat.byte_rx += len; - hci_skb_pkt_type(skb) = hdr[3]; - - err = hci_recv_frame(data->hdev, skb); - if (err < 0) - return err; + switch (hdr[3]) { + case HCI_EVENT_PKT: + case HCI_ACLDATA_PKT: + case HCI_SCODATA_PKT: + case HCI_ISODATA_PKT: + hci_skb_pkt_type(skb) = hdr[3]; + err = hci_recv_frame(data->hdev, skb); + if (err < 0) + return err; + break; + default: + kfree_skb(skb); + return -EINVAL; + } sdio_writeb(data->func, 0x00, REG_PC_RRT, NULL); -- cgit v1.2.3 From f25a96c8eb46b86744540ade61c2dbc844c23dbd Mon Sep 17 00:00:00 2001 From: Guillaume La Roque Date: Wed, 15 Jan 2020 11:12:43 +0100 Subject: Bluetooth: hci_bcm: enable IRQ capability from devicetree Add support for getting IRQ directly from DT instead of relying on converting a GPIO to IRQ. This is needed for platforms with GPIO controllers that that do not support gpiod_to_irq(). Reviewed-by: Kevin Hilman Signed-off-by: Guillaume La Roque Signed-off-by: Marcel Holtmann --- drivers/bluetooth/hci_bcm.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/bluetooth') diff --git a/drivers/bluetooth/hci_bcm.c b/drivers/bluetooth/hci_bcm.c index 769bb4404bd1..b236cb11c0dc 100644 --- a/drivers/bluetooth/hci_bcm.c +++ b/drivers/bluetooth/hci_bcm.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -1151,6 +1152,8 @@ static int bcm_of_probe(struct bcm_device *bdev) device_property_read_u32(bdev->dev, "max-speed", &bdev->oper_speed); device_property_read_u8_array(bdev->dev, "brcm,bt-pcm-int-params", bdev->pcm_int_params, 5); + bdev->irq = of_irq_get_byname(bdev->dev->of_node, "host-wakeup"); + return 0; } -- cgit v1.2.3 From 5559904ccc0867a0ce796761681e40defe4a5f44 Mon Sep 17 00:00:00 2001 From: Rocky Liao Date: Wed, 15 Jan 2020 16:55:50 +0800 Subject: Bluetooth: hci_qca: Add QCA Rome power off support to the qca_power_shutdown() Current qca_power_shutdown() only supports wcn399x, this patch adds Rome power off support to it. For Rome it just needs to pull down the bt_en GPIO to power off it. This patch also replaces all the power off operation in qca_close() with the unified qca_power_shutdown() call. Signed-off-by: Rocky Liao Reviewed-by: Matthias Kaehlcke Signed-off-by: Marcel Holtmann --- drivers/bluetooth/hci_qca.c | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) (limited to 'drivers/bluetooth') diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c index 992622dc1263..ecb74965be10 100644 --- a/drivers/bluetooth/hci_qca.c +++ b/drivers/bluetooth/hci_qca.c @@ -663,7 +663,6 @@ static int qca_flush(struct hci_uart *hu) /* Close protocol */ static int qca_close(struct hci_uart *hu) { - struct qca_serdev *qcadev; struct qca_data *qca = hu->priv; BT_DBG("hu %p qca close", hu); @@ -679,14 +678,7 @@ static int qca_close(struct hci_uart *hu) destroy_workqueue(qca->workqueue); qca->hu = NULL; - if (hu->serdev) { - qcadev = serdev_device_get_drvdata(hu->serdev); - if (qca_is_wcn399x(qcadev->btsoc_type)) - qca_power_shutdown(hu); - else - gpiod_set_value_cansleep(qcadev->bt_en, 0); - - } + qca_power_shutdown(hu); kfree_skb(qca->rx_skb); @@ -1685,6 +1677,7 @@ static void qca_power_shutdown(struct hci_uart *hu) struct qca_serdev *qcadev; struct qca_data *qca = hu->priv; unsigned long flags; + enum qca_btsoc_type soc_type = qca_soc_type(hu); qcadev = serdev_device_get_drvdata(hu->serdev); @@ -1697,11 +1690,22 @@ static void qca_power_shutdown(struct hci_uart *hu) qca_flush(hu); spin_unlock_irqrestore(&qca->hci_ibs_lock, flags); - host_set_baudrate(hu, 2400); - qca_send_power_pulse(hu, false); - qca_regulator_disable(qcadev); hu->hdev->hw_error = NULL; hu->hdev->cmd_timeout = NULL; + + /* Non-serdev device usually is powered by external power + * and don't need additional action in driver for power down + */ + if (!hu->serdev) + return; + + if (qca_is_wcn399x(soc_type)) { + host_set_baudrate(hu, 2400); + qca_send_power_pulse(hu, false); + qca_regulator_disable(qcadev); + } else { + gpiod_set_value_cansleep(qcadev->bt_en, 0); + } } static int qca_power_off(struct hci_dev *hdev) -- cgit v1.2.3 From bb2500ab0270aa0d1d39614f08f4f9a2dc0df8c1 Mon Sep 17 00:00:00 2001 From: Rocky Liao Date: Wed, 15 Jan 2020 16:55:51 +0800 Subject: Bluetooth: hci_qca: Retry btsoc initialize when it fails This patch adds the retry of btsoc initialization when it fails. There are reports that the btsoc initialization may fail on some platforms but the repro ratio is very low. The symptoms is the firmware downloading failed due to the UART write timed out. The failure may be caused by UART, platform HW or the btsoc itself but it's very difficlut to root cause, given the repro ratio is very low. Add a retry for the btsoc initialization can work around most of the failures and make Bluetooth finally works. Signed-off-by: Rocky Liao Reviewed-by: Matthias Kaehlcke Signed-off-by: Marcel Holtmann --- drivers/bluetooth/hci_qca.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'drivers/bluetooth') diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c index ecb74965be10..1139142e8eed 100644 --- a/drivers/bluetooth/hci_qca.c +++ b/drivers/bluetooth/hci_qca.c @@ -55,6 +55,9 @@ /* Controller debug log header */ #define QCA_DEBUG_HANDLE 0x2EDC +/* max retry count when init fails */ +#define MAX_INIT_RETRIES 3 + /* Controller dump header */ #define QCA_SSR_DUMP_HANDLE 0x0108 #define QCA_DUMP_PACKET_SIZE 255 @@ -1539,6 +1542,7 @@ static int qca_setup(struct hci_uart *hu) struct hci_dev *hdev = hu->hdev; struct qca_data *qca = hu->priv; unsigned int speed, qca_baudrate = QCA_BAUDRATE_115200; + unsigned int retries = 0; enum qca_btsoc_type soc_type = qca_soc_type(hu); const char *firmware_name = qca_get_firmware_name(hu); int ret; @@ -1559,6 +1563,7 @@ static int qca_setup(struct hci_uart *hu) bt_dev_info(hdev, "setting up %s", qca_is_wcn399x(soc_type) ? "wcn399x" : "ROME"); +retry: ret = qca_power_on(hdev); if (ret) return ret; @@ -1613,6 +1618,20 @@ static int qca_setup(struct hci_uart *hu) * patch/nvm-config is found, so run with original fw/config. */ ret = 0; + } else { + if (retries < MAX_INIT_RETRIES) { + qca_power_shutdown(hu); + if (hu->serdev) { + serdev_device_close(hu->serdev); + ret = serdev_device_open(hu->serdev); + if (ret) { + bt_dev_err(hdev, "failed to open port"); + return ret; + } + } + retries++; + goto retry; + } } /* Setup bdaddr */ -- cgit v1.2.3 From ae563183b647b3bdb47e8a78a5de879adf733735 Mon Sep 17 00:00:00 2001 From: Rocky Liao Date: Thu, 16 Jan 2020 11:22:54 +0800 Subject: Bluetooth: hci_qca: Enable power off/on support during hci down/up for QCA Rome This patch registers hdev->shutdown() callback and also sets HCI_QUIRK_NON_PERSISTENT_SETUP for QCA Rome. It will power-off the BT chip during hci down and power-on/initialize the chip again during hci up. As wcn399x already enabled this, this patch also removed the callback register and QUIRK setting in qca_setup() for wcn399x and uniformly do this in the probe() routine. Signed-off-by: Rocky Liao Signed-off-by: Marcel Holtmann --- drivers/bluetooth/hci_qca.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) (limited to 'drivers/bluetooth') diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c index 1139142e8eed..d6e0c99ee5eb 100644 --- a/drivers/bluetooth/hci_qca.c +++ b/drivers/bluetooth/hci_qca.c @@ -1569,12 +1569,7 @@ retry: return ret; if (qca_is_wcn399x(soc_type)) { - /* Enable NON_PERSISTENT_SETUP QUIRK to ensure to execute - * 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_read_soc_version(hdev, &soc_ver, soc_type); if (ret) @@ -1813,6 +1808,7 @@ static int qca_init_regulators(struct qca_power *qca, static int qca_serdev_probe(struct serdev_device *serdev) { struct qca_serdev *qcadev; + struct hci_dev *hdev; const struct qca_vreg_data *data; int err; @@ -1838,7 +1834,7 @@ static int qca_serdev_probe(struct serdev_device *serdev) data->num_vregs); if (err) { BT_ERR("Failed to init regulators:%d", err); - goto out; + return err; } qcadev->bt_power->vregs_on = false; @@ -1851,7 +1847,7 @@ static int qca_serdev_probe(struct serdev_device *serdev) err = hci_uart_register_device(&qcadev->serdev_hu, &qca_proto); if (err) { BT_ERR("wcn3990 serdev registration failed"); - goto out; + return err; } } else { qcadev->btsoc_type = QCA_ROME; @@ -1877,12 +1873,18 @@ static int qca_serdev_probe(struct serdev_device *serdev) return err; err = hci_uart_register_device(&qcadev->serdev_hu, &qca_proto); - if (err) + if (err) { + BT_ERR("Rome serdev registration failed"); clk_disable_unprepare(qcadev->susclk); + return err; + } } -out: return err; + hdev = qcadev->serdev_hu.hdev; + set_bit(HCI_QUIRK_NON_PERSISTENT_SETUP, &hdev->quirks); + hdev->shutdown = qca_power_off; + return 0; } static void qca_serdev_remove(struct serdev_device *serdev) -- cgit v1.2.3 From 268d3636dfb22254324774de1f8875174b3be064 Mon Sep 17 00:00:00 2001 From: Maxim Mikityanskiy Date: Fri, 24 Jan 2020 19:15:35 +0200 Subject: Bluetooth: btrtl: Use kvmalloc for FW allocations Currently, kmemdup is applied to the firmware data, and it invokes kmalloc under the hood. The firmware size and patch_length are big (more than PAGE_SIZE), and on some low-end systems (like ASUS E202SA) kmalloc may fail to allocate a contiguous chunk under high memory usage and fragmentation: Bluetooth: hci0: RTL: examining hci_ver=06 hci_rev=000a lmp_ver=06 lmp_subver=8821 Bluetooth: hci0: RTL: rom_version status=0 version=1 Bluetooth: hci0: RTL: loading rtl_bt/rtl8821a_fw.bin kworker/u9:2: page allocation failure: order:4, mode:0x40cc0(GFP_KERNEL|__GFP_COMP), nodemask=(null),cpuset=/,mems_allowed=0 As firmware load happens on each resume, Bluetooth will stop working after several iterations, when the kernel fails to allocate an order-4 page. This patch replaces kmemdup with kvmalloc+memcpy. It's not required to have a contiguous chunk here, because it's not mapped to the device directly. Signed-off-by: Maxim Mikityanskiy Signed-off-by: Marcel Holtmann --- drivers/bluetooth/btrtl.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) (limited to 'drivers/bluetooth') diff --git a/drivers/bluetooth/btrtl.c b/drivers/bluetooth/btrtl.c index f838537f9f89..577cfa3329db 100644 --- a/drivers/bluetooth/btrtl.c +++ b/drivers/bluetooth/btrtl.c @@ -370,11 +370,11 @@ static int rtlbt_parse_firmware(struct hci_dev *hdev, * the end. */ len = patch_length; - buf = kmemdup(btrtl_dev->fw_data + patch_offset, patch_length, - GFP_KERNEL); + buf = kvmalloc(patch_length, GFP_KERNEL); if (!buf) return -ENOMEM; + memcpy(buf, btrtl_dev->fw_data + patch_offset, patch_length - 4); memcpy(buf + patch_length - 4, &epatch_info->fw_version, 4); *_buf = buf; @@ -460,8 +460,10 @@ static int rtl_load_file(struct hci_dev *hdev, const char *name, u8 **buff) if (ret < 0) return ret; ret = fw->size; - *buff = kmemdup(fw->data, ret, GFP_KERNEL); - if (!*buff) + *buff = kvmalloc(fw->size, GFP_KERNEL); + if (*buff) + memcpy(*buff, fw->data, ret); + else ret = -ENOMEM; release_firmware(fw); @@ -499,14 +501,14 @@ static int btrtl_setup_rtl8723b(struct hci_dev *hdev, goto out; if (btrtl_dev->cfg_len > 0) { - tbuff = kzalloc(ret + btrtl_dev->cfg_len, GFP_KERNEL); + tbuff = kvzalloc(ret + btrtl_dev->cfg_len, GFP_KERNEL); if (!tbuff) { ret = -ENOMEM; goto out; } memcpy(tbuff, fw_data, ret); - kfree(fw_data); + kvfree(fw_data); memcpy(tbuff + ret, btrtl_dev->cfg_data, btrtl_dev->cfg_len); ret += btrtl_dev->cfg_len; @@ -519,14 +521,14 @@ static int btrtl_setup_rtl8723b(struct hci_dev *hdev, ret = rtl_download_firmware(hdev, fw_data, ret); out: - kfree(fw_data); + kvfree(fw_data); return ret; } void btrtl_free(struct btrtl_device_info *btrtl_dev) { - kfree(btrtl_dev->fw_data); - kfree(btrtl_dev->cfg_data); + kvfree(btrtl_dev->fw_data); + kvfree(btrtl_dev->cfg_data); kfree(btrtl_dev); } EXPORT_SYMBOL_GPL(btrtl_free); -- cgit v1.2.3