diff options
Diffstat (limited to 'drivers/bluetooth/hci_bcm.c')
-rw-r--r-- | drivers/bluetooth/hci_bcm.c | 107 |
1 files changed, 106 insertions, 1 deletions
diff --git a/drivers/bluetooth/hci_bcm.c b/drivers/bluetooth/hci_bcm.c index 1ec0b4a5ffa6..23523e140a9a 100644 --- a/drivers/bluetooth/hci_bcm.c +++ b/drivers/bluetooth/hci_bcm.c @@ -24,6 +24,7 @@ #include <linux/kernel.h> #include <linux/errno.h> #include <linux/skbuff.h> +#include <linux/firmware.h> #include <net/bluetooth/bluetooth.h> #include <net/bluetooth/hci_core.h> @@ -36,6 +37,55 @@ struct bcm_data { struct sk_buff_head txq; }; +static int bcm_set_baudrate(struct hci_uart *hu, unsigned int speed) +{ + struct hci_dev *hdev = hu->hdev; + struct sk_buff *skb; + struct bcm_update_uart_baud_rate param; + + if (speed > 3000000) { + struct bcm_write_uart_clock_setting clock; + + clock.type = BCM_UART_CLOCK_48MHZ; + + BT_DBG("%s: Set Controller clock (%d)", hdev->name, clock.type); + + /* This Broadcom specific command changes the UART's controller + * clock for baud rate > 3000000. + */ + skb = __hci_cmd_sync(hdev, 0xfc45, 1, &clock, HCI_INIT_TIMEOUT); + if (IS_ERR(skb)) { + int err = PTR_ERR(skb); + BT_ERR("%s: BCM: failed to write clock command (%d)", + hdev->name, err); + return err; + } + + kfree_skb(skb); + } + + BT_DBG("%s: Set Controller UART speed to %d bit/s", hdev->name, speed); + + param.zero = cpu_to_le16(0); + param.baud_rate = cpu_to_le32(speed); + + /* This Broadcom specific command changes the UART's controller baud + * rate. + */ + skb = __hci_cmd_sync(hdev, 0xfc18, sizeof(param), ¶m, + HCI_INIT_TIMEOUT); + if (IS_ERR(skb)) { + int err = PTR_ERR(skb); + BT_ERR("%s: BCM: failed to write update baudrate command (%d)", + hdev->name, err); + return err; + } + + kfree_skb(skb); + + return 0; +} + static int bcm_open(struct hci_uart *hu) { struct bcm_data *bcm; @@ -79,11 +129,62 @@ static int bcm_flush(struct hci_uart *hu) static int bcm_setup(struct hci_uart *hu) { + char fw_name[64]; + const struct firmware *fw; + unsigned int speed; + int err; + BT_DBG("hu %p", hu); hu->hdev->set_bdaddr = btbcm_set_bdaddr; - return btbcm_setup_patchram(hu->hdev); + err = btbcm_initialize(hu->hdev, fw_name, sizeof(fw_name)); + if (err) + return err; + + err = request_firmware(&fw, fw_name, &hu->hdev->dev); + if (err < 0) { + BT_INFO("%s: BCM: Patch %s not found", hu->hdev->name, fw_name); + return 0; + } + + err = btbcm_patchram(hu->hdev, fw); + if (err) { + BT_INFO("%s: BCM: Patch failed (%d)", hu->hdev->name, err); + goto finalize; + } + + /* 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) + hci_uart_set_baudrate(hu, 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 (speed) { + err = bcm_set_baudrate(hu, speed); + if (!err) + hci_uart_set_baudrate(hu, speed); + } + +finalize: + release_firmware(fw); + + err = btbcm_finalize(hu->hdev); + + return err; } static const struct h4_recv_pkt bcm_recv_pkts[] = { @@ -104,6 +205,7 @@ static int bcm_recv(struct hci_uart *hu, const void *data, int count) if (IS_ERR(bcm->rx_skb)) { int err = PTR_ERR(bcm->rx_skb); BT_ERR("%s: Frame reassembly failed (%d)", hu->hdev->name, err); + bcm->rx_skb = NULL; return err; } @@ -133,10 +235,13 @@ static struct sk_buff *bcm_dequeue(struct hci_uart *hu) static const struct hci_uart_proto bcm_proto = { .id = HCI_UART_BCM, .name = "BCM", + .init_speed = 115200, + .oper_speed = 4000000, .open = bcm_open, .close = bcm_close, .flush = bcm_flush, .setup = bcm_setup, + .set_baudrate = bcm_set_baudrate, .recv = bcm_recv, .enqueue = bcm_enqueue, .dequeue = bcm_dequeue, |