Signed-off-by: Ilya Faenson <[email protected]>
---
 drivers/bluetooth/btbcm.c | 155 ++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 150 insertions(+), 5 deletions(-)

diff --git a/drivers/bluetooth/btbcm.c b/drivers/bluetooth/btbcm.c
index 4bba866..8b5530d 100644
--- a/drivers/bluetooth/btbcm.c
+++ b/drivers/bluetooth/btbcm.c
@@ -3,6 +3,7 @@
  *  Bluetooth support for Broadcom devices
  *
  *  Copyright (C) 2015  Intel Corporation
+ *  Copyright (C) 2015  Broadcom Corporation
  *
  *
  *  This program is free software; you can redistribute it and/or modify
@@ -23,14 +24,16 @@
 
 #include <linux/module.h>
 #include <linux/firmware.h>
+#include <linux/tty.h>
 #include <asm/unaligned.h>
 
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
 
+#include "hci_uart.h"
 #include "btbcm.h"
 
-#define VERSION "0.1"
+#define VERSION "0.2"
 
 #define BDADDR_BCM20702A0 (&(bdaddr_t) {{0x00, 0xa0, 0x02, 0x70, 0x20, 0x00}})
 
@@ -246,8 +249,10 @@ static struct sk_buff *btbcm_read_usb_product(struct 
hci_dev *hdev)
 static const struct {
        u16 subver;
        const char *name;
+       u32 baud_rate;  /* operational baud rate */
 } bcm_uart_subver_table[] = {
-       { 0x410e, "BCM43341B0"  },      /* 002.001.014 */
+       { 0x410e, "BCM43341B0", 3000000},                    /* 002.001.014 */
+       { 0x610c, "BCM4354_003.001.012.0306.0659", 3000000}, /* 003.001.012 */
        { }
 };
 
@@ -268,6 +273,127 @@ static const struct {
        { }
 };
 
+/*
+ * Set the UART into the defaults
+ */
+int btbcm_init_uart(struct hci_uart *hu)
+{
+       struct tty_struct *tty = hu->tty;
+       struct ktermios ktermios;
+       int err, speed;
+
+       /* Flush the line discipline buffers and the TTY buffers */
+       if (tty->ldisc->ops->flush_buffer)
+               tty->ldisc->ops->flush_buffer(tty);
+       tty_driver_flush_buffer(tty);
+
+
+       /* Init UART to default settings */
+       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 | CBAUD);
+       ktermios.c_cflag |= CS8;
+       ktermios.c_cflag |= CRTSCTS;
+       ktermios.c_cflag |= B115200;
+       ktermios.c_ispeed = 115200;
+       ktermios.c_ospeed = 115200;
+       err = tty_set_termios(tty, &ktermios);
+       if (err) {
+               BT_DBG("init_uart set_termios failure %d", err);
+               return err;
+       }
+
+       speed = tty_get_baud_rate(tty);
+
+       BT_DBG("init_uart set_termios completed, spd %d", speed);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(btbcm_init_uart);
+
+/*
+ * Set the baud rate on the UART and the device
+ */
+int btbcm_set_baud_rate(struct hci_uart *hu, int baud_rate)
+{
+       struct tty_struct *tty = hu->tty;
+       struct ktermios ktermios;
+       int status, speed, cflag;
+       struct sk_buff *skb;
+       u8 enable = 0x01;
+       u8 baud_rate_vsc_pars[] = {0, 0, 0, 0x10, 0x0e, 0};
+
+       /* If the baud rate is higher than 3000000, change the clock */
+       if (baud_rate > 3000000) {
+               skb = __hci_cmd_sync(hu->hdev, 0xfc45, 1, &enable,
+                                    HCI_INIT_TIMEOUT);
+               if (IS_ERR(skb)) {
+                       status = PTR_ERR(skb);
+                       return status;
+               }
+
+               kfree_skb(skb);
+               BT_DBG("set_baud_rate write UART 48 MHz VSC succeeded");
+       }
+
+       /* Now let the device know about the rate change */
+       put_unaligned_le32((u32)baud_rate, &baud_rate_vsc_pars[2]);
+       skb = __hci_cmd_sync(hu->hdev, 0xfc18, sizeof(baud_rate_vsc_pars),
+                            baud_rate_vsc_pars, HCI_INIT_TIMEOUT);
+       if (IS_ERR(skb)) {
+               status = PTR_ERR(skb);
+               BT_ERR("set_baud_rate VSC failed (%d)", status);
+               return status;
+       }
+
+       kfree_skb(skb);
+       BT_DBG("set_baud_rate VSC succeeded");
+
+       /* Set UART into this rate as well */
+       ktermios = tty->termios;
+       BT_DBG("set_baud_rate start flags c_o %x c_l %x c_c %x spd %d/%d",
+              ktermios.c_oflag, ktermios.c_lflag, ktermios.c_cflag,
+              ktermios.c_ispeed, ktermios.c_ospeed);
+       switch (baud_rate) {
+       case 115200:
+               cflag |= B115200; break;
+       case 921600:
+               cflag |= B921600; break;
+       case 3000000:
+               cflag |= B3000000; break;
+       case 3500000:
+               cflag |= B3500000; break;
+       case 4000000:
+               cflag |= B4000000; break;
+       default:
+               BT_DBG("set_baud_rate unknown rate %d", baud_rate);
+               return -EINVAL;
+       }
+
+       ktermios.c_cflag &= ~CBAUD;
+       ktermios.c_cflag |= cflag;
+       ktermios.c_ispeed = baud_rate;
+       ktermios.c_ospeed = baud_rate;
+       status = tty_set_termios(tty, &ktermios);
+       if (status) {
+               BT_DBG("set_baud_rate set_termios failure %d", status);
+               return status;
+       }
+
+       speed = tty_get_baud_rate(tty);
+       BT_DBG("set_baud_rate set_termios completed, spd %d", speed);
+       ktermios = tty->termios;
+       BT_DBG("set_baud_rate flags c_o %x c_l %x c_c %x spd %d/%d",
+              ktermios.c_oflag, ktermios.c_lflag, ktermios.c_cflag,
+              ktermios.c_ispeed, ktermios.c_ospeed);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(btbcm_set_baud_rate);
+
 int btbcm_setup_patchram(struct hci_dev *hdev)
 {
        char fw_name[64];
@@ -275,7 +401,8 @@ int btbcm_setup_patchram(struct hci_dev *hdev)
        const char *hw_name = NULL;
        struct sk_buff *skb;
        struct hci_rp_read_local_version *ver;
-       int i, err;
+       int i, err, is_uart = false;
+       struct hci_uart *hu = hci_get_drvdata(hdev);
 
        /* Reset */
        err = btbcm_reset(hdev);
@@ -297,14 +424,18 @@ int btbcm_setup_patchram(struct hci_dev *hdev)
        if (IS_ERR(skb))
                return PTR_ERR(skb);
 
-       BT_INFO("%s: BCM: chip id %u", hdev->name, skb->data[1]);
+       BT_INFO("%s: BCM: chip id %u, rev 0x%x subver 0x%x",
+               hdev->name, skb->data[1], rev, subver);
        kfree_skb(skb);
 
        switch ((rev & 0xf000) >> 12) {
        case 0:
+       case 1:
                for (i = 0; bcm_uart_subver_table[i].name; i++) {
                        if (subver == bcm_uart_subver_table[i].subver) {
                                hw_name = bcm_uart_subver_table[i].name;
+                               BT_INFO("UART firmware found: %s", hw_name);
+                               is_uart = true;
                                break;
                        }
                }
@@ -312,7 +443,7 @@ int btbcm_setup_patchram(struct hci_dev *hdev)
                snprintf(fw_name, sizeof(fw_name), "brcm/%s.hcd",
                         hw_name ? : "BCM");
                break;
-       case 1:
+
        case 2:
                /* Read USB Product Info */
                skb = btbcm_read_usb_product(hdev);
@@ -345,11 +476,25 @@ int btbcm_setup_patchram(struct hci_dev *hdev)
        if (err == -ENOENT)
                return 0;
 
+       /* Once the patch is downloaded, the device is back at default rate */
+       if (is_uart) {
+               err = btbcm_init_uart(hu);
+               if (err)
+                       return 0;
+       }
+
        /* Reset */
        err = btbcm_reset(hdev);
        if (err)
                return err;
 
+       if (is_uart) {
+               err = btbcm_set_baud_rate(hu,
+                                         bcm_uart_subver_table[i].baud_rate);
+               if (err)
+                       return 0;
+       }
+
        /* Read Local Version Info */
        skb = btbcm_read_local_version(hdev);
        if (IS_ERR(skb))
-- 
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to