From patchwork Mon Jan 1 20:42:17 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Martin Blumenstingl X-Patchwork-Id: 10139583 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 30723601A1 for ; Mon, 1 Jan 2018 20:43:28 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 223F528639 for ; Mon, 1 Jan 2018 20:43:28 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 16B55288D1; Mon, 1 Jan 2018 20:43:28 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-4.2 required=2.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, DKIM_VALID, FREEMAIL_FROM, RCVD_IN_DNSWL_MED autolearn=ham version=3.3.1 Received: from bombadil.infradead.org (bombadil.infradead.org [65.50.211.133]) (using TLSv1.2 with cipher AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 432AE28639 for ; Mon, 1 Jan 2018 20:43:27 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:MIME-Version:Cc:List-Subscribe: List-Help:List-Post:List-Archive:List-Unsubscribe:List-Id:References: In-Reply-To:Message-Id:Date:Subject:To:From:Reply-To:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Owner; bh=+VxzYoYt5fV088RXy8PiCpvvA5o7JooPpXGB3pibp4U=; b=SzNfutvuefqn5iKdUsM2yPIhjH ZEoAz9LjwGkzJPX4VvpqH+8Dxhb20LBz8YIwhTG3ZekbrVrvd1zALRn+/LMmr6d6lOeB4xhqOJ73r AOjSFWWXZqTE+1n61oZF6SawFpWkMwCwoxU8wwoTDMj/p2c/Irjm6fqGFOd41czDHyoPOtW5rE+7T cAtSfEFtmnbx0t+mqxdN0rL8S5Yg5N2b0h/1/l/P3QsAcbKwRINel4OkPKJMAT7u/EdItH7E3I5Ks yOBEgjeR6lE3b7pOo6b/lcaBhx+pRRuWcZS9kE6W6HPmNH8oK3e66tWke5GwFyMyPZ0oWjXmlhB6B OsLe8MbQ==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.89 #1 (Red Hat Linux)) id 1eW6vU-0003Ns-HK; Mon, 01 Jan 2018 20:43:16 +0000 Received: from mail-wm0-x244.google.com ([2a00:1450:400c:c09::244]) by bombadil.infradead.org with esmtps (Exim 4.89 #1 (Red Hat Linux)) id 1eW6v6-00034d-9I for linux-amlogic@lists.infradead.org; Mon, 01 Jan 2018 20:43:08 +0000 Received: by mail-wm0-x244.google.com with SMTP id g75so58740858wme.0 for ; Mon, 01 Jan 2018 12:42:45 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=googlemail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=uyow6xA2APCE+GAKypw5wPbdHUtyhMlKo7XPJrnzYgE=; b=I6L9QQdBigi2Kj2uVzbpDg6tOyePC4jGl+oWscPSDXcZUZBJ+zsadm5KjTByYRi8iu MoFVstgTJJnisnjSx5tiQtZjs+3fqil9ksxDlv6TWCdECZeRWHkJ+k+t+zHnrym4FwV5 +w/BA+/K5pgqg++byifXvpZEadVr60tY1cKMlYWcq7Zeq32/TmBDywKvbVtHO7d13U4E 5sAj7c+v+8rs1jFIHpXO4uh0+BIIlrcmuZFw1whjpR19EWqvT5Lm2BfMgbuTnSbHOFJf Q71DaTZti2lHLWeM9KZa6scH2MrbdCHwS9giNjXSlcbHuShgI4cTgv6qPp8qcAOA7WdP 2NwQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=uyow6xA2APCE+GAKypw5wPbdHUtyhMlKo7XPJrnzYgE=; b=HNgh2cU8ZsUHDD13zsg7MVdazC03C3Kfs0+CnwX9kaS4J8UAQerit7nHLzIrcksmR/ w2vKk8lFYYAdJnHJjJtUy/TC5+8jNwcDTYFpxrZU7bR8mr8N0BnK4jBLVLyxLicuLYPT Y8LxeITHnnmt2R19ibptX/hDunwFqHyUsRONjH4f1N8uSC8Hz9N+DiF4dL8T5oP/0NDy mVn7PdvRbV8QtQcOYF8L99iGxZ8se2FWexjYdbv1AEVzg26oPST9jHZCXa/h3ST8KYFX f09ofLSZSr2keihyS+YCKi7XNpOL4vUM7DzsiXnkjDImm8sEJL6A4cC4G+S54GnyAwEI fYPQ== X-Gm-Message-State: AKGB3mJngiwhkLymdC7KVxEaDP3HB+z3/lH4CqZvSyiytiu00EnZoUdb hQTPYTgn+nKE3GPiFqin/LY= X-Google-Smtp-Source: ACJfBot3IaDkUYXhh2cExqfy5E0tjHgV0uss00x1xF0wwNm/NuggKQV+GxnJL06LAVLM/U9SNXtyTw== X-Received: by 10.28.111.15 with SMTP id k15mr33887541wmc.139.1514839364363; Mon, 01 Jan 2018 12:42:44 -0800 (PST) Received: from blackbox.darklights.net (p5DE386EE.dip0.t-ipconnect.de. [93.227.134.238]) by smtp.googlemail.com with ESMTPSA id c53sm57657032wrg.10.2018.01.01.12.42.42 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Mon, 01 Jan 2018 12:42:43 -0800 (PST) From: Martin Blumenstingl To: robh+dt@kernel.org, devicetree@vger.kernel.org, linux-bluetooth@vger.kernel.org, linux-serial@vger.kernel.org Subject: [RFC v2 9/9] Bluetooth: hci_h5: add support for Realtek UART Bluetooth modules Date: Mon, 1 Jan 2018 21:42:17 +0100 Message-Id: <20180101204217.26165-10-martin.blumenstingl@googlemail.com> X-Mailer: git-send-email 2.15.1 In-Reply-To: <20180101204217.26165-1-martin.blumenstingl@googlemail.com> References: <20180101204217.26165-1-martin.blumenstingl@googlemail.com> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20180101_124253_285703_0BC7DCD1 X-CRM114-Status: GOOD ( 21.86 ) X-BeenThere: linux-amlogic@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: mark.rutland@arm.com, johan.hedberg@gmail.com, gregkh@linuxfoundation.org, Martin Blumenstingl , gustavo@padovan.org, marcel@holtmann.org, johan@kernel.org, drake@endlessm.com, carlo@endlessm.com, jslaby@suse.com, linux-amlogic@lists.infradead.org, Larry.Finger@lwfinger.net MIME-Version: 1.0 Sender: "linux-amlogic" Errors-To: linux-amlogic-bounces+patchwork-linux-amlogic=patchwork.kernel.org@lists.infradead.org X-Virus-Scanned: ClamAV using ClamSMTP Realtek RTL8723BS and RTL8723DS are SDIO wifi chips with an embedded Bluetooth controller which connects to the host via UART. The H5 protocol is used for communication between host and device. The Realtek "rtl8723bs_bt" and "rtl8723ds_bt" userspace Bluetooth UART initialization tools (rtk_hciattach) use the following sequence: 1) send H5 sync pattern (already supported by hci_h5) 2) get LMP version (already supported by btrtl) 3) get ROM version (already supported by btrtl) 4) load the firmware and config for the current chipset (already supported by btrtl) 5) read UART settings from the config blob (already supported by btrtl) 6) send UART settings via a vendor command to the device (which changes the baudrate of the device and enables or disables flow control depending on the config) 7) change the baudrate and flow control settings on the host 8) send the firmware and config blob to the device (already supported by btrtl) This uses the serdev library as well as the existing btrtl driver to initialize the Bluetooth functionality, which consists of: - identifying the device and loading the corresponding firmware and config blobs (steps #2, #3 and #4) - configuring the baudrate and flow control (steps #6 and #7) - uploading the firmware to the device (step #8) Signed-off-by: Martin Blumenstingl --- drivers/bluetooth/Kconfig | 1 + drivers/bluetooth/hci_h5.c | 205 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 206 insertions(+) diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig index 60e1c7d6986d..3001f1200c72 100644 --- a/drivers/bluetooth/Kconfig +++ b/drivers/bluetooth/Kconfig @@ -146,6 +146,7 @@ config BT_HCIUART_LL config BT_HCIUART_3WIRE bool "Three-wire UART (H5) protocol support" depends on BT_HCIUART + select BT_RTL if SERIAL_DEV_BUS help The HCI Three-wire UART Transport Layer makes it possible to user the Bluetooth HCI over a serial port interface. The HCI diff --git a/drivers/bluetooth/hci_h5.c b/drivers/bluetooth/hci_h5.c index 6cfc2f276250..a03acc3b1b52 100644 --- a/drivers/bluetooth/hci_h5.c +++ b/drivers/bluetooth/hci_h5.c @@ -28,7 +28,14 @@ #include #include +#include +#include +#include +#include +#include + #include "hci_uart.h" +#include "btrtl.h" #define HCI_3WIRE_ACK_PKT 0 #define HCI_3WIRE_LINK_PKT 15 @@ -97,6 +104,13 @@ struct h5 { } sleep; }; +struct h5_device { + struct hci_uart hu; + struct gpio_desc *enable_gpio; + struct gpio_desc *reset_gpio; + int (*vendor_setup)(struct h5_device *h5_dev); +}; + static void h5_reset_rx(struct h5 *h5); static void h5_link_control(struct hci_uart *hu, const void *data, size_t len) @@ -190,6 +204,7 @@ static int h5_open(struct hci_uart *hu) { struct h5 *h5; const unsigned char sync[] = { 0x01, 0x7e }; + int err; BT_DBG("hu %p", hu); @@ -210,6 +225,14 @@ static int h5_open(struct hci_uart *hu) h5->tx_win = H5_TX_WIN_MAX; + if (hu->serdev) { + err = serdev_device_open(hu->serdev); + if (err) { + bt_dev_err(hu->hdev, "failed to open serdev: %d", err); + return err; + } + } + /* Send initial sync request */ h5_link_control(hu, sync, sizeof(sync)); mod_timer(&h5->timer, jiffies + H5_SYNC_TIMEOUT); @@ -217,6 +240,25 @@ static int h5_open(struct hci_uart *hu) return 0; } +static int h5_setup(struct hci_uart *hu) +{ + int err; + struct h5_device *h5_dev; + + if (!hu->serdev) + return 0; + + h5_dev = serdev_device_get_drvdata(hu->serdev); + + if (h5_dev->vendor_setup) { + err = h5_dev->vendor_setup(h5_dev); + if (err) + return err; + } + + return 0; +} + static int h5_close(struct hci_uart *hu) { struct h5 *h5 = hu->priv; @@ -227,6 +269,15 @@ static int h5_close(struct hci_uart *hu) skb_queue_purge(&h5->rel); skb_queue_purge(&h5->unrel); + if (hu->serdev) { + struct h5_device *h5_dev; + + h5_dev = serdev_device_get_drvdata(hu->serdev); + gpiod_set_value_cansleep(h5_dev->enable_gpio, 0); + + serdev_device_close(hu->serdev); + } + kfree(h5); return 0; @@ -736,10 +787,160 @@ static int h5_flush(struct hci_uart *hu) return 0; } +#if IS_ENABLED(CONFIG_SERIAL_DEV_BUS) +static int h5_setup_realtek(struct h5_device *h5_dev) +{ + struct hci_uart *hu = &h5_dev->hu; + int err = 0, retry = 3; + struct sk_buff *skb; + struct btrtl_device_info *btrtl_dev; + __le32 baudrate_data; + u32 device_baudrate; + unsigned int controller_baudrate; + bool flow_control; + + /* devices always start with flow control disabled and even parity */ + serdev_device_set_flow_control(hu->serdev, false); + serdev_device_set_parity(hu->serdev, true, false); + + do { + /* disable the device and put it into reset. some devices only + * have one of these lines, so we toggle both here to support + * all combinations. + */ + gpiod_set_value_cansleep(h5_dev->reset_gpio, 1); + gpiod_set_value_cansleep(h5_dev->enable_gpio, 0); + + /* wait until the device is disabled and/or reset. 500ms are + * chosen by manually testing on a RTL8723BS. shorter wait + * times lead to a non-responding device. + */ + msleep(500); + + /* take the device out of reset and enable it. */ + gpiod_set_value_cansleep(h5_dev->reset_gpio, 0); + gpiod_set_value_cansleep(h5_dev->enable_gpio, 1); + + /* after that we need to wait 500ms, otherwise the device might + * not respond in all cases. this was determined by testing + * with a RTL8723BS. + */ + msleep(500); + + btrtl_dev = btrtl_initialize(hu->hdev); + if (!IS_ERR(btrtl_dev)) + break; + + /* Toggle the enable and reset pins above and try again */ + } while (retry--); + + if (IS_ERR(btrtl_dev)) + return PTR_ERR(btrtl_dev); + + err = btrtl_get_uart_settings(hu->hdev, btrtl_dev, + &controller_baudrate, &device_baudrate, + &flow_control); + if (err) + goto out_free; + + baudrate_data = cpu_to_le32(device_baudrate); + skb = __hci_cmd_sync(hu->hdev, 0xfc17, sizeof(baudrate_data), + &baudrate_data, HCI_INIT_TIMEOUT); + if (IS_ERR(skb)) { + bt_dev_err(hu->hdev, "set baud rate command failed"); + err = -PTR_ERR(skb); + goto out_free; + } else { + kfree_skb(skb); + } + + serdev_device_set_baudrate(hu->serdev, controller_baudrate); + serdev_device_set_flow_control(hu->serdev, flow_control); + + err = btrtl_download_firmware(hu->hdev, btrtl_dev); + +out_free: + btrtl_free(btrtl_dev); + + return err; +} + +static const struct hci_uart_proto h5p; + +static int hci_h5_probe(struct serdev_device *serdev) +{ + struct hci_uart *hu; + struct h5_device *h5_dev; + + h5_dev = devm_kzalloc(&serdev->dev, sizeof(*h5_dev), GFP_KERNEL); + if (!h5_dev) + return -ENOMEM; + + hu = &h5_dev->hu; + hu->serdev = serdev; + + serdev_device_set_drvdata(serdev, h5_dev); + + h5_dev->vendor_setup = of_device_get_match_data(&serdev->dev); + + h5_dev->enable_gpio = devm_gpiod_get_optional(&serdev->dev, "enable", + GPIOD_OUT_HIGH); + if (IS_ERR(h5_dev->enable_gpio)) + return PTR_ERR(h5_dev->enable_gpio); + + h5_dev->reset_gpio = devm_gpiod_get_optional(&serdev->dev, "reset", + GPIOD_OUT_LOW); + if (IS_ERR(h5_dev->reset_gpio)) + return PTR_ERR(h5_dev->reset_gpio); + + hci_uart_set_speeds(hu, 115200, 0); + + return hci_uart_register_device(hu, &h5p); +} + +static void hci_h5_remove(struct serdev_device *serdev) +{ + struct h5_device *h5_dev = serdev_device_get_drvdata(serdev); + struct hci_uart *hu = &h5_dev->hu; + struct hci_dev *hdev = hu->hdev; + + cancel_work_sync(&hu->write_work); + + hci_unregister_dev(hdev); + hci_free_dev(hdev); + hu->proto->close(hu); +} + +#ifdef CONFIG_OF +static const struct of_device_id hci_h5_of_match[] = { + { + .compatible = "realtek,rtl8723bs-bluetooth", + .data = h5_setup_realtek + }, + { + .compatible = "realtek,rtl8723ds-bluetooth", + .data = h5_setup_realtek + }, + {}, +}; +MODULE_DEVICE_TABLE(of, hci_h5_of_match); +#endif + +static struct serdev_device_driver hci_h5_drv = { + .driver = { + .name = "hci-h5", + .of_match_table = of_match_ptr(hci_h5_of_match), + }, + .probe = hci_h5_probe, + .remove = hci_h5_remove, +}; +#endif + static const struct hci_uart_proto h5p = { .id = HCI_UART_3WIRE, .name = "Three-wire (H5)", .open = h5_open, + .setup = h5_setup, .close = h5_close, .recv = h5_recv, .enqueue = h5_enqueue, @@ -749,10 +950,14 @@ static const struct hci_uart_proto h5p = { int __init h5_init(void) { + serdev_device_driver_register(&hci_h5_drv); + return hci_uart_register_proto(&h5p); } int __exit h5_deinit(void) { + serdev_device_driver_unregister(&hci_h5_drv); + return hci_uart_unregister_proto(&h5p); }