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, 2480 insertions, 0 deletions
diff --git a/ti-bluetooth.patch b/ti-bluetooth.patch
new file mode 100644
index 000000000..288eb7c94
--- /dev/null
+++ b/ti-bluetooth.patch
@@ -0,0 +1,2480 @@
+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
+