diff --git a/arm64-hikey-fixes.patch b/arm64-hikey-fixes.patch new file mode 100644 index 000000000..18bc05b2b --- /dev/null +++ b/arm64-hikey-fixes.patch @@ -0,0 +1,77 @@ +From patchwork Sat Apr 8 07:18:40 2017 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: reset: hi6220: Set module license so that it can be loaded +From: Jeremy Linton +X-Patchwork-Id: 9670985 +Message-Id: <20170408071840.29380-1-lintonrjeremy@gmail.com> +To: linux-kernel@vger.kernel.org +Cc: p.zabel@pengutronix.de, saberlily.xia@hisilicon.com, + puck.chen@hisilicon.com, xinliang.liu@linaro.org, + Jeremy Linton +Date: Sat, 8 Apr 2017 02:18:40 -0500 + +The hi6220_reset driver can be built as a standalone module +yet it cannot be loaded because it depends on GPL exported symbols. + +Lets set the module license so that the module loads, and things like +the on-board kirin drm starts working. + +Signed-off-by: Jeremy Linton +reviewed-by: Xinliang Liu +--- + drivers/reset/hisilicon/hi6220_reset.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/drivers/reset/hisilicon/hi6220_reset.c b/drivers/reset/hisilicon/hi6220_reset.c +index 35ce53e..d5e5229 100644 +--- a/drivers/reset/hisilicon/hi6220_reset.c ++++ b/drivers/reset/hisilicon/hi6220_reset.c +@@ -155,3 +155,5 @@ static int __init hi6220_reset_init(void) + } + + postcore_initcall(hi6220_reset_init); ++ ++MODULE_LICENSE("GPL v2"); +From patchwork Mon Apr 3 05:28:42 2017 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v2,1/2] regulator: hi655x: Describe consumed platform device +From: Jeremy Linton +X-Patchwork-Id: 9658793 +Message-Id: <20170403052843.12711-2-lintonrjeremy@gmail.com> +To: linux-kernel@vger.kernel.org +Cc: broonie@kernel.org, lgirdwood@gmail.com, puck.chen@hisilicon.com, + lee.jones@linaro.org, Jeremy Linton +Date: Mon, 3 Apr 2017 00:28:42 -0500 + +The hi655x-regulator driver consumes a similarly named platform device. +Adding that to the module device table, allows modprobe to locate this +driver once the device is created. + +Signed-off-by: Jeremy Linton +--- + drivers/regulator/hi655x-regulator.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/drivers/regulator/hi655x-regulator.c b/drivers/regulator/hi655x-regulator.c +index 065c100..36ae54b 100644 +--- a/drivers/regulator/hi655x-regulator.c ++++ b/drivers/regulator/hi655x-regulator.c +@@ -214,7 +214,14 @@ static int hi655x_regulator_probe(struct platform_device *pdev) + return 0; + } + ++static const struct platform_device_id hi655x_regulator_table[] = { ++ { .name = "hi655x-regulator" }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(platform, hi655x_regulator_table); ++ + static struct platform_driver hi655x_regulator_driver = { ++ .id_table = hi655x_regulator_table, + .driver = { + .name = "hi655x-regulator", + }, diff --git a/baseconfig/CONFIG_BT_HCIUART_NOKIA b/baseconfig/CONFIG_BT_HCIUART_NOKIA new file mode 100644 index 000000000..6f09b31e6 --- /dev/null +++ b/baseconfig/CONFIG_BT_HCIUART_NOKIA @@ -0,0 +1 @@ +CONFIG_BT_HCIUART_NOKIA=m diff --git a/kernel-aarch64-debug.config b/kernel-aarch64-debug.config index 1e1cbc576..d9083a1b2 100644 --- a/kernel-aarch64-debug.config +++ b/kernel-aarch64-debug.config @@ -638,6 +638,7 @@ CONFIG_BT_HCIUART_INTEL=y CONFIG_BT_HCIUART_LL=y CONFIG_BT_HCIUART=m CONFIG_BT_HCIUART_MRVL=y +CONFIG_BT_HCIUART_NOKIA=m CONFIG_BT_HCIUART_QCA=y CONFIG_BT_HCIVHCI=m CONFIG_BT_HIDP=m diff --git a/kernel-aarch64.config b/kernel-aarch64.config index fe9587afd..86e056de8 100644 --- a/kernel-aarch64.config +++ b/kernel-aarch64.config @@ -638,6 +638,7 @@ CONFIG_BT_HCIUART_INTEL=y CONFIG_BT_HCIUART_LL=y CONFIG_BT_HCIUART=m CONFIG_BT_HCIUART_MRVL=y +CONFIG_BT_HCIUART_NOKIA=m CONFIG_BT_HCIUART_QCA=y CONFIG_BT_HCIVHCI=m CONFIG_BT_HIDP=m diff --git a/kernel-armv7hl-debug.config b/kernel-armv7hl-debug.config index 6c598ab47..ff3a7c4a0 100644 --- a/kernel-armv7hl-debug.config +++ b/kernel-armv7hl-debug.config @@ -683,6 +683,7 @@ CONFIG_BT_HCIUART_INTEL=y CONFIG_BT_HCIUART_LL=y CONFIG_BT_HCIUART=m CONFIG_BT_HCIUART_MRVL=y +CONFIG_BT_HCIUART_NOKIA=m CONFIG_BT_HCIUART_QCA=y CONFIG_BT_HCIVHCI=m CONFIG_BT_HIDP=m diff --git a/kernel-armv7hl-lpae-debug.config b/kernel-armv7hl-lpae-debug.config index 0dbac4136..883acd24e 100644 --- a/kernel-armv7hl-lpae-debug.config +++ b/kernel-armv7hl-lpae-debug.config @@ -661,6 +661,7 @@ CONFIG_BT_HCIUART_INTEL=y CONFIG_BT_HCIUART_LL=y CONFIG_BT_HCIUART=m CONFIG_BT_HCIUART_MRVL=y +CONFIG_BT_HCIUART_NOKIA=m CONFIG_BT_HCIUART_QCA=y CONFIG_BT_HCIVHCI=m CONFIG_BT_HIDP=m diff --git a/kernel-armv7hl-lpae.config b/kernel-armv7hl-lpae.config index d7b2ff2d2..e5cc31deb 100644 --- a/kernel-armv7hl-lpae.config +++ b/kernel-armv7hl-lpae.config @@ -660,6 +660,7 @@ CONFIG_BT_HCIUART_INTEL=y CONFIG_BT_HCIUART_LL=y CONFIG_BT_HCIUART=m CONFIG_BT_HCIUART_MRVL=y +CONFIG_BT_HCIUART_NOKIA=m CONFIG_BT_HCIUART_QCA=y CONFIG_BT_HCIVHCI=m CONFIG_BT_HIDP=m diff --git a/kernel-armv7hl.config b/kernel-armv7hl.config index 404f1a57d..50d43f6d0 100644 --- a/kernel-armv7hl.config +++ b/kernel-armv7hl.config @@ -682,6 +682,7 @@ CONFIG_BT_HCIUART_INTEL=y CONFIG_BT_HCIUART_LL=y CONFIG_BT_HCIUART=m CONFIG_BT_HCIUART_MRVL=y +CONFIG_BT_HCIUART_NOKIA=m CONFIG_BT_HCIUART_QCA=y CONFIG_BT_HCIVHCI=m CONFIG_BT_HIDP=m diff --git a/kernel-i686-PAE.config b/kernel-i686-PAE.config index c84ec515d..5abdbcab8 100644 --- a/kernel-i686-PAE.config +++ b/kernel-i686-PAE.config @@ -559,6 +559,7 @@ CONFIG_BT_HCIUART_INTEL=y CONFIG_BT_HCIUART_LL=y CONFIG_BT_HCIUART=m CONFIG_BT_HCIUART_MRVL=y +CONFIG_BT_HCIUART_NOKIA=m CONFIG_BT_HCIUART_QCA=y CONFIG_BT_HCIVHCI=m CONFIG_BT_HIDP=m diff --git a/kernel-i686-PAEdebug.config b/kernel-i686-PAEdebug.config index 6dc7d7e87..7203ee62a 100644 --- a/kernel-i686-PAEdebug.config +++ b/kernel-i686-PAEdebug.config @@ -560,6 +560,7 @@ CONFIG_BT_HCIUART_INTEL=y CONFIG_BT_HCIUART_LL=y CONFIG_BT_HCIUART=m CONFIG_BT_HCIUART_MRVL=y +CONFIG_BT_HCIUART_NOKIA=m CONFIG_BT_HCIUART_QCA=y CONFIG_BT_HCIVHCI=m CONFIG_BT_HIDP=m diff --git a/kernel-i686-debug.config b/kernel-i686-debug.config index 2b1a5a106..060c8ca63 100644 --- a/kernel-i686-debug.config +++ b/kernel-i686-debug.config @@ -560,6 +560,7 @@ CONFIG_BT_HCIUART_INTEL=y CONFIG_BT_HCIUART_LL=y CONFIG_BT_HCIUART=m CONFIG_BT_HCIUART_MRVL=y +CONFIG_BT_HCIUART_NOKIA=m CONFIG_BT_HCIUART_QCA=y CONFIG_BT_HCIVHCI=m CONFIG_BT_HIDP=m diff --git a/kernel-i686.config b/kernel-i686.config index cf02575a4..3db49d926 100644 --- a/kernel-i686.config +++ b/kernel-i686.config @@ -559,6 +559,7 @@ CONFIG_BT_HCIUART_INTEL=y CONFIG_BT_HCIUART_LL=y CONFIG_BT_HCIUART=m CONFIG_BT_HCIUART_MRVL=y +CONFIG_BT_HCIUART_NOKIA=m CONFIG_BT_HCIUART_QCA=y CONFIG_BT_HCIVHCI=m CONFIG_BT_HIDP=m diff --git a/kernel-ppc64-debug.config b/kernel-ppc64-debug.config index e8e73f933..2d3eb6e78 100644 --- a/kernel-ppc64-debug.config +++ b/kernel-ppc64-debug.config @@ -546,6 +546,7 @@ CONFIG_BT_HCIUART_INTEL=y CONFIG_BT_HCIUART_LL=y CONFIG_BT_HCIUART=m CONFIG_BT_HCIUART_MRVL=y +CONFIG_BT_HCIUART_NOKIA=m CONFIG_BT_HCIUART_QCA=y CONFIG_BT_HCIVHCI=m CONFIG_BT_HIDP=m diff --git a/kernel-ppc64.config b/kernel-ppc64.config index 72743ef0f..b9cd98a11 100644 --- a/kernel-ppc64.config +++ b/kernel-ppc64.config @@ -545,6 +545,7 @@ CONFIG_BT_HCIUART_INTEL=y CONFIG_BT_HCIUART_LL=y CONFIG_BT_HCIUART=m CONFIG_BT_HCIUART_MRVL=y +CONFIG_BT_HCIUART_NOKIA=m CONFIG_BT_HCIUART_QCA=y CONFIG_BT_HCIVHCI=m CONFIG_BT_HIDP=m diff --git a/kernel-ppc64le-debug.config b/kernel-ppc64le-debug.config index 8d2a54f5f..07978aa09 100644 --- a/kernel-ppc64le-debug.config +++ b/kernel-ppc64le-debug.config @@ -503,6 +503,7 @@ CONFIG_BT_HCIUART_INTEL=y CONFIG_BT_HCIUART_LL=y CONFIG_BT_HCIUART=m CONFIG_BT_HCIUART_MRVL=y +CONFIG_BT_HCIUART_NOKIA=m CONFIG_BT_HCIUART_QCA=y CONFIG_BT_HCIVHCI=m CONFIG_BT_HIDP=m diff --git a/kernel-ppc64le.config b/kernel-ppc64le.config index 29c3dc97e..c11684038 100644 --- a/kernel-ppc64le.config +++ b/kernel-ppc64le.config @@ -502,6 +502,7 @@ CONFIG_BT_HCIUART_INTEL=y CONFIG_BT_HCIUART_LL=y CONFIG_BT_HCIUART=m CONFIG_BT_HCIUART_MRVL=y +CONFIG_BT_HCIUART_NOKIA=m CONFIG_BT_HCIUART_QCA=y CONFIG_BT_HCIVHCI=m CONFIG_BT_HIDP=m diff --git a/kernel-ppc64p7-debug.config b/kernel-ppc64p7-debug.config index cd1ed79cc..a56bc2c25 100644 --- a/kernel-ppc64p7-debug.config +++ b/kernel-ppc64p7-debug.config @@ -503,6 +503,7 @@ CONFIG_BT_HCIUART_INTEL=y CONFIG_BT_HCIUART_LL=y CONFIG_BT_HCIUART=m CONFIG_BT_HCIUART_MRVL=y +CONFIG_BT_HCIUART_NOKIA=m CONFIG_BT_HCIUART_QCA=y CONFIG_BT_HCIVHCI=m CONFIG_BT_HIDP=m diff --git a/kernel-ppc64p7.config b/kernel-ppc64p7.config index 07cdc6ea6..5ce624981 100644 --- a/kernel-ppc64p7.config +++ b/kernel-ppc64p7.config @@ -502,6 +502,7 @@ CONFIG_BT_HCIUART_INTEL=y CONFIG_BT_HCIUART_LL=y CONFIG_BT_HCIUART=m CONFIG_BT_HCIUART_MRVL=y +CONFIG_BT_HCIUART_NOKIA=m CONFIG_BT_HCIUART_QCA=y CONFIG_BT_HCIVHCI=m CONFIG_BT_HIDP=m diff --git a/kernel-s390x-debug.config b/kernel-s390x-debug.config index 552ed722c..61bace894 100644 --- a/kernel-s390x-debug.config +++ b/kernel-s390x-debug.config @@ -501,6 +501,7 @@ CONFIG_BT_HCIUART_INTEL=y CONFIG_BT_HCIUART_LL=y CONFIG_BT_HCIUART=m CONFIG_BT_HCIUART_MRVL=y +CONFIG_BT_HCIUART_NOKIA=m CONFIG_BT_HCIUART_QCA=y CONFIG_BT_HCIVHCI=m CONFIG_BT_HIDP=m diff --git a/kernel-s390x.config b/kernel-s390x.config index 9f4f6f878..d3e5960ae 100644 --- a/kernel-s390x.config +++ b/kernel-s390x.config @@ -500,6 +500,7 @@ CONFIG_BT_HCIUART_INTEL=y CONFIG_BT_HCIUART_LL=y CONFIG_BT_HCIUART=m CONFIG_BT_HCIUART_MRVL=y +CONFIG_BT_HCIUART_NOKIA=m CONFIG_BT_HCIUART_QCA=y CONFIG_BT_HCIVHCI=m CONFIG_BT_HIDP=m diff --git a/kernel-x86_64-debug.config b/kernel-x86_64-debug.config index def1583e2..da135992f 100644 --- a/kernel-x86_64-debug.config +++ b/kernel-x86_64-debug.config @@ -563,6 +563,7 @@ CONFIG_BT_HCIUART_INTEL=y CONFIG_BT_HCIUART_LL=y CONFIG_BT_HCIUART=m CONFIG_BT_HCIUART_MRVL=y +CONFIG_BT_HCIUART_NOKIA=m CONFIG_BT_HCIUART_QCA=y CONFIG_BT_HCIVHCI=m CONFIG_BT_HIDP=m diff --git a/kernel-x86_64.config b/kernel-x86_64.config index cbee0a1ce..7135612fe 100644 --- a/kernel-x86_64.config +++ b/kernel-x86_64.config @@ -562,6 +562,7 @@ CONFIG_BT_HCIUART_INTEL=y CONFIG_BT_HCIUART_LL=y CONFIG_BT_HCIUART=m CONFIG_BT_HCIUART_MRVL=y +CONFIG_BT_HCIUART_NOKIA=m CONFIG_BT_HCIUART_QCA=y CONFIG_BT_HCIVHCI=m CONFIG_BT_HIDP=m diff --git a/kernel.spec b/kernel.spec index 75c54dda6..acc7f8314 100644 --- a/kernel.spec +++ b/kernel.spec @@ -528,6 +528,12 @@ Patch425: ARM-tegra-usb-no-reset.patch Patch426: AllWinner-h3.patch Patch427: AllWinner-net-emac.patch +# http://www.spinics.net/lists/linux-bluetooth/msg70169.html +# https://www.spinics.net/lists/devicetree/msg170619.html +Patch428: ti-bluetooth.patch + +Patch429: arm64-hikey-fixes.patch + # http://www.spinics.net/lists/devicetree/msg163238.html Patch430: bcm2837-initial-support.patch @@ -2169,6 +2175,10 @@ fi # # %changelog +* Wed Apr 12 2017 Peter Robinson +- Add support for TI Bluetooth modules +- Add fixes for 96boards HiKey + * Tue Apr 11 2017 Laura Abbott - 4.11.0-0.rc6.git1.1 - Linux v4.11-rc6-4-gc08e611 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 +X-Patchwork-Id: 9278297 +Message-Id: <1471058078-5579-2-git-send-email-sre@kernel.org> +To: Sebastian Reichel , Tony Lindgren , + Rob Herring , Mark Rutland , + Marcel Holtmann , + Greg Kroah-Hartman , + Jiri Slaby +Cc: Ville Tervo , + =?UTF-8?q?Filip=20Matijevi=C4=87?= , + Aaro Koskinen , Pavel Machek , + =?UTF-8?q?Pali=20Roh=C3=A1r?= , + 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 + +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 +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 +Acked-by: Pavel Machek +Signed-off-by: Sebastian Reichel +Signed-off-by: Rob Herring +--- + 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 + #include + #include ++#include + + #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 +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 +Acked-by: Rob Herring +Signed-off-by: Sebastian Reichel +Signed-off-by: Rob Herring +--- + 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 + #include ++#include + + 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 +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 +Signed-off-by: Sebastian Reichel +Signed-off-by: Rob Herring +--- + 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 + #include + #include ++#include + + 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 +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 +Signed-off-by: Sebastian Reichel +Signed-off-by: Rob Herring +--- + 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 +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 +Cc: Marcel Holtmann +Cc: Gustavo Padovan +Cc: Johan Hedberg +Cc: linux-bluetooth@vger.kernel.org +Acked-by: Pavel Machek +[Fix style issues reported by Pavel] +Signed-off-by: Sebastian Reichel +Signed-off-by: Rob Herring +--- + 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 ++ * ++ * Based on hci_ldisc.c: ++ * ++ * Copyright (C) 2000-2001 Qualcomm Incorporated ++ * Copyright (C) 2002-2003 Maxim Krasnyansky ++ * Copyright (C) 2004-2005 Marcel Holtmann ++ * ++ * 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 ++#include ++#include ++#include ++ ++#include ++#include ++ ++#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 +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 +Signed-off-by: Sebastian Reichel +Signed-off-by: Rob Herring +--- + 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 +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 +Signed-off-by: Rob Herring +--- + 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 +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 +Signed-off-by: Sebastian Reichel + +Changes since PATCHv1: + * change compatible strings + * mention active high/low state for GPIOs +Signed-off-by: Rob Herring +--- + .../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 +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 +Signed-off-by: Rob Herring +--- + 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 ++ * Copyright (C) 2015-2017 Sebastian Reichel ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 +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 +Cc: Mark Rutland +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 +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 +Cc: Marcel Holtmann +Cc: Gustavo Padovan +Cc: Johan Hedberg +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 +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 +Cc: Marcel Holtmann +Cc: Gustavo Padovan +Cc: Johan Hedberg +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 + #include + #include ++#include + #include + #include + #include + + #include +-#include + #include + #include + #include + #include ++#include + #include ++#include + + #include + #include ++#include + + #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 +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 +Cc: Wei Xu +Cc: Mark Rutland +--- + 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 +