diff options
Diffstat (limited to 'ti-bluetooth.patch')
-rw-r--r-- | ti-bluetooth.patch | 2480 |
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 - |