summaryrefslogtreecommitdiffstats
path: root/ti-bluetooth.patch
diff options
context:
space:
mode:
Diffstat (limited to 'ti-bluetooth.patch')
-rw-r--r--ti-bluetooth.patch2480
1 files changed, 0 insertions, 2480 deletions
diff --git a/ti-bluetooth.patch b/ti-bluetooth.patch
deleted file mode 100644
index 288eb7c94..000000000
--- a/ti-bluetooth.patch
+++ /dev/null
@@ -1,2480 +0,0 @@
-From patchwork Sat Aug 13 03:14:32 2016
-Content-Type: text/plain; charset="utf-8"
-MIME-Version: 1.0
-Content-Transfer-Encoding: 7bit
-Subject: [RFC,1/7] tty: serial: omap: add UPF_BOOT_AUTOCONF flag for DT init
-From: Sebastian Reichel <sre@kernel.org>
-X-Patchwork-Id: 9278297
-Message-Id: <1471058078-5579-2-git-send-email-sre@kernel.org>
-To: Sebastian Reichel <sre@kernel.org>, Tony Lindgren <tony@atomide.com>,
- Rob Herring <robh+dt@kernel.org>, Mark Rutland <mark.rutland@arm.com>,
- Marcel Holtmann <marcel@holtmann.org>,
- Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
- Jiri Slaby <jslaby@suse.com>
-Cc: Ville Tervo <ville.tervo@iki.fi>,
- =?UTF-8?q?Filip=20Matijevi=C4=87?= <filip.matijevic.pz@gmail.com>,
- Aaro Koskinen <aaro.koskinen@iki.fi>, Pavel Machek <pavel@ucw.cz>,
- =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali.rohar@gmail.com>,
- ivo.g.dimitrov.75@gmail.com, linux-bluetooth@vger.kernel.org,
- linux-serial@vger.kernel.org, linux-omap@vger.kernel.org,
- devicetree@vger.kernel.org, linux-kernel@vger.kernel.org
-Date: Sat, 13 Aug 2016 05:14:32 +0200
-
----
- drivers/tty/serial/omap-serial.c | 3 +++
- 1 file changed, 3 insertions(+)
-Acked-by: Pavel Machek <pavel@ucw.cz>
-
-diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c
-index a2a529994ba5..7c2c77789c2c 100644
---- a/drivers/tty/serial/omap-serial.c
-+++ b/drivers/tty/serial/omap-serial.c
-@@ -1542,6 +1542,9 @@ static struct omap_uart_port_info *of_get_uart_port_info(struct device *dev)
-
- of_property_read_u32(dev->of_node, "clock-frequency",
- &omap_up_info->uartclk);
-+
-+ omap_up_info->flags = UPF_BOOT_AUTOCONF;
-+
- return omap_up_info;
- }
-
-From 6102245c5711e73b83ad79ab0f2c3ec040262a87 Mon Sep 17 00:00:00 2001
-From: Sebastian Reichel <sre@kernel.org>
-Date: Tue, 28 Mar 2017 17:59:31 +0200
-Subject: [PATCH 01/13] serdev: add serdev_device_wait_until_sent
-
-Add method, which waits until the transmission buffer has been sent.
-Note, that the change in ttyport_write_wakeup is related, since
-tty_wait_until_sent will hang without that change.
-
-Acked-by: Rob Herring <robh@kernel.org>
-Acked-by: Pavel Machek <pavel@ucw.cz>
-Signed-off-by: Sebastian Reichel <sre@kernel.org>
-Signed-off-by: Rob Herring <robh@kernel.org>
----
- drivers/tty/serdev/core.c | 11 +++++++++++
- drivers/tty/serdev/serdev-ttyport.c | 18 ++++++++++++++----
- include/linux/serdev.h | 3 +++
- 3 files changed, 28 insertions(+), 4 deletions(-)
-
-diff --git a/drivers/tty/serdev/core.c b/drivers/tty/serdev/core.c
-index f4c6c90add78..a63b74031e22 100644
---- a/drivers/tty/serdev/core.c
-+++ b/drivers/tty/serdev/core.c
-@@ -173,6 +173,17 @@ void serdev_device_set_flow_control(struct serdev_device *serdev, bool enable)
- }
- EXPORT_SYMBOL_GPL(serdev_device_set_flow_control);
-
-+void serdev_device_wait_until_sent(struct serdev_device *serdev, long timeout)
-+{
-+ struct serdev_controller *ctrl = serdev->ctrl;
-+
-+ if (!ctrl || !ctrl->ops->wait_until_sent)
-+ return;
-+
-+ ctrl->ops->wait_until_sent(ctrl, timeout);
-+}
-+EXPORT_SYMBOL_GPL(serdev_device_wait_until_sent);
-+
- static int serdev_drv_probe(struct device *dev)
- {
- const struct serdev_device_driver *sdrv = to_serdev_device_driver(dev->driver);
-diff --git a/drivers/tty/serdev/serdev-ttyport.c b/drivers/tty/serdev/serdev-ttyport.c
-index d05393594f15..50dc75c4d204 100644
---- a/drivers/tty/serdev/serdev-ttyport.c
-+++ b/drivers/tty/serdev/serdev-ttyport.c
-@@ -14,6 +14,7 @@
- #include <linux/serdev.h>
- #include <linux/tty.h>
- #include <linux/tty_driver.h>
-+#include <linux/poll.h>
-
- #define SERPORT_ACTIVE 1
-
-@@ -46,11 +47,11 @@ static void ttyport_write_wakeup(struct tty_port *port)
- struct serdev_controller *ctrl = port->client_data;
- struct serport *serport = serdev_controller_get_drvdata(ctrl);
-
-- if (!test_and_clear_bit(TTY_DO_WRITE_WAKEUP, &port->tty->flags))
-- return;
--
-- if (test_bit(SERPORT_ACTIVE, &serport->flags))
-+ if (test_and_clear_bit(TTY_DO_WRITE_WAKEUP, &port->tty->flags) &&
-+ test_bit(SERPORT_ACTIVE, &serport->flags))
- serdev_controller_write_wakeup(ctrl);
-+
-+ wake_up_interruptible_poll(&port->tty->write_wait, POLLOUT);
- }
-
- static const struct tty_port_client_operations client_ops = {
-@@ -167,6 +168,14 @@ static void ttyport_set_flow_control(struct serdev_controller *ctrl, bool enable
- tty_set_termios(tty, &ktermios);
- }
-
-+static void ttyport_wait_until_sent(struct serdev_controller *ctrl, long timeout)
-+{
-+ struct serport *serport = serdev_controller_get_drvdata(ctrl);
-+ struct tty_struct *tty = serport->tty;
-+
-+ tty_wait_until_sent(tty, timeout);
-+}
-+
- static const struct serdev_controller_ops ctrl_ops = {
- .write_buf = ttyport_write_buf,
- .write_flush = ttyport_write_flush,
-@@ -175,6 +184,7 @@ static const struct serdev_controller_ops ctrl_ops = {
- .close = ttyport_close,
- .set_flow_control = ttyport_set_flow_control,
- .set_baudrate = ttyport_set_baudrate,
-+ .wait_until_sent = ttyport_wait_until_sent,
- };
-
- struct device *serdev_tty_port_register(struct tty_port *port,
-diff --git a/include/linux/serdev.h b/include/linux/serdev.h
-index 9519da6253a8..a308b206d204 100644
---- a/include/linux/serdev.h
-+++ b/include/linux/serdev.h
-@@ -81,6 +81,7 @@ struct serdev_controller_ops {
- void (*close)(struct serdev_controller *);
- void (*set_flow_control)(struct serdev_controller *, bool);
- unsigned int (*set_baudrate)(struct serdev_controller *, unsigned int);
-+ void (*wait_until_sent)(struct serdev_controller *, long);
- };
-
- /**
-@@ -186,6 +187,7 @@ int serdev_device_open(struct serdev_device *);
- void serdev_device_close(struct serdev_device *);
- unsigned int serdev_device_set_baudrate(struct serdev_device *, unsigned int);
- void serdev_device_set_flow_control(struct serdev_device *, bool);
-+void serdev_device_wait_until_sent(struct serdev_device *, long);
- int serdev_device_write_buf(struct serdev_device *, const unsigned char *, size_t);
- void serdev_device_write_flush(struct serdev_device *);
- int serdev_device_write_room(struct serdev_device *);
-@@ -223,6 +225,7 @@ static inline unsigned int serdev_device_set_baudrate(struct serdev_device *sdev
- return 0;
- }
- static inline void serdev_device_set_flow_control(struct serdev_device *sdev, bool enable) {}
-+static inline void serdev_device_wait_until_sent(struct serdev_device *sdev, long timeout) {}
- static inline int serdev_device_write_buf(struct serdev_device *sdev, const unsigned char *buf, size_t count)
- {
- return -ENODEV;
---
-2.12.2
-
-From 6e1713b03eab6f42251bad76ab05e7e1aa28b199 Mon Sep 17 00:00:00 2001
-From: Sebastian Reichel <sre@kernel.org>
-Date: Tue, 28 Mar 2017 17:59:32 +0200
-Subject: [PATCH 02/13] serdev: implement get/set tiocm
-
-Add method for getting and setting tiocm.
-
-Acked-by: Pavel Machek <pavel@ucw.cz>
-Acked-by: Rob Herring <robh@kernel.org>
-Signed-off-by: Sebastian Reichel <sre@kernel.org>
-Signed-off-by: Rob Herring <robh@kernel.org>
----
- drivers/tty/serdev/core.c | 22 ++++++++++++++++++++++
- drivers/tty/serdev/serdev-ttyport.c | 24 ++++++++++++++++++++++++
- include/linux/serdev.h | 13 +++++++++++++
- 3 files changed, 59 insertions(+)
-
-diff --git a/drivers/tty/serdev/core.c b/drivers/tty/serdev/core.c
-index a63b74031e22..1e1cbae3a0ea 100644
---- a/drivers/tty/serdev/core.c
-+++ b/drivers/tty/serdev/core.c
-@@ -184,6 +184,28 @@ void serdev_device_wait_until_sent(struct serdev_device *serdev, long timeout)
- }
- EXPORT_SYMBOL_GPL(serdev_device_wait_until_sent);
-
-+int serdev_device_get_tiocm(struct serdev_device *serdev)
-+{
-+ struct serdev_controller *ctrl = serdev->ctrl;
-+
-+ if (!ctrl || !ctrl->ops->get_tiocm)
-+ return -ENOTSUPP;
-+
-+ return ctrl->ops->get_tiocm(ctrl);
-+}
-+EXPORT_SYMBOL_GPL(serdev_device_get_tiocm);
-+
-+int serdev_device_set_tiocm(struct serdev_device *serdev, int set, int clear)
-+{
-+ struct serdev_controller *ctrl = serdev->ctrl;
-+
-+ if (!ctrl || !ctrl->ops->set_tiocm)
-+ return -ENOTSUPP;
-+
-+ return ctrl->ops->set_tiocm(ctrl, set, clear);
-+}
-+EXPORT_SYMBOL_GPL(serdev_device_set_tiocm);
-+
- static int serdev_drv_probe(struct device *dev)
- {
- const struct serdev_device_driver *sdrv = to_serdev_device_driver(dev->driver);
-diff --git a/drivers/tty/serdev/serdev-ttyport.c b/drivers/tty/serdev/serdev-ttyport.c
-index 50dc75c4d204..487c88f6aa0e 100644
---- a/drivers/tty/serdev/serdev-ttyport.c
-+++ b/drivers/tty/serdev/serdev-ttyport.c
-@@ -176,6 +176,28 @@ static void ttyport_wait_until_sent(struct serdev_controller *ctrl, long timeout
- tty_wait_until_sent(tty, timeout);
- }
-
-+static int ttyport_get_tiocm(struct serdev_controller *ctrl)
-+{
-+ struct serport *serport = serdev_controller_get_drvdata(ctrl);
-+ struct tty_struct *tty = serport->tty;
-+
-+ if (!tty->ops->tiocmget)
-+ return -ENOTSUPP;
-+
-+ return tty->driver->ops->tiocmget(tty);
-+}
-+
-+static int ttyport_set_tiocm(struct serdev_controller *ctrl, unsigned int set, unsigned int clear)
-+{
-+ struct serport *serport = serdev_controller_get_drvdata(ctrl);
-+ struct tty_struct *tty = serport->tty;
-+
-+ if (!tty->ops->tiocmset)
-+ return -ENOTSUPP;
-+
-+ return tty->driver->ops->tiocmset(tty, set, clear);
-+}
-+
- static const struct serdev_controller_ops ctrl_ops = {
- .write_buf = ttyport_write_buf,
- .write_flush = ttyport_write_flush,
-@@ -185,6 +207,8 @@ static const struct serdev_controller_ops ctrl_ops = {
- .set_flow_control = ttyport_set_flow_control,
- .set_baudrate = ttyport_set_baudrate,
- .wait_until_sent = ttyport_wait_until_sent,
-+ .get_tiocm = ttyport_get_tiocm,
-+ .set_tiocm = ttyport_set_tiocm,
- };
-
- struct device *serdev_tty_port_register(struct tty_port *port,
-diff --git a/include/linux/serdev.h b/include/linux/serdev.h
-index a308b206d204..e29a270f603c 100644
---- a/include/linux/serdev.h
-+++ b/include/linux/serdev.h
-@@ -15,6 +15,7 @@
-
- #include <linux/types.h>
- #include <linux/device.h>
-+#include <linux/termios.h>
-
- struct serdev_controller;
- struct serdev_device;
-@@ -82,6 +83,8 @@ struct serdev_controller_ops {
- void (*set_flow_control)(struct serdev_controller *, bool);
- unsigned int (*set_baudrate)(struct serdev_controller *, unsigned int);
- void (*wait_until_sent)(struct serdev_controller *, long);
-+ int (*get_tiocm)(struct serdev_controller *);
-+ int (*set_tiocm)(struct serdev_controller *, unsigned int, unsigned int);
- };
-
- /**
-@@ -188,6 +191,8 @@ void serdev_device_close(struct serdev_device *);
- unsigned int serdev_device_set_baudrate(struct serdev_device *, unsigned int);
- void serdev_device_set_flow_control(struct serdev_device *, bool);
- void serdev_device_wait_until_sent(struct serdev_device *, long);
-+int serdev_device_get_tiocm(struct serdev_device *);
-+int serdev_device_set_tiocm(struct serdev_device *, int, int);
- int serdev_device_write_buf(struct serdev_device *, const unsigned char *, size_t);
- void serdev_device_write_flush(struct serdev_device *);
- int serdev_device_write_room(struct serdev_device *);
-@@ -226,6 +231,14 @@ static inline unsigned int serdev_device_set_baudrate(struct serdev_device *sdev
- }
- static inline void serdev_device_set_flow_control(struct serdev_device *sdev, bool enable) {}
- static inline void serdev_device_wait_until_sent(struct serdev_device *sdev, long timeout) {}
-+static inline int serdev_device_get_tiocm(struct serdev_device *serdev)
-+{
-+ return -ENOTSUPP;
-+}
-+static inline int serdev_device_set_tiocm(struct serdev_device *serdev, int set, int clear)
-+{
-+ return -ENOTSUPP;
-+}
- static inline int serdev_device_write_buf(struct serdev_device *sdev, const unsigned char *buf, size_t count)
- {
- return -ENODEV;
---
-2.12.2
-
-From 9425a7e94dba6d5585c542c50f253f8fbf764a81 Mon Sep 17 00:00:00 2001
-From: Sebastian Reichel <sre@kernel.org>
-Date: Tue, 28 Mar 2017 17:59:33 +0200
-Subject: [PATCH 03/13] serdev: add helpers for cts and rts handling
-
-Add serdev helper functions for handling of cts and rts
-lines using the serdev's tiocm functions.
-
-Acked-by: Rob Herring <robh@kernel.org>
-Signed-off-by: Sebastian Reichel <sre@kernel.org>
-Signed-off-by: Rob Herring <robh@kernel.org>
----
- include/linux/serdev.h | 31 +++++++++++++++++++++++++++++++
- 1 file changed, 31 insertions(+)
-
-diff --git a/include/linux/serdev.h b/include/linux/serdev.h
-index e29a270f603c..37395b8eb8f1 100644
---- a/include/linux/serdev.h
-+++ b/include/linux/serdev.h
-@@ -16,6 +16,7 @@
- #include <linux/types.h>
- #include <linux/device.h>
- #include <linux/termios.h>
-+#include <linux/delay.h>
-
- struct serdev_controller;
- struct serdev_device;
-@@ -254,6 +255,36 @@ static inline int serdev_device_write_room(struct serdev_device *sdev)
-
- #endif /* CONFIG_SERIAL_DEV_BUS */
-
-+static inline bool serdev_device_get_cts(struct serdev_device *serdev)
-+{
-+ int status = serdev_device_get_tiocm(serdev);
-+ return !!(status & TIOCM_CTS);
-+}
-+
-+static inline int serdev_device_wait_for_cts(struct serdev_device *serdev, bool state, int timeout_ms)
-+{
-+ unsigned long timeout;
-+ bool signal;
-+
-+ timeout = jiffies + msecs_to_jiffies(timeout_ms);
-+ while (time_is_after_jiffies(timeout)) {
-+ signal = serdev_device_get_cts(serdev);
-+ if (signal == state)
-+ return 0;
-+ usleep_range(1000, 2000);
-+ }
-+
-+ return -ETIMEDOUT;
-+}
-+
-+static inline int serdev_device_set_rts(struct serdev_device *serdev, bool enable)
-+{
-+ if (enable)
-+ return serdev_device_set_tiocm(serdev, TIOCM_RTS, 0);
-+ else
-+ return serdev_device_set_tiocm(serdev, 0, TIOCM_RTS);
-+}
-+
- /*
- * serdev hooks into TTY core
- */
---
-2.12.2
-
-From 8656be75e893514bac2aa817f1c1b35e8dbd9e5b Mon Sep 17 00:00:00 2001
-From: Sebastian Reichel <sre@kernel.org>
-Date: Tue, 28 Mar 2017 17:59:34 +0200
-Subject: [PATCH 04/13] Bluetooth: hci_uart: add support for word alignment
-
-This will be used by Nokia's H4+ protocol, which
-uses 2-byte aligned packets.
-
-Acked-by: Pavel Machek <pavel@ucw.cz>
-Signed-off-by: Sebastian Reichel <sre@kernel.org>
-Signed-off-by: Rob Herring <robh@kernel.org>
----
- drivers/bluetooth/hci_h4.c | 17 +++++++++++++++++
- drivers/bluetooth/hci_ldisc.c | 4 ++++
- drivers/bluetooth/hci_uart.h | 3 +++
- 3 files changed, 24 insertions(+)
-
-diff --git a/drivers/bluetooth/hci_h4.c b/drivers/bluetooth/hci_h4.c
-index 635597b6e168..82e5a32b87a4 100644
---- a/drivers/bluetooth/hci_h4.c
-+++ b/drivers/bluetooth/hci_h4.c
-@@ -171,9 +171,20 @@ 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)
- {
-+ struct hci_uart *hu = hci_get_drvdata(hdev);
-+ u8 alignment = hu->alignment;
-+
- while (count) {
- int i, len;
-
-+ /* remove padding bytes from buffer */
-+ for (; hu->padding && count > 0; hu->padding--) {
-+ count--;
-+ buffer++;
-+ }
-+ if (!count)
-+ break;
-+
- if (!skb) {
- for (i = 0; i < pkts_count; i++) {
- if (buffer[0] != (&pkts[i])->type)
-@@ -253,11 +264,17 @@ struct sk_buff *h4_recv_buf(struct hci_dev *hdev, struct sk_buff *skb,
- }
-
- if (!dlen) {
-+ hu->padding = (skb->len - 1) % alignment;
-+ hu->padding = (alignment - hu->padding) % alignment;
-+
- /* No more data, complete frame */
- (&pkts[i])->recv(hdev, skb);
- skb = NULL;
- }
- } else {
-+ hu->padding = (skb->len - 1) % alignment;
-+ hu->padding = (alignment - hu->padding) % alignment;
-+
- /* Complete frame */
- (&pkts[i])->recv(hdev, skb);
- skb = NULL;
-diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c
-index 9497c469efd2..0ec8a94bd712 100644
---- a/drivers/bluetooth/hci_ldisc.c
-+++ b/drivers/bluetooth/hci_ldisc.c
-@@ -459,6 +459,10 @@ static int hci_uart_tty_open(struct tty_struct *tty)
- hu->tty = tty;
- tty->receive_room = 65536;
-
-+ /* disable alignment support by default */
-+ hu->alignment = 1;
-+ hu->padding = 0;
-+
- INIT_WORK(&hu->init_ready, hci_uart_init_work);
- INIT_WORK(&hu->write_work, hci_uart_write_work);
-
-diff --git a/drivers/bluetooth/hci_uart.h b/drivers/bluetooth/hci_uart.h
-index 070139513e65..4aff50960cac 100644
---- a/drivers/bluetooth/hci_uart.h
-+++ b/drivers/bluetooth/hci_uart.h
-@@ -92,6 +92,9 @@ struct hci_uart {
-
- unsigned int init_speed;
- unsigned int oper_speed;
-+
-+ u8 alignment;
-+ u8 padding;
- };
-
- /* HCI_UART proto flag bits */
---
-2.12.2
-
-From f2d830b35ce8c834eaa895ff29c461455024f05e Mon Sep 17 00:00:00 2001
-From: Rob Herring <robh@kernel.org>
-Date: Tue, 28 Mar 2017 17:59:35 +0200
-Subject: [PATCH 05/13] Bluetooth: hci_uart: add serdev driver support library
-
-This adds library functions for serdev based BT drivers. This is largely
-copied from hci_ldisc.c and modified to use serdev calls. There's a little
-bit of duplication, but I avoided intermixing this as the ldisc code should
-eventually go away.
-
-Signed-off-by: Rob Herring <robh@kernel.org>
-Cc: Marcel Holtmann <marcel@holtmann.org>
-Cc: Gustavo Padovan <gustavo@padovan.org>
-Cc: Johan Hedberg <johan.hedberg@gmail.com>
-Cc: linux-bluetooth@vger.kernel.org
-Acked-by: Pavel Machek <pavel@ucw.cz>
-[Fix style issues reported by Pavel]
-Signed-off-by: Sebastian Reichel <sre@kernel.org>
-Signed-off-by: Rob Herring <robh@kernel.org>
----
- drivers/bluetooth/Makefile | 1 +
- drivers/bluetooth/hci_serdev.c | 361 +++++++++++++++++++++++++++++++++++++++++
- drivers/bluetooth/hci_uart.h | 4 +
- 3 files changed, 366 insertions(+)
- create mode 100644 drivers/bluetooth/hci_serdev.c
-
-diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile
-index 80627187c8b6..fd571689eed6 100644
---- a/drivers/bluetooth/Makefile
-+++ b/drivers/bluetooth/Makefile
-@@ -29,6 +29,7 @@ btmrvl-y := btmrvl_main.o
- btmrvl-$(CONFIG_DEBUG_FS) += btmrvl_debugfs.o
-
- hci_uart-y := hci_ldisc.o
-+hci_uart-$(CONFIG_SERIAL_DEV_BUS) += hci_serdev.o
- hci_uart-$(CONFIG_BT_HCIUART_H4) += hci_h4.o
- hci_uart-$(CONFIG_BT_HCIUART_BCSP) += hci_bcsp.o
- hci_uart-$(CONFIG_BT_HCIUART_LL) += hci_ll.o
-diff --git a/drivers/bluetooth/hci_serdev.c b/drivers/bluetooth/hci_serdev.c
-new file mode 100644
-index 000000000000..f5ccb2c7ef92
---- /dev/null
-+++ b/drivers/bluetooth/hci_serdev.c
-@@ -0,0 +1,361 @@
-+/*
-+ * Bluetooth HCI serdev driver lib
-+ *
-+ * Copyright (C) 2017 Linaro, Ltd., Rob Herring <robh@kernel.org>
-+ *
-+ * Based on hci_ldisc.c:
-+ *
-+ * Copyright (C) 2000-2001 Qualcomm Incorporated
-+ * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
-+ * Copyright (C) 2004-2005 Marcel Holtmann <marcel@holtmann.org>
-+ *
-+ * This program is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-+ * GNU General Public License for more details.
-+ *
-+ */
-+
-+#include <linux/kernel.h>
-+#include <linux/types.h>
-+#include <linux/serdev.h>
-+#include <linux/skbuff.h>
-+
-+#include <net/bluetooth/bluetooth.h>
-+#include <net/bluetooth/hci_core.h>
-+
-+#include "hci_uart.h"
-+
-+struct serdev_device_ops hci_serdev_client_ops;
-+
-+static inline void hci_uart_tx_complete(struct hci_uart *hu, int pkt_type)
-+{
-+ struct hci_dev *hdev = hu->hdev;
-+
-+ /* Update HCI stat counters */
-+ switch (pkt_type) {
-+ case HCI_COMMAND_PKT:
-+ hdev->stat.cmd_tx++;
-+ break;
-+
-+ case HCI_ACLDATA_PKT:
-+ hdev->stat.acl_tx++;
-+ break;
-+
-+ case HCI_SCODATA_PKT:
-+ hdev->stat.sco_tx++;
-+ break;
-+ }
-+}
-+
-+static inline struct sk_buff *hci_uart_dequeue(struct hci_uart *hu)
-+{
-+ struct sk_buff *skb = hu->tx_skb;
-+
-+ if (!skb)
-+ skb = hu->proto->dequeue(hu);
-+ else
-+ hu->tx_skb = NULL;
-+
-+ return skb;
-+}
-+
-+static void hci_uart_write_work(struct work_struct *work)
-+{
-+ struct hci_uart *hu = container_of(work, struct hci_uart, write_work);
-+ struct serdev_device *serdev = hu->serdev;
-+ struct hci_dev *hdev = hu->hdev;
-+ struct sk_buff *skb;
-+
-+ /* REVISIT:
-+ * should we cope with bad skbs or ->write() returning an error value?
-+ */
-+ do {
-+ clear_bit(HCI_UART_TX_WAKEUP, &hu->tx_state);
-+
-+ while ((skb = hci_uart_dequeue(hu))) {
-+ int len;
-+
-+ len = serdev_device_write_buf(serdev,
-+ skb->data, skb->len);
-+ hdev->stat.byte_tx += len;
-+
-+ skb_pull(skb, len);
-+ if (skb->len) {
-+ hu->tx_skb = skb;
-+ break;
-+ }
-+
-+ hci_uart_tx_complete(hu, hci_skb_pkt_type(skb));
-+ kfree_skb(skb);
-+ }
-+ } while(test_bit(HCI_UART_TX_WAKEUP, &hu->tx_state));
-+
-+ clear_bit(HCI_UART_SENDING, &hu->tx_state);
-+}
-+
-+/* ------- Interface to HCI layer ------ */
-+
-+/* Initialize device */
-+static int hci_uart_open(struct hci_dev *hdev)
-+{
-+ struct hci_uart *hu = hci_get_drvdata(hdev);
-+
-+ BT_DBG("%s %p", hdev->name, hdev);
-+
-+ serdev_device_set_client_ops(hu->serdev, &hci_serdev_client_ops);
-+
-+ return serdev_device_open(hu->serdev);
-+}
-+
-+/* Reset device */
-+static int hci_uart_flush(struct hci_dev *hdev)
-+{
-+ struct hci_uart *hu = hci_get_drvdata(hdev);
-+
-+ BT_DBG("hdev %p serdev %p", hdev, hu->serdev);
-+
-+ if (hu->tx_skb) {
-+ kfree_skb(hu->tx_skb); hu->tx_skb = NULL;
-+ }
-+
-+ /* Flush any pending characters in the driver and discipline. */
-+ serdev_device_write_flush(hu->serdev);
-+
-+ if (test_bit(HCI_UART_PROTO_READY, &hu->flags))
-+ hu->proto->flush(hu);
-+
-+ return 0;
-+}
-+
-+/* Close device */
-+static int hci_uart_close(struct hci_dev *hdev)
-+{
-+ struct hci_uart *hu = hci_get_drvdata(hdev);
-+
-+ BT_DBG("hdev %p", hdev);
-+
-+ hci_uart_flush(hdev);
-+ hdev->flush = NULL;
-+
-+ serdev_device_close(hu->serdev);
-+
-+ return 0;
-+}
-+
-+/* Send frames from HCI layer */
-+static int hci_uart_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
-+{
-+ struct hci_uart *hu = hci_get_drvdata(hdev);
-+
-+ BT_DBG("%s: type %d len %d", hdev->name, hci_skb_pkt_type(skb),
-+ skb->len);
-+
-+ hu->proto->enqueue(hu, skb);
-+
-+ hci_uart_tx_wakeup(hu);
-+
-+ return 0;
-+}
-+
-+static int hci_uart_setup(struct hci_dev *hdev)
-+{
-+ struct hci_uart *hu = hci_get_drvdata(hdev);
-+ struct hci_rp_read_local_version *ver;
-+ struct sk_buff *skb;
-+ unsigned int speed;
-+ int err;
-+
-+ /* Init speed if any */
-+ if (hu->init_speed)
-+ speed = hu->init_speed;
-+ else if (hu->proto->init_speed)
-+ speed = hu->proto->init_speed;
-+ else
-+ speed = 0;
-+
-+ if (speed)
-+ serdev_device_set_baudrate(hu->serdev, speed);
-+
-+ /* Operational speed if any */
-+ if (hu->oper_speed)
-+ speed = hu->oper_speed;
-+ else if (hu->proto->oper_speed)
-+ speed = hu->proto->oper_speed;
-+ else
-+ speed = 0;
-+
-+ if (hu->proto->set_baudrate && speed) {
-+ err = hu->proto->set_baudrate(hu, speed);
-+ if (err)
-+ BT_ERR("%s: failed to set baudrate", hdev->name);
-+ else
-+ serdev_device_set_baudrate(hu->serdev, speed);
-+ }
-+
-+ if (hu->proto->setup)
-+ return hu->proto->setup(hu);
-+
-+ if (!test_bit(HCI_UART_VND_DETECT, &hu->hdev_flags))
-+ return 0;
-+
-+ skb = __hci_cmd_sync(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL,
-+ HCI_INIT_TIMEOUT);
-+ if (IS_ERR(skb)) {
-+ BT_ERR("%s: Reading local version information failed (%ld)",
-+ hdev->name, PTR_ERR(skb));
-+ return 0;
-+ }
-+
-+ if (skb->len != sizeof(*ver)) {
-+ BT_ERR("%s: Event length mismatch for version information",
-+ hdev->name);
-+ }
-+
-+ kfree_skb(skb);
-+ return 0;
-+}
-+
-+/** hci_uart_write_wakeup - transmit buffer wakeup
-+ * @serdev: serial device
-+ *
-+ * This function is called by the serdev framework when it accepts
-+ * more data being sent.
-+ */
-+static void hci_uart_write_wakeup(struct serdev_device *serdev)
-+{
-+ struct hci_uart *hu = serdev_device_get_drvdata(serdev);
-+
-+ BT_DBG("");
-+
-+ if (!hu || serdev != hu->serdev) {
-+ WARN_ON(1);
-+ return;
-+ }
-+
-+ if (test_bit(HCI_UART_PROTO_READY, &hu->flags))
-+ hci_uart_tx_wakeup(hu);
-+}
-+
-+/** hci_uart_receive_buf - receive buffer wakeup
-+ * @serdev: serial device
-+ * @data: pointer to received data
-+ * @count: count of received data in bytes
-+ *
-+ * This function is called by the serdev framework when it received data
-+ * in the RX buffer.
-+ *
-+ * Return: number of processed bytes
-+ */
-+static int hci_uart_receive_buf(struct serdev_device *serdev, const u8 *data,
-+ size_t count)
-+{
-+ struct hci_uart *hu = serdev_device_get_drvdata(serdev);
-+
-+ if (!hu || serdev != hu->serdev) {
-+ WARN_ON(1);
-+ return 0;
-+ }
-+
-+ if (!test_bit(HCI_UART_PROTO_READY, &hu->flags))
-+ return 0;
-+
-+ /* It does not need a lock here as it is already protected by a mutex in
-+ * tty caller
-+ */
-+ hu->proto->recv(hu, data, count);
-+
-+ if (hu->hdev)
-+ hu->hdev->stat.byte_rx += count;
-+
-+ return count;
-+}
-+
-+struct serdev_device_ops hci_serdev_client_ops = {
-+ .receive_buf = hci_uart_receive_buf,
-+ .write_wakeup = hci_uart_write_wakeup,
-+};
-+
-+int hci_uart_register_device(struct hci_uart *hu,
-+ const struct hci_uart_proto *p)
-+{
-+ int err;
-+ struct hci_dev *hdev;
-+
-+ BT_DBG("");
-+
-+ err = p->open(hu);
-+ if (err)
-+ return err;
-+
-+ hu->proto = p;
-+ set_bit(HCI_UART_PROTO_READY, &hu->flags);
-+
-+ /* Initialize and register HCI device */
-+ hdev = hci_alloc_dev();
-+ if (!hdev) {
-+ BT_ERR("Can't allocate HCI device");
-+ err = -ENOMEM;
-+ goto err_alloc;
-+ }
-+
-+ hu->hdev = hdev;
-+
-+ hdev->bus = HCI_UART;
-+ hci_set_drvdata(hdev, hu);
-+
-+ INIT_WORK(&hu->write_work, hci_uart_write_work);
-+
-+ /* Only when vendor specific setup callback is provided, consider
-+ * the manufacturer information valid. This avoids filling in the
-+ * value for Ericsson when nothing is specified.
-+ */
-+ if (hu->proto->setup)
-+ hdev->manufacturer = hu->proto->manufacturer;
-+
-+ hdev->open = hci_uart_open;
-+ hdev->close = hci_uart_close;
-+ hdev->flush = hci_uart_flush;
-+ hdev->send = hci_uart_send_frame;
-+ hdev->setup = hci_uart_setup;
-+ SET_HCIDEV_DEV(hdev, &hu->serdev->dev);
-+
-+ if (test_bit(HCI_UART_RAW_DEVICE, &hu->hdev_flags))
-+ set_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks);
-+
-+ if (test_bit(HCI_UART_EXT_CONFIG, &hu->hdev_flags))
-+ set_bit(HCI_QUIRK_EXTERNAL_CONFIG, &hdev->quirks);
-+
-+ if (!test_bit(HCI_UART_RESET_ON_INIT, &hu->hdev_flags))
-+ set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks);
-+
-+ if (test_bit(HCI_UART_CREATE_AMP, &hu->hdev_flags))
-+ hdev->dev_type = HCI_AMP;
-+ else
-+ hdev->dev_type = HCI_PRIMARY;
-+
-+ if (test_bit(HCI_UART_INIT_PENDING, &hu->hdev_flags))
-+ return 0;
-+
-+ if (hci_register_dev(hdev) < 0) {
-+ BT_ERR("Can't register HCI device");
-+ err = -ENODEV;
-+ goto err_register;
-+ }
-+
-+ set_bit(HCI_UART_REGISTERED, &hu->flags);
-+
-+ return 0;
-+
-+err_register:
-+ hci_free_dev(hdev);
-+err_alloc:
-+ clear_bit(HCI_UART_PROTO_READY, &hu->flags);
-+ p->close(hu);
-+ return err;
-+}
-diff --git a/drivers/bluetooth/hci_uart.h b/drivers/bluetooth/hci_uart.h
-index 4aff50960cac..1b41c661bbb8 100644
---- a/drivers/bluetooth/hci_uart.h
-+++ b/drivers/bluetooth/hci_uart.h
-@@ -58,6 +58,7 @@
- #define HCI_UART_VND_DETECT 5
-
- struct hci_uart;
-+struct serdev_device;
-
- struct hci_uart_proto {
- unsigned int id;
-@@ -77,6 +78,7 @@ struct hci_uart_proto {
-
- struct hci_uart {
- struct tty_struct *tty;
-+ struct serdev_device *serdev;
- struct hci_dev *hdev;
- unsigned long flags;
- unsigned long hdev_flags;
-@@ -108,6 +110,8 @@ struct hci_uart {
-
- int hci_uart_register_proto(const struct hci_uart_proto *p);
- int hci_uart_unregister_proto(const struct hci_uart_proto *p);
-+int hci_uart_register_device(struct hci_uart *hu, const struct hci_uart_proto *p);
-+
- int hci_uart_tx_wakeup(struct hci_uart *hu);
- int hci_uart_init_ready(struct hci_uart *hu);
- void hci_uart_init_tty(struct hci_uart *hu);
---
-2.12.2
-
-From c76b6c5106713b00e183ccb757bc15732d369a33 Mon Sep 17 00:00:00 2001
-From: Sebastian Reichel <sre@kernel.org>
-Date: Tue, 28 Mar 2017 17:59:36 +0200
-Subject: [PATCH 06/13] Bluetooth: hci_serdev: do not open device in hci open
-
-The device driver may need to communicate with the UART
-device while the Bluetooth device is closed (e.g. due
-to interrupts).
-
-Acked-by: Pavel Machek <pavel@ucw.cz>
-Signed-off-by: Sebastian Reichel <sre@kernel.org>
-Signed-off-by: Rob Herring <robh@kernel.org>
----
- drivers/bluetooth/hci_serdev.c | 12 +++---------
- 1 file changed, 3 insertions(+), 9 deletions(-)
-
-diff --git a/drivers/bluetooth/hci_serdev.c b/drivers/bluetooth/hci_serdev.c
-index f5ccb2c7ef92..3b8ac0ece3fb 100644
---- a/drivers/bluetooth/hci_serdev.c
-+++ b/drivers/bluetooth/hci_serdev.c
-@@ -104,13 +104,9 @@ static void hci_uart_write_work(struct work_struct *work)
- /* Initialize device */
- static int hci_uart_open(struct hci_dev *hdev)
- {
-- struct hci_uart *hu = hci_get_drvdata(hdev);
--
- BT_DBG("%s %p", hdev->name, hdev);
-
-- serdev_device_set_client_ops(hu->serdev, &hci_serdev_client_ops);
--
-- return serdev_device_open(hu->serdev);
-+ return 0;
- }
-
- /* Reset device */
-@@ -136,15 +132,11 @@ static int hci_uart_flush(struct hci_dev *hdev)
- /* Close device */
- static int hci_uart_close(struct hci_dev *hdev)
- {
-- struct hci_uart *hu = hci_get_drvdata(hdev);
--
- BT_DBG("hdev %p", hdev);
-
- hci_uart_flush(hdev);
- hdev->flush = NULL;
-
-- serdev_device_close(hu->serdev);
--
- return 0;
- }
-
-@@ -289,6 +281,8 @@ int hci_uart_register_device(struct hci_uart *hu,
-
- BT_DBG("");
-
-+ serdev_device_set_client_ops(hu->serdev, &hci_serdev_client_ops);
-+
- err = p->open(hu);
- if (err)
- return err;
---
-2.12.2
-
-From 7fe8800d90c5e0f614e72c475687a68a76592241 Mon Sep 17 00:00:00 2001
-From: Sebastian Reichel <sre@kernel.org>
-Date: Tue, 28 Mar 2017 17:59:37 +0200
-Subject: [PATCH 07/13] Bluetooth: hci_serdev: allow modular drivers
-
-For bluetooth protocol driver only supporting serdev it makes
-sense to follow common practice and built them into their own
-module.
-
-Such modules need access to hci_uart_register_device and
-hci_uart_tx_wakeup for using the common protocol helpers.
-
-Signed-off-by: Sebastian Reichel <sre@kernel.org>
-Signed-off-by: Rob Herring <robh@kernel.org>
----
- drivers/bluetooth/hci_ldisc.c | 1 +
- drivers/bluetooth/hci_serdev.c | 1 +
- 2 files changed, 2 insertions(+)
-
-diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c
-index 0ec8a94bd712..17bcbc13623f 100644
---- a/drivers/bluetooth/hci_ldisc.c
-+++ b/drivers/bluetooth/hci_ldisc.c
-@@ -134,6 +134,7 @@ int hci_uart_tx_wakeup(struct hci_uart *hu)
-
- return 0;
- }
-+EXPORT_SYMBOL_GPL(hci_uart_tx_wakeup);
-
- static void hci_uart_write_work(struct work_struct *work)
- {
-diff --git a/drivers/bluetooth/hci_serdev.c b/drivers/bluetooth/hci_serdev.c
-index 3b8ac0ece3fb..7de0edc0ff8c 100644
---- a/drivers/bluetooth/hci_serdev.c
-+++ b/drivers/bluetooth/hci_serdev.c
-@@ -353,3 +353,4 @@ int hci_uart_register_device(struct hci_uart *hu,
- p->close(hu);
- return err;
- }
-+EXPORT_SYMBOL_GPL(hci_uart_register_device);
---
-2.12.2
-
-From 68bfebdd86741d45508148c6fa13b5bd21116c7e Mon Sep 17 00:00:00 2001
-From: Sebastian Reichel <sre@kernel.org>
-Date: Tue, 28 Mar 2017 17:59:38 +0200
-Subject: [PATCH 08/13] dt-bindings: net: bluetooth: Add nokia-bluetooth
-
-Add binding document for serial bluetooth chips using
-Nokia H4+ protocol.
-
-Acked-by: Rob Herring <robh@kernel.org>
-Signed-off-by: Sebastian Reichel <sre@kernel.org>
-
-Changes since PATCHv1:
- * change compatible strings
- * mention active high/low state for GPIOs
-Signed-off-by: Rob Herring <robh@kernel.org>
----
- .../devicetree/bindings/net/nokia-bluetooth.txt | 51 ++++++++++++++++++++++
- 1 file changed, 51 insertions(+)
- create mode 100644 Documentation/devicetree/bindings/net/nokia-bluetooth.txt
-
-diff --git a/Documentation/devicetree/bindings/net/nokia-bluetooth.txt b/Documentation/devicetree/bindings/net/nokia-bluetooth.txt
-new file mode 100644
-index 000000000000..42be7dc9a70b
---- /dev/null
-+++ b/Documentation/devicetree/bindings/net/nokia-bluetooth.txt
-@@ -0,0 +1,51 @@
-+Nokia Bluetooth Chips
-+---------------------
-+
-+Nokia phones often come with UART connected bluetooth chips from different
-+vendors and modified device API. Those devices speak a protocol named H4+
-+(also known as h4p) by Nokia, which is similar to the H4 protocol from the
-+Bluetooth standard. In addition to the H4 protocol it specifies two more
-+UART status lines for wakeup of UART transceivers to improve power management
-+and a few new packet types used to negotiate uart speed.
-+
-+Required properties:
-+
-+ - compatible: should contain "nokia,h4p-bluetooth" as well as one of the following:
-+ * "brcm,bcm2048-nokia"
-+ * "ti,wl1271-bluetooth-nokia"
-+ - reset-gpios: GPIO specifier, used to reset the BT module (active low)
-+ - bluetooth-wakeup-gpios: GPIO specifier, used to wakeup the BT module (active high)
-+ - host-wakeup-gpios: GPIO specifier, used to wakeup the host processor (active high)
-+ - clock-names: should be "sysclk"
-+ - clocks: should contain a clock specifier for every name in clock-names
-+
-+Optional properties:
-+
-+ - None
-+
-+Example:
-+
-+/ {
-+ /* controlled (enabled/disabled) directly by BT module */
-+ bluetooth_clk: vctcxo {
-+ compatible = "fixed-clock";
-+ #clock-cells = <0>;
-+ clock-frequency = <38400000>;
-+ };
-+};
-+
-+&uart2 {
-+ pinctrl-names = "default";
-+ pinctrl-0 = <&uart2_pins>;
-+
-+ bluetooth {
-+ compatible = "ti,wl1271-bluetooth-nokia", "nokia,h4p-bluetooth";
-+
-+ reset-gpios = <&gpio1 26 GPIO_ACTIVE_LOW>; /* gpio26 */
-+ host-wakeup-gpios = <&gpio4 5 GPIO_ACTIVE_HIGH>; /* gpio101 */
-+ bluetooth-wakeup-gpios = <&gpio2 5 GPIO_ACTIVE_HIGH>; /* gpio37 */
-+
-+ clocks = <&bluetooth_clk>;
-+ clock-names = "sysclk";
-+ };
-+};
---
-2.12.2
-
-From 90753299ed56ee7c5807c09b2185094a91a2cbbe Mon Sep 17 00:00:00 2001
-From: Sebastian Reichel <sre@kernel.org>
-Date: Tue, 28 Mar 2017 17:59:39 +0200
-Subject: [PATCH 09/13] Bluetooth: add nokia driver
-
-This adds a driver for the Nokia H4+ protocol, which is used
-at least on the Nokia N9, N900 & N950.
-
-Signed-off-by: Sebastian Reichel <sre@kernel.org>
-Signed-off-by: Rob Herring <robh@kernel.org>
----
- drivers/bluetooth/Kconfig | 12 +
- drivers/bluetooth/Makefile | 2 +
- drivers/bluetooth/hci_nokia.c | 819 ++++++++++++++++++++++++++++++++++++++++++
- 3 files changed, 833 insertions(+)
- create mode 100644 drivers/bluetooth/hci_nokia.c
-
-diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig
-index 08e054507d0b..479e2eacd1aa 100644
---- a/drivers/bluetooth/Kconfig
-+++ b/drivers/bluetooth/Kconfig
-@@ -86,6 +86,18 @@ config BT_HCIUART_H4
-
- Say Y here to compile support for HCI UART (H4) protocol.
-
-+config BT_HCIUART_NOKIA
-+ tristate "UART Nokia H4+ protocol support"
-+ depends on BT_HCIUART
-+ depends on SERIAL_DEV_BUS
-+ depends on PM
-+ help
-+ Nokia H4+ is serial protocol for communication between Bluetooth
-+ device and host. This protocol is required for Bluetooth devices
-+ with UART interface in Nokia devices.
-+
-+ Say Y here to compile support for Nokia's H4+ protocol.
-+
- config BT_HCIUART_BCSP
- bool "BCSP protocol support"
- depends on BT_HCIUART
-diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile
-index fd571689eed6..a7f237320f4b 100644
---- a/drivers/bluetooth/Makefile
-+++ b/drivers/bluetooth/Makefile
-@@ -25,6 +25,8 @@ obj-$(CONFIG_BT_BCM) += btbcm.o
- obj-$(CONFIG_BT_RTL) += btrtl.o
- obj-$(CONFIG_BT_QCA) += btqca.o
-
-+obj-$(CONFIG_BT_HCIUART_NOKIA) += hci_nokia.o
-+
- btmrvl-y := btmrvl_main.o
- btmrvl-$(CONFIG_DEBUG_FS) += btmrvl_debugfs.o
-
-diff --git a/drivers/bluetooth/hci_nokia.c b/drivers/bluetooth/hci_nokia.c
-new file mode 100644
-index 000000000000..c77f04af01bf
---- /dev/null
-+++ b/drivers/bluetooth/hci_nokia.c
-@@ -0,0 +1,819 @@
-+/*
-+ * Bluetooth HCI UART H4 driver with Nokia Extensions AKA Nokia H4+
-+ *
-+ * Copyright (C) 2015 Marcel Holtmann <marcel@holtmann.org>
-+ * Copyright (C) 2015-2017 Sebastian Reichel <sre@kernel.org>
-+ *
-+ * This program is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-+ * GNU General Public License for more details.
-+ */
-+
-+#include <linux/module.h>
-+#include <linux/clk.h>
-+#include <linux/kernel.h>
-+#include <linux/types.h>
-+#include <linux/interrupt.h>
-+#include <linux/pm_runtime.h>
-+#include <linux/firmware.h>
-+#include <linux/slab.h>
-+#include <linux/string.h>
-+#include <linux/errno.h>
-+#include <linux/skbuff.h>
-+#include <linux/gpio/consumer.h>
-+#include <linux/unaligned/le_struct.h>
-+#include <net/bluetooth/bluetooth.h>
-+#include <net/bluetooth/hci_core.h>
-+#include <linux/serdev.h>
-+
-+#include "hci_uart.h"
-+#include "btbcm.h"
-+
-+#define NOKIA_ID_BCM2048 0x04
-+#define NOKIA_ID_TI1271 0x31
-+
-+#define FIRMWARE_BCM2048 "nokia/bcmfw.bin"
-+#define FIRMWARE_TI1271 "nokia/ti1273.bin"
-+
-+#define HCI_NOKIA_NEG_PKT 0x06
-+#define HCI_NOKIA_ALIVE_PKT 0x07
-+#define HCI_NOKIA_RADIO_PKT 0x08
-+
-+#define HCI_NOKIA_NEG_HDR_SIZE 1
-+#define HCI_NOKIA_MAX_NEG_SIZE 255
-+#define HCI_NOKIA_ALIVE_HDR_SIZE 1
-+#define HCI_NOKIA_MAX_ALIVE_SIZE 255
-+#define HCI_NOKIA_RADIO_HDR_SIZE 2
-+#define HCI_NOKIA_MAX_RADIO_SIZE 255
-+
-+#define NOKIA_PROTO_PKT 0x44
-+#define NOKIA_PROTO_BYTE 0x4c
-+
-+#define NOKIA_NEG_REQ 0x00
-+#define NOKIA_NEG_ACK 0x20
-+#define NOKIA_NEG_NAK 0x40
-+
-+#define H4_TYPE_SIZE 1
-+
-+#define NOKIA_RECV_ALIVE \
-+ .type = HCI_NOKIA_ALIVE_PKT, \
-+ .hlen = HCI_NOKIA_ALIVE_HDR_SIZE, \
-+ .loff = 0, \
-+ .lsize = 1, \
-+ .maxlen = HCI_NOKIA_MAX_ALIVE_SIZE \
-+
-+#define NOKIA_RECV_NEG \
-+ .type = HCI_NOKIA_NEG_PKT, \
-+ .hlen = HCI_NOKIA_NEG_HDR_SIZE, \
-+ .loff = 0, \
-+ .lsize = 1, \
-+ .maxlen = HCI_NOKIA_MAX_NEG_SIZE \
-+
-+#define NOKIA_RECV_RADIO \
-+ .type = HCI_NOKIA_RADIO_PKT, \
-+ .hlen = HCI_NOKIA_RADIO_HDR_SIZE, \
-+ .loff = 1, \
-+ .lsize = 1, \
-+ .maxlen = HCI_NOKIA_MAX_RADIO_SIZE \
-+
-+struct hci_nokia_neg_hdr {
-+ u8 dlen;
-+} __packed;
-+
-+struct hci_nokia_neg_cmd {
-+ u8 ack;
-+ u16 baud;
-+ u16 unused1;
-+ u8 proto;
-+ u16 sys_clk;
-+ u16 unused2;
-+} __packed;
-+
-+#define NOKIA_ALIVE_REQ 0x55
-+#define NOKIA_ALIVE_RESP 0xcc
-+
-+struct hci_nokia_alive_hdr {
-+ u8 dlen;
-+} __packed;
-+
-+struct hci_nokia_alive_pkt {
-+ u8 mid;
-+ u8 unused;
-+} __packed;
-+
-+struct hci_nokia_neg_evt {
-+ u8 ack;
-+ u16 baud;
-+ u16 unused1;
-+ u8 proto;
-+ u16 sys_clk;
-+ u16 unused2;
-+ u8 man_id;
-+ u8 ver_id;
-+} __packed;
-+
-+#define MAX_BAUD_RATE 3692300
-+#define SETUP_BAUD_RATE 921600
-+#define INIT_BAUD_RATE 120000
-+
-+struct hci_nokia_radio_hdr {
-+ u8 evt;
-+ u8 dlen;
-+} __packed;
-+
-+struct nokia_bt_dev {
-+ struct hci_uart hu;
-+ struct serdev_device *serdev;
-+
-+ struct gpio_desc *reset;
-+ struct gpio_desc *wakeup_host;
-+ struct gpio_desc *wakeup_bt;
-+ unsigned long sysclk_speed;
-+
-+ int wake_irq;
-+ struct sk_buff *rx_skb;
-+ struct sk_buff_head txq;
-+ bdaddr_t bdaddr;
-+
-+ int init_error;
-+ struct completion init_completion;
-+
-+ u8 man_id;
-+ u8 ver_id;
-+
-+ bool initialized;
-+ bool tx_enabled;
-+ bool rx_enabled;
-+};
-+
-+static int nokia_enqueue(struct hci_uart *hu, struct sk_buff *skb);
-+
-+static void nokia_flow_control(struct serdev_device *serdev, bool enable)
-+{
-+ if (enable) {
-+ serdev_device_set_rts(serdev, true);
-+ serdev_device_set_flow_control(serdev, true);
-+ } else {
-+ serdev_device_set_flow_control(serdev, false);
-+ serdev_device_set_rts(serdev, false);
-+ }
-+}
-+
-+static irqreturn_t wakeup_handler(int irq, void *data)
-+{
-+ struct nokia_bt_dev *btdev = data;
-+ struct device *dev = &btdev->serdev->dev;
-+ int wake_state = gpiod_get_value(btdev->wakeup_host);
-+
-+ if (btdev->rx_enabled == wake_state)
-+ return IRQ_HANDLED;
-+
-+ if (wake_state)
-+ pm_runtime_get(dev);
-+ else
-+ pm_runtime_put(dev);
-+
-+ btdev->rx_enabled = wake_state;
-+
-+ return IRQ_HANDLED;
-+}
-+
-+static int nokia_reset(struct hci_uart *hu)
-+{
-+ struct nokia_bt_dev *btdev = hu->priv;
-+ struct device *dev = &btdev->serdev->dev;
-+ int err;
-+
-+ /* reset routine */
-+ gpiod_set_value_cansleep(btdev->reset, 1);
-+ gpiod_set_value_cansleep(btdev->wakeup_bt, 1);
-+
-+ msleep(100);
-+
-+ /* safety check */
-+ err = gpiod_get_value_cansleep(btdev->wakeup_host);
-+ if (err == 1) {
-+ dev_err(dev, "reset: host wakeup not low!");
-+ return -EPROTO;
-+ }
-+
-+ /* flush queue */
-+ serdev_device_write_flush(btdev->serdev);
-+
-+ /* init uart */
-+ nokia_flow_control(btdev->serdev, false);
-+ serdev_device_set_baudrate(btdev->serdev, INIT_BAUD_RATE);
-+
-+ gpiod_set_value_cansleep(btdev->reset, 0);
-+
-+ /* wait for cts */
-+ err = serdev_device_wait_for_cts(btdev->serdev, true, 200);
-+ if (err < 0) {
-+ dev_err(dev, "CTS not received: %d", err);
-+ return err;
-+ }
-+
-+ nokia_flow_control(btdev->serdev, true);
-+
-+ return 0;
-+}
-+
-+static int nokia_send_alive_packet(struct hci_uart *hu)
-+{
-+ struct nokia_bt_dev *btdev = hu->priv;
-+ struct device *dev = &btdev->serdev->dev;
-+ struct hci_nokia_alive_hdr *hdr;
-+ struct hci_nokia_alive_pkt *pkt;
-+ struct sk_buff *skb;
-+ int len;
-+
-+ init_completion(&btdev->init_completion);
-+
-+ len = H4_TYPE_SIZE + sizeof(*hdr) + sizeof(*pkt);
-+ skb = bt_skb_alloc(len, GFP_KERNEL);
-+ if (!skb)
-+ return -ENOMEM;
-+
-+ hci_skb_pkt_type(skb) = HCI_NOKIA_ALIVE_PKT;
-+ memset(skb->data, 0x00, len);
-+
-+ hdr = (struct hci_nokia_alive_hdr *)skb_put(skb, sizeof(*hdr));
-+ hdr->dlen = sizeof(*pkt);
-+ pkt = (struct hci_nokia_alive_pkt *)skb_put(skb, sizeof(*pkt));
-+ pkt->mid = NOKIA_ALIVE_REQ;
-+
-+ nokia_enqueue(hu, skb);
-+ hci_uart_tx_wakeup(hu);
-+
-+ dev_dbg(dev, "Alive sent");
-+
-+ if (!wait_for_completion_interruptible_timeout(&btdev->init_completion,
-+ msecs_to_jiffies(1000))) {
-+ return -ETIMEDOUT;
-+ }
-+
-+ if (btdev->init_error < 0)
-+ return btdev->init_error;
-+
-+ return 0;
-+}
-+
-+static int nokia_send_negotiation(struct hci_uart *hu)
-+{
-+ struct nokia_bt_dev *btdev = hu->priv;
-+ struct device *dev = &btdev->serdev->dev;
-+ struct hci_nokia_neg_cmd *neg_cmd;
-+ struct hci_nokia_neg_hdr *neg_hdr;
-+ struct sk_buff *skb;
-+ int len, err;
-+ u16 baud = DIV_ROUND_CLOSEST(btdev->sysclk_speed * 10, SETUP_BAUD_RATE);
-+ int sysclk = btdev->sysclk_speed / 1000;
-+
-+ len = H4_TYPE_SIZE + sizeof(*neg_hdr) + sizeof(*neg_cmd);
-+ skb = bt_skb_alloc(len, GFP_KERNEL);
-+ if (!skb)
-+ return -ENOMEM;
-+
-+ hci_skb_pkt_type(skb) = HCI_NOKIA_NEG_PKT;
-+
-+ neg_hdr = (struct hci_nokia_neg_hdr *)skb_put(skb, sizeof(*neg_hdr));
-+ neg_hdr->dlen = sizeof(*neg_cmd);
-+
-+ neg_cmd = (struct hci_nokia_neg_cmd *)skb_put(skb, sizeof(*neg_cmd));
-+ neg_cmd->ack = NOKIA_NEG_REQ;
-+ neg_cmd->baud = cpu_to_le16(baud);
-+ neg_cmd->unused1 = 0x0000;
-+ neg_cmd->proto = NOKIA_PROTO_BYTE;
-+ neg_cmd->sys_clk = cpu_to_le16(sysclk);
-+ neg_cmd->unused2 = 0x0000;
-+
-+ btdev->init_error = 0;
-+ init_completion(&btdev->init_completion);
-+
-+ nokia_enqueue(hu, skb);
-+ hci_uart_tx_wakeup(hu);
-+
-+ dev_dbg(dev, "Negotiation sent");
-+
-+ if (!wait_for_completion_interruptible_timeout(&btdev->init_completion,
-+ msecs_to_jiffies(10000))) {
-+ return -ETIMEDOUT;
-+ }
-+
-+ if (btdev->init_error < 0)
-+ return btdev->init_error;
-+
-+ /* Change to previously negotiated speed. Flow Control
-+ * is disabled until bluetooth adapter is ready to avoid
-+ * broken bytes being received.
-+ */
-+ nokia_flow_control(btdev->serdev, false);
-+ serdev_device_set_baudrate(btdev->serdev, SETUP_BAUD_RATE);
-+ err = serdev_device_wait_for_cts(btdev->serdev, true, 200);
-+ if (err < 0) {
-+ dev_err(dev, "CTS not received: %d", err);
-+ return err;
-+ }
-+ nokia_flow_control(btdev->serdev, true);
-+
-+ dev_dbg(dev, "Negotiation successful");
-+
-+ return 0;
-+}
-+
-+static int nokia_setup_fw(struct hci_uart *hu)
-+{
-+ struct nokia_bt_dev *btdev = hu->priv;
-+ struct device *dev = &btdev->serdev->dev;
-+ const char *fwname;
-+ const struct firmware *fw;
-+ const u8 *fw_ptr;
-+ size_t fw_size;
-+ int err;
-+
-+ dev_dbg(dev, "setup firmware");
-+
-+ if (btdev->man_id == NOKIA_ID_BCM2048) {
-+ fwname = FIRMWARE_BCM2048;
-+ } else if (btdev->man_id == NOKIA_ID_TI1271) {
-+ fwname = FIRMWARE_TI1271;
-+ } else {
-+ dev_err(dev, "Unsupported bluetooth device!");
-+ return -ENODEV;
-+ }
-+
-+ err = request_firmware(&fw, fwname, dev);
-+ if (err < 0) {
-+ dev_err(dev, "%s: Failed to load Nokia firmware file (%d)",
-+ hu->hdev->name, err);
-+ return err;
-+ }
-+
-+ fw_ptr = fw->data;
-+ fw_size = fw->size;
-+
-+ while (fw_size >= 4) {
-+ u16 pkt_size = get_unaligned_le16(fw_ptr);
-+ u8 pkt_type = fw_ptr[2];
-+ const struct hci_command_hdr *cmd;
-+ u16 opcode;
-+ struct sk_buff *skb;
-+
-+ switch (pkt_type) {
-+ case HCI_COMMAND_PKT:
-+ cmd = (struct hci_command_hdr *)(fw_ptr + 3);
-+ opcode = le16_to_cpu(cmd->opcode);
-+
-+ skb = __hci_cmd_sync(hu->hdev, opcode, cmd->plen,
-+ fw_ptr + 3 + HCI_COMMAND_HDR_SIZE,
-+ HCI_INIT_TIMEOUT);
-+ if (IS_ERR(skb)) {
-+ err = PTR_ERR(skb);
-+ dev_err(dev, "%s: FW command %04x failed (%d)",
-+ hu->hdev->name, opcode, err);
-+ goto done;
-+ }
-+ kfree_skb(skb);
-+ break;
-+ case HCI_NOKIA_RADIO_PKT:
-+ case HCI_NOKIA_NEG_PKT:
-+ case HCI_NOKIA_ALIVE_PKT:
-+ break;
-+ }
-+
-+ fw_ptr += pkt_size + 2;
-+ fw_size -= pkt_size + 2;
-+ }
-+
-+done:
-+ release_firmware(fw);
-+ return err;
-+}
-+
-+static int nokia_setup(struct hci_uart *hu)
-+{
-+ struct nokia_bt_dev *btdev = hu->priv;
-+ struct device *dev = &btdev->serdev->dev;
-+ int err;
-+
-+ btdev->initialized = false;
-+
-+ nokia_flow_control(btdev->serdev, false);
-+
-+ pm_runtime_get_sync(dev);
-+
-+ if (btdev->tx_enabled) {
-+ gpiod_set_value_cansleep(btdev->wakeup_bt, 0);
-+ pm_runtime_put(&btdev->serdev->dev);
-+ btdev->tx_enabled = false;
-+ }
-+
-+ dev_dbg(dev, "protocol setup");
-+
-+ /* 0. reset connection */
-+ err = nokia_reset(hu);
-+ if (err < 0) {
-+ dev_err(dev, "Reset failed: %d", err);
-+ goto out;
-+ }
-+
-+ /* 1. negotiate speed etc */
-+ err = nokia_send_negotiation(hu);
-+ if (err < 0) {
-+ dev_err(dev, "Negotiation failed: %d", err);
-+ goto out;
-+ }
-+
-+ /* 2. verify correct setup using alive packet */
-+ err = nokia_send_alive_packet(hu);
-+ if (err < 0) {
-+ dev_err(dev, "Alive check failed: %d", err);
-+ goto out;
-+ }
-+
-+ /* 3. send firmware */
-+ err = nokia_setup_fw(hu);
-+ if (err < 0) {
-+ dev_err(dev, "Could not setup FW: %d", err);
-+ goto out;
-+ }
-+
-+ nokia_flow_control(btdev->serdev, false);
-+ serdev_device_set_baudrate(btdev->serdev, MAX_BAUD_RATE);
-+ nokia_flow_control(btdev->serdev, true);
-+
-+ if (btdev->man_id == NOKIA_ID_BCM2048) {
-+ hu->hdev->set_bdaddr = btbcm_set_bdaddr;
-+ set_bit(HCI_QUIRK_INVALID_BDADDR, &hu->hdev->quirks);
-+ dev_dbg(dev, "bcm2048 has invalid bluetooth address!");
-+ }
-+
-+ dev_dbg(dev, "protocol setup done!");
-+
-+ gpiod_set_value_cansleep(btdev->wakeup_bt, 0);
-+ pm_runtime_put(dev);
-+ btdev->tx_enabled = false;
-+ btdev->initialized = true;
-+
-+ return 0;
-+out:
-+ pm_runtime_put(dev);
-+
-+ return err;
-+}
-+
-+static int nokia_open(struct hci_uart *hu)
-+{
-+ struct device *dev = &hu->serdev->dev;
-+
-+ dev_dbg(dev, "protocol open");
-+
-+ serdev_device_open(hu->serdev);
-+
-+ pm_runtime_enable(dev);
-+
-+ return 0;
-+}
-+
-+static int nokia_flush(struct hci_uart *hu)
-+{
-+ struct nokia_bt_dev *btdev = hu->priv;
-+
-+ dev_dbg(&btdev->serdev->dev, "flush device");
-+
-+ skb_queue_purge(&btdev->txq);
-+
-+ return 0;
-+}
-+
-+static int nokia_close(struct hci_uart *hu)
-+{
-+ struct nokia_bt_dev *btdev = hu->priv;
-+ struct device *dev = &btdev->serdev->dev;
-+
-+ dev_dbg(dev, "close device");
-+
-+ btdev->initialized = false;
-+
-+ skb_queue_purge(&btdev->txq);
-+
-+ kfree_skb(btdev->rx_skb);
-+
-+ /* disable module */
-+ gpiod_set_value(btdev->reset, 1);
-+ gpiod_set_value(btdev->wakeup_bt, 0);
-+
-+ pm_runtime_disable(&btdev->serdev->dev);
-+ serdev_device_close(btdev->serdev);
-+
-+ return 0;
-+}
-+
-+/* Enqueue frame for transmittion (padding, crc, etc) */
-+static int nokia_enqueue(struct hci_uart *hu, struct sk_buff *skb)
-+{
-+ struct nokia_bt_dev *btdev = hu->priv;
-+ int err;
-+
-+ /* Prepend skb with frame type */
-+ memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
-+
-+ /* Packets must be word aligned */
-+ if (skb->len % 2) {
-+ err = skb_pad(skb, 1);
-+ if (err)
-+ return err;
-+ *skb_put(skb, 1) = 0x00;
-+ }
-+
-+ skb_queue_tail(&btdev->txq, skb);
-+
-+ return 0;
-+}
-+
-+static int nokia_recv_negotiation_packet(struct hci_dev *hdev,
-+ struct sk_buff *skb)
-+{
-+ struct hci_uart *hu = hci_get_drvdata(hdev);
-+ struct nokia_bt_dev *btdev = hu->priv;
-+ struct device *dev = &btdev->serdev->dev;
-+ struct hci_nokia_neg_hdr *hdr;
-+ struct hci_nokia_neg_evt *evt;
-+ int ret = 0;
-+
-+ hdr = (struct hci_nokia_neg_hdr *)skb->data;
-+ if (hdr->dlen != sizeof(*evt)) {
-+ btdev->init_error = -EIO;
-+ ret = -EIO;
-+ goto finish_neg;
-+ }
-+
-+ evt = (struct hci_nokia_neg_evt *)skb_pull(skb, sizeof(*hdr));
-+
-+ if (evt->ack != NOKIA_NEG_ACK) {
-+ dev_err(dev, "Negotiation received: wrong reply");
-+ btdev->init_error = -EINVAL;
-+ ret = -EINVAL;
-+ goto finish_neg;
-+ }
-+
-+ btdev->man_id = evt->man_id;
-+ btdev->ver_id = evt->ver_id;
-+
-+ dev_dbg(dev, "Negotiation received: baud=%u:clk=%u:manu=%u:vers=%u",
-+ evt->baud, evt->sys_clk, evt->man_id, evt->ver_id);
-+
-+finish_neg:
-+ complete(&btdev->init_completion);
-+ kfree_skb(skb);
-+ return ret;
-+}
-+
-+static int nokia_recv_alive_packet(struct hci_dev *hdev, struct sk_buff *skb)
-+{
-+ struct hci_uart *hu = hci_get_drvdata(hdev);
-+ struct nokia_bt_dev *btdev = hu->priv;
-+ struct device *dev = &btdev->serdev->dev;
-+ struct hci_nokia_alive_hdr *hdr;
-+ struct hci_nokia_alive_pkt *pkt;
-+ int ret = 0;
-+
-+ hdr = (struct hci_nokia_alive_hdr *)skb->data;
-+ if (hdr->dlen != sizeof(*pkt)) {
-+ dev_err(dev, "Corrupted alive message");
-+ btdev->init_error = -EIO;
-+ ret = -EIO;
-+ goto finish_alive;
-+ }
-+
-+ pkt = (struct hci_nokia_alive_pkt *)skb_pull(skb, sizeof(*hdr));
-+
-+ if (pkt->mid != NOKIA_ALIVE_RESP) {
-+ dev_err(dev, "Alive received: invalid response: 0x%02x!",
-+ pkt->mid);
-+ btdev->init_error = -EINVAL;
-+ ret = -EINVAL;
-+ goto finish_alive;
-+ }
-+
-+ dev_dbg(dev, "Alive received");
-+
-+finish_alive:
-+ complete(&btdev->init_completion);
-+ kfree_skb(skb);
-+ return ret;
-+}
-+
-+static int nokia_recv_radio(struct hci_dev *hdev, struct sk_buff *skb)
-+{
-+ /* Packets received on the dedicated radio channel are
-+ * HCI events and so feed them back into the core.
-+ */
-+ hci_skb_pkt_type(skb) = HCI_EVENT_PKT;
-+ return hci_recv_frame(hdev, skb);
-+}
-+
-+/* Recv data */
-+static const struct h4_recv_pkt nokia_recv_pkts[] = {
-+ { H4_RECV_ACL, .recv = hci_recv_frame },
-+ { H4_RECV_SCO, .recv = hci_recv_frame },
-+ { H4_RECV_EVENT, .recv = hci_recv_frame },
-+ { NOKIA_RECV_ALIVE, .recv = nokia_recv_alive_packet },
-+ { NOKIA_RECV_NEG, .recv = nokia_recv_negotiation_packet },
-+ { NOKIA_RECV_RADIO, .recv = nokia_recv_radio },
-+};
-+
-+static int nokia_recv(struct hci_uart *hu, const void *data, int count)
-+{
-+ struct nokia_bt_dev *btdev = hu->priv;
-+ struct device *dev = &btdev->serdev->dev;
-+ int err;
-+
-+ if (!test_bit(HCI_UART_REGISTERED, &hu->flags))
-+ return -EUNATCH;
-+
-+ btdev->rx_skb = h4_recv_buf(hu->hdev, btdev->rx_skb, data, count,
-+ nokia_recv_pkts, ARRAY_SIZE(nokia_recv_pkts));
-+ if (IS_ERR(btdev->rx_skb)) {
-+ err = PTR_ERR(btdev->rx_skb);
-+ dev_err(dev, "Frame reassembly failed (%d)", err);
-+ btdev->rx_skb = NULL;
-+ return err;
-+ }
-+
-+ return count;
-+}
-+
-+static struct sk_buff *nokia_dequeue(struct hci_uart *hu)
-+{
-+ struct nokia_bt_dev *btdev = hu->priv;
-+ struct device *dev = &btdev->serdev->dev;
-+ struct sk_buff *result = skb_dequeue(&btdev->txq);
-+
-+ if (!btdev->initialized)
-+ return result;
-+
-+ if (btdev->tx_enabled == !!result)
-+ return result;
-+
-+ if (result) {
-+ pm_runtime_get_sync(dev);
-+ gpiod_set_value_cansleep(btdev->wakeup_bt, 1);
-+ } else {
-+ serdev_device_wait_until_sent(btdev->serdev, 0);
-+ gpiod_set_value_cansleep(btdev->wakeup_bt, 0);
-+ pm_runtime_put(dev);
-+ }
-+
-+ btdev->tx_enabled = !!result;
-+
-+ return result;
-+}
-+
-+static const struct hci_uart_proto nokia_proto = {
-+ .id = HCI_UART_NOKIA,
-+ .name = "Nokia",
-+ .open = nokia_open,
-+ .close = nokia_close,
-+ .recv = nokia_recv,
-+ .enqueue = nokia_enqueue,
-+ .dequeue = nokia_dequeue,
-+ .flush = nokia_flush,
-+ .setup = nokia_setup,
-+ .manufacturer = 1,
-+};
-+
-+static int nokia_bluetooth_serdev_probe(struct serdev_device *serdev)
-+{
-+ struct device *dev = &serdev->dev;
-+ struct nokia_bt_dev *btdev;
-+ struct clk *sysclk;
-+ int err = 0;
-+
-+ btdev = devm_kzalloc(dev, sizeof(*btdev), GFP_KERNEL);
-+ if (!btdev)
-+ return -ENOMEM;
-+
-+ btdev->hu.serdev = btdev->serdev = serdev;
-+ serdev_device_set_drvdata(serdev, btdev);
-+
-+ btdev->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
-+ if (IS_ERR(btdev->reset)) {
-+ err = PTR_ERR(btdev->reset);
-+ dev_err(dev, "could not get reset gpio: %d", err);
-+ return err;
-+ }
-+
-+ btdev->wakeup_host = devm_gpiod_get(dev, "host-wakeup", GPIOD_IN);
-+ if (IS_ERR(btdev->wakeup_host)) {
-+ err = PTR_ERR(btdev->wakeup_host);
-+ dev_err(dev, "could not get host wakeup gpio: %d", err);
-+ return err;
-+ }
-+
-+ btdev->wake_irq = gpiod_to_irq(btdev->wakeup_host);
-+
-+ err = devm_request_threaded_irq(dev, btdev->wake_irq, NULL,
-+ wakeup_handler,
-+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
-+ "wakeup", btdev);
-+ if (err) {
-+ dev_err(dev, "could request wakeup irq: %d", err);
-+ return err;
-+ }
-+
-+ btdev->wakeup_bt = devm_gpiod_get(dev, "bluetooth-wakeup",
-+ GPIOD_OUT_LOW);
-+ if (IS_ERR(btdev->wakeup_bt)) {
-+ err = PTR_ERR(btdev->wakeup_bt);
-+ dev_err(dev, "could not get BT wakeup gpio: %d", err);
-+ return err;
-+ }
-+
-+ sysclk = devm_clk_get(dev, "sysclk");
-+ if (IS_ERR(sysclk)) {
-+ err = PTR_ERR(sysclk);
-+ dev_err(dev, "could not get sysclk: %d", err);
-+ return err;
-+ }
-+
-+ clk_prepare_enable(sysclk);
-+ btdev->sysclk_speed = clk_get_rate(sysclk);
-+ clk_disable_unprepare(sysclk);
-+
-+ skb_queue_head_init(&btdev->txq);
-+
-+ btdev->hu.priv = btdev;
-+ btdev->hu.alignment = 2; /* Nokia H4+ is word aligned */
-+
-+ err = hci_uart_register_device(&btdev->hu, &nokia_proto);
-+ if (err) {
-+ dev_err(dev, "could not register bluetooth uart: %d", err);
-+ return err;
-+ }
-+
-+ return 0;
-+}
-+
-+static void nokia_bluetooth_serdev_remove(struct serdev_device *serdev)
-+{
-+ struct nokia_bt_dev *btdev = serdev_device_get_drvdata(serdev);
-+ struct hci_uart *hu = &btdev->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);
-+
-+ pm_runtime_disable(&btdev->serdev->dev);
-+}
-+
-+static int nokia_bluetooth_runtime_suspend(struct device *dev)
-+{
-+ struct serdev_device *serdev = to_serdev_device(dev);
-+
-+ nokia_flow_control(serdev, false);
-+ return 0;
-+}
-+
-+static int nokia_bluetooth_runtime_resume(struct device *dev)
-+{
-+ struct serdev_device *serdev = to_serdev_device(dev);
-+
-+ nokia_flow_control(serdev, true);
-+ return 0;
-+}
-+
-+static const struct dev_pm_ops nokia_bluetooth_pm_ops = {
-+ SET_RUNTIME_PM_OPS(nokia_bluetooth_runtime_suspend,
-+ nokia_bluetooth_runtime_resume,
-+ NULL)
-+};
-+
-+#ifdef CONFIG_OF
-+static const struct of_device_id nokia_bluetooth_of_match[] = {
-+ { .compatible = "nokia,h4p-bluetooth", },
-+ {},
-+};
-+MODULE_DEVICE_TABLE(of, nokia_bluetooth_of_match);
-+#endif
-+
-+static struct serdev_device_driver nokia_bluetooth_serdev_driver = {
-+ .probe = nokia_bluetooth_serdev_probe,
-+ .remove = nokia_bluetooth_serdev_remove,
-+ .driver = {
-+ .name = "nokia-bluetooth",
-+ .pm = &nokia_bluetooth_pm_ops,
-+ .of_match_table = of_match_ptr(nokia_bluetooth_of_match),
-+ },
-+};
-+
-+module_serdev_device_driver(nokia_bluetooth_serdev_driver);
---
-2.12.2
-
-From 5593378d18f2487bdce87a687b4263c9626510cb Mon Sep 17 00:00:00 2001
-From: Rob Herring <robh@kernel.org>
-Date: Wed, 5 Apr 2017 12:10:13 -0500
-Subject: [PATCH 10/13] dt-bindings: net: Add TI WiLink shared transport
- binding
-
-Add serial slave device binding for the TI WiLink series of Bluetooth/FM/GPS
-devices.
-
-Signed-off-by: Rob Herring <robh@kernel.org>
-Cc: Mark Rutland <mark.rutland@arm.com>
-Cc: netdev@vger.kernel.org
-Cc: devicetree@vger.kernel.org
----
- .../devicetree/bindings/net/ti,wilink-st.txt | 35 ++++++++++++++++++++++
- 1 file changed, 35 insertions(+)
- create mode 100644 Documentation/devicetree/bindings/net/ti,wilink-st.txt
-
-diff --git a/Documentation/devicetree/bindings/net/ti,wilink-st.txt b/Documentation/devicetree/bindings/net/ti,wilink-st.txt
-new file mode 100644
-index 000000000000..cbad73a84ac4
---- /dev/null
-+++ b/Documentation/devicetree/bindings/net/ti,wilink-st.txt
-@@ -0,0 +1,35 @@
-+TI WiLink 7/8 (wl12xx/wl18xx) Shared Transport BT/FM/GPS devices
-+
-+TI WiLink devices have a UART interface for providing Bluetooth, FM radio,
-+and GPS over what's called "shared transport". The shared transport is
-+standard BT HCI protocol with additional channels for the other functions.
-+
-+These devices also have a separate WiFi interface as described in
-+wireless/ti,wlcore.txt.
-+
-+This bindings follows the UART slave device binding in
-+../serial/slave-device.txt.
-+
-+Required properties:
-+ - compatible: should be one of the following:
-+ "ti,wl1271-st"
-+ "ti,wl1273-st"
-+ "ti,wl1831-st"
-+ "ti,wl1835-st"
-+ "ti,wl1837-st"
-+
-+Optional properties:
-+ - enable-gpios : GPIO signal controlling enabling of BT. Active high.
-+ - vio-supply : Vio input supply (1.8V)
-+ - vbat-supply : Vbat input supply (2.9-4.8V)
-+
-+Example:
-+
-+&serial0 {
-+ compatible = "ns16550a";
-+ ...
-+ bluetooth {
-+ compatible = "ti,wl1835-st";
-+ enable-gpios = <&gpio1 7 GPIO_ACTIVE_HIGH>;
-+ };
-+};
---
-2.12.2
-
-From d22a4564d1b8e1eeb7c442171cedcc3ae809cfff Mon Sep 17 00:00:00 2001
-From: Rob Herring <robh@kernel.org>
-Date: Tue, 17 Jan 2017 09:58:10 -0600
-Subject: [PATCH 11/13] bluetooth: hci_uart: remove unused hci_uart_init_tty
-
-There are no users of hci_uart_init_tty, so remove it.
-
-Signed-off-by: Rob Herring <robh@kernel.org>
-Cc: Marcel Holtmann <marcel@holtmann.org>
-Cc: Gustavo Padovan <gustavo@padovan.org>
-Cc: Johan Hedberg <johan.hedberg@gmail.com>
-Cc: linux-bluetooth@vger.kernel.org
----
- drivers/bluetooth/hci_ldisc.c | 19 -------------------
- drivers/bluetooth/hci_uart.h | 1 -
- 2 files changed, 20 deletions(-)
-
-diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c
-index 17bcbc13623f..cec4438ede01 100644
---- a/drivers/bluetooth/hci_ldisc.c
-+++ b/drivers/bluetooth/hci_ldisc.c
-@@ -319,25 +319,6 @@ void hci_uart_set_speeds(struct hci_uart *hu, unsigned int init_speed,
- hu->oper_speed = oper_speed;
- }
-
--void hci_uart_init_tty(struct hci_uart *hu)
--{
-- struct tty_struct *tty = hu->tty;
-- struct ktermios ktermios;
--
-- /* Bring the UART into a known 8 bits no parity hw fc state */
-- ktermios = tty->termios;
-- ktermios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP |
-- INLCR | IGNCR | ICRNL | IXON);
-- ktermios.c_oflag &= ~OPOST;
-- ktermios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
-- ktermios.c_cflag &= ~(CSIZE | PARENB);
-- ktermios.c_cflag |= CS8;
-- ktermios.c_cflag |= CRTSCTS;
--
-- /* tty_set_termios() return not checked as it is always 0 */
-- tty_set_termios(tty, &ktermios);
--}
--
- void hci_uart_set_baudrate(struct hci_uart *hu, unsigned int speed)
- {
- struct tty_struct *tty = hu->tty;
-diff --git a/drivers/bluetooth/hci_uart.h b/drivers/bluetooth/hci_uart.h
-index 1b41c661bbb8..2b05e557fad0 100644
---- a/drivers/bluetooth/hci_uart.h
-+++ b/drivers/bluetooth/hci_uart.h
-@@ -114,7 +114,6 @@ int hci_uart_register_device(struct hci_uart *hu, const struct hci_uart_proto *p
-
- int hci_uart_tx_wakeup(struct hci_uart *hu);
- int hci_uart_init_ready(struct hci_uart *hu);
--void hci_uart_init_tty(struct hci_uart *hu);
- void hci_uart_set_baudrate(struct hci_uart *hu, unsigned int speed);
- void hci_uart_set_flow_control(struct hci_uart *hu, bool enable);
- void hci_uart_set_speeds(struct hci_uart *hu, unsigned int init_speed,
---
-2.12.2
-
-From 4664fe8eb293288c1fba2ebc50ac50131f6125cf Mon Sep 17 00:00:00 2001
-From: Rob Herring <robh@kernel.org>
-Date: Wed, 18 Jan 2017 11:41:25 -0600
-Subject: [PATCH 12/13] bluetooth: hci_uart: add LL protocol serdev driver
- support
-
-Turns out that the LL protocol and the TI-ST are the same thing AFAICT.
-The TI-ST adds firmware loading, GPIO control, and shared access for
-NFC, FM radio, etc. For now, we're only implementing what is needed for
-BT. This mirrors other drivers like BCM and Intel, but uses the new
-serdev bus.
-
-The firmware loading is greatly simplified by using existing
-infrastructure to send commands. It may be a bit slower than the
-original code using synchronous functions, but the real bottleneck is
-likely doing firmware load at 115.2kbps.
-
-Signed-off-by: Rob Herring <robh@kernel.org>
-Cc: Marcel Holtmann <marcel@holtmann.org>
-Cc: Gustavo Padovan <gustavo@padovan.org>
-Cc: Johan Hedberg <johan.hedberg@gmail.com>
-Cc: linux-bluetooth@vger.kernel.org
----
- drivers/bluetooth/hci_ll.c | 261 ++++++++++++++++++++++++++++++++++++++++++++-
- 1 file changed, 260 insertions(+), 1 deletion(-)
-
-diff --git a/drivers/bluetooth/hci_ll.c b/drivers/bluetooth/hci_ll.c
-index 02692fe30279..9b2054f5502d 100644
---- a/drivers/bluetooth/hci_ll.c
-+++ b/drivers/bluetooth/hci_ll.c
-@@ -34,20 +34,23 @@
- #include <linux/sched.h>
- #include <linux/types.h>
- #include <linux/fcntl.h>
-+#include <linux/firmware.h>
- #include <linux/interrupt.h>
- #include <linux/ptrace.h>
- #include <linux/poll.h>
-
- #include <linux/slab.h>
--#include <linux/tty.h>
- #include <linux/errno.h>
- #include <linux/string.h>
- #include <linux/signal.h>
- #include <linux/ioctl.h>
-+#include <linux/serdev.h>
- #include <linux/skbuff.h>
-+#include <linux/ti_wilink_st.h>
-
- #include <net/bluetooth/bluetooth.h>
- #include <net/bluetooth/hci_core.h>
-+#include <linux/gpio/consumer.h>
-
- #include "hci_uart.h"
-
-@@ -76,6 +79,12 @@ struct hcill_cmd {
- u8 cmd;
- } __packed;
-
-+struct ll_device {
-+ struct hci_uart hu;
-+ struct serdev_device *serdev;
-+ struct gpio_desc *enable_gpio;
-+};
-+
- struct ll_struct {
- unsigned long rx_state;
- unsigned long rx_count;
-@@ -136,6 +145,9 @@ static int ll_open(struct hci_uart *hu)
-
- hu->priv = ll;
-
-+ if (hu->serdev)
-+ serdev_device_open(hu->serdev);
-+
- return 0;
- }
-
-@@ -164,6 +176,13 @@ static int ll_close(struct hci_uart *hu)
-
- kfree_skb(ll->rx_skb);
-
-+ if (hu->serdev) {
-+ struct ll_device *lldev = serdev_device_get_drvdata(hu->serdev);
-+ gpiod_set_value_cansleep(lldev->enable_gpio, 0);
-+
-+ serdev_device_close(hu->serdev);
-+ }
-+
- hu->priv = NULL;
-
- kfree(ll);
-@@ -505,9 +524,245 @@ static struct sk_buff *ll_dequeue(struct hci_uart *hu)
- return skb_dequeue(&ll->txq);
- }
-
-+#ifdef CONFIG_SERIAL_DEV_BUS
-+static int read_local_version(struct hci_dev *hdev)
-+{
-+ int err = 0;
-+ unsigned short version = 0;
-+ struct sk_buff *skb;
-+ struct hci_rp_read_local_version *ver;
-+
-+ skb = __hci_cmd_sync(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL, HCI_INIT_TIMEOUT);
-+ if (IS_ERR(skb)) {
-+ bt_dev_err(hdev, "Reading TI version information failed (%ld)",
-+ PTR_ERR(skb));
-+ err = PTR_ERR(skb);
-+ goto out;
-+ }
-+ if (skb->len != sizeof(*ver)) {
-+ err = -EILSEQ;
-+ goto out;
-+ }
-+
-+ ver = (struct hci_rp_read_local_version *)skb->data;
-+ if (le16_to_cpu(ver->manufacturer) != 13) {
-+ err = -ENODEV;
-+ goto out;
-+ }
-+
-+ version = le16_to_cpu(ver->lmp_subver);
-+
-+out:
-+ if (err) bt_dev_err(hdev, "Failed to read TI version info: %d", err);
-+ kfree_skb(skb);
-+ return err ? err : version;
-+}
-+
-+/**
-+ * download_firmware -
-+ * internal function which parses through the .bts firmware
-+ * script file intreprets SEND, DELAY actions only as of now
-+ */
-+static int download_firmware(struct ll_device *lldev)
-+{
-+ unsigned short chip, min_ver, maj_ver;
-+ int version, err, len;
-+ unsigned char *ptr, *action_ptr;
-+ unsigned char bts_scr_name[40]; /* 40 char long bts scr name? */
-+ const struct firmware *fw;
-+ struct sk_buff *skb;
-+ struct hci_command *cmd;
-+
-+ version = read_local_version(lldev->hu.hdev);
-+ if (version < 0)
-+ return version;
-+
-+ chip = (version & 0x7C00) >> 10;
-+ min_ver = (version & 0x007F);
-+ maj_ver = (version & 0x0380) >> 7;
-+ if (version & 0x8000)
-+ maj_ver |= 0x0008;
-+
-+ snprintf(bts_scr_name, sizeof(bts_scr_name),
-+ "ti-connectivity/TIInit_%d.%d.%d.bts",
-+ chip, maj_ver, min_ver);
-+
-+ err = request_firmware(&fw, bts_scr_name, &lldev->serdev->dev);
-+ if (err || !fw->data || !fw->size) {
-+ bt_dev_err(lldev->hu.hdev, "request_firmware failed(errno %d) for %s",
-+ err, bts_scr_name);
-+ return -EINVAL;
-+ }
-+ ptr = (void *)fw->data;
-+ len = fw->size;
-+ /* bts_header to remove out magic number and
-+ * version
-+ */
-+ ptr += sizeof(struct bts_header);
-+ len -= sizeof(struct bts_header);
-+
-+ while (len > 0 && ptr) {
-+ bt_dev_dbg(lldev->hu.hdev, " action size %d, type %d ",
-+ ((struct bts_action *)ptr)->size,
-+ ((struct bts_action *)ptr)->type);
-+
-+ action_ptr = &(((struct bts_action *)ptr)->data[0]);
-+
-+ switch (((struct bts_action *)ptr)->type) {
-+ case ACTION_SEND_COMMAND: /* action send */
-+ bt_dev_dbg(lldev->hu.hdev, "S");
-+ cmd = (struct hci_command *)action_ptr;
-+ if (cmd->opcode == 0xff36) {
-+ /* ignore remote change
-+ * baud rate HCI VS command */
-+ bt_dev_warn(lldev->hu.hdev, "change remote baud rate command in firmware");
-+ break;
-+ }
-+ if (cmd->prefix != 1)
-+ bt_dev_dbg(lldev->hu.hdev, "command type %d\n", cmd->prefix);
-+
-+ skb = __hci_cmd_sync(lldev->hu.hdev, cmd->opcode, cmd->plen, &cmd->speed, HCI_INIT_TIMEOUT);
-+ if (IS_ERR(skb)) {
-+ bt_dev_err(lldev->hu.hdev, "send command failed\n");
-+ goto out_rel_fw;
-+ }
-+ kfree_skb(skb);
-+ break;
-+ case ACTION_WAIT_EVENT: /* wait */
-+ /* no need to wait as command was synchronous */
-+ bt_dev_dbg(lldev->hu.hdev, "W");
-+ break;
-+ case ACTION_DELAY: /* sleep */
-+ bt_dev_info(lldev->hu.hdev, "sleep command in scr");
-+ mdelay(((struct bts_action_delay *)action_ptr)->msec);
-+ break;
-+ }
-+ len -= (sizeof(struct bts_action) +
-+ ((struct bts_action *)ptr)->size);
-+ ptr += sizeof(struct bts_action) +
-+ ((struct bts_action *)ptr)->size;
-+ }
-+
-+out_rel_fw:
-+ /* fw download complete */
-+ release_firmware(fw);
-+ return err;
-+}
-+
-+static int ll_setup(struct hci_uart *hu)
-+{
-+ int err, retry = 3;
-+ struct ll_device *lldev;
-+ struct serdev_device *serdev = hu->serdev;
-+ u32 speed;
-+
-+ if (!serdev)
-+ return 0;
-+
-+ lldev = serdev_device_get_drvdata(serdev);
-+
-+ serdev_device_set_flow_control(serdev, true);
-+
-+ do {
-+ /* Configure BT_EN to HIGH state */
-+ gpiod_set_value_cansleep(lldev->enable_gpio, 0);
-+ msleep(5);
-+ gpiod_set_value_cansleep(lldev->enable_gpio, 1);
-+ msleep(100);
-+
-+ err = download_firmware(lldev);
-+ if (!err)
-+ break;
-+
-+ /* Toggle BT_EN and retry */
-+ bt_dev_err(hu->hdev, "download firmware failed, retrying...");
-+ } while (retry--);
-+
-+ if (err)
-+ return err;
-+
-+ /* Operational speed if any */
-+ if (hu->oper_speed)
-+ speed = hu->oper_speed;
-+ else if (hu->proto->oper_speed)
-+ speed = hu->proto->oper_speed;
-+ else
-+ speed = 0;
-+
-+ if (speed) {
-+ struct sk_buff *skb = __hci_cmd_sync(hu->hdev, 0xff36, sizeof(speed), &speed, HCI_INIT_TIMEOUT);
-+ if (!IS_ERR(skb)) {
-+ kfree_skb(skb);
-+ serdev_device_set_baudrate(serdev, speed);
-+ }
-+ }
-+
-+ return 0;
-+}
-+
-+static const struct hci_uart_proto llp;
-+
-+static int hci_ti_probe(struct serdev_device *serdev)
-+{
-+ struct hci_uart *hu;
-+ struct ll_device *lldev;
-+ u32 max_speed = 3000000;
-+
-+ lldev = devm_kzalloc(&serdev->dev, sizeof(struct ll_device), GFP_KERNEL);
-+ if (!lldev)
-+ return -ENOMEM;
-+ hu = &lldev->hu;
-+
-+ serdev_device_set_drvdata(serdev, lldev);
-+ lldev->serdev = hu->serdev = serdev;
-+
-+ lldev->enable_gpio = devm_gpiod_get_optional(&serdev->dev, "enable", GPIOD_OUT_LOW);
-+ if (IS_ERR(lldev->enable_gpio))
-+ return PTR_ERR(lldev->enable_gpio);
-+
-+ of_property_read_u32(serdev->dev.of_node, "max-speed", &max_speed);
-+ hci_uart_set_speeds(hu, 115200, max_speed);
-+
-+ return hci_uart_register_device(hu, &llp);
-+}
-+
-+static void hci_ti_remove(struct serdev_device *serdev)
-+{
-+ struct ll_device *lldev = serdev_device_get_drvdata(serdev);
-+ struct hci_uart *hu = &lldev->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);
-+}
-+
-+static const struct of_device_id hci_ti_of_match[] = {
-+ { .compatible = "ti,wl1831-st" },
-+ { .compatible = "ti,wl1835-st" },
-+ { .compatible = "ti,wl1837-st" },
-+ {},
-+};
-+MODULE_DEVICE_TABLE(of, hci_ti_of_match);
-+
-+static struct serdev_device_driver hci_ti_drv = {
-+ .driver = {
-+ .name = "hci-ti",
-+ .of_match_table = of_match_ptr(hci_ti_of_match),
-+ },
-+ .probe = hci_ti_probe,
-+ .remove = hci_ti_remove,
-+};
-+#else
-+#define ll_setup NULL
-+#endif
-+
- static const struct hci_uart_proto llp = {
- .id = HCI_UART_LL,
- .name = "LL",
-+ .setup = ll_setup,
- .open = ll_open,
- .close = ll_close,
- .recv = ll_recv,
-@@ -518,10 +773,14 @@ static const struct hci_uart_proto llp = {
-
- int __init ll_init(void)
- {
-+ serdev_device_driver_register(&hci_ti_drv);
-+
- return hci_uart_register_proto(&llp);
- }
-
- int __exit ll_deinit(void)
- {
-+ serdev_device_driver_unregister(&hci_ti_drv);
-+
- return hci_uart_unregister_proto(&llp);
- }
---
-2.12.2
-
-From 33bbcaa7b1a4639a5e149e725a674324e7487b17 Mon Sep 17 00:00:00 2001
-From: Rob Herring <robh@kernel.org>
-Date: Mon, 21 Nov 2016 15:21:56 -0600
-Subject: [PATCH 13/13] arm64: dts: hikey: add WL1835 Bluetooth device node
-
-This adds the serial slave device for the WL1835 Bluetooth interface.
-
-Signed-off-by: Rob Herring <robh@kernel.org>
-Cc: Wei Xu <xuwei5@hisilicon.com>
-Cc: Mark Rutland <mark.rutland@arm.com>
----
- arch/arm64/boot/dts/hisilicon/hi6220-hikey.dts | 5 +++++
- 1 file changed, 5 insertions(+)
-
-diff --git a/arch/arm64/boot/dts/hisilicon/hi6220-hikey.dts b/arch/arm64/boot/dts/hisilicon/hi6220-hikey.dts
-index dba3c131c62c..9b4ba7169210 100644
---- a/arch/arm64/boot/dts/hisilicon/hi6220-hikey.dts
-+++ b/arch/arm64/boot/dts/hisilicon/hi6220-hikey.dts
-@@ -98,6 +98,11 @@
- assigned-clocks = <&sys_ctrl HI6220_UART1_SRC>;
- assigned-clock-rates = <150000000>;
- status = "ok";
-+
-+ bluetooth {
-+ compatible = "ti,wl1835-st";
-+ enable-gpios = <&gpio1 7 GPIO_ACTIVE_HIGH>;
-+ };
- };
-
- uart2: uart@f7112000 {
---
-2.12.2
-