From 4a67e5d4adbf3b419f17924322f468ac5cb8c14f Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Mon, 25 Feb 2019 13:11:37 -0600 Subject: Bluetooth: mgmt: Use struct_size() helper Make use of the struct_size() helper instead of an open-coded version in order to avoid any potential type mistakes, in particular in the context in which this code is being used. So, change the following form: sizeof(*rp) + (sizeof(rp->entry[0]) * count); to : struct_size(rp, entry, count) Notice that, in this case, variable rp_len is not necessary, hence it is removed. This code was detected with the help of Coccinelle. Signed-off-by: Gustavo A. R. Silva Signed-off-by: Marcel Holtmann --- net/bluetooth/mgmt.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index ccce954f8146..1e2acaddcdfd 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -474,7 +474,6 @@ static int read_ext_index_list(struct sock *sk, struct hci_dev *hdev, { struct mgmt_rp_read_ext_index_list *rp; struct hci_dev *d; - size_t rp_len; u16 count; int err; @@ -488,8 +487,7 @@ static int read_ext_index_list(struct sock *sk, struct hci_dev *hdev, count++; } - rp_len = sizeof(*rp) + (sizeof(rp->entry[0]) * count); - rp = kmalloc(rp_len, GFP_ATOMIC); + rp = kmalloc(struct_size(rp, entry, count), GFP_ATOMIC); if (!rp) { read_unlock(&hci_dev_list_lock); return -ENOMEM; @@ -525,7 +523,6 @@ static int read_ext_index_list(struct sock *sk, struct hci_dev *hdev, } rp->num_controllers = cpu_to_le16(count); - rp_len = sizeof(*rp) + (sizeof(rp->entry[0]) * count); read_unlock(&hci_dev_list_lock); @@ -538,7 +535,8 @@ static int read_ext_index_list(struct sock *sk, struct hci_dev *hdev, hci_sock_clear_flag(sk, HCI_MGMT_UNCONF_INDEX_EVENTS); err = mgmt_cmd_complete(sk, MGMT_INDEX_NONE, - MGMT_OP_READ_EXT_INDEX_LIST, 0, rp, rp_len); + MGMT_OP_READ_EXT_INDEX_LIST, 0, rp, + struct_size(rp, entry, count)); kfree(rp); -- cgit v1.2.3-59-g8ed1b From 56897b217a1d0a91c9920cb418d6b3fe922f590a Mon Sep 17 00:00:00 2001 From: Kefeng Wang Date: Sat, 23 Feb 2019 12:33:27 +0800 Subject: Bluetooth: hci_ldisc: Postpone HCI_UART_PROTO_READY bit set in hci_uart_set_proto() task A: task B: hci_uart_set_proto flush_to_ldisc - p->open(hu) -> h5_open //alloc h5 - receive_buf - set_bit HCI_UART_PROTO_READY - tty_port_default_receive_buf - hci_uart_register_dev - tty_ldisc_receive_buf - hci_uart_tty_receive - test_bit HCI_UART_PROTO_READY - h5_recv - clear_bit HCI_UART_PROTO_READY while() { - p->open(hu) -> h5_close //free h5 - h5_rx_3wire_hdr - h5_reset() //use-after-free } It could use ioctl to set hci uart proto, but there is a use-after-free issue when hci_uart_register_dev() fail in hci_uart_set_proto(), see stack above, fix this by setting HCI_UART_PROTO_READY bit only when hci_uart_register_dev() return success. Reported-by: syzbot+899a33dc0fa0dbaf06a6@syzkaller.appspotmail.com Signed-off-by: Kefeng Wang Reviewed-by: Jeremy Cline Signed-off-by: Marcel Holtmann --- drivers/bluetooth/hci_ldisc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index 4918fefc4a6f..9562e72c1ae5 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -696,14 +696,13 @@ static int hci_uart_set_proto(struct hci_uart *hu, int id) return -EPROTONOSUPPORT; 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); return err; } + set_bit(HCI_UART_PROTO_READY, &hu->flags); return 0; } -- cgit v1.2.3-59-g8ed1b From 7a0e5b15ca458dd47e4c60b7fa9f22b84c7068c7 Mon Sep 17 00:00:00 2001 From: Matthias Kaehlcke Date: Tue, 19 Feb 2019 12:05:57 -0800 Subject: Bluetooth: Add quirk for reading BD_ADDR from fwnode property Add HCI_QUIRK_USE_BDADDR_PROPERTY to allow controllers to retrieve the public Bluetooth address from the firmware node property 'local-bd-address'. If quirk is set and the property does not exist or is invalid the controller is marked as unconfigured. Signed-off-by: Matthias Kaehlcke Reviewed-by: Balakrishna Godavarthi Tested-by: Balakrishna Godavarthi Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci.h | 12 ++++++++++++ net/bluetooth/hci_core.c | 43 +++++++++++++++++++++++++++++++++++++++++++ net/bluetooth/mgmt.c | 6 ++++-- 3 files changed, 59 insertions(+), 2 deletions(-) diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index c36dc1e20556..fbba43e9bef5 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -158,6 +158,18 @@ enum { */ HCI_QUIRK_INVALID_BDADDR, + /* When this quirk is set, the public Bluetooth address + * initially reported by HCI Read BD Address command + * is considered invalid. The public BD Address can be + * specified in the fwnode property 'local-bd-address'. + * If this property does not exist or is invalid controller + * configuration is required before this device can be used. + * + * This quirk can be set before hci_register_dev is called or + * during the hdev->setup vendor callback. + */ + HCI_QUIRK_USE_BDADDR_PROPERTY, + /* When this quirk is set, the duplicate filtering during * scanning is based on Bluetooth devices addresses. To allow * RSSI based updates, restart scanning if needed. diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 26e3d36aee29..d6b2540ba7f8 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -1355,6 +1356,32 @@ done: return err; } +/** + * hci_dev_get_bd_addr_from_property - Get the Bluetooth Device Address + * (BD_ADDR) for a HCI device from + * a firmware node property. + * @hdev: The HCI device + * + * Search the firmware node for 'local-bd-address'. + * + * All-zero BD addresses are rejected, because those could be properties + * that exist in the firmware tables, but were not updated by the firmware. For + * example, the DTS could define 'local-bd-address', with zero BD addresses. + */ +static void hci_dev_get_bd_addr_from_property(struct hci_dev *hdev) +{ + struct fwnode_handle *fwnode = dev_fwnode(hdev->dev.parent); + bdaddr_t ba; + int ret; + + ret = fwnode_property_read_u8_array(fwnode, "local-bd-address", + (u8 *)&ba, sizeof(ba)); + if (ret < 0 || !bacmp(&ba, BDADDR_ANY)) + return; + + bacpy(&hdev->public_addr, &ba); +} + static int hci_dev_do_open(struct hci_dev *hdev) { int ret = 0; @@ -1422,6 +1449,22 @@ static int hci_dev_do_open(struct hci_dev *hdev) if (hdev->setup) ret = hdev->setup(hdev); + if (ret) + goto setup_failed; + + if (test_bit(HCI_QUIRK_USE_BDADDR_PROPERTY, &hdev->quirks)) { + if (!bacmp(&hdev->public_addr, BDADDR_ANY)) + hci_dev_get_bd_addr_from_property(hdev); + + if (bacmp(&hdev->public_addr, BDADDR_ANY) && + hdev->set_bdaddr) + ret = hdev->set_bdaddr(hdev, + &hdev->public_addr); + else + ret = -EADDRNOTAVAIL; + } + +setup_failed: /* The transport driver can set these quirks before * creating the HCI device or in its setup callback. * diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 1e2acaddcdfd..2457f408d17d 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -549,7 +549,8 @@ static bool is_configured(struct hci_dev *hdev) !hci_dev_test_flag(hdev, HCI_EXT_CONFIGURED)) return false; - if (test_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks) && + if ((test_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks) || + test_bit(HCI_QUIRK_USE_BDADDR_PROPERTY, &hdev->quirks)) && !bacmp(&hdev->public_addr, BDADDR_ANY)) return false; @@ -564,7 +565,8 @@ static __le32 get_missing_options(struct hci_dev *hdev) !hci_dev_test_flag(hdev, HCI_EXT_CONFIGURED)) options |= MGMT_OPTION_EXTERNAL_CONFIG; - if (test_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks) && + if ((test_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks) || + test_bit(HCI_QUIRK_USE_BDADDR_PROPERTY, &hdev->quirks)) && !bacmp(&hdev->public_addr, BDADDR_ANY)) options |= MGMT_OPTION_PUBLIC_ADDRESS; -- cgit v1.2.3-59-g8ed1b From de79a9df169269d6481b39d46d14768337ee4463 Mon Sep 17 00:00:00 2001 From: Matthias Kaehlcke Date: Tue, 19 Feb 2019 12:05:58 -0800 Subject: Bluetooth: btqcomsmd: use HCI_QUIRK_USE_BDADDR_PROPERTY Use the HCI_QUIRK_USE_BDADDR_PROPERTY quirk to let the HCI core handle the reading of 'local-bd-address'. With this there is no need to set HCI_QUIRK_INVALID_BDADDR, the case of a non-existing or invalid fwnode property is handled by the core code. Signed-off-by: Matthias Kaehlcke Reviewed-by: Balakrishna Godavarthi Signed-off-by: Marcel Holtmann --- drivers/bluetooth/btqcomsmd.c | 31 +++---------------------------- 1 file changed, 3 insertions(+), 28 deletions(-) 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; -- cgit v1.2.3-59-g8ed1b From 5971752de44c8e1e3964c12185bcc3e9017e86d3 Mon Sep 17 00:00:00 2001 From: Matthias Kaehlcke Date: Tue, 19 Feb 2019 12:05:59 -0800 Subject: Bluetooth: hci_qca: Set HCI_QUIRK_USE_BDADDR_PROPERTY for wcn3990 Set quirk for wcn3990 to read BD_ADDR from a firmware node property. Signed-off-by: Matthias Kaehlcke Tested-by: Balakrishna Godavarthi Signed-off-by: Marcel Holtmann --- drivers/bluetooth/hci_qca.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c index 5e03504c4e0c..26efc2ef98d9 100644 --- a/drivers/bluetooth/hci_qca.c +++ b/drivers/bluetooth/hci_qca.c @@ -1192,6 +1192,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) -- cgit v1.2.3-59-g8ed1b From 9836b80208b2253ae8f1130a93566bb2f6d505a9 Mon Sep 17 00:00:00 2001 From: Matthias Kaehlcke Date: Tue, 26 Feb 2019 11:46:45 -0800 Subject: Bluetooth: hci_qca: Pass boolean 'on/off' to qca_send_power_pulse() There are only two types of power pulses 'on' or 'off', pass a boolean instead of the power pulse 'command'. Signed-off-by: Matthias Kaehlcke Reviewed-by: Balakrishna Godavarthi Signed-off-by: Marcel Holtmann --- drivers/bluetooth/hci_qca.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c index 26efc2ef98d9..9be3769a4042 100644 --- a/drivers/bluetooth/hci_qca.c +++ b/drivers/bluetooth/hci_qca.c @@ -1004,10 +1004,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_uart *hu, u8 cmd) +static int qca_send_power_pulse(struct hci_uart *hu, bool on) { int ret; int timeout = msecs_to_jiffies(POWER_PULSE_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 @@ -1138,12 +1139,12 @@ 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(hu, 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(hu, QCA_WCN3990_POWERON_PULSE); + ret = qca_send_power_pulse(hu, true); if (ret) return ret; @@ -1290,7 +1291,7 @@ static void qca_power_shutdown(struct hci_uart *hu) spin_unlock_irqrestore(&qca->hci_ibs_lock, flags); host_set_baudrate(hu, 2400); - qca_send_power_pulse(hu, QCA_WCN3990_POWEROFF_PULSE); + qca_send_power_pulse(hu, false); qca_power_setup(hu, false); } -- cgit v1.2.3-59-g8ed1b From ad571d725c9786a138d663fb26ae91d84e705900 Mon Sep 17 00:00:00 2001 From: Matthias Kaehlcke Date: Tue, 26 Feb 2019 11:46:46 -0800 Subject: Bluetooth: hci_qca: Move boot delay to qca_send_power_pulse() After sending a power on pulse the driver has a delay of 100ms to allow the host controller to boot. Move the delay into qca_send_power_pulse(), since it is directly related with the power-on pulse. Signed-off-by: Matthias Kaehlcke Reviewed-by: Balakrishna Godavarthi Signed-off-by: Marcel Holtmann --- drivers/bluetooth/hci_qca.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c index 9be3769a4042..bcc70352eb95 100644 --- a/drivers/bluetooth/hci_qca.c +++ b/drivers/bluetooth/hci_qca.c @@ -1036,6 +1036,9 @@ static int qca_send_power_pulse(struct hci_uart *hu, bool on) usleep_range(100, 200); hci_uart_set_flow_control(hu, false); + if (on) + msleep(100); + return 0; } @@ -1148,9 +1151,6 @@ static int qca_wcn3990_init(struct hci_uart *hu) 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 -- cgit v1.2.3-59-g8ed1b From 0ebcddd8e06e94e476f4718af238e9ae67531b04 Mon Sep 17 00:00:00 2001 From: Matthias Kaehlcke Date: Tue, 26 Feb 2019 11:46:47 -0800 Subject: Bluetooth: hci_qca: Add delay after power-off pulse During initialization the power-on pulse is currently sent inmediately after the prior power-off pulse. With this initialization often fails at boot time: [ 15.205224] Bluetooth: hci0: setting up wcn3990 [ 17.341062] Bluetooth: hci0: command 0xfc00 tx timeout [ 22.101453] ERROR: Bluetooth initialization failed [ 25.337740] Bluetooth: hci0: Reading QCA version information failed (-110) After a power-off pulse wait 10ms to give the controller time to power off. Remove the previous short settling delay, it isn't needed anymore. Signed-off-by: Matthias Kaehlcke Reviewed-by: Balakrishna Godavarthi Signed-off-by: Marcel Holtmann --- drivers/bluetooth/hci_qca.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c index bcc70352eb95..f56d2459ea19 100644 --- a/drivers/bluetooth/hci_qca.c +++ b/drivers/bluetooth/hci_qca.c @@ -1031,13 +1031,13 @@ static int qca_send_power_pulse(struct hci_uart *hu, bool on) } serdev_device_wait_until_sent(hu->serdev, timeout); - - /* Wait for 100 uS for SoC to settle down */ - usleep_range(100, 200); hci_uart_set_flow_control(hu, false); + /* Give to controller time to boot/shutdown */ if (on) msleep(100); + else + msleep(10); return 0; } -- cgit v1.2.3-59-g8ed1b From 6d10cd5cbd6cbd0c4f401bbf3e121ef2240d989b Mon Sep 17 00:00:00 2001 From: Matthias Kaehlcke Date: Tue, 26 Feb 2019 12:08:47 -0800 Subject: Bluetooth: hci_qca: Use msleep() instead of open coding it Call msleep() in qca_set_baudrate() instead of reimplementing it. Signed-off-by: Matthias Kaehlcke Signed-off-by: Marcel Holtmann --- drivers/bluetooth/hci_qca.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c index f56d2459ea19..82f6cec4f71e 100644 --- a/drivers/bluetooth/hci_qca.c +++ b/drivers/bluetooth/hci_qca.c @@ -989,9 +989,7 @@ static int qca_set_baudrate(struct hci_dev *hdev, uint8_t baudrate) * 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); + msleep(BAUDRATE_SETTLE_TIMEOUT_MS); return 0; } -- cgit v1.2.3-59-g8ed1b From 4c409af04d766e05f154c0e87381dc02858fb78e Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Fri, 22 Feb 2019 14:53:43 -0800 Subject: Bluetooth: btusb: add QCA6174A compatible properties We may need to specify a GPIO wake pin for this device, so add a compatible property for it. There are at least to USB PID/VID variations of this chip: one with a Lite-On ID and one with an Atheros ID. Signed-off-by: Brian Norris Reviewed-by: Matthias Kaehlcke Signed-off-by: Marcel Holtmann --- drivers/bluetooth/btusb.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index d4c8d989e714..ded198328f21 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -2917,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); -- cgit v1.2.3-59-g8ed1b From 7d19261bc0eb35080231f109687d119b183abab8 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Fri, 22 Feb 2019 14:53:44 -0800 Subject: dt-bindings: net: btusb: add QCA6174A IDs There are two USB PID/VID variations I've seen for this chip, and I want to utilize the 'interrupts' property defined here already. Signed-off-by: Brian Norris Reviewed-by: Matthias Kaehlcke Reviewed-by: Rob Herring Signed-off-by: Marcel Holtmann --- Documentation/devicetree/bindings/net/btusb.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/net/btusb.txt b/Documentation/devicetree/bindings/net/btusb.txt index 37d67926dd6d..b1ad6ee68e90 100644 --- a/Documentation/devicetree/bindings/net/btusb.txt +++ b/Documentation/devicetree/bindings/net/btusb.txt @@ -9,6 +9,9 @@ Required properties: (more may be added later) are: "usb1286,204e" (Marvell 8997) + "usbcf3,e300" (Qualcomm QCA6174A) + "usb4ca,301a" (Qualcomm QCA6174A (Lite-On)) + Also, vendors that use btusb may have device additional properties, e.g: Documentation/devicetree/bindings/net/marvell-bt-8xxx.txt -- cgit v1.2.3-59-g8ed1b From 5364a0b4f4be0b429836426c4a43f2a9ff9597b2 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Fri, 22 Feb 2019 14:53:45 -0800 Subject: arm64: dts: rockchip: move QCA6174A wakeup pin into its USB node Currently, we don't coordinate BT USB activity with our handling of the BT out-of-band wake pin, and instead just use gpio-keys. That causes problems because we have no way of distinguishing wake activity due to a BT device (e.g., mouse) vs. the BT controller (e.g., re-configuring wake mask before suspend). This can cause spurious wake events just because we, for instance, try to reconfigure the host controller's event mask before suspending. We can avoid these synchronization problems by handling the BT wake pin directly in the btusb driver -- for all activity up until BT controller suspend(), we simply listen to normal USB activity (e.g., to know the difference between device and host activity); once we're really ready to suspend the host controller, there should be no more host activity, and only *then* do we unmask the GPIO interrupt. This is already supported by btusb; we just need to describe the wake pin in the right node. We list 2 compatible properties, since both PID/VID pairs show up on Scarlet devices, and they're both essentially identical QCA6174A-based modules. Also note that the polarity was wrong before: Qualcomm implemented WAKE as active high, not active low. We only got away with this because gpio-keys always reconfigured us as bi-directional edge-triggered. Finally, we have an external pull-up and a level-shifter on this line (we didn't notice Qualcomm's polarity in the initial design), so we can't do pull-down. Switch to pull-none. Signed-off-by: Brian Norris Reviewed-by: Matthias Kaehlcke Signed-off-by: Marcel Holtmann --- .../boot/dts/rockchip/rk3399-gru-chromebook.dtsi | 13 ++++++ .../boot/dts/rockchip/rk3399-gru-scarlet.dtsi | 46 ++++++++++++++-------- arch/arm64/boot/dts/rockchip/rk3399-gru.dtsi | 13 ------ 3 files changed, 42 insertions(+), 30 deletions(-) diff --git a/arch/arm64/boot/dts/rockchip/rk3399-gru-chromebook.dtsi b/arch/arm64/boot/dts/rockchip/rk3399-gru-chromebook.dtsi index c400be64170e..931640e9aed4 100644 --- a/arch/arm64/boot/dts/rockchip/rk3399-gru-chromebook.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3399-gru-chromebook.dtsi @@ -200,6 +200,19 @@ pinctrl-0 = <&bl_en>; pwm-delay-us = <10000>; }; + + gpio_keys: gpio-keys { + compatible = "gpio-keys"; + pinctrl-names = "default"; + pinctrl-0 = <&bt_host_wake_l>; + + wake_on_bt: wake-on-bt { + label = "Wake-on-Bluetooth"; + gpios = <&gpio0 3 GPIO_ACTIVE_LOW>; + linux,code = ; + wakeup-source; + }; + }; }; &ppvar_bigcpu { diff --git a/arch/arm64/boot/dts/rockchip/rk3399-gru-scarlet.dtsi b/arch/arm64/boot/dts/rockchip/rk3399-gru-scarlet.dtsi index fc50b3ef758c..62ea7d6a7d4a 100644 --- a/arch/arm64/boot/dts/rockchip/rk3399-gru-scarlet.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3399-gru-scarlet.dtsi @@ -175,6 +175,21 @@ pinctrl-0 = <&dmic_en>; wakeup-delay-ms = <250>; }; + + gpio_keys: gpio-keys { + compatible = "gpio-keys"; + pinctrl-names = "default"; + pinctrl-0 = <&pen_eject_odl>; + + pen-insert { + label = "Pen Insert"; + /* Insert = low, eject = high */ + gpios = <&gpio1 1 GPIO_ACTIVE_LOW>; + linux,code = ; + linux,input-type = ; + wakeup-source; + }; + }; }; /* pp900_s0 aliases */ @@ -328,20 +343,6 @@ camera: &i2c7 { <400000000>; }; -&gpio_keys { - pinctrl-names = "default"; - pinctrl-0 = <&bt_host_wake_l>, <&pen_eject_odl>; - - pen-insert { - label = "Pen Insert"; - /* Insert = low, eject = high */ - gpios = <&gpio1 1 GPIO_ACTIVE_LOW>; - linux,code = ; - linux,input-type = ; - wakeup-source; - }; -}; - &i2c_tunnel { google,remote-bus = <0>; }; @@ -437,8 +438,19 @@ camera: &i2c7 { status = "okay"; }; -&wake_on_bt { - gpios = <&gpio1 2 GPIO_ACTIVE_LOW>; +&usb_host0_ohci { + #address-cells = <1>; + #size-cells = <0>; + + qca_bt: bluetooth@1 { + compatible = "usbcf3,e300", "usb4ca,301a"; + reg = <1>; + pinctrl-names = "default"; + pinctrl-0 = <&bt_host_wake_l>; + interrupt-parent = <&gpio1>; + interrupts = <2 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "wakeup"; + }; }; /* PINCTRL OVERRIDES */ @@ -455,7 +467,7 @@ camera: &i2c7 { }; &bt_host_wake_l { - rockchip,pins = <1 2 RK_FUNC_GPIO &pcfg_pull_up>; + rockchip,pins = <1 2 RK_FUNC_GPIO &pcfg_pull_none>; }; &ec_ap_int_l { diff --git a/arch/arm64/boot/dts/rockchip/rk3399-gru.dtsi b/arch/arm64/boot/dts/rockchip/rk3399-gru.dtsi index ea607a601a86..da03fa9c5662 100644 --- a/arch/arm64/boot/dts/rockchip/rk3399-gru.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3399-gru.dtsi @@ -269,19 +269,6 @@ #clock-cells = <0>; }; - gpio_keys: gpio-keys { - compatible = "gpio-keys"; - pinctrl-names = "default"; - pinctrl-0 = <&bt_host_wake_l>; - - wake_on_bt: wake-on-bt { - label = "Wake-on-Bluetooth"; - gpios = <&gpio0 3 GPIO_ACTIVE_LOW>; - linux,code = ; - wakeup-source; - }; - }; - max98357a: max98357a { compatible = "maxim,max98357a"; pinctrl-names = "default"; -- cgit v1.2.3-59-g8ed1b From 94d66714739242d5f6f7410bf044239e33226073 Mon Sep 17 00:00:00 2001 From: Matthias Kaehlcke Date: Wed, 27 Feb 2019 15:52:23 -0800 Subject: Bluetooth: hci_qca: Reduce delay after sending baudrate request for WCN3990 The current 300ms delay after a baudrate change is extremely long. For WCN3990 it is sufficient to wait 10ms after the baudrate change request has been sent over the wire. Signed-off-by: Matthias Kaehlcke Signed-off-by: Marcel Holtmann --- drivers/bluetooth/hci_qca.c | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c index 82f6cec4f71e..237aea34b69f 100644 --- a/drivers/bluetooth/hci_qca.c +++ b/drivers/bluetooth/hci_qca.c @@ -59,8 +59,7 @@ #define IBS_WAKE_RETRANS_TIMEOUT_MS 100 #define IBS_TX_IDLE_TIMEOUT_MS 2000 -#define BAUDRATE_SETTLE_TIMEOUT_MS 300 -#define POWER_PULSE_TRANS_TIMEOUT_MS 100 +#define CMD_TRANS_TIMEOUT_MS 100 /* susclk rate */ #define SUSCLK_RATE_32KHZ 32768 @@ -964,6 +963,7 @@ 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 qca_serdev *qcadev; struct sk_buff *skb; u8 cmd[] = { 0x01, 0x48, 0xFC, 0x01, 0x00 }; @@ -985,11 +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 - */ - msleep(BAUDRATE_SETTLE_TIMEOUT_MS); + 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) + msleep(10); + else + msleep(300); return 0; } @@ -1005,7 +1015,7 @@ static inline void host_set_baudrate(struct hci_uart *hu, unsigned int speed) static int qca_send_power_pulse(struct hci_uart *hu, bool on) { int ret; - int timeout = msecs_to_jiffies(POWER_PULSE_TRANS_TIMEOUT_MS); + 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 -- cgit v1.2.3-59-g8ed1b From b805c403c859756175fefc213065125da16b808d Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Fri, 1 Mar 2019 10:14:07 +0800 Subject: dt-bindings: net: bluetooth: add support for MediaTek MT7663U and MT7668U UART devices Update binding document with adding support of MT7663U and MT7668U UART devices to mediatek-bluetooth. Reviewed-by: Rob Herring Signed-off-by: Sean Wang Signed-off-by: Marcel Holtmann --- .../devicetree/bindings/net/mediatek-bluetooth.txt | 64 ++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/Documentation/devicetree/bindings/net/mediatek-bluetooth.txt b/Documentation/devicetree/bindings/net/mediatek-bluetooth.txt index 14ceb2a5b4e8..41a7dcc80f5b 100644 --- a/Documentation/devicetree/bindings/net/mediatek-bluetooth.txt +++ b/Documentation/devicetree/bindings/net/mediatek-bluetooth.txt @@ -33,3 +33,67 @@ Example: clock-names = "ref"; }; }; + +MediaTek UART based Bluetooth Devices +================================== + +This device is a serial attached device to UART device and thus it must be a +child node of the serial node with UART. + +Please refer to the following documents for generic properties: + + Documentation/devicetree/bindings/serial/slave-device.txt + +Required properties: + +- compatible: Must be + "mediatek,mt7663u-bluetooth": for MT7663U device + "mediatek,mt7668u-bluetooth": for MT7668U device +- vcc-supply: Main voltage regulator +- pinctrl-names: Should be "default", "runtime" +- pinctrl-0: Should contain UART RXD low when the device is powered up to + enter proper bootstrap mode. +- pinctrl-1: Should contain UART mode pin ctrl + +Optional properties: + +- reset-gpios: GPIO used to reset the device whose initial state keeps low, + if the GPIO is missing, then board-level design should be + guaranteed. +- current-speed: Current baud rate of the device whose defaults to 921600 + +Example: + + uart1_pins_boot: uart1-default { + pins-dat { + pinmux = ; + output-low; + }; + }; + + uart1_pins_runtime: uart1-runtime { + pins-dat { + pinmux = , + ; + }; + }; + + uart1: serial@11003000 { + compatible = "mediatek,mt7623-uart", + "mediatek,mt6577-uart"; + reg = <0 0x11003000 0 0x400>; + interrupts = ; + clocks = <&pericfg CLK_PERI_UART1_SEL>, + <&pericfg CLK_PERI_UART1>; + clock-names = "baud", "bus"; + + bluetooth { + compatible = "mediatek,mt7663u-bluetooth"; + vcc-supply = <®_5v>; + reset-gpios = <&pio 24 GPIO_ACTIVE_LOW>; + pinctrl-names = "default", "runtime"; + pinctrl-0 = <&uart1_pins_boot>; + pinctrl-1 = <&uart1_pins_runtime>; + current-speed = <921600>; + }; + }; -- cgit v1.2.3-59-g8ed1b From 22eaf6c9946ae269061d95bb9ceee02524166474 Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Sun, 3 Mar 2019 02:44:09 +0800 Subject: Bluetooth: mediatek: add support for MediaTek MT7663U and MT7668U UART devices This adds the support of enabling MT7663U and MT7668U Bluetooth function running on the top of btmtkuart driver. There are a few differences between MT766[3,8]U and MT7622 where MT766[3,8]U are standalone devices based on UART transport while MT7622 bluetooth is a built-in device on MediaTek SoC communicating with the host through BTIF serial transport. Thus, extra setup sequence is necessary for these standalone devices such as remote regulator and reset control via GPIO, baud rate changing handshake between the host and device and so on. Signed-off-by: Sean Wang Signed-off-by: Marcel Holtmann --- drivers/bluetooth/btmtkuart.c | 281 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 271 insertions(+), 10 deletions(-) diff --git a/drivers/bluetooth/btmtkuart.c b/drivers/bluetooth/btmtkuart.c index e73b1013ba73..b0b680dd69f4 100644 --- a/drivers/bluetooth/btmtkuart.c +++ b/drivers/bluetooth/btmtkuart.c @@ -12,11 +12,15 @@ #include #include #include +#include #include #include #include #include +#include +#include #include +#include #include #include @@ -25,18 +29,26 @@ #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_SEMAPHORE = 0x17, @@ -57,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; @@ -100,6 +117,14 @@ struct btmtkuart_dev { 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; @@ -110,8 +135,15 @@ struct btmtkuart_dev { u8 stp_pad[6]; u8 stp_cursor; u16 stp_dlen; + + const struct btmtkuart_data *data; }; +#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) { @@ -202,7 +234,7 @@ err_free_skb: 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; @@ -211,7 +243,7 @@ static int mtk_setup_fw(struct hci_dev *hdev) 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; @@ -523,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; @@ -546,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; } @@ -606,8 +657,74 @@ static int btmtkuart_func_query(struct hci_dev *hdev) 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, ¶m, 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; @@ -618,6 +735,28 @@ static int btmtkuart_setup(struct hci_dev *hdev) 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; @@ -637,7 +776,7 @@ static int btmtkuart_setup(struct hci_dev *hdev) } /* 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; @@ -754,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); @@ -798,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) @@ -812,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); @@ -840,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); -- cgit v1.2.3-59-g8ed1b