kernel: import series to support Renesas Bluetooth HCI
authorDaniel Golle <daniel@makrotopia.org>
Fri, 17 Nov 2023 01:59:20 +0000 (01:59 +0000)
committerDaniel Golle <daniel@makrotopia.org>
Fri, 17 Nov 2023 02:31:18 +0000 (02:31 +0000)
Import series from Renesas meta-da14531 tree to support DA14531 BLE
chip connected via UART.

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
package/kernel/linux/modules/other.mk
target/linux/generic/pending-5.15/900-0001-Support-for-Renesas-Bluetooth-Low-Energy-HCI-over-UA.patch [new file with mode: 0644]
target/linux/generic/pending-5.15/900-0002-Adding-DA16600-reset-by-GPIO.patch [new file with mode: 0644]
target/linux/generic/pending-5.15/900-0003-Bluetooth-hci_renesas-Fix-compiler-warning.patch [new file with mode: 0644]
target/linux/generic/pending-5.15/900-0004-Bluetooth-hci_renesas-Fix-issue-with-reset-pin-direc.patch [new file with mode: 0644]
target/linux/generic/pending-5.15/900-0005-Bluetooth-hci_renesas-Complete-the-setup-regardless-.patch [new file with mode: 0644]
target/linux/generic/pending-6.1/900-0001-Support-for-Renesas-Bluetooth-Low-Energy-HCI-over-UA.patch [new file with mode: 0644]
target/linux/generic/pending-6.1/900-0002-Adding-DA16600-reset-by-GPIO.patch [new file with mode: 0644]
target/linux/generic/pending-6.1/900-0003-Bluetooth-hci_renesas-Fix-compiler-warning.patch [new file with mode: 0644]
target/linux/generic/pending-6.1/900-0004-Bluetooth-hci_renesas-Fix-issue-with-reset-pin-direc.patch [new file with mode: 0644]
target/linux/generic/pending-6.1/900-0005-Bluetooth-hci_renesas-Complete-the-setup-regardless-.patch [new file with mode: 0644]

index face579472773631dac372e20e7e1c121d36bfd4..7cd0d9c7a249779d2fd0b833b0230e4c7b135885 100644 (file)
@@ -47,6 +47,7 @@ define KernelPackage/bluetooth
        CONFIG_BT_HCIUART_INTEL=n \
        CONFIG_BT_HCIUART_H4 \
        CONFIG_BT_HCIUART_NOKIA=n \
+       CONFIG_BT_HCIUART_RENESAS=y \
        CONFIG_BT_HIDP
   $(call AddDepends/rfkill)
   FILES:= \
diff --git a/target/linux/generic/pending-5.15/900-0001-Support-for-Renesas-Bluetooth-Low-Energy-HCI-over-UA.patch b/target/linux/generic/pending-5.15/900-0001-Support-for-Renesas-Bluetooth-Low-Energy-HCI-over-UA.patch
new file mode 100644 (file)
index 0000000..8ad173a
--- /dev/null
@@ -0,0 +1,571 @@
+From e7eaf003090c6b2e56ce1d39b30b221ed7283de3 Mon Sep 17 00:00:00 2001
+From: Alvin Park <alvin.park.pv@renesas.com>
+Date: Thu, 17 Mar 2022 19:39:57 +0900
+Subject: [PATCH] Support for Renesas Bluetooth Low Energy HCI over UART
+
+---
+ drivers/bluetooth/Kconfig       |  13 +
+ drivers/bluetooth/Makefile      |   1 +
+ drivers/bluetooth/hci_ldisc.c   |   6 +
+ drivers/bluetooth/hci_renesas.c | 474 ++++++++++++++++++++++++++++++++
+ drivers/bluetooth/hci_uart.h    |   8 +-
+ 5 files changed, 501 insertions(+), 1 deletion(-)
+ create mode 100644 drivers/bluetooth/hci_renesas.c
+
+--- a/drivers/bluetooth/Kconfig
++++ b/drivers/bluetooth/Kconfig
+@@ -257,6 +257,19 @@ config BT_HCIUART_MRVL
+         Say Y here to compile support for HCI MRVL protocol.
++config BT_HCIUART_RENESAS
++      bool "Renesas protocol support"
++      depends on BT_HCIUART
++      depends on BT_HCIUART_SERDEV
++      select BT_HCIUART_H4
++      help
++        The Renesas protocol support enables Bluetooth Low Energy HCI
++        over serial.
++        This protocol is required for the Renesas Bluetooth Low Energy
++        devices with UART interface.
++
++        Say Y here to compile support for HCI Renesas protocol.
++
+ config BT_HCIBCM203X
+       tristate "HCI BCM203x USB driver"
+       depends on USB
+--- a/drivers/bluetooth/Makefile
++++ b/drivers/bluetooth/Makefile
+@@ -47,4 +47,5 @@ hci_uart-$(CONFIG_BT_HCIUART_BCM)    += hci
+ hci_uart-$(CONFIG_BT_HCIUART_QCA)     += hci_qca.o
+ hci_uart-$(CONFIG_BT_HCIUART_AG6XX)   += hci_ag6xx.o
+ hci_uart-$(CONFIG_BT_HCIUART_MRVL)    += hci_mrvl.o
++hci_uart-$(CONFIG_BT_HCIUART_RENESAS) += hci_renesas.o
+ hci_uart-objs                         := $(hci_uart-y)
+--- a/drivers/bluetooth/hci_ldisc.c
++++ b/drivers/bluetooth/hci_ldisc.c
+@@ -880,6 +880,9 @@ static int __init hci_uart_init(void)
+ #ifdef CONFIG_BT_HCIUART_MRVL
+       mrvl_init();
+ #endif
++#ifdef CONFIG_BT_HCIUART_RENESAS
++      renesas_init();
++#endif
+       return 0;
+ }
+@@ -916,6 +919,9 @@ static void __exit hci_uart_exit(void)
+ #ifdef CONFIG_BT_HCIUART_MRVL
+       mrvl_deinit();
+ #endif
++#ifdef CONFIG_BT_HCIUART_RENESAS
++      renesas_deinit();
++#endif
+       tty_unregister_ldisc(&hci_uart_ldisc);
+ }
+--- /dev/null
++++ b/drivers/bluetooth/hci_renesas.c
+@@ -0,0 +1,474 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * Renesas Bluetooth Low Energy HCI UART driver
++ *
++ * Copyright (C) 2022  Renesas Electroics Corp.
++ *
++ * Alvin Park <alvin.park.pv@renesas.com>
++ *
++ * This file is subject to the terms and conditions of the GNU General Public
++ * License.  See the file "COPYING" in the main directory of this archive
++ * for more details.
++*/
++
++#include <linux/kernel.h>
++#include <linux/errno.h>
++#include <linux/skbuff.h>
++#include <linux/firmware.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/serdev.h>
++#include <net/bluetooth/bluetooth.h>
++#include <net/bluetooth/hci_core.h>
++
++#include "hci_uart.h"
++
++enum {
++      STATE_FW_STX_PENDING,
++      STATE_FW_ACK_PENDING,
++      STATE_FW_CRC_PENDING
++};
++
++enum {
++      STATE_FW_INIT,
++      STATE_FW_SIZE,
++      STATE_FW_PROG,
++      STATE_FW_DONE,
++      STATE_FW_BOOTED
++};
++
++#define STX           0x02
++#define SOH           0x01
++#define ACK           0x06
++#define NACK          0x15
++#define CRC_INIT      0x00
++
++struct renesas_serdev {
++      struct hci_uart hu;
++};
++
++struct renesas_data {
++      struct sk_buff *rx_skb;
++      struct sk_buff_head txq;
++      struct sk_buff_head rawq;
++      unsigned long flags;
++      unsigned int state;
++      u8 id, rev, fw_crc;
++};
++
++static const struct h4_recv_pkt renesas_recv_pkts[] = {
++      { H4_RECV_ACL,       .recv = hci_recv_frame     },
++      { H4_RECV_SCO,       .recv = hci_recv_frame     },
++      { H4_RECV_EVENT,     .recv = hci_recv_frame     },
++};
++
++static int renesas_open(struct hci_uart *hu)
++{
++      struct renesas_data *rdata;
++      int ret;
++
++      BT_DBG("hu %p", hu);
++
++      if (!hci_uart_has_flow_control(hu))
++              return -EOPNOTSUPP;
++
++      rdata = kzalloc(sizeof(*rdata), GFP_KERNEL);
++      if (!rdata)
++              return -ENOMEM;
++
++      skb_queue_head_init(&rdata->txq);
++      skb_queue_head_init(&rdata->rawq);
++
++      hu->priv = rdata;
++
++      if (hu->serdev) {
++              ret = serdev_device_open(hu->serdev);
++              if (ret) {
++                      kfree(rdata);
++                      return ret;
++              }
++      }
++
++      return 0;
++}
++
++static int renesas_close(struct hci_uart *hu)
++{
++      struct renesas_data *rdata = hu->priv;
++
++      BT_DBG("hu %p", hu);
++
++      if (hu->serdev)
++              serdev_device_close(hu->serdev);
++
++      skb_queue_purge(&rdata->txq);
++      skb_queue_purge(&rdata->rawq);
++      kfree_skb(rdata->rx_skb);
++      kfree(rdata);
++
++      hu->priv = NULL;
++      return 0;
++}
++
++static int renesas_flush(struct hci_uart *hu)
++{
++      struct renesas_data *rdata = hu->priv;
++
++      BT_DBG("hu %p", hu);
++
++      skb_queue_purge(&rdata->txq);
++      skb_queue_purge(&rdata->rawq);
++
++      return 0;
++}
++
++static int renesas_send_ack(struct hci_uart *hu, unsigned char type)
++{
++      struct renesas_data *rdata = hu->priv;
++      struct sk_buff *skb;
++
++      skb = alloc_skb(1, GFP_ATOMIC);
++      if (!skb) {
++              bt_dev_err(hu->hdev, "Unable to alloc ack/nak packet");
++              return -ENOMEM;
++      }
++      skb_put_u8(skb, type);
++
++      skb_queue_tail(&rdata->rawq, skb);
++      hci_uart_tx_wakeup(hu);
++
++      return 0;
++}
++
++static int renesas_send_fw_size(struct hci_uart *hu, u16 length)
++{
++      struct renesas_data *rdata = hu->priv;
++      struct sk_buff *skb;
++
++      skb = alloc_skb(3, GFP_ATOMIC);
++      if (!skb) {
++              bt_dev_err(hu->hdev, "Failed to alloc mem for FW size packet");
++              return -ENOMEM;
++      }
++
++      skb_put_u8(skb, SOH);
++      skb_put_u8(skb, length);
++      skb_put_u8(skb, length>>8);
++
++      skb_queue_tail(&rdata->rawq, skb);
++      hci_uart_tx_wakeup(hu);
++
++      return 0;
++}
++
++static int renesas_load_firmware(struct hci_dev *hdev, const char *name)
++{
++      struct hci_uart *hu = hci_get_drvdata(hdev);
++      struct renesas_data *rdata = hu->priv;
++      const struct firmware *fw = NULL;
++      const u8 *fw_ptr, *fw_max;
++      u16 fw_size = 0;
++      int err, i;
++
++      err = request_firmware(&fw, name, &hdev->dev);
++      if (err < 0) {
++              bt_dev_err(hdev, "Failed to load firmware file %s", name);
++              return err;
++      }
++
++      fw_ptr = fw->data;
++      fw_max = fw->data + fw->size;
++      fw_size = fw->size;
++
++      bt_dev_info(hdev, "Loading %s", name);
++
++      /* update crc */
++      for (i = 0; i < fw->size; i++)
++              rdata->fw_crc ^= fw_ptr[i];
++
++      rdata->state = STATE_FW_INIT;
++      set_bit(STATE_FW_STX_PENDING, &rdata->flags);
++
++      while (rdata->state != STATE_FW_DONE) {
++              struct sk_buff *skb;
++
++              /* Controller drives the firmware load by sending firmware
++               * request packets containing the expected fragment size.
++               */
++              err = wait_on_bit_timeout(&rdata->flags, STATE_FW_STX_PENDING,
++                                        TASK_INTERRUPTIBLE,
++                                        msecs_to_jiffies(5000));
++              if (err == 1) {
++                      bt_dev_err(hdev, "Firmware load interrupted");
++                      err = -EINTR;
++                      break;
++              } else if (err) {
++                      bt_dev_err(hdev, "Firmware request timeout");
++                      err = -ETIMEDOUT;
++                      break;
++              }
++
++              switch(rdata->state) {
++                      case STATE_FW_INIT:
++                              bt_dev_dbg(hdev, "Firmware request, expecting %d bytes", 
++                                      fw->size);
++
++                              set_bit(STATE_FW_ACK_PENDING, &rdata->flags);
++                              err = renesas_send_fw_size(hu, fw_size);
++                              if (err) {
++                                      break;
++                              }
++
++                              rdata->state = STATE_FW_SIZE;
++                              break;
++
++                      case STATE_FW_SIZE:
++                              err = wait_on_bit_timeout(&rdata->flags, STATE_FW_ACK_PENDING,
++                                              TASK_INTERRUPTIBLE,
++                                              msecs_to_jiffies(2000));
++                              if (err == 1) {
++                                      bt_dev_err(hdev, "Firmware load interrupted");
++                                      err = -EINTR;
++                                      break;
++                              } else if (err) {
++                                      bt_dev_err(hdev, "Firmware request timeout");
++                                      err = -ETIMEDOUT;
++                                      break;
++                              }
++
++                              set_bit(STATE_FW_CRC_PENDING, &rdata->flags);
++
++                              skb = alloc_skb(fw_size, GFP_KERNEL);
++                              if (!skb) {
++                                      bt_dev_err(hdev, "Failed to alloc mem for FW packet");
++                                      err = -ENOMEM;
++                                      break;
++                              }
++                              skb_put_data(skb, fw_ptr, fw_size);
++                              skb_queue_tail(&rdata->rawq, skb);
++                              hci_uart_tx_wakeup(hu);
++
++                              rdata->state = STATE_FW_PROG;
++                              break;
++
++                      case STATE_FW_PROG:
++                              err = wait_on_bit_timeout(&rdata->flags, STATE_FW_CRC_PENDING,
++                                              TASK_INTERRUPTIBLE,
++                                              msecs_to_jiffies(2000));
++                              if (err == 1) {
++                                      bt_dev_err(hdev, "Firmware load interrupted");
++                                      err = -EINTR;
++                                      break;
++                              } else if (err) {
++                                      bt_dev_err(hdev, "Firmware request timeout");
++                                      err = -ETIMEDOUT;
++                                      break;
++                              }
++
++                              err = renesas_send_ack(hu, ACK);
++                              if (err) {
++                                      break;
++                              }
++
++                              bt_dev_dbg(hdev, "Firmware has been loaded");
++
++                              set_bit(STATE_FW_STX_PENDING, &rdata->flags);
++                              rdata->state = STATE_FW_DONE;
++                              break;
++
++                      default:
++                              break;
++              }
++
++              if (err) {
++                      break;
++              }
++      }
++
++      release_firmware(fw);
++      return err;
++}
++
++static int renesas_setup(struct hci_uart *hu)
++{
++      struct renesas_data *rdata = hu->priv;
++      int err;
++
++      hci_uart_set_flow_control(hu, true);
++
++      err = renesas_load_firmware(hu->hdev, "renesas/hci_531.bin");
++      if (err)
++              return err;
++
++      /* wait for HCI application to start */
++      usleep_range(8000, 10000);
++
++      rdata->state = STATE_FW_BOOTED;
++
++      hci_uart_set_flow_control(hu, false);
++
++      return 0;
++}
++
++static int renesas_recv(struct hci_uart *hu, const void *data, int count)
++{
++      struct renesas_data *rdata = hu->priv;
++      u8 *data_ptr = (u8 *)data;
++
++      switch(rdata->state) {
++              case STATE_FW_INIT:
++                      if (*data_ptr == STX) {
++                              if (!test_bit(STATE_FW_STX_PENDING, &rdata->flags)) {
++                                      bt_dev_err(hu->hdev, "Received unexpected STX");
++                                      return -EINVAL;
++                              }
++                              clear_bit(STATE_FW_STX_PENDING, &rdata->flags);
++                              wake_up_bit(&rdata->flags, STATE_FW_STX_PENDING);
++                      }
++                      break;
++
++              case STATE_FW_SIZE:
++                      if (*data_ptr == ACK) {
++                              if (!test_bit(STATE_FW_ACK_PENDING, &rdata->flags)) {
++                                      bt_dev_err(hu->hdev, "Received unexpected ACK");
++                                      return -EINVAL;
++                              }
++                              clear_bit(STATE_FW_ACK_PENDING, &rdata->flags);
++                              wake_up_bit(&rdata->flags, STATE_FW_ACK_PENDING);
++                      } else {
++                              bt_dev_err(hu->hdev, "Received unexpected data (%x)", *data_ptr);
++                              return -EINVAL;
++                      }
++                      break;
++
++              case STATE_FW_PROG:
++                      if (!test_bit(STATE_FW_CRC_PENDING, &rdata->flags)) {
++                              bt_dev_err(hu->hdev, "Received unexpected CRC");
++                              return -EINVAL;
++                      }
++
++                      if (rdata->fw_crc != *data_ptr) {
++                              bt_dev_err(hu->hdev, "Received CRC %02x, "
++                                      "which does not match "
++                                      "computed CRC %02x.\n",
++                                      *data_ptr, rdata->fw_crc);
++                              return -EINVAL;
++                      }
++
++                      clear_bit(STATE_FW_CRC_PENDING, &rdata->flags);
++                      wake_up_bit(&rdata->flags, STATE_FW_CRC_PENDING);
++                      break;
++
++              case STATE_FW_DONE:
++                      /* Do nothing */
++                      break;
++
++              case STATE_FW_BOOTED:
++                      rdata->rx_skb = h4_recv_buf(hu->hdev, rdata->rx_skb, data, count,
++                                                              renesas_recv_pkts,
++                                                              ARRAY_SIZE(renesas_recv_pkts));
++                      if (IS_ERR(rdata->rx_skb)) {
++                              int err = PTR_ERR(rdata->rx_skb);
++                              bt_dev_err(hu->hdev, "Frame reassembly failed (%d)", err);
++                              rdata->rx_skb = NULL;
++                              return err;
++                      }
++                      break;
++
++              default:
++                      bt_dev_err(hu->hdev, "Unknown state (%d)", rdata->state);
++                      break;
++      }
++
++      return count;
++}
++
++static int renesas_enqueue(struct hci_uart *hu, struct sk_buff *skb)
++{
++      struct renesas_data *rdata = hu->priv;
++
++      skb_queue_tail(&rdata->txq, skb);
++      return 0;
++}
++
++static struct sk_buff *renesas_dequeue(struct hci_uart *hu)
++{
++      struct renesas_data *rdata = hu->priv;
++      struct sk_buff *skb;
++
++      skb = skb_dequeue(&rdata->txq);
++      if (!skb) {
++              /* Any raw data ? */
++              skb = skb_dequeue(&rdata->rawq);
++      } else {
++              /* Prepend skb with frame type */
++              memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
++      }
++
++      return skb;
++}
++
++static const struct hci_uart_proto renesas_proto = {
++      .id             = HCI_UART_RENESAS,
++      .name           = "Renesas",
++      .init_speed     = 115200,
++      .open           = renesas_open,
++      .close          = renesas_close,
++      .flush          = renesas_flush,
++      .setup          = renesas_setup,
++      .recv           = renesas_recv,
++      .enqueue        = renesas_enqueue,
++      .dequeue        = renesas_dequeue,
++};
++
++static int renesas_serdev_probe(struct serdev_device *serdev)
++{
++      struct renesas_serdev *rdatadev;
++
++      rdatadev = devm_kzalloc(&serdev->dev, sizeof(*rdatadev), GFP_KERNEL);
++      if (!rdatadev)
++              return -ENOMEM;
++
++      rdatadev->hu.serdev = serdev;
++      serdev_device_set_drvdata(serdev, rdatadev);
++
++      return hci_uart_register_device(&rdatadev->hu, &renesas_proto);
++}
++
++static void renesas_serdev_remove(struct serdev_device *serdev)
++{
++      struct renesas_serdev *rdatadev = serdev_device_get_drvdata(serdev);
++
++      hci_uart_unregister_device(&rdatadev->hu);
++}
++
++#ifdef CONFIG_OF
++static const struct of_device_id renesas_bluetooth_of_match[] = {
++      { .compatible = "renesas,DA14531" },
++      { },
++};
++MODULE_DEVICE_TABLE(of, renesas_bluetooth_of_match);
++#endif
++
++static struct serdev_device_driver renesas_serdev_driver = {
++      .probe = renesas_serdev_probe,
++      .remove = renesas_serdev_remove,
++      .driver = {
++              .name = "hci_uart_renesas",
++              .of_match_table = of_match_ptr(renesas_bluetooth_of_match),
++      },
++};
++
++int __init renesas_init(void)
++{
++      serdev_device_driver_register(&renesas_serdev_driver);
++
++      return hci_uart_register_proto(&renesas_proto);
++}
++
++int __exit renesas_deinit(void)
++{
++      serdev_device_driver_unregister(&renesas_serdev_driver);
++
++      return hci_uart_unregister_proto(&renesas_proto);
++}
+--- a/drivers/bluetooth/hci_uart.h
++++ b/drivers/bluetooth/hci_uart.h
+@@ -20,7 +20,7 @@
+ #define HCIUARTGETFLAGS               _IOR('U', 204, int)
+ /* UART protocols */
+-#define HCI_UART_MAX_PROTO    12
++#define HCI_UART_MAX_PROTO    13
+ #define HCI_UART_H4   0
+ #define HCI_UART_BCSP 1
+@@ -34,6 +34,7 @@
+ #define HCI_UART_AG6XX        9
+ #define HCI_UART_NOKIA        10
+ #define HCI_UART_MRVL 11
++#define HCI_UART_RENESAS      12
+ #define HCI_UART_RAW_DEVICE   0
+ #define HCI_UART_RESET_ON_INIT        1
+@@ -200,3 +201,8 @@ int ag6xx_deinit(void);
+ int mrvl_init(void);
+ int mrvl_deinit(void);
+ #endif
++
++#ifdef CONFIG_BT_HCIUART_RENESAS
++int renesas_init(void);
++int renesas_deinit(void);
++#endif
diff --git a/target/linux/generic/pending-5.15/900-0002-Adding-DA16600-reset-by-GPIO.patch b/target/linux/generic/pending-5.15/900-0002-Adding-DA16600-reset-by-GPIO.patch
new file mode 100644 (file)
index 0000000..85f48c7
--- /dev/null
@@ -0,0 +1,115 @@
+From 03b82d621adb7145d008eabb8995f14ad9582d56 Mon Sep 17 00:00:00 2001
+From: Alvin Park <alvin.park.pv@renesas.com>
+Date: Wed, 4 May 2022 17:02:26 +0900
+Subject: [PATCH] Adding DA16600 reset by GPIO
+
+---
+ drivers/bluetooth/hci_renesas.c | 51 ++++++++++++++++++++++++++++++++-
+ 1 file changed, 50 insertions(+), 1 deletion(-)
+
+--- a/drivers/bluetooth/hci_renesas.c
++++ b/drivers/bluetooth/hci_renesas.c
+@@ -18,6 +18,7 @@
+ #include <linux/module.h>
+ #include <linux/of.h>
+ #include <linux/serdev.h>
++#include <linux/gpio/consumer.h>
+ #include <net/bluetooth/bluetooth.h>
+ #include <net/bluetooth/hci_core.h>
+@@ -37,6 +38,10 @@ enum {
+       STATE_FW_BOOTED
+ };
++#define VERSION "0.1"
++
++#define FIRMWARE_DA14531      "renesas/hci_531.bin"
++
+ #define STX           0x02
+ #define SOH           0x01
+ #define ACK           0x06
+@@ -45,6 +50,8 @@ enum {
+ struct renesas_serdev {
+       struct hci_uart hu;
++
++      struct gpio_desc *reset;
+ };
+ struct renesas_data {
+@@ -161,6 +168,32 @@ static int renesas_send_fw_size(struct h
+       return 0;
+ }
++static void reset_device(struct gpio_desc *gpio)
++{
++      if (gpiod_get_value(gpio) == 0) {
++              gpiod_set_value_cansleep(gpio, 1);
++              usleep_range(1000, 2000);
++      }
++      gpiod_set_value_cansleep(gpio, 0);
++      usleep_range(5000, 10000);
++      gpiod_direction_input(gpio);
++}
++
++static void renesas_reset(struct hci_uart *hu)
++{
++      struct serdev_device *serdev = hu->serdev;
++      if (serdev) {
++              struct renesas_serdev *rdatadev = serdev_device_get_drvdata(serdev);
++              if (rdatadev && rdatadev->reset) {
++                      reset_device(rdatadev->reset);
++              } else {
++                      bt_dev_warn(hu->hdev, "Reset pin is not available!");
++              }
++      } else {
++              bt_dev_warn(hu->hdev, "Reset is required!");
++      }
++}
++
+ static int renesas_load_firmware(struct hci_dev *hdev, const char *name)
+ {
+       struct hci_uart *hu = hci_get_drvdata(hdev);
+@@ -189,6 +222,9 @@ static int renesas_load_firmware(struct
+       rdata->state = STATE_FW_INIT;
+       set_bit(STATE_FW_STX_PENDING, &rdata->flags);
++      /* reset */
++      renesas_reset(hu);
++
+       while (rdata->state != STATE_FW_DONE) {
+               struct sk_buff *skb;
+@@ -296,7 +332,7 @@ static int renesas_setup(struct hci_uart
+       hci_uart_set_flow_control(hu, true);
+-      err = renesas_load_firmware(hu->hdev, "renesas/hci_531.bin");
++      err = renesas_load_firmware(hu->hdev, FIRMWARE_DA14531);
+       if (err)
+               return err;
+@@ -432,6 +468,13 @@ static int renesas_serdev_probe(struct s
+       rdatadev->hu.serdev = serdev;
+       serdev_device_set_drvdata(serdev, rdatadev);
++      rdatadev->reset = devm_gpiod_get(&serdev->dev, "reset", GPIOD_OUT_HIGH);
++      if (IS_ERR(rdatadev->reset)) {
++              int err = 0;
++              err = PTR_ERR(rdatadev->reset);
++              dev_warn(&serdev->dev, "could not get reset gpio: %d", err);
++      }
++
+       return hci_uart_register_device(&rdatadev->hu, &renesas_proto);
+ }
+@@ -472,3 +515,9 @@ int __exit renesas_deinit(void)
+       return hci_uart_unregister_proto(&renesas_proto);
+ }
++
++MODULE_AUTHOR("Alvin Park <alvin.park.pv@renesas.com>");
++MODULE_DESCRIPTION("Renesas Bluetooth Serial driver ver " VERSION);
++MODULE_VERSION(VERSION);
++MODULE_LICENSE("GPL");
++MODULE_FIRMWARE(FIRMWARE_DA14531);
diff --git a/target/linux/generic/pending-5.15/900-0003-Bluetooth-hci_renesas-Fix-compiler-warning.patch b/target/linux/generic/pending-5.15/900-0003-Bluetooth-hci_renesas-Fix-compiler-warning.patch
new file mode 100644 (file)
index 0000000..cb59859
--- /dev/null
@@ -0,0 +1,21 @@
+From 5cd6cc75738c0eccbd837c2b24b278fe29677efb Mon Sep 17 00:00:00 2001
+From: Fabrizio Castro <fabrizio.castro.jz@renesas.com>
+Date: Mon, 15 Aug 2022 15:45:32 +0100
+Subject: [PATCH] Bluetooth: hci_renesas: Fix compiler warning
+
+Signed-off-by: Fabrizio Castro <fabrizio.castro.jz@renesas.com>
+---
+ drivers/bluetooth/hci_renesas.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/bluetooth/hci_renesas.c
++++ b/drivers/bluetooth/hci_renesas.c
+@@ -246,7 +246,7 @@ static int renesas_load_firmware(struct
+               switch(rdata->state) {
+                       case STATE_FW_INIT:
+-                              bt_dev_dbg(hdev, "Firmware request, expecting %d bytes", 
++                              bt_dev_dbg(hdev, "Firmware request, expecting %lu bytes", 
+                                       fw->size);
+                               set_bit(STATE_FW_ACK_PENDING, &rdata->flags);
diff --git a/target/linux/generic/pending-5.15/900-0004-Bluetooth-hci_renesas-Fix-issue-with-reset-pin-direc.patch b/target/linux/generic/pending-5.15/900-0004-Bluetooth-hci_renesas-Fix-issue-with-reset-pin-direc.patch
new file mode 100644 (file)
index 0000000..8a7d7c1
--- /dev/null
@@ -0,0 +1,24 @@
+From 31defedfd6a8e0c0b3265f86a4d82e28358662d6 Mon Sep 17 00:00:00 2001
+From: Fabrizio Castro <fabrizio.castro.jz@renesas.com>
+Date: Mon, 15 Aug 2022 15:49:32 +0100
+Subject: [PATCH] Bluetooth: hci_renesas: Fix issue with reset pin direction
+
+Making the reset pin an input after resetting may not work
+if there is a pullup or a pulldown on the pin, therefore just
+keep the direction of the pin as output throughout.
+
+Signed-off-by: Fabrizio Castro <fabrizio.castro.jz@renesas.com>
+---
+ drivers/bluetooth/hci_renesas.c | 1 -
+ 1 file changed, 1 deletion(-)
+
+--- a/drivers/bluetooth/hci_renesas.c
++++ b/drivers/bluetooth/hci_renesas.c
+@@ -176,7 +176,6 @@ static void reset_device(struct gpio_des
+       }
+       gpiod_set_value_cansleep(gpio, 0);
+       usleep_range(5000, 10000);
+-      gpiod_direction_input(gpio);
+ }
+ static void renesas_reset(struct hci_uart *hu)
diff --git a/target/linux/generic/pending-5.15/900-0005-Bluetooth-hci_renesas-Complete-the-setup-regardless-.patch b/target/linux/generic/pending-5.15/900-0005-Bluetooth-hci_renesas-Complete-the-setup-regardless-.patch
new file mode 100644 (file)
index 0000000..45ddd49
--- /dev/null
@@ -0,0 +1,39 @@
+From e04d45f39ed3f1a2aa1ab3605b653ef943902ffe Mon Sep 17 00:00:00 2001
+From: Fabrizio Castro <fabrizio.castro.jz@renesas.com>
+Date: Mon, 15 Aug 2022 15:56:03 +0100
+Subject: [PATCH] Bluetooth: hci_renesas: Complete the setup regardless of
+ firmware load
+
+The firmware loading step doesn't currently work, and the current
+implementation of the driver leaves the serial port with the wrong
+configuration, preventing the system from using the bluetooth device
+further.
+Disregard the outcome of the firmware loading step for now, so that we
+can use the bluetooth device.
+
+Signed-off-by: Fabrizio Castro <fabrizio.castro.jz@renesas.com>
+---
+ drivers/bluetooth/hci_renesas.c | 12 +++++++-----
+ 1 file changed, 7 insertions(+), 5 deletions(-)
+
+--- a/drivers/bluetooth/hci_renesas.c
++++ b/drivers/bluetooth/hci_renesas.c
+@@ -332,11 +332,13 @@ static int renesas_setup(struct hci_uart
+       hci_uart_set_flow_control(hu, true);
+       err = renesas_load_firmware(hu->hdev, FIRMWARE_DA14531);
+-      if (err)
+-              return err;
+-
+-      /* wait for HCI application to start */
+-      usleep_range(8000, 10000);
++      if (err) {
++              bt_dev_warn(hu->hdev, "Continuing despite being unable to "
++                          "load the firmware");
++      } else {
++              /* wait for HCI application to start */
++              usleep_range(8000, 10000);
++      }
+       rdata->state = STATE_FW_BOOTED;
diff --git a/target/linux/generic/pending-6.1/900-0001-Support-for-Renesas-Bluetooth-Low-Energy-HCI-over-UA.patch b/target/linux/generic/pending-6.1/900-0001-Support-for-Renesas-Bluetooth-Low-Energy-HCI-over-UA.patch
new file mode 100644 (file)
index 0000000..48222c0
--- /dev/null
@@ -0,0 +1,571 @@
+From e7eaf003090c6b2e56ce1d39b30b221ed7283de3 Mon Sep 17 00:00:00 2001
+From: Alvin Park <alvin.park.pv@renesas.com>
+Date: Thu, 17 Mar 2022 19:39:57 +0900
+Subject: [PATCH] Support for Renesas Bluetooth Low Energy HCI over UART
+
+---
+ drivers/bluetooth/Kconfig       |  13 +
+ drivers/bluetooth/Makefile      |   1 +
+ drivers/bluetooth/hci_ldisc.c   |   6 +
+ drivers/bluetooth/hci_renesas.c | 474 ++++++++++++++++++++++++++++++++
+ drivers/bluetooth/hci_uart.h    |   8 +-
+ 5 files changed, 501 insertions(+), 1 deletion(-)
+ create mode 100644 drivers/bluetooth/hci_renesas.c
+
+--- a/drivers/bluetooth/Kconfig
++++ b/drivers/bluetooth/Kconfig
+@@ -262,6 +262,19 @@ config BT_HCIUART_MRVL
+         Say Y here to compile support for HCI MRVL protocol.
++config BT_HCIUART_RENESAS
++      bool "Renesas protocol support"
++      depends on BT_HCIUART
++      depends on BT_HCIUART_SERDEV
++      select BT_HCIUART_H4
++      help
++        The Renesas protocol support enables Bluetooth Low Energy HCI
++        over serial.
++        This protocol is required for the Renesas Bluetooth Low Energy
++        devices with UART interface.
++
++        Say Y here to compile support for HCI Renesas protocol.
++
+ config BT_HCIBCM203X
+       tristate "HCI BCM203x USB driver"
+       depends on USB
+--- a/drivers/bluetooth/Makefile
++++ b/drivers/bluetooth/Makefile
+@@ -48,4 +48,5 @@ hci_uart-$(CONFIG_BT_HCIUART_BCM)    += hci
+ hci_uart-$(CONFIG_BT_HCIUART_QCA)     += hci_qca.o
+ hci_uart-$(CONFIG_BT_HCIUART_AG6XX)   += hci_ag6xx.o
+ hci_uart-$(CONFIG_BT_HCIUART_MRVL)    += hci_mrvl.o
++hci_uart-$(CONFIG_BT_HCIUART_RENESAS) += hci_renesas.o
+ hci_uart-objs                         := $(hci_uart-y)
+--- a/drivers/bluetooth/hci_ldisc.c
++++ b/drivers/bluetooth/hci_ldisc.c
+@@ -882,6 +882,9 @@ static int __init hci_uart_init(void)
+ #ifdef CONFIG_BT_HCIUART_MRVL
+       mrvl_init();
+ #endif
++#ifdef CONFIG_BT_HCIUART_RENESAS
++      renesas_init();
++#endif
+       return 0;
+ }
+@@ -918,6 +921,9 @@ static void __exit hci_uart_exit(void)
+ #ifdef CONFIG_BT_HCIUART_MRVL
+       mrvl_deinit();
+ #endif
++#ifdef CONFIG_BT_HCIUART_RENESAS
++      renesas_deinit();
++#endif
+       tty_unregister_ldisc(&hci_uart_ldisc);
+ }
+--- /dev/null
++++ b/drivers/bluetooth/hci_renesas.c
+@@ -0,0 +1,474 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * Renesas Bluetooth Low Energy HCI UART driver
++ *
++ * Copyright (C) 2022  Renesas Electroics Corp.
++ *
++ * Alvin Park <alvin.park.pv@renesas.com>
++ *
++ * This file is subject to the terms and conditions of the GNU General Public
++ * License.  See the file "COPYING" in the main directory of this archive
++ * for more details.
++*/
++
++#include <linux/kernel.h>
++#include <linux/errno.h>
++#include <linux/skbuff.h>
++#include <linux/firmware.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/serdev.h>
++#include <net/bluetooth/bluetooth.h>
++#include <net/bluetooth/hci_core.h>
++
++#include "hci_uart.h"
++
++enum {
++      STATE_FW_STX_PENDING,
++      STATE_FW_ACK_PENDING,
++      STATE_FW_CRC_PENDING
++};
++
++enum {
++      STATE_FW_INIT,
++      STATE_FW_SIZE,
++      STATE_FW_PROG,
++      STATE_FW_DONE,
++      STATE_FW_BOOTED
++};
++
++#define STX           0x02
++#define SOH           0x01
++#define ACK           0x06
++#define NACK          0x15
++#define CRC_INIT      0x00
++
++struct renesas_serdev {
++      struct hci_uart hu;
++};
++
++struct renesas_data {
++      struct sk_buff *rx_skb;
++      struct sk_buff_head txq;
++      struct sk_buff_head rawq;
++      unsigned long flags;
++      unsigned int state;
++      u8 id, rev, fw_crc;
++};
++
++static const struct h4_recv_pkt renesas_recv_pkts[] = {
++      { H4_RECV_ACL,       .recv = hci_recv_frame     },
++      { H4_RECV_SCO,       .recv = hci_recv_frame     },
++      { H4_RECV_EVENT,     .recv = hci_recv_frame     },
++};
++
++static int renesas_open(struct hci_uart *hu)
++{
++      struct renesas_data *rdata;
++      int ret;
++
++      BT_DBG("hu %p", hu);
++
++      if (!hci_uart_has_flow_control(hu))
++              return -EOPNOTSUPP;
++
++      rdata = kzalloc(sizeof(*rdata), GFP_KERNEL);
++      if (!rdata)
++              return -ENOMEM;
++
++      skb_queue_head_init(&rdata->txq);
++      skb_queue_head_init(&rdata->rawq);
++
++      hu->priv = rdata;
++
++      if (hu->serdev) {
++              ret = serdev_device_open(hu->serdev);
++              if (ret) {
++                      kfree(rdata);
++                      return ret;
++              }
++      }
++
++      return 0;
++}
++
++static int renesas_close(struct hci_uart *hu)
++{
++      struct renesas_data *rdata = hu->priv;
++
++      BT_DBG("hu %p", hu);
++
++      if (hu->serdev)
++              serdev_device_close(hu->serdev);
++
++      skb_queue_purge(&rdata->txq);
++      skb_queue_purge(&rdata->rawq);
++      kfree_skb(rdata->rx_skb);
++      kfree(rdata);
++
++      hu->priv = NULL;
++      return 0;
++}
++
++static int renesas_flush(struct hci_uart *hu)
++{
++      struct renesas_data *rdata = hu->priv;
++
++      BT_DBG("hu %p", hu);
++
++      skb_queue_purge(&rdata->txq);
++      skb_queue_purge(&rdata->rawq);
++
++      return 0;
++}
++
++static int renesas_send_ack(struct hci_uart *hu, unsigned char type)
++{
++      struct renesas_data *rdata = hu->priv;
++      struct sk_buff *skb;
++
++      skb = alloc_skb(1, GFP_ATOMIC);
++      if (!skb) {
++              bt_dev_err(hu->hdev, "Unable to alloc ack/nak packet");
++              return -ENOMEM;
++      }
++      skb_put_u8(skb, type);
++
++      skb_queue_tail(&rdata->rawq, skb);
++      hci_uart_tx_wakeup(hu);
++
++      return 0;
++}
++
++static int renesas_send_fw_size(struct hci_uart *hu, u16 length)
++{
++      struct renesas_data *rdata = hu->priv;
++      struct sk_buff *skb;
++
++      skb = alloc_skb(3, GFP_ATOMIC);
++      if (!skb) {
++              bt_dev_err(hu->hdev, "Failed to alloc mem for FW size packet");
++              return -ENOMEM;
++      }
++
++      skb_put_u8(skb, SOH);
++      skb_put_u8(skb, length);
++      skb_put_u8(skb, length>>8);
++
++      skb_queue_tail(&rdata->rawq, skb);
++      hci_uart_tx_wakeup(hu);
++
++      return 0;
++}
++
++static int renesas_load_firmware(struct hci_dev *hdev, const char *name)
++{
++      struct hci_uart *hu = hci_get_drvdata(hdev);
++      struct renesas_data *rdata = hu->priv;
++      const struct firmware *fw = NULL;
++      const u8 *fw_ptr, *fw_max;
++      u16 fw_size = 0;
++      int err, i;
++
++      err = request_firmware(&fw, name, &hdev->dev);
++      if (err < 0) {
++              bt_dev_err(hdev, "Failed to load firmware file %s", name);
++              return err;
++      }
++
++      fw_ptr = fw->data;
++      fw_max = fw->data + fw->size;
++      fw_size = fw->size;
++
++      bt_dev_info(hdev, "Loading %s", name);
++
++      /* update crc */
++      for (i = 0; i < fw->size; i++)
++              rdata->fw_crc ^= fw_ptr[i];
++
++      rdata->state = STATE_FW_INIT;
++      set_bit(STATE_FW_STX_PENDING, &rdata->flags);
++
++      while (rdata->state != STATE_FW_DONE) {
++              struct sk_buff *skb;
++
++              /* Controller drives the firmware load by sending firmware
++               * request packets containing the expected fragment size.
++               */
++              err = wait_on_bit_timeout(&rdata->flags, STATE_FW_STX_PENDING,
++                                        TASK_INTERRUPTIBLE,
++                                        msecs_to_jiffies(5000));
++              if (err == 1) {
++                      bt_dev_err(hdev, "Firmware load interrupted");
++                      err = -EINTR;
++                      break;
++              } else if (err) {
++                      bt_dev_err(hdev, "Firmware request timeout");
++                      err = -ETIMEDOUT;
++                      break;
++              }
++
++              switch(rdata->state) {
++                      case STATE_FW_INIT:
++                              bt_dev_dbg(hdev, "Firmware request, expecting %d bytes", 
++                                      fw->size);
++
++                              set_bit(STATE_FW_ACK_PENDING, &rdata->flags);
++                              err = renesas_send_fw_size(hu, fw_size);
++                              if (err) {
++                                      break;
++                              }
++
++                              rdata->state = STATE_FW_SIZE;
++                              break;
++
++                      case STATE_FW_SIZE:
++                              err = wait_on_bit_timeout(&rdata->flags, STATE_FW_ACK_PENDING,
++                                              TASK_INTERRUPTIBLE,
++                                              msecs_to_jiffies(2000));
++                              if (err == 1) {
++                                      bt_dev_err(hdev, "Firmware load interrupted");
++                                      err = -EINTR;
++                                      break;
++                              } else if (err) {
++                                      bt_dev_err(hdev, "Firmware request timeout");
++                                      err = -ETIMEDOUT;
++                                      break;
++                              }
++
++                              set_bit(STATE_FW_CRC_PENDING, &rdata->flags);
++
++                              skb = alloc_skb(fw_size, GFP_KERNEL);
++                              if (!skb) {
++                                      bt_dev_err(hdev, "Failed to alloc mem for FW packet");
++                                      err = -ENOMEM;
++                                      break;
++                              }
++                              skb_put_data(skb, fw_ptr, fw_size);
++                              skb_queue_tail(&rdata->rawq, skb);
++                              hci_uart_tx_wakeup(hu);
++
++                              rdata->state = STATE_FW_PROG;
++                              break;
++
++                      case STATE_FW_PROG:
++                              err = wait_on_bit_timeout(&rdata->flags, STATE_FW_CRC_PENDING,
++                                              TASK_INTERRUPTIBLE,
++                                              msecs_to_jiffies(2000));
++                              if (err == 1) {
++                                      bt_dev_err(hdev, "Firmware load interrupted");
++                                      err = -EINTR;
++                                      break;
++                              } else if (err) {
++                                      bt_dev_err(hdev, "Firmware request timeout");
++                                      err = -ETIMEDOUT;
++                                      break;
++                              }
++
++                              err = renesas_send_ack(hu, ACK);
++                              if (err) {
++                                      break;
++                              }
++
++                              bt_dev_dbg(hdev, "Firmware has been loaded");
++
++                              set_bit(STATE_FW_STX_PENDING, &rdata->flags);
++                              rdata->state = STATE_FW_DONE;
++                              break;
++
++                      default:
++                              break;
++              }
++
++              if (err) {
++                      break;
++              }
++      }
++
++      release_firmware(fw);
++      return err;
++}
++
++static int renesas_setup(struct hci_uart *hu)
++{
++      struct renesas_data *rdata = hu->priv;
++      int err;
++
++      hci_uart_set_flow_control(hu, true);
++
++      err = renesas_load_firmware(hu->hdev, "renesas/hci_531.bin");
++      if (err)
++              return err;
++
++      /* wait for HCI application to start */
++      usleep_range(8000, 10000);
++
++      rdata->state = STATE_FW_BOOTED;
++
++      hci_uart_set_flow_control(hu, false);
++
++      return 0;
++}
++
++static int renesas_recv(struct hci_uart *hu, const void *data, int count)
++{
++      struct renesas_data *rdata = hu->priv;
++      u8 *data_ptr = (u8 *)data;
++
++      switch(rdata->state) {
++              case STATE_FW_INIT:
++                      if (*data_ptr == STX) {
++                              if (!test_bit(STATE_FW_STX_PENDING, &rdata->flags)) {
++                                      bt_dev_err(hu->hdev, "Received unexpected STX");
++                                      return -EINVAL;
++                              }
++                              clear_bit(STATE_FW_STX_PENDING, &rdata->flags);
++                              wake_up_bit(&rdata->flags, STATE_FW_STX_PENDING);
++                      }
++                      break;
++
++              case STATE_FW_SIZE:
++                      if (*data_ptr == ACK) {
++                              if (!test_bit(STATE_FW_ACK_PENDING, &rdata->flags)) {
++                                      bt_dev_err(hu->hdev, "Received unexpected ACK");
++                                      return -EINVAL;
++                              }
++                              clear_bit(STATE_FW_ACK_PENDING, &rdata->flags);
++                              wake_up_bit(&rdata->flags, STATE_FW_ACK_PENDING);
++                      } else {
++                              bt_dev_err(hu->hdev, "Received unexpected data (%x)", *data_ptr);
++                              return -EINVAL;
++                      }
++                      break;
++
++              case STATE_FW_PROG:
++                      if (!test_bit(STATE_FW_CRC_PENDING, &rdata->flags)) {
++                              bt_dev_err(hu->hdev, "Received unexpected CRC");
++                              return -EINVAL;
++                      }
++
++                      if (rdata->fw_crc != *data_ptr) {
++                              bt_dev_err(hu->hdev, "Received CRC %02x, "
++                                      "which does not match "
++                                      "computed CRC %02x.\n",
++                                      *data_ptr, rdata->fw_crc);
++                              return -EINVAL;
++                      }
++
++                      clear_bit(STATE_FW_CRC_PENDING, &rdata->flags);
++                      wake_up_bit(&rdata->flags, STATE_FW_CRC_PENDING);
++                      break;
++
++              case STATE_FW_DONE:
++                      /* Do nothing */
++                      break;
++
++              case STATE_FW_BOOTED:
++                      rdata->rx_skb = h4_recv_buf(hu->hdev, rdata->rx_skb, data, count,
++                                                              renesas_recv_pkts,
++                                                              ARRAY_SIZE(renesas_recv_pkts));
++                      if (IS_ERR(rdata->rx_skb)) {
++                              int err = PTR_ERR(rdata->rx_skb);
++                              bt_dev_err(hu->hdev, "Frame reassembly failed (%d)", err);
++                              rdata->rx_skb = NULL;
++                              return err;
++                      }
++                      break;
++
++              default:
++                      bt_dev_err(hu->hdev, "Unknown state (%d)", rdata->state);
++                      break;
++      }
++
++      return count;
++}
++
++static int renesas_enqueue(struct hci_uart *hu, struct sk_buff *skb)
++{
++      struct renesas_data *rdata = hu->priv;
++
++      skb_queue_tail(&rdata->txq, skb);
++      return 0;
++}
++
++static struct sk_buff *renesas_dequeue(struct hci_uart *hu)
++{
++      struct renesas_data *rdata = hu->priv;
++      struct sk_buff *skb;
++
++      skb = skb_dequeue(&rdata->txq);
++      if (!skb) {
++              /* Any raw data ? */
++              skb = skb_dequeue(&rdata->rawq);
++      } else {
++              /* Prepend skb with frame type */
++              memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
++      }
++
++      return skb;
++}
++
++static const struct hci_uart_proto renesas_proto = {
++      .id             = HCI_UART_RENESAS,
++      .name           = "Renesas",
++      .init_speed     = 115200,
++      .open           = renesas_open,
++      .close          = renesas_close,
++      .flush          = renesas_flush,
++      .setup          = renesas_setup,
++      .recv           = renesas_recv,
++      .enqueue        = renesas_enqueue,
++      .dequeue        = renesas_dequeue,
++};
++
++static int renesas_serdev_probe(struct serdev_device *serdev)
++{
++      struct renesas_serdev *rdatadev;
++
++      rdatadev = devm_kzalloc(&serdev->dev, sizeof(*rdatadev), GFP_KERNEL);
++      if (!rdatadev)
++              return -ENOMEM;
++
++      rdatadev->hu.serdev = serdev;
++      serdev_device_set_drvdata(serdev, rdatadev);
++
++      return hci_uart_register_device(&rdatadev->hu, &renesas_proto);
++}
++
++static void renesas_serdev_remove(struct serdev_device *serdev)
++{
++      struct renesas_serdev *rdatadev = serdev_device_get_drvdata(serdev);
++
++      hci_uart_unregister_device(&rdatadev->hu);
++}
++
++#ifdef CONFIG_OF
++static const struct of_device_id renesas_bluetooth_of_match[] = {
++      { .compatible = "renesas,DA14531" },
++      { },
++};
++MODULE_DEVICE_TABLE(of, renesas_bluetooth_of_match);
++#endif
++
++static struct serdev_device_driver renesas_serdev_driver = {
++      .probe = renesas_serdev_probe,
++      .remove = renesas_serdev_remove,
++      .driver = {
++              .name = "hci_uart_renesas",
++              .of_match_table = of_match_ptr(renesas_bluetooth_of_match),
++      },
++};
++
++int __init renesas_init(void)
++{
++      serdev_device_driver_register(&renesas_serdev_driver);
++
++      return hci_uart_register_proto(&renesas_proto);
++}
++
++int __exit renesas_deinit(void)
++{
++      serdev_device_driver_unregister(&renesas_serdev_driver);
++
++      return hci_uart_unregister_proto(&renesas_proto);
++}
+--- a/drivers/bluetooth/hci_uart.h
++++ b/drivers/bluetooth/hci_uart.h
+@@ -20,7 +20,7 @@
+ #define HCIUARTGETFLAGS               _IOR('U', 204, int)
+ /* UART protocols */
+-#define HCI_UART_MAX_PROTO    12
++#define HCI_UART_MAX_PROTO    13
+ #define HCI_UART_H4   0
+ #define HCI_UART_BCSP 1
+@@ -34,6 +34,7 @@
+ #define HCI_UART_AG6XX        9
+ #define HCI_UART_NOKIA        10
+ #define HCI_UART_MRVL 11
++#define HCI_UART_RENESAS      12
+ #define HCI_UART_RAW_DEVICE   0
+ #define HCI_UART_RESET_ON_INIT        1
+@@ -200,3 +201,8 @@ int ag6xx_deinit(void);
+ int mrvl_init(void);
+ int mrvl_deinit(void);
+ #endif
++
++#ifdef CONFIG_BT_HCIUART_RENESAS
++int renesas_init(void);
++int renesas_deinit(void);
++#endif
diff --git a/target/linux/generic/pending-6.1/900-0002-Adding-DA16600-reset-by-GPIO.patch b/target/linux/generic/pending-6.1/900-0002-Adding-DA16600-reset-by-GPIO.patch
new file mode 100644 (file)
index 0000000..85f48c7
--- /dev/null
@@ -0,0 +1,115 @@
+From 03b82d621adb7145d008eabb8995f14ad9582d56 Mon Sep 17 00:00:00 2001
+From: Alvin Park <alvin.park.pv@renesas.com>
+Date: Wed, 4 May 2022 17:02:26 +0900
+Subject: [PATCH] Adding DA16600 reset by GPIO
+
+---
+ drivers/bluetooth/hci_renesas.c | 51 ++++++++++++++++++++++++++++++++-
+ 1 file changed, 50 insertions(+), 1 deletion(-)
+
+--- a/drivers/bluetooth/hci_renesas.c
++++ b/drivers/bluetooth/hci_renesas.c
+@@ -18,6 +18,7 @@
+ #include <linux/module.h>
+ #include <linux/of.h>
+ #include <linux/serdev.h>
++#include <linux/gpio/consumer.h>
+ #include <net/bluetooth/bluetooth.h>
+ #include <net/bluetooth/hci_core.h>
+@@ -37,6 +38,10 @@ enum {
+       STATE_FW_BOOTED
+ };
++#define VERSION "0.1"
++
++#define FIRMWARE_DA14531      "renesas/hci_531.bin"
++
+ #define STX           0x02
+ #define SOH           0x01
+ #define ACK           0x06
+@@ -45,6 +50,8 @@ enum {
+ struct renesas_serdev {
+       struct hci_uart hu;
++
++      struct gpio_desc *reset;
+ };
+ struct renesas_data {
+@@ -161,6 +168,32 @@ static int renesas_send_fw_size(struct h
+       return 0;
+ }
++static void reset_device(struct gpio_desc *gpio)
++{
++      if (gpiod_get_value(gpio) == 0) {
++              gpiod_set_value_cansleep(gpio, 1);
++              usleep_range(1000, 2000);
++      }
++      gpiod_set_value_cansleep(gpio, 0);
++      usleep_range(5000, 10000);
++      gpiod_direction_input(gpio);
++}
++
++static void renesas_reset(struct hci_uart *hu)
++{
++      struct serdev_device *serdev = hu->serdev;
++      if (serdev) {
++              struct renesas_serdev *rdatadev = serdev_device_get_drvdata(serdev);
++              if (rdatadev && rdatadev->reset) {
++                      reset_device(rdatadev->reset);
++              } else {
++                      bt_dev_warn(hu->hdev, "Reset pin is not available!");
++              }
++      } else {
++              bt_dev_warn(hu->hdev, "Reset is required!");
++      }
++}
++
+ static int renesas_load_firmware(struct hci_dev *hdev, const char *name)
+ {
+       struct hci_uart *hu = hci_get_drvdata(hdev);
+@@ -189,6 +222,9 @@ static int renesas_load_firmware(struct
+       rdata->state = STATE_FW_INIT;
+       set_bit(STATE_FW_STX_PENDING, &rdata->flags);
++      /* reset */
++      renesas_reset(hu);
++
+       while (rdata->state != STATE_FW_DONE) {
+               struct sk_buff *skb;
+@@ -296,7 +332,7 @@ static int renesas_setup(struct hci_uart
+       hci_uart_set_flow_control(hu, true);
+-      err = renesas_load_firmware(hu->hdev, "renesas/hci_531.bin");
++      err = renesas_load_firmware(hu->hdev, FIRMWARE_DA14531);
+       if (err)
+               return err;
+@@ -432,6 +468,13 @@ static int renesas_serdev_probe(struct s
+       rdatadev->hu.serdev = serdev;
+       serdev_device_set_drvdata(serdev, rdatadev);
++      rdatadev->reset = devm_gpiod_get(&serdev->dev, "reset", GPIOD_OUT_HIGH);
++      if (IS_ERR(rdatadev->reset)) {
++              int err = 0;
++              err = PTR_ERR(rdatadev->reset);
++              dev_warn(&serdev->dev, "could not get reset gpio: %d", err);
++      }
++
+       return hci_uart_register_device(&rdatadev->hu, &renesas_proto);
+ }
+@@ -472,3 +515,9 @@ int __exit renesas_deinit(void)
+       return hci_uart_unregister_proto(&renesas_proto);
+ }
++
++MODULE_AUTHOR("Alvin Park <alvin.park.pv@renesas.com>");
++MODULE_DESCRIPTION("Renesas Bluetooth Serial driver ver " VERSION);
++MODULE_VERSION(VERSION);
++MODULE_LICENSE("GPL");
++MODULE_FIRMWARE(FIRMWARE_DA14531);
diff --git a/target/linux/generic/pending-6.1/900-0003-Bluetooth-hci_renesas-Fix-compiler-warning.patch b/target/linux/generic/pending-6.1/900-0003-Bluetooth-hci_renesas-Fix-compiler-warning.patch
new file mode 100644 (file)
index 0000000..cb59859
--- /dev/null
@@ -0,0 +1,21 @@
+From 5cd6cc75738c0eccbd837c2b24b278fe29677efb Mon Sep 17 00:00:00 2001
+From: Fabrizio Castro <fabrizio.castro.jz@renesas.com>
+Date: Mon, 15 Aug 2022 15:45:32 +0100
+Subject: [PATCH] Bluetooth: hci_renesas: Fix compiler warning
+
+Signed-off-by: Fabrizio Castro <fabrizio.castro.jz@renesas.com>
+---
+ drivers/bluetooth/hci_renesas.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/bluetooth/hci_renesas.c
++++ b/drivers/bluetooth/hci_renesas.c
+@@ -246,7 +246,7 @@ static int renesas_load_firmware(struct
+               switch(rdata->state) {
+                       case STATE_FW_INIT:
+-                              bt_dev_dbg(hdev, "Firmware request, expecting %d bytes", 
++                              bt_dev_dbg(hdev, "Firmware request, expecting %lu bytes", 
+                                       fw->size);
+                               set_bit(STATE_FW_ACK_PENDING, &rdata->flags);
diff --git a/target/linux/generic/pending-6.1/900-0004-Bluetooth-hci_renesas-Fix-issue-with-reset-pin-direc.patch b/target/linux/generic/pending-6.1/900-0004-Bluetooth-hci_renesas-Fix-issue-with-reset-pin-direc.patch
new file mode 100644 (file)
index 0000000..8a7d7c1
--- /dev/null
@@ -0,0 +1,24 @@
+From 31defedfd6a8e0c0b3265f86a4d82e28358662d6 Mon Sep 17 00:00:00 2001
+From: Fabrizio Castro <fabrizio.castro.jz@renesas.com>
+Date: Mon, 15 Aug 2022 15:49:32 +0100
+Subject: [PATCH] Bluetooth: hci_renesas: Fix issue with reset pin direction
+
+Making the reset pin an input after resetting may not work
+if there is a pullup or a pulldown on the pin, therefore just
+keep the direction of the pin as output throughout.
+
+Signed-off-by: Fabrizio Castro <fabrizio.castro.jz@renesas.com>
+---
+ drivers/bluetooth/hci_renesas.c | 1 -
+ 1 file changed, 1 deletion(-)
+
+--- a/drivers/bluetooth/hci_renesas.c
++++ b/drivers/bluetooth/hci_renesas.c
+@@ -176,7 +176,6 @@ static void reset_device(struct gpio_des
+       }
+       gpiod_set_value_cansleep(gpio, 0);
+       usleep_range(5000, 10000);
+-      gpiod_direction_input(gpio);
+ }
+ static void renesas_reset(struct hci_uart *hu)
diff --git a/target/linux/generic/pending-6.1/900-0005-Bluetooth-hci_renesas-Complete-the-setup-regardless-.patch b/target/linux/generic/pending-6.1/900-0005-Bluetooth-hci_renesas-Complete-the-setup-regardless-.patch
new file mode 100644 (file)
index 0000000..45ddd49
--- /dev/null
@@ -0,0 +1,39 @@
+From e04d45f39ed3f1a2aa1ab3605b653ef943902ffe Mon Sep 17 00:00:00 2001
+From: Fabrizio Castro <fabrizio.castro.jz@renesas.com>
+Date: Mon, 15 Aug 2022 15:56:03 +0100
+Subject: [PATCH] Bluetooth: hci_renesas: Complete the setup regardless of
+ firmware load
+
+The firmware loading step doesn't currently work, and the current
+implementation of the driver leaves the serial port with the wrong
+configuration, preventing the system from using the bluetooth device
+further.
+Disregard the outcome of the firmware loading step for now, so that we
+can use the bluetooth device.
+
+Signed-off-by: Fabrizio Castro <fabrizio.castro.jz@renesas.com>
+---
+ drivers/bluetooth/hci_renesas.c | 12 +++++++-----
+ 1 file changed, 7 insertions(+), 5 deletions(-)
+
+--- a/drivers/bluetooth/hci_renesas.c
++++ b/drivers/bluetooth/hci_renesas.c
+@@ -332,11 +332,13 @@ static int renesas_setup(struct hci_uart
+       hci_uart_set_flow_control(hu, true);
+       err = renesas_load_firmware(hu->hdev, FIRMWARE_DA14531);
+-      if (err)
+-              return err;
+-
+-      /* wait for HCI application to start */
+-      usleep_range(8000, 10000);
++      if (err) {
++              bt_dev_warn(hu->hdev, "Continuing despite being unable to "
++                          "load the firmware");
++      } else {
++              /* wait for HCI application to start */
++              usleep_range(8000, 10000);
++      }
+       rdata->state = STATE_FW_BOOTED;