Message ID | 1524144898-4645-3-git-send-email-bgodavar@codeaurora.org (mailing list archive) |
---|---|
State | Not Applicable, archived |
Delegated to: | Andy Gross |
Headers | show |
Hi Balakrishna, On 19/04/2018 15:34, Balakrishna Godavarthi wrote: > - Add support to set voltage/current of various regualtors > to power up/down BT QCA chip wcn3990. > - Add firmware download support for BT QCA chip wcn3990. > > Signed-off-by: Balakrishna Godavarthi <bgodavar@codeaurora.org> > --- > drivers/bluetooth/btqca.c | 393 +++++++++++++++++++++++------- > drivers/bluetooth/btqca.h | 49 +++- > drivers/bluetooth/hci_qca.c | 501 +++++++++++++++++++++++++++++++++------ > include/net/bluetooth/hci_core.h | 3 +- > net/bluetooth/hci_core.c | 2 +- > net/bluetooth/hci_request.c | 23 ++ > net/bluetooth/hci_request.h | 2 + > 7 files changed, 812 insertions(+), 161 deletions(-) This is a pretty big patch. You should at least put the renaming of the rome_* functions and structures into a separate patch. Also there are some coding style issues. You can use ./scripts/checkpatch.pl to check and fix them. Regards, Thierry > > diff --git a/drivers/bluetooth/btqca.c b/drivers/bluetooth/btqca.c > index 0bbdfce..5b47df8 100644 > --- a/drivers/bluetooth/btqca.c > +++ b/drivers/bluetooth/btqca.c > @@ -1,7 +1,7 @@ > /* > * Bluetooth supports for Qualcomm Atheros chips > * > - * Copyright (c) 2015 The Linux Foundation. All rights reserved. > + * Copyright (c) 2015, 2018 The Linux Foundation. All rights reserved. > * > * This program is free software; you can redistribute it and/or modify > * it under the terms of the GNU General Public License version 2 > @@ -19,82 +19,18 @@ > */ > #include <linux/module.h> > #include <linux/firmware.h> > - > #include <net/bluetooth/bluetooth.h> > #include <net/bluetooth/hci_core.h> > - > #include "btqca.h" > > #define VERSION "0.1" > > -static int rome_patch_ver_req(struct hci_dev *hdev, u32 *rome_version) > -{ > - struct sk_buff *skb; > - struct edl_event_hdr *edl; > - struct rome_version *ver; > - char cmd; > - int err = 0; > - > - BT_DBG("%s: ROME Patch Version Request", hdev->name); > - > - cmd = EDL_PATCH_VER_REQ_CMD; > - skb = __hci_cmd_sync_ev(hdev, EDL_PATCH_CMD_OPCODE, EDL_PATCH_CMD_LEN, > - &cmd, HCI_VENDOR_PKT, HCI_INIT_TIMEOUT); > - if (IS_ERR(skb)) { > - err = PTR_ERR(skb); > - BT_ERR("%s: Failed to read version of ROME (%d)", hdev->name, > - err); > - return err; > - } > - > - if (skb->len != sizeof(*edl) + sizeof(*ver)) { > - BT_ERR("%s: Version size mismatch len %d", hdev->name, > - skb->len); > - err = -EILSEQ; > - goto out; > - } > - > - edl = (struct edl_event_hdr *)(skb->data); > - if (!edl) { > - BT_ERR("%s: TLV with no header", hdev->name); > - err = -EILSEQ; > - goto out; > - } > - > - if (edl->cresp != EDL_CMD_REQ_RES_EVT || > - edl->rtype != EDL_APP_VER_RES_EVT) { > - BT_ERR("%s: Wrong packet received %d %d", hdev->name, > - edl->cresp, edl->rtype); > - err = -EIO; > - goto out; > - } > - > - ver = (struct rome_version *)(edl->data); > - > - BT_DBG("%s: Product:0x%08x", hdev->name, le32_to_cpu(ver->product_id)); > - BT_DBG("%s: Patch :0x%08x", hdev->name, le16_to_cpu(ver->patch_ver)); > - BT_DBG("%s: ROM :0x%08x", hdev->name, le16_to_cpu(ver->rome_ver)); > - BT_DBG("%s: SOC :0x%08x", hdev->name, le32_to_cpu(ver->soc_id)); > - > - /* ROME chipset version can be decided by patch and SoC > - * version, combination with upper 2 bytes from SoC > - * and lower 2 bytes from patch will be used. > - */ > - *rome_version = (le32_to_cpu(ver->soc_id) << 16) | > - (le16_to_cpu(ver->rome_ver) & 0x0000ffff); > - > -out: > - kfree_skb(skb); > - > - return err; > -} > - > -static int rome_reset(struct hci_dev *hdev) > +static int qca_btsoc_reset(struct hci_dev *hdev) > { > struct sk_buff *skb; > int err; > > - BT_DBG("%s: ROME HCI_RESET", hdev->name); > + BT_DBG("%s: ROME/wcn3990 HCI_RESET", hdev->name); > > skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT); > if (IS_ERR(skb)) { > @@ -108,7 +44,7 @@ static int rome_reset(struct hci_dev *hdev) > return 0; > } > > -static void rome_tlv_check_data(struct rome_config *config, > +static void rome_tlv_check_data(struct qca_config *config, > const struct firmware *fw) > { > const u8 *data; > @@ -194,8 +130,121 @@ static void rome_tlv_check_data(struct rome_config *config, > } > } > > -static int rome_tlv_send_segment(struct hci_dev *hdev, int idx, int seg_size, > - const u8 *data) > +static void cherokee_tlv_check_data(struct qca_config *config, > + const struct firmware *fw, bool *dwnd_flag) > +{ > + const u8 *data; > + u32 type_len; > + u16 tag_id, tag_len; > + int idx, length; > + struct tlv_type_hdr *tlv; > + struct cherokee_tlv_type_patch *tlv_patch; > + struct tlv_type_nvm *tlv_nvm; > + > + tlv = (struct tlv_type_hdr *)fw->data; > + > + type_len = le32_to_cpu(tlv->type_len); > + length = (type_len >> 8) & 0x00ffffff; > + > + BT_DBG("TLV Type\t\t : 0x%x", type_len & 0x000000ff); > + BT_DBG("Length\t\t : %d bytes", length); > + > + switch (config->type) { > + case TLV_TYPE_PATCH: > + tlv_patch = (struct tlv_type_patch *)tlv->data; > + BT_DBG("Total Length\t\t : %d bytes", > + le32_to_cpu(tlv_patch->total_size)); > + BT_DBG("Patch Data Length\t : %d bytes", > + le32_to_cpu(tlv_patch->data_length)); > + BT_DBG("Signing Format Version : 0x%x", > + tlv_patch->format_version); > + BT_DBG("Signature Algorithm\t : 0x%x", > + tlv_patch->signature); > + BT_DBG("Download flag\t : 0x%x", > + tlv_patch->dwnd_cfg); > + BT_DBG("Reserved\t\t : 0x%x", > + le16_to_cpu(tlv_patch->reserved1)); > + BT_DBG("Product ID\t\t : 0x%04x", > + le16_to_cpu(tlv_patch->product_id)); > + BT_DBG("Rom Build Version\t : 0x%04x", > + le16_to_cpu(tlv_patch->rom_build)); > + BT_DBG("Patch Version\t\t : 0x%04x", > + le16_to_cpu(tlv_patch->patch_version)); > + BT_DBG("Reserved\t\t : 0x%x", > + le16_to_cpu(tlv_patch->reserved2)); > + BT_DBG("Patch Entry Address\t : 0x%x", > + le32_to_cpu(tlv_patch->entry)); > + /* If the download flag is 0x03, don't wait > + * for response from soc,i.e. 1 to n-1 > + * segment download. > + */ > + if (tlv_patch->dwnd_cfg == 0x03) > + *dwnd_flag = false; > + else > + *dwnd_flag = true; > + break; > + > + case TLV_TYPE_NVM: > + idx = 0; > + data = tlv->data; > + while (idx < length) { > + tlv_nvm = (struct tlv_type_nvm *)(data + idx); > + tag_id = le16_to_cpu(tlv_nvm->tag_id); > + tag_len = le16_to_cpu(tlv_nvm->tag_len); > + > + /* Update NVM tags as needed */ > + switch (tag_id) { > + case EDL_TAG_ID_HCI: > + /* HCI transport layer parameters > + * enabling software inband sleep > + * onto controller side. > + */ > + tlv_nvm->data[0] |= 0x80; > + > + /* UART Baud Rate */ > + tlv_nvm->data[2] = config->user_baud_rate; > + break; > + case EDL_TAG_ID_DEEP_SLEEP: > + /* Sleep enable mask > + * enabling deep sleep feature on controller. > + */ > + tlv_nvm->data[0] |= 0x01; > + break; > + } > + > + idx += (sizeof(u16) + sizeof(u16) + 8 + tag_len); > + } > + break; > + default: > + BT_ERR("Unknown TLV type %d", config->type); > + break; > + } > +} > + > +static int cherokee_tlv_send_segment(struct hci_dev *hdev, int idx, > + int seg_size, const u8 *data) > +{ > + u8 cmd[MAX_SIZE_PER_TLV_SEGMENT + 2]; > + int err = 0; > + > + BT_DBG("%s: Download segment #%d size %d", hdev->name, idx, seg_size); > + > + cmd[0] = EDL_PATCH_TLV_REQ_CMD; > + cmd[1] = seg_size; > + memcpy(cmd + 2, data, seg_size); > + > + err = __hci_cmd_no_event(hdev, EDL_PATCH_CMD_OPCODE, seg_size + 2, > + cmd); > + if (err < 0) { > + BT_ERR("%s: Failed to send TLV segment (%d)", hdev->name, err); > + return err; > + } > + > + return err; > +} > + > +static int qca_btsoc_tlv_send_segment(struct hci_dev *hdev, int idx, > + int seg_size, const u8 *data) > { > struct sk_buff *skb; > struct edl_event_hdr *edl; > @@ -264,16 +313,56 @@ static int rome_tlv_download_request(struct hci_dev *hdev, > data = fw->data; > for (i = 0; i < total_segment; i++) { > buffer = data + i * MAX_SIZE_PER_TLV_SEGMENT; > - ret = rome_tlv_send_segment(hdev, i, MAX_SIZE_PER_TLV_SEGMENT, > - buffer); > + ret = qca_btsoc_tlv_send_segment(hdev, i, > + MAX_SIZE_PER_TLV_SEGMENT, buffer); > if (ret < 0) > return -EIO; > } > > if (remain_size) { > buffer = data + total_segment * MAX_SIZE_PER_TLV_SEGMENT; > - ret = rome_tlv_send_segment(hdev, total_segment, remain_size, > - buffer); > + ret = qca_btsoc_tlv_send_segment(hdev, total_segment, > + remain_size, buffer); > + if (ret < 0) > + return -EIO; > + } > + > + return 0; > +} > +static int cherokee_tlv_download_request(struct hci_dev *hdev, > + const struct firmware *fw) > +{ > + const u8 *buffer, *data; > + int total_segment, remain_size; > + int ret, i; > + > + if (!fw || !fw->data) > + return -EINVAL; > + > + total_segment = fw->size / MAX_SIZE_PER_TLV_SEGMENT; > + remain_size = fw->size % MAX_SIZE_PER_TLV_SEGMENT; > + > + BT_DBG("%s: Total segment num %d remain size %d total size %zu", > + hdev->name, total_segment, remain_size, fw->size); > + > + data = fw->data; > + for (i = 0; i < total_segment; i++) { > + buffer = data + i * MAX_SIZE_PER_TLV_SEGMENT; > + /* Read response from soc for last segment sent */ > + if (!remain_size && ((i+1) == total_segment)) > + ret = qca_btsoc_tlv_send_segment(hdev, i, > + MAX_SIZE_PER_TLV_SEGMENT, buffer); > + else > + ret = cherokee_tlv_send_segment(hdev, i, > + MAX_SIZE_PER_TLV_SEGMENT, buffer); > + if (ret < 0) > + return -EIO; > + } > + > + if (remain_size) { > + buffer = data + total_segment * MAX_SIZE_PER_TLV_SEGMENT; > + ret = qca_btsoc_tlv_send_segment(hdev, total_segment, > + remain_size, buffer); > if (ret < 0) > return -EIO; > } > @@ -282,7 +371,7 @@ static int rome_tlv_download_request(struct hci_dev *hdev, > } > > static int rome_download_firmware(struct hci_dev *hdev, > - struct rome_config *config) > + struct qca_config *config) > { > const struct firmware *fw; > int ret; > @@ -309,7 +398,46 @@ static int rome_download_firmware(struct hci_dev *hdev, > return ret; > } > > -int qca_set_bdaddr_rome(struct hci_dev *hdev, const bdaddr_t *bdaddr) > +static int cherokee_download_firmware(struct hci_dev *hdev, > + struct qca_config *config) > +{ > + const struct firmware *fw; > + bool dwnd_flag = true; > + int ret; > + > + BT_INFO("%s: wcn3990 Downloading %s", hdev->name, config->fwname); > + > + ret = request_firmware(&fw, config->fwname, &hdev->dev); > + if (ret) { > + BT_ERR("%s: Failed to request file: %s (%d)", hdev->name, > + config->fwname, ret); > + return ret; > + } > + > + cherokee_tlv_check_data(config, fw, &dwnd_flag); > + /* check whether the download flag is set.if bit is enabled > + * terminate the ram patch download. As we are not supporting, > + * receiving of response from soc for every segment sent. > + * We look for response from soc for last segment. > + */ > + if (dwnd_flag == true && config->type == TLV_TYPE_PATCH) { > + BT_ERR("%s: btsoc download flag enabled", hdev->name); > + return -EOPNOTSUPP; > + } > + > + ret = cherokee_tlv_download_request(hdev, fw); > + if (ret) { > + BT_ERR("%s: Failed to download file: %s (%d)", hdev->name, > + config->fwname, ret); > + } > + > + release_firmware(fw); > + > + return ret; > +} > + > + > +int qca_btsoc_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr) > { > struct sk_buff *skb; > u8 cmd[9]; > @@ -332,12 +460,12 @@ int qca_set_bdaddr_rome(struct hci_dev *hdev, const bdaddr_t *bdaddr) > > return 0; > } > -EXPORT_SYMBOL_GPL(qca_set_bdaddr_rome); > +EXPORT_SYMBOL_GPL(qca_btsoc_set_bdaddr); > > int qca_uart_setup_rome(struct hci_dev *hdev, uint8_t baudrate) > { > u32 rome_ver = 0; > - struct rome_config config; > + struct qca_config config; > int err; > > BT_DBG("%s: ROME setup on UART", hdev->name); > @@ -345,7 +473,7 @@ int qca_uart_setup_rome(struct hci_dev *hdev, uint8_t baudrate) > config.user_baud_rate = baudrate; > > /* Get ROME version information */ > - err = rome_patch_ver_req(hdev, &rome_ver); > + err = qca_btsoc_patch_ver_req(hdev, &rome_ver); > if (err < 0 || rome_ver == 0) { > BT_ERR("%s: Failed to get version 0x%x", hdev->name, err); > return err; > @@ -374,7 +502,7 @@ int qca_uart_setup_rome(struct hci_dev *hdev, uint8_t baudrate) > } > > /* Perform HCI reset */ > - err = rome_reset(hdev); > + err = qca_btsoc_reset(hdev); > if (err < 0) { > BT_ERR("%s: Failed to run HCI_RESET (%d)", hdev->name, err); > return err; > @@ -386,6 +514,111 @@ int qca_uart_setup_rome(struct hci_dev *hdev, uint8_t baudrate) > } > EXPORT_SYMBOL_GPL(qca_uart_setup_rome); > > +int qca_btsoc_patch_ver_req(struct hci_dev *hdev, u32 *soc_version) > +{ > + struct sk_buff *skb; > + struct edl_event_hdr *edl; > + struct qca_btsoc_version *ver; > + char cmd; > + int err = 0; > + > + BT_DBG("%s: BTSOC Patch Version Request", hdev->name); > + > + cmd = EDL_PATCH_VER_REQ_CMD; > + skb = __hci_cmd_sync_ev(hdev, EDL_PATCH_CMD_OPCODE, EDL_PATCH_CMD_LEN, > + &cmd, HCI_VENDOR_PKT, HCI_INIT_TIMEOUT); > + if (IS_ERR(skb)) { > + err = PTR_ERR(skb); > + BT_ERR("%s: Failed to read version of ROME (%d)", hdev->name, > + err); > + return err; > + } > + > + if (skb->len != sizeof(*edl) + sizeof(*ver)) { > + BT_ERR("%s: Version size mismatch len %d", hdev->name, > + skb->len); > + err = -EILSEQ; > + goto out; > + } > + > + edl = (struct edl_event_hdr *)(skb->data); > + if (!edl) { > + BT_ERR("%s: TLV with no header", hdev->name); > + err = -EILSEQ; > + goto out; > + } > + > + if (edl->cresp != EDL_CMD_REQ_RES_EVT || > + edl->rtype != EDL_APP_VER_RES_EVT) { > + BT_ERR("%s: Wrong packet received %d %d", hdev->name, > + edl->cresp, edl->rtype); > + err = -EIO; > + goto out; > + } > + > + ver = (struct qca_btsoc_version *)(edl->data); > + > + BT_DBG("%s: Product:0x%08x", hdev->name, le32_to_cpu(ver->product_id)); > + BT_DBG("%s: Patch :0x%08x", hdev->name, le16_to_cpu(ver->patch_ver)); > + BT_DBG("%s: ROM :0x%08x", hdev->name, le16_to_cpu(ver->btsoc_ver)); > + BT_DBG("%s: SOC :0x%08x", hdev->name, le32_to_cpu(ver->soc_id)); > + > + /* BTSOC chipset version can be decided by patch and SoC > + * version, combination with upper 2 bytes from SoC > + * and lower 2 bytes from patch will be used. > + */ > + *soc_version = (le32_to_cpu(ver->soc_id) << 16) | > + (le16_to_cpu(ver->btsoc_ver) & 0x0000ffff); > + > +out: > + kfree_skb(skb); > + > + return err; > +} > +EXPORT_SYMBOL_GPL(qca_btsoc_patch_ver_req); > + > +int qca_uart_setup_cherokee(struct hci_dev *hdev, uint8_t baudrate, > + u32 *cherokee_ver) > +{ > + struct qca_config config; > + int err; > + > + BT_DBG("%s: wcn3990 setup on UART", hdev->name); > + config.user_baud_rate = baudrate; > + > + /* Download rampatch file */ > + config.type = TLV_TYPE_PATCH; > + snprintf(config.fwname, sizeof(config.fwname), "qca/rampatch_%08x.tlv", > + *cherokee_ver); > + err = cherokee_download_firmware(hdev, &config); > + if (err < 0) { > + BT_ERR("%s: Failed to download patch (%d)", hdev->name, err); > + return err; > + } > + > + /* Download NVM configuration */ > + config.type = TLV_TYPE_NVM; > + snprintf(config.fwname, sizeof(config.fwname), "qca/nvm_%08x.bin", > + *cherokee_ver); > + err = cherokee_download_firmware(hdev, &config); > + if (err < 0) { > + BT_ERR("%s: Failed to download NVM (%d)", hdev->name, err); > + return err; > + } > + > + /* Perform HCI reset */ > + err = qca_btsoc_reset(hdev); > + if (err < 0) { > + BT_ERR("%s: Failed to run HCI_RESET (%d)", hdev->name, err); > + return err; > + } > + > + BT_INFO("%s: wcn3990 setup on UART is completed", hdev->name); > + > + return 0; > +} > +EXPORT_SYMBOL_GPL(qca_uart_setup_cherokee); > + > MODULE_AUTHOR("Ben Young Tae Kim <ytkim@qca.qualcomm.com>"); > MODULE_DESCRIPTION("Bluetooth support for Qualcomm Atheros family ver " VERSION); > MODULE_VERSION(VERSION); > diff --git a/drivers/bluetooth/btqca.h b/drivers/bluetooth/btqca.h > index 65e994b..7608423 100644 > --- a/drivers/bluetooth/btqca.h > +++ b/drivers/bluetooth/btqca.h > @@ -1,7 +1,7 @@ > /* > * Bluetooth supports for Qualcomm Atheros ROME chips > * > - * Copyright (c) 2015 The Linux Foundation. All rights reserved. > + * Copyright (c) 2015, 2018 The Linux Foundation. All rights reserved. > * > * This program is free software; you can redistribute it and/or modify > * it under the terms of the GNU General Public License version 2 > @@ -37,6 +37,9 @@ > #define EDL_TAG_ID_HCI (17) > #define EDL_TAG_ID_DEEP_SLEEP (27) > > +#define CHEROKEE_POWERON_PULSE (0xFC) > +#define CHEROKEE_POWEROFF_PULSE (0xC0) > + > enum qca_bardrate { > QCA_BAUDRATE_115200 = 0, > QCA_BAUDRATE_57600, > @@ -66,7 +69,7 @@ enum rome_tlv_type { > TLV_TYPE_NVM > }; > > -struct rome_config { > +struct qca_config { > u8 type; > char fwname[64]; > uint8_t user_baud_rate; > @@ -78,13 +81,14 @@ struct edl_event_hdr { > __u8 data[0]; > } __packed; > > -struct rome_version { > +struct qca_btsoc_version { > __le32 product_id; > __le16 patch_ver; > - __le16 rome_ver; > + __le16 btsoc_ver; > __le32 soc_id; > } __packed; > > + > struct tlv_seg_resp { > __u8 result; > } __packed; > @@ -102,6 +106,21 @@ struct tlv_type_patch { > __le32 entry; > } __packed; > > +struct cherokee_tlv_type_patch { > + __le32 total_size; > + __le32 data_length; > + __u8 format_version; > + __u8 signature; > + __u8 dwnd_cfg; > + __le16 reserved1; > + __le16 product_id; > + __le16 rom_build; > + __le16 patch_version; > + __le16 reserved2; > + __le32 entry; > +} __packed; > + > + > struct tlv_type_nvm { > __le16 tag_id; > __le16 tag_len; > @@ -115,14 +134,21 @@ struct tlv_type_hdr { > __u8 data[0]; > } __packed; > > +int qca_btsoc_cleanup(struct hci_dev *hdev); > +int btqca_power_setup(bool on); > + > #if IS_ENABLED(CONFIG_BT_QCA) > > -int qca_set_bdaddr_rome(struct hci_dev *hdev, const bdaddr_t *bdaddr); > +int qca_btsoc_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr); > int qca_uart_setup_rome(struct hci_dev *hdev, uint8_t baudrate); > +int qca_uart_setup_cherokee(struct hci_dev *hdev, uint8_t baudrate, > + u32 *cherokee_ver); > +int qca_btsoc_patch_ver_req(struct hci_dev *hdev, u32 *soc_version); > > #else > > -static inline int qca_set_bdaddr_rome(struct hci_dev *hdev, const bdaddr_t *bdaddr) > +static inline int qca_btsoc_set_bdaddr(struct hci_dev *hdev, > + const bdaddr_t *bdaddr) > { > return -EOPNOTSUPP; > } > @@ -132,4 +158,15 @@ static inline int qca_uart_setup_rome(struct hci_dev *hdev, int speed) > return -EOPNOTSUPP; > } > > +static inline int qca_uart_setup_cherokee(struct hci_dev *hdev, > + uint8_t baudrate, u32 *cherokee_ver) > +{ > + return -EOPNOTSUPP; > +} > + > +static int qca_btsoc_patch_ver_req(struct hci_dev *hdev, u32 *cherokee_version) > +{ > + return -EOPNOTSUPP; > +} > + > #endif > diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c > index b7547e6..1343e63 100644 > --- a/drivers/bluetooth/hci_qca.c > +++ b/drivers/bluetooth/hci_qca.c > @@ -5,7 +5,7 @@ > * protocol extension to H4. > * > * Copyright (C) 2007 Texas Instruments, Inc. > - * Copyright (c) 2010, 2012 The Linux Foundation. All rights reserved. > + * Copyright (c) 2010, 2012, 2018 The Linux Foundation. All rights reserved. > * > * Acknowledgements: > * This file is based on hci_ll.c, which was... > @@ -35,9 +35,15 @@ > #include <linux/mod_devicetable.h> > #include <linux/module.h> > #include <linux/serdev.h> > - > #include <net/bluetooth/bluetooth.h> > #include <net/bluetooth/hci_core.h> > +#include <asm-generic/delay.h> > +#include <linux/tty.h> > +#include <linux/platform_device.h> > +#include <linux/delay.h> > +#include <linux/slab.h> > +#include <linux/regulator/consumer.h> > +#include <linux/of_device.h> > > #include "hci_uart.h" > #include "btqca.h" > @@ -84,12 +90,12 @@ struct qca_data { > struct hci_uart *hu; > struct sk_buff *rx_skb; > struct sk_buff_head txq; > - struct sk_buff_head tx_wait_q; /* HCI_IBS wait queue */ > - spinlock_t hci_ibs_lock; /* HCI_IBS state lock */ > - u8 tx_ibs_state; /* HCI_IBS transmit side power state*/ > - u8 rx_ibs_state; /* HCI_IBS receive side power state */ > - bool tx_vote; /* Clock must be on for TX */ > - bool rx_vote; /* Clock must be on for RX */ > + struct sk_buff_head tx_wait_q; /* HCI_IBS wait queue */ > + spinlock_t hci_ibs_lock; /* HCI_IBS state lock */ > + u8 tx_ibs_state; /* HCI_IBS transmit side power state*/ > + u8 rx_ibs_state; /* HCI_IBS receive side power state */ > + bool tx_vote; /* Clock must be on for TX */ > + bool rx_vote; /* Clock must be on for RX */ > struct timer_list tx_idle_timer; > u32 tx_idle_delay; > struct timer_list wake_retrans_timer; > @@ -119,12 +125,49 @@ struct qca_data { > u64 votes_off; > }; > > +enum btqca_soc_t { > + BTQCA_INVALID = -1, > + BTQCA_AR3002, > + BTQCA_ROME, > + BTQCA_CHEROKEE > +}; > + > struct qca_serdev { > struct hci_uart serdev_hu; > struct gpio_desc *bt_en; > struct clk *susclk; > + enum btqca_soc_t btsoc_type; > +}; > + > +/* > + * voltage regulator information required for configuring the > + * QCA bluetooth chipset > + */ > +struct btqca_vreg { > + const char *name; > + unsigned int min_v; > + unsigned int max_v; > + unsigned int load_ua; > }; > > +struct btqca_vreg_data { > + enum btqca_soc_t soc_type; > + struct btqca_vreg *vregs; > + size_t num_vregs; > +}; > + > +/* > + * Platform data for the QCA bluetooth power driver. > + */ > +struct btqca_power { > + struct device *dev; > + struct btqca_vreg_data *vreg_data; > + struct regulator_bulk_data *vreg_bulk; > + bool vreg_status; > +}; > + > +static struct btqca_power *qca; > + > static void __serial_clock_on(struct tty_struct *tty) > { > /* TODO: Some chipset requires to enable UART clock on client > @@ -462,9 +505,11 @@ static int qca_open(struct hci_uart *hu) > > if (hu->serdev) { > serdev_device_open(hu->serdev); > - > qcadev = serdev_device_get_drvdata(hu->serdev); > - gpiod_set_value_cansleep(qcadev->bt_en, 1); > + if (qcadev->btsoc_type == BTQCA_CHEROKEE) > + btqca_power_setup(true); > + else > + gpiod_set_value_cansleep(qcadev->bt_en, 1); > } > > BT_DBG("HCI_UART_QCA open, tx_idle_delay=%u, wake_retrans=%u", > @@ -553,7 +598,10 @@ static int qca_close(struct hci_uart *hu) > serdev_device_close(hu->serdev); > > qcadev = serdev_device_get_drvdata(hu->serdev); > - gpiod_set_value_cansleep(qcadev->bt_en, 0); > + if (qcadev->btsoc_type == BTQCA_CHEROKEE) > + qca_btsoc_cleanup(hu->hdev); > + else > + gpiod_set_value_cansleep(qcadev->bt_en, 0); > } > > kfree_skb(qca->rx_skb); > @@ -873,6 +921,8 @@ static uint8_t qca_get_baudrate_value(int speed) > return QCA_BAUDRATE_2000000; > case 3000000: > return QCA_BAUDRATE_3000000; > + case 3200000: > + return QCA_BAUDRATE_3200000; > case 3500000: > return QCA_BAUDRATE_3500000; > default: > @@ -887,7 +937,7 @@ static int qca_set_baudrate(struct hci_dev *hdev, uint8_t baudrate) > struct sk_buff *skb; > u8 cmd[] = { 0x01, 0x48, 0xFC, 0x01, 0x00 }; > > - if (baudrate > QCA_BAUDRATE_3000000) > + if (baudrate > QCA_BAUDRATE_3200000) > return -EINVAL; > > cmd[4] = baudrate; > @@ -924,62 +974,263 @@ static inline void host_set_baudrate(struct hci_uart *hu, unsigned int speed) > hci_uart_set_baudrate(hu, speed); > } > > +static int qca_send_poweron_cmd(struct hci_dev *hdev) > +{ > + struct hci_uart *hu = hci_get_drvdata(hdev); > + struct qca_data *qca = hu->priv; > + struct sk_buff *skb; > + u8 cmd; > + > + BT_DBG("%s sending power on command to btsoc", hdev->name); > + /* By sending 0xFC host is trying to power up the soc */ > + cmd = CHEROKEE_POWERON_PULSE; > + skb = bt_skb_alloc(sizeof(cmd), GFP_ATOMIC); > + if (!skb) { > + BT_ERR("Failed to allocate memory for skb packet"); > + return -ENOMEM; > + } > + > + skb_put_data(skb, &cmd, sizeof(cmd)); > + hci_skb_pkt_type(skb) = HCI_COMMAND_PKT; > + > + skb_queue_tail(&qca->txq, skb); > + hci_uart_tx_wakeup(hu); > + > + /* Wait for 100 us for soc to settle down */ > + set_current_state(TASK_UNINTERRUPTIBLE); > + schedule_timeout(usecs_to_jiffies(100)); > + set_current_state(TASK_INTERRUPTIBLE); > + > + return 0; > +} > + > +static int qca_send_poweroff_cmd(struct hci_dev *hdev) > +{ > + struct hci_uart *hu = hci_get_drvdata(hdev); > + struct qca_data *qca = hu->priv; > + struct sk_buff *skb; > + u8 cmd; > + > + BT_DBG("%s sending power off command to btsoc", hdev->name); > + /* By sending 0xC0 host is trying to power off the soc */ > + cmd = CHEROKEE_POWEROFF_PULSE; > + skb = bt_skb_alloc(sizeof(cmd), GFP_ATOMIC); > + if (!skb) { > + BT_ERR("Failed to allocate memory for skb packet"); > + return -ENOMEM; > + } > + > + skb_put_data(skb, &cmd, sizeof(cmd)); > + hci_skb_pkt_type(skb) = HCI_COMMAND_PKT; > + > + skb_queue_tail(&qca->txq, skb); > + hci_uart_tx_wakeup(hu); > + > + /* Wait for 100 us for soc to settle down */ > + set_current_state(TASK_UNINTERRUPTIBLE); > + schedule_timeout(usecs_to_jiffies(100)); > + set_current_state(TASK_INTERRUPTIBLE); > + > + return 0; > +} > + > +static int qca_serdev_open(struct hci_uart *hu) > +{ > + int ret = 0; > + > + if (hu->serdev) > + serdev_device_open(hu->serdev); > + else { > + BT_ERR("%s:open operation not supported", hu->hdev->name); > + ret = 1; > + } > + > + return ret; > +} > + > +int qca_btsoc_cleanup(struct hci_dev *hdev) > +{ > + struct hci_uart *hu = hci_get_drvdata(hdev); > + > + /* change host baud rate before sending power off command */ > + host_set_baudrate(hu, 2400); > + /* send 0xC0 command to btsoc before turning off regulators */ > + qca_send_poweroff_cmd(hdev); > + /* turn off btsoc */ > + return btqca_power_setup(false); > +} > + > +static int qca_serdev_close(struct hci_uart *hu) > +{ > + int ret = 0; > + > + if (hu->serdev) > + serdev_device_close(hu->serdev); > + else { > + BT_ERR("%s:close operation not supported", hu->hdev->name); > + ret = 1; > + } > + > + return ret; > +} > + > static int qca_setup(struct hci_uart *hu) > { > struct hci_dev *hdev = hu->hdev; > struct qca_data *qca = hu->priv; > + struct qca_serdev *qcadev; > unsigned int speed, qca_baudrate = QCA_BAUDRATE_115200; > int ret; > + unsigned int soc_ver; > + > + qcadev = serdev_device_get_drvdata(hu->serdev); > + > + switch (qcadev->btsoc_type) { > + case BTQCA_CHEROKEE: > + > + BT_INFO("%s:setting up wcn3990", hdev->name); > + /* Patch downloading has to be done without IBS mode */ > + clear_bit(STATE_IN_BAND_SLEEP_ENABLED, &qca->flags); > + /* Setup initial baudrate */ > + speed = 0; > + if (hu->init_speed) > + speed = hu->init_speed; > + else if (hu->proto->init_speed) > + speed = hu->proto->init_speed; > + > + if (speed) > + host_set_baudrate(hu, speed); > + else { > + BT_ERR("%s:initial speed %u", hdev->name, speed); > + return -1; > + } > > - BT_INFO("%s: ROME setup", hdev->name); > + /* clear flow control- for sync cmd*/ > + hci_uart_set_flow_control(hu, true); > + /* send poweron command to btsoc */ > + ret = qca_send_poweron_cmd(hdev); > + if (ret) { > + BT_ERR("%s:sending sync command failed", hdev->name); > + return ret; > + } > + > + /* close port */ > + ret = qca_serdev_close(hu); > + if (ret) > + return ret; > + /* reopen port */ > + ret = qca_serdev_open(hu); > + if (ret) > + return ret; > + /* Setup initial baudrate */ > + speed = 0; > + if (hu->init_speed) > + speed = hu->init_speed; > + else if (hu->proto->init_speed) > + speed = hu->proto->init_speed; > + if (speed) > + host_set_baudrate(hu, speed); > + else { > + BT_ERR("%s:initial speed %u", hdev->name, speed); > + return -1; > + } > > - /* Patch downloading has to be done without IBS mode */ > - clear_bit(STATE_IN_BAND_SLEEP_ENABLED, &qca->flags); > + /* Enable flow control */ > + hci_uart_set_flow_control(hu, false); > + /* wait until flow control settled */ > + mdelay(100); > > - /* Setup initial baudrate */ > - speed = 0; > - if (hu->init_speed) > - speed = hu->init_speed; > - else if (hu->proto->init_speed) > - speed = hu->proto->init_speed; > + ret = qca_btsoc_patch_ver_req(hdev, &soc_ver); > + if (ret < 0 || soc_ver == 0) { > + BT_ERR("%s: Failed to get version 0x%x", hdev->name, > + ret); > + return ret; > + } > > - if (speed) > - host_set_baudrate(hu, speed); > + BT_INFO("%s:wcn3990 controller version 0x%08x", hdev->name, > + soc_ver); > > - /* Setup user speed if needed */ > - speed = 0; > - if (hu->oper_speed) > - speed = hu->oper_speed; > - else if (hu->proto->oper_speed) > + /* clear flow control */ > + hci_uart_set_flow_control(hu, true); > + /* set operating speed */ > speed = hu->proto->oper_speed; > + if (speed) { > + qca_baudrate = qca_get_baudrate_value(speed); > + BT_INFO("%s: Set UART speed to %d", hdev->name, speed); > + ret = qca_set_baudrate(hdev, qca_baudrate); > + if (ret) { > + BT_ERR("%s:Failed to change the baud rate(%d)", > + hdev->name, ret); > + return ret; > + } > + if (speed) > + host_set_baudrate(hu, speed); > + else { > + BT_ERR("%s:Error in setting operator speed:%u", > + hdev->name, speed); > + return -1; > + } > + } > > - if (speed) { > - qca_baudrate = qca_get_baudrate_value(speed); > + /* Set flow control */ > + hci_uart_set_flow_control(hu, false); > + /*Setup patch and NVM configurations */ > + ret = qca_uart_setup_cherokee(hdev, qca_baudrate, &soc_ver); > + if (!ret) { > + set_bit(STATE_IN_BAND_SLEEP_ENABLED, &qca->flags); > + qca_debugfs_init(hdev); > + } > > - BT_INFO("%s: Set UART speed to %d", hdev->name, speed); > - ret = qca_set_baudrate(hdev, qca_baudrate); > - if (ret) { > - BT_ERR("%s: Failed to change the baud rate (%d)", > - hdev->name, ret); > - return ret; > + /* Setup wcn3990 bdaddr */ > + hu->hdev->set_bdaddr = qca_btsoc_set_bdaddr; > + > + return ret; > + > + default: > + BT_INFO("%s: ROME setup", hdev->name); > + /* Patch downloading has to be done without IBS mode */ > + clear_bit(STATE_IN_BAND_SLEEP_ENABLED, &qca->flags); > + /* Setup initial baudrate */ > + speed = 0; > + if (hu->init_speed) > + speed = hu->init_speed; > + else if (hu->proto->init_speed) > + speed = hu->proto->init_speed; > + if (speed) > + hci_uart_set_baudrate(hu, speed); > + > + /* Setup user speed if needed */ > + speed = 0; > + if (hu->oper_speed) > + speed = hu->oper_speed; > + else if (hu->proto->oper_speed) > + speed = hu->proto->oper_speed; > + > + if (speed) { > + qca_baudrate = qca_get_baudrate_value(speed); > + > + BT_INFO("%s: Set UART speed to %d", hdev->name, speed); > + ret = qca_set_baudrate(hdev, qca_baudrate); > + if (ret) { > + BT_ERR("%s:Failed to change the baud rate(%d)", > + hdev->name, ret); > + return ret; > + } > + host_set_baudrate(hu, speed); > } > - host_set_baudrate(hu, speed); > - } > > - /* Setup patch / NVM configurations */ > - ret = qca_uart_setup_rome(hdev, qca_baudrate); > - if (!ret) { > - set_bit(STATE_IN_BAND_SLEEP_ENABLED, &qca->flags); > - qca_debugfs_init(hdev); > - } else if (ret == -ENOENT) { > - /* No patch/nvm-config found, run with original fw/config */ > - ret = 0; > - } > + /* Setup patch / NVM configurations */ > + ret = qca_uart_setup_rome(hdev, qca_baudrate); > + if (!ret) { > + set_bit(STATE_IN_BAND_SLEEP_ENABLED, &qca->flags); > + qca_debugfs_init(hdev); > + } > > - /* Setup bdaddr */ > - hu->hdev->set_bdaddr = qca_set_bdaddr_rome; > + /* Setup bdaddr */ > + hu->hdev->set_bdaddr = qca_btsoc_set_bdaddr; > > - return ret; > + return ret; > + } > } > > static struct hci_uart_proto qca_proto = { > @@ -997,42 +1248,142 @@ static int qca_setup(struct hci_uart *hu) > .dequeue = qca_dequeue, > }; > > +static const struct btqca_vreg_data cherokee_data = { > + .soc_type = BTQCA_CHEROKEE, > + .vregs = (struct btqca_vreg []) { > + { "vddio", 1352000, 1352000, 0 }, > + { "vddxtal", 1904000, 2040000, 0 }, > + { "vddcore", 1800000, 1800000, 1 }, > + { "vddpa", 130400, 1304000, 1 }, > + { "vddldo", 3000000, 3312000, 1 }, > + { "vddpwd", 3312000, 3600000, 0 }, > + }, > + .num_vregs = 6, > +}; > + > +int btqca_power_setup(bool on) > +{ > + int ret = 0; > + int i; > + struct btqca_vreg *vregs; > + > + if (!qca || !qca->vreg_data || !qca->vreg_bulk) > + return -EINVAL; > + vregs = qca->vreg_data->vregs; > + > + BT_DBG("on: %d", on); > + > + /* turn on if regualtors are off */ > + if (on == true && qca->vreg_status == false) { > + qca->vreg_status = true; > + for (i = 0; i < qca->vreg_data->num_vregs; i++) { > + regulator_set_voltage(qca->vreg_bulk[i].consumer, > + vregs[i].min_v, > + vregs[i].max_v); > + > + if (vregs[i].load_ua) > + regulator_set_load(qca->vreg_bulk[i].consumer, > + vregs[i].load_ua); > + > + regulator_enable(qca->vreg_bulk[i].consumer); > + } > + } else if (on == false && qca->vreg_status == true) { > + qca->vreg_status = false; > + for (i = 0; i < qca->vreg_data->num_vregs; i++) { > + regulator_disable(qca->vreg_bulk[i].consumer); > + regulator_set_voltage(qca->vreg_bulk[i].consumer, > + 0, vregs[i].max_v); > + if (vregs[i].load_ua) > + regulator_set_load(qca->vreg_bulk[i].consumer, > + 0); > + } > + } > + > + return ret; > +} > + > +static int init_regulators(struct btqca_power *qca, > + const struct btqca_vreg *vregs, > + size_t num_vregs) > +{ > + int i; > + > + qca->vreg_bulk = devm_kzalloc(qca->dev, num_vregs * > + sizeof(struct regulator_bulk_data), > + GFP_KERNEL); > + if (!qca->vreg_bulk) > + return -ENOMEM; > + > + for (i = 0; i < num_vregs; i++) > + qca->vreg_bulk[i].supply = vregs[i].name; > + > + return devm_regulator_bulk_get(qca->dev, num_vregs, qca->vreg_bulk); > +} > + > static int qca_serdev_probe(struct serdev_device *serdev) > { > struct qca_serdev *qcadev; > - int err; > + struct btqca_vreg_data *data; > + int err = 0; > > qcadev = devm_kzalloc(&serdev->dev, sizeof(*qcadev), GFP_KERNEL); > if (!qcadev) > return -ENOMEM; > > qcadev->serdev_hu.serdev = serdev; > + data = of_device_get_match_data(&serdev->dev); > + if (data && data->soc_type == BTQCA_CHEROKEE) > + qcadev->btsoc_type = BTQCA_CHEROKEE; > + else > + qcadev->btsoc_type = BTQCA_ROME; > + > serdev_device_set_drvdata(serdev, qcadev); > + if (qcadev->btsoc_type == BTQCA_CHEROKEE) { > + qca = kzalloc(sizeof(struct btqca_power), GFP_KERNEL); > + if (!qca) > + return -ENOMEM; > + > + qca->dev = &serdev->dev; > + qca->vreg_data = data; > + err = init_regulators(qca, data->vregs, data->num_vregs); > + if (err) { > + BT_ERR("Failed to init regualtors:%d", err); > + kfree(qca); > + } > > - qcadev->bt_en = devm_gpiod_get(&serdev->dev, "enable", > - GPIOD_OUT_LOW); > - if (IS_ERR(qcadev->bt_en)) { > - dev_err(&serdev->dev, "failed to acquire enable gpio\n"); > - return PTR_ERR(qcadev->bt_en); > - } > + /* set voltage regulator status as false */ > + qca->vreg_status = false; > + err = hci_uart_register_device(&qcadev->serdev_hu, &qca_proto); > + if (err) { > + BT_ERR("wcn3990 serdev registration failed"); > + kfree(qca); > + } > + } else { > + qcadev->bt_en = devm_gpiod_get(&serdev->dev, "enable", > + GPIOD_OUT_LOW); > + if (IS_ERR(qcadev->bt_en)) { > + dev_err(&serdev->dev, "failed to acquire enable gpio\n"); > + return PTR_ERR(qcadev->bt_en); > + } > > - qcadev->susclk = devm_clk_get(&serdev->dev, NULL); > - if (IS_ERR(qcadev->susclk)) { > - dev_err(&serdev->dev, "failed to acquire clk\n"); > - return PTR_ERR(qcadev->susclk); > - } > + qcadev->susclk = devm_clk_get(&serdev->dev, NULL); > + if (IS_ERR(qcadev->susclk)) { > + dev_err(&serdev->dev, "failed to acquire clk\n"); > + return PTR_ERR(qcadev->susclk); > + } > > - err = clk_set_rate(qcadev->susclk, SUSCLK_RATE_32KHZ); > - if (err) > - return err; > + err = clk_set_rate(qcadev->susclk, SUSCLK_RATE_32KHZ); > + if (err) > + return err; > > - err = clk_prepare_enable(qcadev->susclk); > - if (err) > - return err; > + err = clk_prepare_enable(qcadev->susclk); > + if (err) > + return err; > > - err = hci_uart_register_device(&qcadev->serdev_hu, &qca_proto); > - if (err) > - clk_disable_unprepare(qcadev->susclk); > + err = hci_uart_register_device(&qcadev->serdev_hu, &qca_proto); > + if (err) > + clk_disable_unprepare(qcadev->susclk); > + } > > return err; > } > @@ -1042,12 +1393,16 @@ static void qca_serdev_remove(struct serdev_device *serdev) > struct qca_serdev *qcadev = serdev_device_get_drvdata(serdev); > > hci_uart_unregister_device(&qcadev->serdev_hu); > - > - clk_disable_unprepare(qcadev->susclk); > + if (qcadev->btsoc_type == BTQCA_CHEROKEE) { > + btqca_power_setup(false); > + kfree(qca); > + } else > + clk_disable_unprepare(qcadev->susclk); > } > > static const struct of_device_id qca_bluetooth_of_match[] = { > { .compatible = "qcom,qca6174-bt" }, > + { .compatible = "qca,wcn3990-bt", .data = &cherokee_data}, > { /* sentinel */ } > }; > MODULE_DEVICE_TABLE(of, qca_bluetooth_of_match); > diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h > index c10cb79..b932ec50 100644 > --- a/include/net/bluetooth/hci_core.h > +++ b/include/net/bluetooth/hci_core.h > @@ -1418,7 +1418,8 @@ struct sk_buff *__hci_cmd_sync(struct hci_dev *hdev, u16 opcode, u32 plen, > const void *param, u32 timeout); > struct sk_buff *__hci_cmd_sync_ev(struct hci_dev *hdev, u16 opcode, u32 plen, > const void *param, u8 event, u32 timeout); > - > +int __hci_cmd_no_event(struct hci_dev *hdev, u16 opcode, u32 plen, > + const void *param); > int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen, > const void *param); > void hci_send_acl(struct hci_chan *chan, struct sk_buff *skb, __u16 flags); > diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c > index 97ef85e..8addd97 100644 > --- a/net/bluetooth/hci_core.c > +++ b/net/bluetooth/hci_core.c > @@ -3370,7 +3370,7 @@ int hci_unregister_cb(struct hci_cb *cb) > } > EXPORT_SYMBOL(hci_unregister_cb); > > -static void hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb) > +void hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb) > { > int err; > > diff --git a/net/bluetooth/hci_request.c b/net/bluetooth/hci_request.c > index f7d6ba6..1ffee59a 100644 > --- a/net/bluetooth/hci_request.c > +++ b/net/bluetooth/hci_request.c > @@ -114,6 +114,29 @@ void hci_req_sync_cancel(struct hci_dev *hdev, int err) > } > } > > +/* Queue a frame to an asynchronous transfer to btdevice, > + * with out any event from btdevice. > + */ > +int __hci_cmd_no_event(struct hci_dev *hdev, u16 opcode, u32 plen, > + const void *param) > +{ > + struct sk_buff *skb; > + > + BT_DBG("%s opcode 0x%4.4x plen %d", hdev->name, opcode, plen); > + > + skb = hci_prepare_cmd(hdev, opcode, plen, param); > + if (!skb) { > + BT_ERR("%s no memory for command (opcode 0x%4.4x)", > + hdev->name, opcode); > + return -ENOMEM; > + } > + > + hci_send_frame(hdev, skb); > + > + return 0; > +} > +EXPORT_SYMBOL(__hci_cmd_no_event); > + > struct sk_buff *__hci_cmd_sync_ev(struct hci_dev *hdev, u16 opcode, u32 plen, > const void *param, u8 event, u32 timeout) > { > diff --git a/net/bluetooth/hci_request.h b/net/bluetooth/hci_request.h > index dde77bd..a855edd 100644 > --- a/net/bluetooth/hci_request.h > +++ b/net/bluetooth/hci_request.h > @@ -128,3 +128,5 @@ static inline u16 eir_append_le16(u8 *eir, u16 eir_len, u8 type, u16 data) > > return eir_len; > } > + > +void hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb); > -- To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Hi Thierry, On 2018-04-20 21:41, Thierry Escande wrote: > Hi Balakrishna, > > On 19/04/2018 15:34, Balakrishna Godavarthi wrote: >> - Add support to set voltage/current of various regualtors >> to power up/down BT QCA chip wcn3990. >> - Add firmware download support for BT QCA chip wcn3990. >> >> Signed-off-by: Balakrishna Godavarthi <bgodavar@codeaurora.org> >> --- >> drivers/bluetooth/btqca.c | 393 >> +++++++++++++++++++++++------- >> drivers/bluetooth/btqca.h | 49 +++- >> drivers/bluetooth/hci_qca.c | 501 >> +++++++++++++++++++++++++++++++++------ >> include/net/bluetooth/hci_core.h | 3 +- >> net/bluetooth/hci_core.c | 2 +- >> net/bluetooth/hci_request.c | 23 ++ >> net/bluetooth/hci_request.h | 2 + >> 7 files changed, 812 insertions(+), 161 deletions(-) > > This is a pretty big patch. You should at least put the renaming of > the rome_* functions and structures into a separate patch. yes this will be big patch as we have added support for qualcomm new bluetooth chip. In this we have changed existing function names rome_* to reuse it for new bluetooth chip. I feel we will not face any merge conflicts for btqca.c file. if we put rome_* function and structures into new patch, this make create merge conflicts for hci_qca.c file. > > Also there are some coding style issues. You can use > ./scripts/checkpatch.pl to check and fix them. > will double check the coding style issues. > Regards, > Thierry > >> >> diff --git a/drivers/bluetooth/btqca.c b/drivers/bluetooth/btqca.c >> index 0bbdfce..5b47df8 100644 >> --- a/drivers/bluetooth/btqca.c >> +++ b/drivers/bluetooth/btqca.c >> @@ -1,7 +1,7 @@ >> /* >> * Bluetooth supports for Qualcomm Atheros chips >> * >> - * Copyright (c) 2015 The Linux Foundation. All rights reserved. >> + * Copyright (c) 2015, 2018 The Linux Foundation. All rights >> reserved. >> * >> * This program is free software; you can redistribute it and/or >> modify >> * it under the terms of the GNU General Public License version 2 >> @@ -19,82 +19,18 @@ >> */ >> #include <linux/module.h> >> #include <linux/firmware.h> >> - >> #include <net/bluetooth/bluetooth.h> >> #include <net/bluetooth/hci_core.h> >> - >> #include "btqca.h" >> #define VERSION "0.1" >> -static int rome_patch_ver_req(struct hci_dev *hdev, u32 >> *rome_version) >> -{ >> - struct sk_buff *skb; >> - struct edl_event_hdr *edl; >> - struct rome_version *ver; >> - char cmd; >> - int err = 0; >> - >> - BT_DBG("%s: ROME Patch Version Request", hdev->name); >> - >> - cmd = EDL_PATCH_VER_REQ_CMD; >> - skb = __hci_cmd_sync_ev(hdev, EDL_PATCH_CMD_OPCODE, >> EDL_PATCH_CMD_LEN, >> - &cmd, HCI_VENDOR_PKT, HCI_INIT_TIMEOUT); >> - if (IS_ERR(skb)) { >> - err = PTR_ERR(skb); >> - BT_ERR("%s: Failed to read version of ROME (%d)", hdev->name, >> - err); >> - return err; >> - } >> - >> - if (skb->len != sizeof(*edl) + sizeof(*ver)) { >> - BT_ERR("%s: Version size mismatch len %d", hdev->name, >> - skb->len); >> - err = -EILSEQ; >> - goto out; >> - } >> - >> - edl = (struct edl_event_hdr *)(skb->data); >> - if (!edl) { >> - BT_ERR("%s: TLV with no header", hdev->name); >> - err = -EILSEQ; >> - goto out; >> - } >> - >> - if (edl->cresp != EDL_CMD_REQ_RES_EVT || >> - edl->rtype != EDL_APP_VER_RES_EVT) { >> - BT_ERR("%s: Wrong packet received %d %d", hdev->name, >> - edl->cresp, edl->rtype); >> - err = -EIO; >> - goto out; >> - } >> - >> - ver = (struct rome_version *)(edl->data); >> - >> - BT_DBG("%s: Product:0x%08x", hdev->name, >> le32_to_cpu(ver->product_id)); >> - BT_DBG("%s: Patch :0x%08x", hdev->name, >> le16_to_cpu(ver->patch_ver)); >> - BT_DBG("%s: ROM :0x%08x", hdev->name, >> le16_to_cpu(ver->rome_ver)); >> - BT_DBG("%s: SOC :0x%08x", hdev->name, le32_to_cpu(ver->soc_id)); >> - >> - /* ROME chipset version can be decided by patch and SoC >> - * version, combination with upper 2 bytes from SoC >> - * and lower 2 bytes from patch will be used. >> - */ >> - *rome_version = (le32_to_cpu(ver->soc_id) << 16) | >> - (le16_to_cpu(ver->rome_ver) & 0x0000ffff); >> - >> -out: >> - kfree_skb(skb); >> - >> - return err; >> -} >> - >> -static int rome_reset(struct hci_dev *hdev) >> +static int qca_btsoc_reset(struct hci_dev *hdev) >> { >> struct sk_buff *skb; >> int err; >> - BT_DBG("%s: ROME HCI_RESET", hdev->name); >> + BT_DBG("%s: ROME/wcn3990 HCI_RESET", hdev->name); >> skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, >> HCI_INIT_TIMEOUT); >> if (IS_ERR(skb)) { >> @@ -108,7 +44,7 @@ static int rome_reset(struct hci_dev *hdev) >> return 0; >> } >> -static void rome_tlv_check_data(struct rome_config *config, >> +static void rome_tlv_check_data(struct qca_config *config, >> const struct firmware *fw) >> { >> const u8 *data; >> @@ -194,8 +130,121 @@ static void rome_tlv_check_data(struct >> rome_config *config, >> } >> } >> -static int rome_tlv_send_segment(struct hci_dev *hdev, int idx, int >> seg_size, >> - const u8 *data) >> +static void cherokee_tlv_check_data(struct qca_config *config, >> + const struct firmware *fw, bool *dwnd_flag) >> +{ >> + const u8 *data; >> + u32 type_len; >> + u16 tag_id, tag_len; >> + int idx, length; >> + struct tlv_type_hdr *tlv; >> + struct cherokee_tlv_type_patch *tlv_patch; >> + struct tlv_type_nvm *tlv_nvm; >> + >> + tlv = (struct tlv_type_hdr *)fw->data; >> + >> + type_len = le32_to_cpu(tlv->type_len); >> + length = (type_len >> 8) & 0x00ffffff; >> + >> + BT_DBG("TLV Type\t\t : 0x%x", type_len & 0x000000ff); >> + BT_DBG("Length\t\t : %d bytes", length); >> + >> + switch (config->type) { >> + case TLV_TYPE_PATCH: >> + tlv_patch = (struct tlv_type_patch *)tlv->data; >> + BT_DBG("Total Length\t\t : %d bytes", >> + le32_to_cpu(tlv_patch->total_size)); >> + BT_DBG("Patch Data Length\t : %d bytes", >> + le32_to_cpu(tlv_patch->data_length)); >> + BT_DBG("Signing Format Version : 0x%x", >> + tlv_patch->format_version); >> + BT_DBG("Signature Algorithm\t : 0x%x", >> + tlv_patch->signature); >> + BT_DBG("Download flag\t : 0x%x", >> + tlv_patch->dwnd_cfg); >> + BT_DBG("Reserved\t\t : 0x%x", >> + le16_to_cpu(tlv_patch->reserved1)); >> + BT_DBG("Product ID\t\t : 0x%04x", >> + le16_to_cpu(tlv_patch->product_id)); >> + BT_DBG("Rom Build Version\t : 0x%04x", >> + le16_to_cpu(tlv_patch->rom_build)); >> + BT_DBG("Patch Version\t\t : 0x%04x", >> + le16_to_cpu(tlv_patch->patch_version)); >> + BT_DBG("Reserved\t\t : 0x%x", >> + le16_to_cpu(tlv_patch->reserved2)); >> + BT_DBG("Patch Entry Address\t : 0x%x", >> + le32_to_cpu(tlv_patch->entry)); >> + /* If the download flag is 0x03, don't wait >> + * for response from soc,i.e. 1 to n-1 >> + * segment download. >> + */ >> + if (tlv_patch->dwnd_cfg == 0x03) >> + *dwnd_flag = false; >> + else >> + *dwnd_flag = true; >> + break; >> + >> + case TLV_TYPE_NVM: >> + idx = 0; >> + data = tlv->data; >> + while (idx < length) { >> + tlv_nvm = (struct tlv_type_nvm *)(data + idx); >> + tag_id = le16_to_cpu(tlv_nvm->tag_id); >> + tag_len = le16_to_cpu(tlv_nvm->tag_len); >> + >> + /* Update NVM tags as needed */ >> + switch (tag_id) { >> + case EDL_TAG_ID_HCI: >> + /* HCI transport layer parameters >> + * enabling software inband sleep >> + * onto controller side. >> + */ >> + tlv_nvm->data[0] |= 0x80; >> + >> + /* UART Baud Rate */ >> + tlv_nvm->data[2] = config->user_baud_rate; >> + break; >> + case EDL_TAG_ID_DEEP_SLEEP: >> + /* Sleep enable mask >> + * enabling deep sleep feature on controller. >> + */ >> + tlv_nvm->data[0] |= 0x01; >> + break; >> + } >> + >> + idx += (sizeof(u16) + sizeof(u16) + 8 + tag_len); >> + } >> + break; >> + default: >> + BT_ERR("Unknown TLV type %d", config->type); >> + break; >> + } >> +} >> + >> +static int cherokee_tlv_send_segment(struct hci_dev *hdev, int idx, >> + int seg_size, const u8 *data) >> +{ >> + u8 cmd[MAX_SIZE_PER_TLV_SEGMENT + 2]; >> + int err = 0; >> + >> + BT_DBG("%s: Download segment #%d size %d", hdev->name, idx, >> seg_size); >> + >> + cmd[0] = EDL_PATCH_TLV_REQ_CMD; >> + cmd[1] = seg_size; >> + memcpy(cmd + 2, data, seg_size); >> + >> + err = __hci_cmd_no_event(hdev, EDL_PATCH_CMD_OPCODE, seg_size + 2, >> + cmd); >> + if (err < 0) { >> + BT_ERR("%s: Failed to send TLV segment (%d)", hdev->name, err); >> + return err; >> + } >> + >> + return err; >> +} >> + >> +static int qca_btsoc_tlv_send_segment(struct hci_dev *hdev, int idx, >> + int seg_size, const u8 *data) >> { >> struct sk_buff *skb; >> struct edl_event_hdr *edl; >> @@ -264,16 +313,56 @@ static int rome_tlv_download_request(struct >> hci_dev *hdev, >> data = fw->data; >> for (i = 0; i < total_segment; i++) { >> buffer = data + i * MAX_SIZE_PER_TLV_SEGMENT; >> - ret = rome_tlv_send_segment(hdev, i, MAX_SIZE_PER_TLV_SEGMENT, >> - buffer); >> + ret = qca_btsoc_tlv_send_segment(hdev, i, >> + MAX_SIZE_PER_TLV_SEGMENT, buffer); >> if (ret < 0) >> return -EIO; >> } >> if (remain_size) { >> buffer = data + total_segment * MAX_SIZE_PER_TLV_SEGMENT; >> - ret = rome_tlv_send_segment(hdev, total_segment, remain_size, >> - buffer); >> + ret = qca_btsoc_tlv_send_segment(hdev, total_segment, >> + remain_size, buffer); >> + if (ret < 0) >> + return -EIO; >> + } >> + >> + return 0; >> +} >> +static int cherokee_tlv_download_request(struct hci_dev *hdev, >> + const struct firmware *fw) >> +{ >> + const u8 *buffer, *data; >> + int total_segment, remain_size; >> + int ret, i; >> + >> + if (!fw || !fw->data) >> + return -EINVAL; >> + >> + total_segment = fw->size / MAX_SIZE_PER_TLV_SEGMENT; >> + remain_size = fw->size % MAX_SIZE_PER_TLV_SEGMENT; >> + >> + BT_DBG("%s: Total segment num %d remain size %d total size %zu", >> + hdev->name, total_segment, remain_size, fw->size); >> + >> + data = fw->data; >> + for (i = 0; i < total_segment; i++) { >> + buffer = data + i * MAX_SIZE_PER_TLV_SEGMENT; >> + /* Read response from soc for last segment sent */ >> + if (!remain_size && ((i+1) == total_segment)) >> + ret = qca_btsoc_tlv_send_segment(hdev, i, >> + MAX_SIZE_PER_TLV_SEGMENT, buffer); >> + else >> + ret = cherokee_tlv_send_segment(hdev, i, >> + MAX_SIZE_PER_TLV_SEGMENT, buffer); >> + if (ret < 0) >> + return -EIO; >> + } >> + >> + if (remain_size) { >> + buffer = data + total_segment * MAX_SIZE_PER_TLV_SEGMENT; >> + ret = qca_btsoc_tlv_send_segment(hdev, total_segment, >> + remain_size, buffer); >> if (ret < 0) >> return -EIO; >> } >> @@ -282,7 +371,7 @@ static int rome_tlv_download_request(struct >> hci_dev *hdev, >> } >> static int rome_download_firmware(struct hci_dev *hdev, >> - struct rome_config *config) >> + struct qca_config *config) >> { >> const struct firmware *fw; >> int ret; >> @@ -309,7 +398,46 @@ static int rome_download_firmware(struct hci_dev >> *hdev, >> return ret; >> } >> -int qca_set_bdaddr_rome(struct hci_dev *hdev, const bdaddr_t >> *bdaddr) >> +static int cherokee_download_firmware(struct hci_dev *hdev, >> + struct qca_config *config) >> +{ >> + const struct firmware *fw; >> + bool dwnd_flag = true; >> + int ret; >> + >> + BT_INFO("%s: wcn3990 Downloading %s", hdev->name, config->fwname); >> + >> + ret = request_firmware(&fw, config->fwname, &hdev->dev); >> + if (ret) { >> + BT_ERR("%s: Failed to request file: %s (%d)", hdev->name, >> + config->fwname, ret); >> + return ret; >> + } >> + >> + cherokee_tlv_check_data(config, fw, &dwnd_flag); >> + /* check whether the download flag is set.if bit is enabled >> + * terminate the ram patch download. As we are not supporting, >> + * receiving of response from soc for every segment sent. >> + * We look for response from soc for last segment. >> + */ >> + if (dwnd_flag == true && config->type == TLV_TYPE_PATCH) { >> + BT_ERR("%s: btsoc download flag enabled", hdev->name); >> + return -EOPNOTSUPP; >> + } >> + >> + ret = cherokee_tlv_download_request(hdev, fw); >> + if (ret) { >> + BT_ERR("%s: Failed to download file: %s (%d)", hdev->name, >> + config->fwname, ret); >> + } >> + >> + release_firmware(fw); >> + >> + return ret; >> +} >> + >> + >> +int qca_btsoc_set_bdaddr(struct hci_dev *hdev, const bdaddr_t >> *bdaddr) >> { >> struct sk_buff *skb; >> u8 cmd[9]; >> @@ -332,12 +460,12 @@ int qca_set_bdaddr_rome(struct hci_dev *hdev, >> const bdaddr_t *bdaddr) >> return 0; >> } >> -EXPORT_SYMBOL_GPL(qca_set_bdaddr_rome); >> +EXPORT_SYMBOL_GPL(qca_btsoc_set_bdaddr); >> int qca_uart_setup_rome(struct hci_dev *hdev, uint8_t baudrate) >> { >> u32 rome_ver = 0; >> - struct rome_config config; >> + struct qca_config config; >> int err; >> BT_DBG("%s: ROME setup on UART", hdev->name); >> @@ -345,7 +473,7 @@ int qca_uart_setup_rome(struct hci_dev *hdev, >> uint8_t baudrate) >> config.user_baud_rate = baudrate; >> /* Get ROME version information */ >> - err = rome_patch_ver_req(hdev, &rome_ver); >> + err = qca_btsoc_patch_ver_req(hdev, &rome_ver); >> if (err < 0 || rome_ver == 0) { >> BT_ERR("%s: Failed to get version 0x%x", hdev->name, err); >> return err; >> @@ -374,7 +502,7 @@ int qca_uart_setup_rome(struct hci_dev *hdev, >> uint8_t baudrate) >> } >> /* Perform HCI reset */ >> - err = rome_reset(hdev); >> + err = qca_btsoc_reset(hdev); >> if (err < 0) { >> BT_ERR("%s: Failed to run HCI_RESET (%d)", hdev->name, err); >> return err; >> @@ -386,6 +514,111 @@ int qca_uart_setup_rome(struct hci_dev *hdev, >> uint8_t baudrate) >> } >> EXPORT_SYMBOL_GPL(qca_uart_setup_rome); >> +int qca_btsoc_patch_ver_req(struct hci_dev *hdev, u32 *soc_version) >> +{ >> + struct sk_buff *skb; >> + struct edl_event_hdr *edl; >> + struct qca_btsoc_version *ver; >> + char cmd; >> + int err = 0; >> + >> + BT_DBG("%s: BTSOC Patch Version Request", hdev->name); >> + >> + cmd = EDL_PATCH_VER_REQ_CMD; >> + skb = __hci_cmd_sync_ev(hdev, EDL_PATCH_CMD_OPCODE, >> EDL_PATCH_CMD_LEN, >> + &cmd, HCI_VENDOR_PKT, HCI_INIT_TIMEOUT); >> + if (IS_ERR(skb)) { >> + err = PTR_ERR(skb); >> + BT_ERR("%s: Failed to read version of ROME (%d)", hdev->name, >> + err); >> + return err; >> + } >> + >> + if (skb->len != sizeof(*edl) + sizeof(*ver)) { >> + BT_ERR("%s: Version size mismatch len %d", hdev->name, >> + skb->len); >> + err = -EILSEQ; >> + goto out; >> + } >> + >> + edl = (struct edl_event_hdr *)(skb->data); >> + if (!edl) { >> + BT_ERR("%s: TLV with no header", hdev->name); >> + err = -EILSEQ; >> + goto out; >> + } >> + >> + if (edl->cresp != EDL_CMD_REQ_RES_EVT || >> + edl->rtype != EDL_APP_VER_RES_EVT) { >> + BT_ERR("%s: Wrong packet received %d %d", hdev->name, >> + edl->cresp, edl->rtype); >> + err = -EIO; >> + goto out; >> + } >> + >> + ver = (struct qca_btsoc_version *)(edl->data); >> + >> + BT_DBG("%s: Product:0x%08x", hdev->name, >> le32_to_cpu(ver->product_id)); >> + BT_DBG("%s: Patch :0x%08x", hdev->name, >> le16_to_cpu(ver->patch_ver)); >> + BT_DBG("%s: ROM :0x%08x", hdev->name, >> le16_to_cpu(ver->btsoc_ver)); >> + BT_DBG("%s: SOC :0x%08x", hdev->name, le32_to_cpu(ver->soc_id)); >> + >> + /* BTSOC chipset version can be decided by patch and SoC >> + * version, combination with upper 2 bytes from SoC >> + * and lower 2 bytes from patch will be used. >> + */ >> + *soc_version = (le32_to_cpu(ver->soc_id) << 16) | >> + (le16_to_cpu(ver->btsoc_ver) & 0x0000ffff); >> + >> +out: >> + kfree_skb(skb); >> + >> + return err; >> +} >> +EXPORT_SYMBOL_GPL(qca_btsoc_patch_ver_req); >> + >> +int qca_uart_setup_cherokee(struct hci_dev *hdev, uint8_t baudrate, >> + u32 *cherokee_ver) >> +{ >> + struct qca_config config; >> + int err; >> + >> + BT_DBG("%s: wcn3990 setup on UART", hdev->name); >> + config.user_baud_rate = baudrate; >> + >> + /* Download rampatch file */ >> + config.type = TLV_TYPE_PATCH; >> + snprintf(config.fwname, sizeof(config.fwname), >> "qca/rampatch_%08x.tlv", >> + *cherokee_ver); >> + err = cherokee_download_firmware(hdev, &config); >> + if (err < 0) { >> + BT_ERR("%s: Failed to download patch (%d)", hdev->name, err); >> + return err; >> + } >> + >> + /* Download NVM configuration */ >> + config.type = TLV_TYPE_NVM; >> + snprintf(config.fwname, sizeof(config.fwname), "qca/nvm_%08x.bin", >> + *cherokee_ver); >> + err = cherokee_download_firmware(hdev, &config); >> + if (err < 0) { >> + BT_ERR("%s: Failed to download NVM (%d)", hdev->name, err); >> + return err; >> + } >> + >> + /* Perform HCI reset */ >> + err = qca_btsoc_reset(hdev); >> + if (err < 0) { >> + BT_ERR("%s: Failed to run HCI_RESET (%d)", hdev->name, err); >> + return err; >> + } >> + >> + BT_INFO("%s: wcn3990 setup on UART is completed", hdev->name); >> + >> + return 0; >> +} >> +EXPORT_SYMBOL_GPL(qca_uart_setup_cherokee); >> + >> MODULE_AUTHOR("Ben Young Tae Kim <ytkim@qca.qualcomm.com>"); >> MODULE_DESCRIPTION("Bluetooth support for Qualcomm Atheros family >> ver " VERSION); >> MODULE_VERSION(VERSION); >> diff --git a/drivers/bluetooth/btqca.h b/drivers/bluetooth/btqca.h >> index 65e994b..7608423 100644 >> --- a/drivers/bluetooth/btqca.h >> +++ b/drivers/bluetooth/btqca.h >> @@ -1,7 +1,7 @@ >> /* >> * Bluetooth supports for Qualcomm Atheros ROME chips >> * >> - * Copyright (c) 2015 The Linux Foundation. All rights reserved. >> + * Copyright (c) 2015, 2018 The Linux Foundation. All rights >> reserved. >> * >> * This program is free software; you can redistribute it and/or >> modify >> * it under the terms of the GNU General Public License version 2 >> @@ -37,6 +37,9 @@ >> #define EDL_TAG_ID_HCI (17) >> #define EDL_TAG_ID_DEEP_SLEEP (27) >> +#define CHEROKEE_POWERON_PULSE (0xFC) >> +#define CHEROKEE_POWEROFF_PULSE (0xC0) >> + >> enum qca_bardrate { >> QCA_BAUDRATE_115200 = 0, >> QCA_BAUDRATE_57600, >> @@ -66,7 +69,7 @@ enum rome_tlv_type { >> TLV_TYPE_NVM >> }; >> -struct rome_config { >> +struct qca_config { >> u8 type; >> char fwname[64]; >> uint8_t user_baud_rate; >> @@ -78,13 +81,14 @@ struct edl_event_hdr { >> __u8 data[0]; >> } __packed; >> -struct rome_version { >> +struct qca_btsoc_version { >> __le32 product_id; >> __le16 patch_ver; >> - __le16 rome_ver; >> + __le16 btsoc_ver; >> __le32 soc_id; >> } __packed; >> + >> struct tlv_seg_resp { >> __u8 result; >> } __packed; >> @@ -102,6 +106,21 @@ struct tlv_type_patch { >> __le32 entry; >> } __packed; >> +struct cherokee_tlv_type_patch { >> + __le32 total_size; >> + __le32 data_length; >> + __u8 format_version; >> + __u8 signature; >> + __u8 dwnd_cfg; >> + __le16 reserved1; >> + __le16 product_id; >> + __le16 rom_build; >> + __le16 patch_version; >> + __le16 reserved2; >> + __le32 entry; >> +} __packed; >> + >> + >> struct tlv_type_nvm { >> __le16 tag_id; >> __le16 tag_len; >> @@ -115,14 +134,21 @@ struct tlv_type_hdr { >> __u8 data[0]; >> } __packed; >> +int qca_btsoc_cleanup(struct hci_dev *hdev); >> +int btqca_power_setup(bool on); >> + >> #if IS_ENABLED(CONFIG_BT_QCA) >> -int qca_set_bdaddr_rome(struct hci_dev *hdev, const bdaddr_t >> *bdaddr); >> +int qca_btsoc_set_bdaddr(struct hci_dev *hdev, const bdaddr_t >> *bdaddr); >> int qca_uart_setup_rome(struct hci_dev *hdev, uint8_t baudrate); >> +int qca_uart_setup_cherokee(struct hci_dev *hdev, uint8_t baudrate, >> + u32 *cherokee_ver); >> +int qca_btsoc_patch_ver_req(struct hci_dev *hdev, u32 *soc_version); >> #else >> -static inline int qca_set_bdaddr_rome(struct hci_dev *hdev, const >> bdaddr_t *bdaddr) >> +static inline int qca_btsoc_set_bdaddr(struct hci_dev *hdev, >> + const bdaddr_t *bdaddr) >> { >> return -EOPNOTSUPP; >> } >> @@ -132,4 +158,15 @@ static inline int qca_uart_setup_rome(struct >> hci_dev *hdev, int speed) >> return -EOPNOTSUPP; >> } >> +static inline int qca_uart_setup_cherokee(struct hci_dev *hdev, >> + uint8_t baudrate, u32 *cherokee_ver) >> +{ >> + return -EOPNOTSUPP; >> +} >> + >> +static int qca_btsoc_patch_ver_req(struct hci_dev *hdev, u32 >> *cherokee_version) >> +{ >> + return -EOPNOTSUPP; >> +} >> + >> #endif >> diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c >> index b7547e6..1343e63 100644 >> --- a/drivers/bluetooth/hci_qca.c >> +++ b/drivers/bluetooth/hci_qca.c >> @@ -5,7 +5,7 @@ >> * protocol extension to H4. >> * >> * Copyright (C) 2007 Texas Instruments, Inc. >> - * Copyright (c) 2010, 2012 The Linux Foundation. All rights >> reserved. >> + * Copyright (c) 2010, 2012, 2018 The Linux Foundation. All rights >> reserved. >> * >> * Acknowledgements: >> * This file is based on hci_ll.c, which was... >> @@ -35,9 +35,15 @@ >> #include <linux/mod_devicetable.h> >> #include <linux/module.h> >> #include <linux/serdev.h> >> - >> #include <net/bluetooth/bluetooth.h> >> #include <net/bluetooth/hci_core.h> >> +#include <asm-generic/delay.h> >> +#include <linux/tty.h> >> +#include <linux/platform_device.h> >> +#include <linux/delay.h> >> +#include <linux/slab.h> >> +#include <linux/regulator/consumer.h> >> +#include <linux/of_device.h> >> #include "hci_uart.h" >> #include "btqca.h" >> @@ -84,12 +90,12 @@ struct qca_data { >> struct hci_uart *hu; >> struct sk_buff *rx_skb; >> struct sk_buff_head txq; >> - struct sk_buff_head tx_wait_q; /* HCI_IBS wait queue */ >> - spinlock_t hci_ibs_lock; /* HCI_IBS state lock */ >> - u8 tx_ibs_state; /* HCI_IBS transmit side power state*/ >> - u8 rx_ibs_state; /* HCI_IBS receive side power state */ >> - bool tx_vote; /* Clock must be on for TX */ >> - bool rx_vote; /* Clock must be on for RX */ >> + struct sk_buff_head tx_wait_q; /* HCI_IBS wait queue */ >> + spinlock_t hci_ibs_lock; /* HCI_IBS state lock */ >> + u8 tx_ibs_state; /* HCI_IBS transmit side power state*/ >> + u8 rx_ibs_state; /* HCI_IBS receive side power state */ >> + bool tx_vote; /* Clock must be on for TX */ >> + bool rx_vote; /* Clock must be on for RX */ >> struct timer_list tx_idle_timer; >> u32 tx_idle_delay; >> struct timer_list wake_retrans_timer; >> @@ -119,12 +125,49 @@ struct qca_data { >> u64 votes_off; >> }; >> +enum btqca_soc_t { >> + BTQCA_INVALID = -1, >> + BTQCA_AR3002, >> + BTQCA_ROME, >> + BTQCA_CHEROKEE >> +}; >> + >> struct qca_serdev { >> struct hci_uart serdev_hu; >> struct gpio_desc *bt_en; >> struct clk *susclk; >> + enum btqca_soc_t btsoc_type; >> +}; >> + >> +/* >> + * voltage regulator information required for configuring the >> + * QCA bluetooth chipset >> + */ >> +struct btqca_vreg { >> + const char *name; >> + unsigned int min_v; >> + unsigned int max_v; >> + unsigned int load_ua; >> }; >> +struct btqca_vreg_data { >> + enum btqca_soc_t soc_type; >> + struct btqca_vreg *vregs; >> + size_t num_vregs; >> +}; >> + >> +/* >> + * Platform data for the QCA bluetooth power driver. >> + */ >> +struct btqca_power { >> + struct device *dev; >> + struct btqca_vreg_data *vreg_data; >> + struct regulator_bulk_data *vreg_bulk; >> + bool vreg_status; >> +}; >> + >> +static struct btqca_power *qca; >> + >> static void __serial_clock_on(struct tty_struct *tty) >> { >> /* TODO: Some chipset requires to enable UART clock on client >> @@ -462,9 +505,11 @@ static int qca_open(struct hci_uart *hu) >> if (hu->serdev) { >> serdev_device_open(hu->serdev); >> - >> qcadev = serdev_device_get_drvdata(hu->serdev); >> - gpiod_set_value_cansleep(qcadev->bt_en, 1); >> + if (qcadev->btsoc_type == BTQCA_CHEROKEE) >> + btqca_power_setup(true); >> + else >> + gpiod_set_value_cansleep(qcadev->bt_en, 1); >> } >> BT_DBG("HCI_UART_QCA open, tx_idle_delay=%u, wake_retrans=%u", >> @@ -553,7 +598,10 @@ static int qca_close(struct hci_uart *hu) >> serdev_device_close(hu->serdev); >> qcadev = serdev_device_get_drvdata(hu->serdev); >> - gpiod_set_value_cansleep(qcadev->bt_en, 0); >> + if (qcadev->btsoc_type == BTQCA_CHEROKEE) >> + qca_btsoc_cleanup(hu->hdev); >> + else >> + gpiod_set_value_cansleep(qcadev->bt_en, 0); >> } >> kfree_skb(qca->rx_skb); >> @@ -873,6 +921,8 @@ static uint8_t qca_get_baudrate_value(int speed) >> return QCA_BAUDRATE_2000000; >> case 3000000: >> return QCA_BAUDRATE_3000000; >> + case 3200000: >> + return QCA_BAUDRATE_3200000; >> case 3500000: >> return QCA_BAUDRATE_3500000; >> default: >> @@ -887,7 +937,7 @@ static int qca_set_baudrate(struct hci_dev *hdev, >> uint8_t baudrate) >> struct sk_buff *skb; >> u8 cmd[] = { 0x01, 0x48, 0xFC, 0x01, 0x00 }; >> - if (baudrate > QCA_BAUDRATE_3000000) >> + if (baudrate > QCA_BAUDRATE_3200000) >> return -EINVAL; >> cmd[4] = baudrate; >> @@ -924,62 +974,263 @@ static inline void host_set_baudrate(struct >> hci_uart *hu, unsigned int speed) >> hci_uart_set_baudrate(hu, speed); >> } >> +static int qca_send_poweron_cmd(struct hci_dev *hdev) >> +{ >> + struct hci_uart *hu = hci_get_drvdata(hdev); >> + struct qca_data *qca = hu->priv; >> + struct sk_buff *skb; >> + u8 cmd; >> + >> + BT_DBG("%s sending power on command to btsoc", hdev->name); >> + /* By sending 0xFC host is trying to power up the soc */ >> + cmd = CHEROKEE_POWERON_PULSE; >> + skb = bt_skb_alloc(sizeof(cmd), GFP_ATOMIC); >> + if (!skb) { >> + BT_ERR("Failed to allocate memory for skb packet"); >> + return -ENOMEM; >> + } >> + >> + skb_put_data(skb, &cmd, sizeof(cmd)); >> + hci_skb_pkt_type(skb) = HCI_COMMAND_PKT; >> + >> + skb_queue_tail(&qca->txq, skb); >> + hci_uart_tx_wakeup(hu); >> + >> + /* Wait for 100 us for soc to settle down */ >> + set_current_state(TASK_UNINTERRUPTIBLE); >> + schedule_timeout(usecs_to_jiffies(100)); >> + set_current_state(TASK_INTERRUPTIBLE); >> + >> + return 0; >> +} >> + >> +static int qca_send_poweroff_cmd(struct hci_dev *hdev) >> +{ >> + struct hci_uart *hu = hci_get_drvdata(hdev); >> + struct qca_data *qca = hu->priv; >> + struct sk_buff *skb; >> + u8 cmd; >> + >> + BT_DBG("%s sending power off command to btsoc", hdev->name); >> + /* By sending 0xC0 host is trying to power off the soc */ >> + cmd = CHEROKEE_POWEROFF_PULSE; >> + skb = bt_skb_alloc(sizeof(cmd), GFP_ATOMIC); >> + if (!skb) { >> + BT_ERR("Failed to allocate memory for skb packet"); >> + return -ENOMEM; >> + } >> + >> + skb_put_data(skb, &cmd, sizeof(cmd)); >> + hci_skb_pkt_type(skb) = HCI_COMMAND_PKT; >> + >> + skb_queue_tail(&qca->txq, skb); >> + hci_uart_tx_wakeup(hu); >> + >> + /* Wait for 100 us for soc to settle down */ >> + set_current_state(TASK_UNINTERRUPTIBLE); >> + schedule_timeout(usecs_to_jiffies(100)); >> + set_current_state(TASK_INTERRUPTIBLE); >> + >> + return 0; >> +} >> + >> +static int qca_serdev_open(struct hci_uart *hu) >> +{ >> + int ret = 0; >> + >> + if (hu->serdev) >> + serdev_device_open(hu->serdev); >> + else { >> + BT_ERR("%s:open operation not supported", hu->hdev->name); >> + ret = 1; >> + } >> + >> + return ret; >> +} >> + >> +int qca_btsoc_cleanup(struct hci_dev *hdev) >> +{ >> + struct hci_uart *hu = hci_get_drvdata(hdev); >> + >> + /* change host baud rate before sending power off command */ >> + host_set_baudrate(hu, 2400); >> + /* send 0xC0 command to btsoc before turning off regulators */ >> + qca_send_poweroff_cmd(hdev); >> + /* turn off btsoc */ >> + return btqca_power_setup(false); >> +} >> + >> +static int qca_serdev_close(struct hci_uart *hu) >> +{ >> + int ret = 0; >> + >> + if (hu->serdev) >> + serdev_device_close(hu->serdev); >> + else { >> + BT_ERR("%s:close operation not supported", hu->hdev->name); >> + ret = 1; >> + } >> + >> + return ret; >> +} >> + >> static int qca_setup(struct hci_uart *hu) >> { >> struct hci_dev *hdev = hu->hdev; >> struct qca_data *qca = hu->priv; >> + struct qca_serdev *qcadev; >> unsigned int speed, qca_baudrate = QCA_BAUDRATE_115200; >> int ret; >> + unsigned int soc_ver; >> + >> + qcadev = serdev_device_get_drvdata(hu->serdev); >> + >> + switch (qcadev->btsoc_type) { >> + case BTQCA_CHEROKEE: >> + >> + BT_INFO("%s:setting up wcn3990", hdev->name); >> + /* Patch downloading has to be done without IBS mode */ >> + clear_bit(STATE_IN_BAND_SLEEP_ENABLED, &qca->flags); >> + /* Setup initial baudrate */ >> + speed = 0; >> + if (hu->init_speed) >> + speed = hu->init_speed; >> + else if (hu->proto->init_speed) >> + speed = hu->proto->init_speed; >> + >> + if (speed) >> + host_set_baudrate(hu, speed); >> + else { >> + BT_ERR("%s:initial speed %u", hdev->name, speed); >> + return -1; >> + } >> - BT_INFO("%s: ROME setup", hdev->name); >> + /* clear flow control- for sync cmd*/ >> + hci_uart_set_flow_control(hu, true); >> + /* send poweron command to btsoc */ >> + ret = qca_send_poweron_cmd(hdev); >> + if (ret) { >> + BT_ERR("%s:sending sync command failed", hdev->name); >> + return ret; >> + } >> + >> + /* close port */ >> + ret = qca_serdev_close(hu); >> + if (ret) >> + return ret; >> + /* reopen port */ >> + ret = qca_serdev_open(hu); >> + if (ret) >> + return ret; >> + /* Setup initial baudrate */ >> + speed = 0; >> + if (hu->init_speed) >> + speed = hu->init_speed; >> + else if (hu->proto->init_speed) >> + speed = hu->proto->init_speed; >> + if (speed) >> + host_set_baudrate(hu, speed); >> + else { >> + BT_ERR("%s:initial speed %u", hdev->name, speed); >> + return -1; >> + } >> - /* Patch downloading has to be done without IBS mode */ >> - clear_bit(STATE_IN_BAND_SLEEP_ENABLED, &qca->flags); >> + /* Enable flow control */ >> + hci_uart_set_flow_control(hu, false); >> + /* wait until flow control settled */ >> + mdelay(100); >> - /* Setup initial baudrate */ >> - speed = 0; >> - if (hu->init_speed) >> - speed = hu->init_speed; >> - else if (hu->proto->init_speed) >> - speed = hu->proto->init_speed; >> + ret = qca_btsoc_patch_ver_req(hdev, &soc_ver); >> + if (ret < 0 || soc_ver == 0) { >> + BT_ERR("%s: Failed to get version 0x%x", hdev->name, >> + ret); >> + return ret; >> + } >> - if (speed) >> - host_set_baudrate(hu, speed); >> + BT_INFO("%s:wcn3990 controller version 0x%08x", hdev->name, >> + soc_ver); >> - /* Setup user speed if needed */ >> - speed = 0; >> - if (hu->oper_speed) >> - speed = hu->oper_speed; >> - else if (hu->proto->oper_speed) >> + /* clear flow control */ >> + hci_uart_set_flow_control(hu, true); >> + /* set operating speed */ >> speed = hu->proto->oper_speed; >> + if (speed) { >> + qca_baudrate = qca_get_baudrate_value(speed); >> + BT_INFO("%s: Set UART speed to %d", hdev->name, speed); >> + ret = qca_set_baudrate(hdev, qca_baudrate); >> + if (ret) { >> + BT_ERR("%s:Failed to change the baud rate(%d)", >> + hdev->name, ret); >> + return ret; >> + } >> + if (speed) >> + host_set_baudrate(hu, speed); >> + else { >> + BT_ERR("%s:Error in setting operator speed:%u", >> + hdev->name, speed); >> + return -1; >> + } >> + } >> - if (speed) { >> - qca_baudrate = qca_get_baudrate_value(speed); >> + /* Set flow control */ >> + hci_uart_set_flow_control(hu, false); >> + /*Setup patch and NVM configurations */ >> + ret = qca_uart_setup_cherokee(hdev, qca_baudrate, &soc_ver); >> + if (!ret) { >> + set_bit(STATE_IN_BAND_SLEEP_ENABLED, &qca->flags); >> + qca_debugfs_init(hdev); >> + } >> - BT_INFO("%s: Set UART speed to %d", hdev->name, speed); >> - ret = qca_set_baudrate(hdev, qca_baudrate); >> - if (ret) { >> - BT_ERR("%s: Failed to change the baud rate (%d)", >> - hdev->name, ret); >> - return ret; >> + /* Setup wcn3990 bdaddr */ >> + hu->hdev->set_bdaddr = qca_btsoc_set_bdaddr; >> + >> + return ret; >> + >> + default: >> + BT_INFO("%s: ROME setup", hdev->name); >> + /* Patch downloading has to be done without IBS mode */ >> + clear_bit(STATE_IN_BAND_SLEEP_ENABLED, &qca->flags); >> + /* Setup initial baudrate */ >> + speed = 0; >> + if (hu->init_speed) >> + speed = hu->init_speed; >> + else if (hu->proto->init_speed) >> + speed = hu->proto->init_speed; >> + if (speed) >> + hci_uart_set_baudrate(hu, speed); >> + >> + /* Setup user speed if needed */ >> + speed = 0; >> + if (hu->oper_speed) >> + speed = hu->oper_speed; >> + else if (hu->proto->oper_speed) >> + speed = hu->proto->oper_speed; >> + >> + if (speed) { >> + qca_baudrate = qca_get_baudrate_value(speed); >> + >> + BT_INFO("%s: Set UART speed to %d", hdev->name, speed); >> + ret = qca_set_baudrate(hdev, qca_baudrate); >> + if (ret) { >> + BT_ERR("%s:Failed to change the baud rate(%d)", >> + hdev->name, ret); >> + return ret; >> + } >> + host_set_baudrate(hu, speed); >> } >> - host_set_baudrate(hu, speed); >> - } >> - /* Setup patch / NVM configurations */ >> - ret = qca_uart_setup_rome(hdev, qca_baudrate); >> - if (!ret) { >> - set_bit(STATE_IN_BAND_SLEEP_ENABLED, &qca->flags); >> - qca_debugfs_init(hdev); >> - } else if (ret == -ENOENT) { >> - /* No patch/nvm-config found, run with original fw/config */ >> - ret = 0; >> - } >> + /* Setup patch / NVM configurations */ >> + ret = qca_uart_setup_rome(hdev, qca_baudrate); >> + if (!ret) { >> + set_bit(STATE_IN_BAND_SLEEP_ENABLED, &qca->flags); >> + qca_debugfs_init(hdev); >> + } >> - /* Setup bdaddr */ >> - hu->hdev->set_bdaddr = qca_set_bdaddr_rome; >> + /* Setup bdaddr */ >> + hu->hdev->set_bdaddr = qca_btsoc_set_bdaddr; >> - return ret; >> + return ret; >> + } >> } >> static struct hci_uart_proto qca_proto = { >> @@ -997,42 +1248,142 @@ static int qca_setup(struct hci_uart *hu) >> .dequeue = qca_dequeue, >> }; >> +static const struct btqca_vreg_data cherokee_data = { >> + .soc_type = BTQCA_CHEROKEE, >> + .vregs = (struct btqca_vreg []) { >> + { "vddio", 1352000, 1352000, 0 }, >> + { "vddxtal", 1904000, 2040000, 0 }, >> + { "vddcore", 1800000, 1800000, 1 }, >> + { "vddpa", 130400, 1304000, 1 }, >> + { "vddldo", 3000000, 3312000, 1 }, >> + { "vddpwd", 3312000, 3600000, 0 }, >> + }, >> + .num_vregs = 6, >> +}; >> + >> +int btqca_power_setup(bool on) >> +{ >> + int ret = 0; >> + int i; >> + struct btqca_vreg *vregs; >> + >> + if (!qca || !qca->vreg_data || !qca->vreg_bulk) >> + return -EINVAL; >> + vregs = qca->vreg_data->vregs; >> + >> + BT_DBG("on: %d", on); >> + >> + /* turn on if regualtors are off */ >> + if (on == true && qca->vreg_status == false) { >> + qca->vreg_status = true; >> + for (i = 0; i < qca->vreg_data->num_vregs; i++) { >> + regulator_set_voltage(qca->vreg_bulk[i].consumer, >> + vregs[i].min_v, >> + vregs[i].max_v); >> + >> + if (vregs[i].load_ua) >> + regulator_set_load(qca->vreg_bulk[i].consumer, >> + vregs[i].load_ua); >> + >> + regulator_enable(qca->vreg_bulk[i].consumer); >> + } >> + } else if (on == false && qca->vreg_status == true) { >> + qca->vreg_status = false; >> + for (i = 0; i < qca->vreg_data->num_vregs; i++) { >> + regulator_disable(qca->vreg_bulk[i].consumer); >> + regulator_set_voltage(qca->vreg_bulk[i].consumer, >> + 0, vregs[i].max_v); >> + if (vregs[i].load_ua) >> + regulator_set_load(qca->vreg_bulk[i].consumer, >> + 0); >> + } >> + } >> + >> + return ret; >> +} >> + >> +static int init_regulators(struct btqca_power *qca, >> + const struct btqca_vreg *vregs, >> + size_t num_vregs) >> +{ >> + int i; >> + >> + qca->vreg_bulk = devm_kzalloc(qca->dev, num_vregs * >> + sizeof(struct regulator_bulk_data), >> + GFP_KERNEL); >> + if (!qca->vreg_bulk) >> + return -ENOMEM; >> + >> + for (i = 0; i < num_vregs; i++) >> + qca->vreg_bulk[i].supply = vregs[i].name; >> + >> + return devm_regulator_bulk_get(qca->dev, num_vregs, qca->vreg_bulk); >> +} >> + >> static int qca_serdev_probe(struct serdev_device *serdev) >> { >> struct qca_serdev *qcadev; >> - int err; >> + struct btqca_vreg_data *data; >> + int err = 0; >> qcadev = devm_kzalloc(&serdev->dev, sizeof(*qcadev), GFP_KERNEL); >> if (!qcadev) >> return -ENOMEM; >> qcadev->serdev_hu.serdev = serdev; >> + data = of_device_get_match_data(&serdev->dev); >> + if (data && data->soc_type == BTQCA_CHEROKEE) >> + qcadev->btsoc_type = BTQCA_CHEROKEE; >> + else >> + qcadev->btsoc_type = BTQCA_ROME; >> + >> serdev_device_set_drvdata(serdev, qcadev); >> + if (qcadev->btsoc_type == BTQCA_CHEROKEE) { >> + qca = kzalloc(sizeof(struct btqca_power), GFP_KERNEL); >> + if (!qca) >> + return -ENOMEM; >> + >> + qca->dev = &serdev->dev; >> + qca->vreg_data = data; >> + err = init_regulators(qca, data->vregs, data->num_vregs); >> + if (err) { >> + BT_ERR("Failed to init regualtors:%d", err); >> + kfree(qca); >> + } >> - qcadev->bt_en = devm_gpiod_get(&serdev->dev, "enable", >> - GPIOD_OUT_LOW); >> - if (IS_ERR(qcadev->bt_en)) { >> - dev_err(&serdev->dev, "failed to acquire enable gpio\n"); >> - return PTR_ERR(qcadev->bt_en); >> - } >> + /* set voltage regulator status as false */ >> + qca->vreg_status = false; >> + err = hci_uart_register_device(&qcadev->serdev_hu, &qca_proto); >> + if (err) { >> + BT_ERR("wcn3990 serdev registration failed"); >> + kfree(qca); >> + } >> + } else { >> + qcadev->bt_en = devm_gpiod_get(&serdev->dev, "enable", >> + GPIOD_OUT_LOW); >> + if (IS_ERR(qcadev->bt_en)) { >> + dev_err(&serdev->dev, "failed to acquire enable gpio\n"); >> + return PTR_ERR(qcadev->bt_en); >> + } >> - qcadev->susclk = devm_clk_get(&serdev->dev, NULL); >> - if (IS_ERR(qcadev->susclk)) { >> - dev_err(&serdev->dev, "failed to acquire clk\n"); >> - return PTR_ERR(qcadev->susclk); >> - } >> + qcadev->susclk = devm_clk_get(&serdev->dev, NULL); >> + if (IS_ERR(qcadev->susclk)) { >> + dev_err(&serdev->dev, "failed to acquire clk\n"); >> + return PTR_ERR(qcadev->susclk); >> + } >> - err = clk_set_rate(qcadev->susclk, SUSCLK_RATE_32KHZ); >> - if (err) >> - return err; >> + err = clk_set_rate(qcadev->susclk, SUSCLK_RATE_32KHZ); >> + if (err) >> + return err; >> - err = clk_prepare_enable(qcadev->susclk); >> - if (err) >> - return err; >> + err = clk_prepare_enable(qcadev->susclk); >> + if (err) >> + return err; >> - err = hci_uart_register_device(&qcadev->serdev_hu, &qca_proto); >> - if (err) >> - clk_disable_unprepare(qcadev->susclk); >> + err = hci_uart_register_device(&qcadev->serdev_hu, &qca_proto); >> + if (err) >> + clk_disable_unprepare(qcadev->susclk); >> + } >> return err; >> } >> @@ -1042,12 +1393,16 @@ static void qca_serdev_remove(struct >> serdev_device *serdev) >> struct qca_serdev *qcadev = serdev_device_get_drvdata(serdev); >> hci_uart_unregister_device(&qcadev->serdev_hu); >> - >> - clk_disable_unprepare(qcadev->susclk); >> + if (qcadev->btsoc_type == BTQCA_CHEROKEE) { >> + btqca_power_setup(false); >> + kfree(qca); >> + } else >> + clk_disable_unprepare(qcadev->susclk); >> } >> static const struct of_device_id qca_bluetooth_of_match[] = { >> { .compatible = "qcom,qca6174-bt" }, >> + { .compatible = "qca,wcn3990-bt", .data = &cherokee_data}, >> { /* sentinel */ } >> }; >> MODULE_DEVICE_TABLE(of, qca_bluetooth_of_match); >> diff --git a/include/net/bluetooth/hci_core.h >> b/include/net/bluetooth/hci_core.h >> index c10cb79..b932ec50 100644 >> --- a/include/net/bluetooth/hci_core.h >> +++ b/include/net/bluetooth/hci_core.h >> @@ -1418,7 +1418,8 @@ struct sk_buff *__hci_cmd_sync(struct hci_dev >> *hdev, u16 opcode, u32 plen, >> const void *param, u32 timeout); >> struct sk_buff *__hci_cmd_sync_ev(struct hci_dev *hdev, u16 opcode, >> u32 plen, >> const void *param, u8 event, u32 timeout); >> - >> +int __hci_cmd_no_event(struct hci_dev *hdev, u16 opcode, u32 plen, >> + const void *param); >> int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen, >> const void *param); >> void hci_send_acl(struct hci_chan *chan, struct sk_buff *skb, __u16 >> flags); >> diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c >> index 97ef85e..8addd97 100644 >> --- a/net/bluetooth/hci_core.c >> +++ b/net/bluetooth/hci_core.c >> @@ -3370,7 +3370,7 @@ int hci_unregister_cb(struct hci_cb *cb) >> } >> EXPORT_SYMBOL(hci_unregister_cb); >> -static void hci_send_frame(struct hci_dev *hdev, struct sk_buff >> *skb) >> +void hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb) >> { >> int err; >> diff --git a/net/bluetooth/hci_request.c >> b/net/bluetooth/hci_request.c >> index f7d6ba6..1ffee59a 100644 >> --- a/net/bluetooth/hci_request.c >> +++ b/net/bluetooth/hci_request.c >> @@ -114,6 +114,29 @@ void hci_req_sync_cancel(struct hci_dev *hdev, >> int err) >> } >> } >> +/* Queue a frame to an asynchronous transfer to btdevice, >> + * with out any event from btdevice. >> + */ >> +int __hci_cmd_no_event(struct hci_dev *hdev, u16 opcode, u32 plen, >> + const void *param) >> +{ >> + struct sk_buff *skb; >> + >> + BT_DBG("%s opcode 0x%4.4x plen %d", hdev->name, opcode, plen); >> + >> + skb = hci_prepare_cmd(hdev, opcode, plen, param); >> + if (!skb) { >> + BT_ERR("%s no memory for command (opcode 0x%4.4x)", >> + hdev->name, opcode); >> + return -ENOMEM; >> + } >> + >> + hci_send_frame(hdev, skb); >> + >> + return 0; >> +} >> +EXPORT_SYMBOL(__hci_cmd_no_event); >> + >> struct sk_buff *__hci_cmd_sync_ev(struct hci_dev *hdev, u16 opcode, >> u32 plen, >> const void *param, u8 event, u32 timeout) >> { >> diff --git a/net/bluetooth/hci_request.h b/net/bluetooth/hci_request.h >> index dde77bd..a855edd 100644 >> --- a/net/bluetooth/hci_request.h >> +++ b/net/bluetooth/hci_request.h >> @@ -128,3 +128,5 @@ static inline u16 eir_append_le16(u8 *eir, u16 >> eir_len, u8 type, u16 data) >> return eir_len; >> } >> + >> +void hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb); >> Regards Balakrishna. -- To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
diff --git a/drivers/bluetooth/btqca.c b/drivers/bluetooth/btqca.c index 0bbdfce..5b47df8 100644 --- a/drivers/bluetooth/btqca.c +++ b/drivers/bluetooth/btqca.c @@ -1,7 +1,7 @@ /* * Bluetooth supports for Qualcomm Atheros chips * - * Copyright (c) 2015 The Linux Foundation. All rights reserved. + * Copyright (c) 2015, 2018 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 @@ -19,82 +19,18 @@ */ #include <linux/module.h> #include <linux/firmware.h> - #include <net/bluetooth/bluetooth.h> #include <net/bluetooth/hci_core.h> - #include "btqca.h" #define VERSION "0.1" -static int rome_patch_ver_req(struct hci_dev *hdev, u32 *rome_version) -{ - struct sk_buff *skb; - struct edl_event_hdr *edl; - struct rome_version *ver; - char cmd; - int err = 0; - - BT_DBG("%s: ROME Patch Version Request", hdev->name); - - cmd = EDL_PATCH_VER_REQ_CMD; - skb = __hci_cmd_sync_ev(hdev, EDL_PATCH_CMD_OPCODE, EDL_PATCH_CMD_LEN, - &cmd, HCI_VENDOR_PKT, HCI_INIT_TIMEOUT); - if (IS_ERR(skb)) { - err = PTR_ERR(skb); - BT_ERR("%s: Failed to read version of ROME (%d)", hdev->name, - err); - return err; - } - - if (skb->len != sizeof(*edl) + sizeof(*ver)) { - BT_ERR("%s: Version size mismatch len %d", hdev->name, - skb->len); - err = -EILSEQ; - goto out; - } - - edl = (struct edl_event_hdr *)(skb->data); - if (!edl) { - BT_ERR("%s: TLV with no header", hdev->name); - err = -EILSEQ; - goto out; - } - - if (edl->cresp != EDL_CMD_REQ_RES_EVT || - edl->rtype != EDL_APP_VER_RES_EVT) { - BT_ERR("%s: Wrong packet received %d %d", hdev->name, - edl->cresp, edl->rtype); - err = -EIO; - goto out; - } - - ver = (struct rome_version *)(edl->data); - - BT_DBG("%s: Product:0x%08x", hdev->name, le32_to_cpu(ver->product_id)); - BT_DBG("%s: Patch :0x%08x", hdev->name, le16_to_cpu(ver->patch_ver)); - BT_DBG("%s: ROM :0x%08x", hdev->name, le16_to_cpu(ver->rome_ver)); - BT_DBG("%s: SOC :0x%08x", hdev->name, le32_to_cpu(ver->soc_id)); - - /* ROME chipset version can be decided by patch and SoC - * version, combination with upper 2 bytes from SoC - * and lower 2 bytes from patch will be used. - */ - *rome_version = (le32_to_cpu(ver->soc_id) << 16) | - (le16_to_cpu(ver->rome_ver) & 0x0000ffff); - -out: - kfree_skb(skb); - - return err; -} - -static int rome_reset(struct hci_dev *hdev) +static int qca_btsoc_reset(struct hci_dev *hdev) { struct sk_buff *skb; int err; - BT_DBG("%s: ROME HCI_RESET", hdev->name); + BT_DBG("%s: ROME/wcn3990 HCI_RESET", hdev->name); skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT); if (IS_ERR(skb)) { @@ -108,7 +44,7 @@ static int rome_reset(struct hci_dev *hdev) return 0; } -static void rome_tlv_check_data(struct rome_config *config, +static void rome_tlv_check_data(struct qca_config *config, const struct firmware *fw) { const u8 *data; @@ -194,8 +130,121 @@ static void rome_tlv_check_data(struct rome_config *config, } } -static int rome_tlv_send_segment(struct hci_dev *hdev, int idx, int seg_size, - const u8 *data) +static void cherokee_tlv_check_data(struct qca_config *config, + const struct firmware *fw, bool *dwnd_flag) +{ + const u8 *data; + u32 type_len; + u16 tag_id, tag_len; + int idx, length; + struct tlv_type_hdr *tlv; + struct cherokee_tlv_type_patch *tlv_patch; + struct tlv_type_nvm *tlv_nvm; + + tlv = (struct tlv_type_hdr *)fw->data; + + type_len = le32_to_cpu(tlv->type_len); + length = (type_len >> 8) & 0x00ffffff; + + BT_DBG("TLV Type\t\t : 0x%x", type_len & 0x000000ff); + BT_DBG("Length\t\t : %d bytes", length); + + switch (config->type) { + case TLV_TYPE_PATCH: + tlv_patch = (struct tlv_type_patch *)tlv->data; + BT_DBG("Total Length\t\t : %d bytes", + le32_to_cpu(tlv_patch->total_size)); + BT_DBG("Patch Data Length\t : %d bytes", + le32_to_cpu(tlv_patch->data_length)); + BT_DBG("Signing Format Version : 0x%x", + tlv_patch->format_version); + BT_DBG("Signature Algorithm\t : 0x%x", + tlv_patch->signature); + BT_DBG("Download flag\t : 0x%x", + tlv_patch->dwnd_cfg); + BT_DBG("Reserved\t\t : 0x%x", + le16_to_cpu(tlv_patch->reserved1)); + BT_DBG("Product ID\t\t : 0x%04x", + le16_to_cpu(tlv_patch->product_id)); + BT_DBG("Rom Build Version\t : 0x%04x", + le16_to_cpu(tlv_patch->rom_build)); + BT_DBG("Patch Version\t\t : 0x%04x", + le16_to_cpu(tlv_patch->patch_version)); + BT_DBG("Reserved\t\t : 0x%x", + le16_to_cpu(tlv_patch->reserved2)); + BT_DBG("Patch Entry Address\t : 0x%x", + le32_to_cpu(tlv_patch->entry)); + /* If the download flag is 0x03, don't wait + * for response from soc,i.e. 1 to n-1 + * segment download. + */ + if (tlv_patch->dwnd_cfg == 0x03) + *dwnd_flag = false; + else + *dwnd_flag = true; + break; + + case TLV_TYPE_NVM: + idx = 0; + data = tlv->data; + while (idx < length) { + tlv_nvm = (struct tlv_type_nvm *)(data + idx); + tag_id = le16_to_cpu(tlv_nvm->tag_id); + tag_len = le16_to_cpu(tlv_nvm->tag_len); + + /* Update NVM tags as needed */ + switch (tag_id) { + case EDL_TAG_ID_HCI: + /* HCI transport layer parameters + * enabling software inband sleep + * onto controller side. + */ + tlv_nvm->data[0] |= 0x80; + + /* UART Baud Rate */ + tlv_nvm->data[2] = config->user_baud_rate; + break; + case EDL_TAG_ID_DEEP_SLEEP: + /* Sleep enable mask + * enabling deep sleep feature on controller. + */ + tlv_nvm->data[0] |= 0x01; + break; + } + + idx += (sizeof(u16) + sizeof(u16) + 8 + tag_len); + } + break; + default: + BT_ERR("Unknown TLV type %d", config->type); + break; + } +} + +static int cherokee_tlv_send_segment(struct hci_dev *hdev, int idx, + int seg_size, const u8 *data) +{ + u8 cmd[MAX_SIZE_PER_TLV_SEGMENT + 2]; + int err = 0; + + BT_DBG("%s: Download segment #%d size %d", hdev->name, idx, seg_size); + + cmd[0] = EDL_PATCH_TLV_REQ_CMD; + cmd[1] = seg_size; + memcpy(cmd + 2, data, seg_size); + + err = __hci_cmd_no_event(hdev, EDL_PATCH_CMD_OPCODE, seg_size + 2, + cmd); + if (err < 0) { + BT_ERR("%s: Failed to send TLV segment (%d)", hdev->name, err); + return err; + } + + return err; +} + +static int qca_btsoc_tlv_send_segment(struct hci_dev *hdev, int idx, + int seg_size, const u8 *data) { struct sk_buff *skb; struct edl_event_hdr *edl; @@ -264,16 +313,56 @@ static int rome_tlv_download_request(struct hci_dev *hdev, data = fw->data; for (i = 0; i < total_segment; i++) { buffer = data + i * MAX_SIZE_PER_TLV_SEGMENT; - ret = rome_tlv_send_segment(hdev, i, MAX_SIZE_PER_TLV_SEGMENT, - buffer); + ret = qca_btsoc_tlv_send_segment(hdev, i, + MAX_SIZE_PER_TLV_SEGMENT, buffer); if (ret < 0) return -EIO; } if (remain_size) { buffer = data + total_segment * MAX_SIZE_PER_TLV_SEGMENT; - ret = rome_tlv_send_segment(hdev, total_segment, remain_size, - buffer); + ret = qca_btsoc_tlv_send_segment(hdev, total_segment, + remain_size, buffer); + if (ret < 0) + return -EIO; + } + + return 0; +} +static int cherokee_tlv_download_request(struct hci_dev *hdev, + const struct firmware *fw) +{ + const u8 *buffer, *data; + int total_segment, remain_size; + int ret, i; + + if (!fw || !fw->data) + return -EINVAL; + + total_segment = fw->size / MAX_SIZE_PER_TLV_SEGMENT; + remain_size = fw->size % MAX_SIZE_PER_TLV_SEGMENT; + + BT_DBG("%s: Total segment num %d remain size %d total size %zu", + hdev->name, total_segment, remain_size, fw->size); + + data = fw->data; + for (i = 0; i < total_segment; i++) { + buffer = data + i * MAX_SIZE_PER_TLV_SEGMENT; + /* Read response from soc for last segment sent */ + if (!remain_size && ((i+1) == total_segment)) + ret = qca_btsoc_tlv_send_segment(hdev, i, + MAX_SIZE_PER_TLV_SEGMENT, buffer); + else + ret = cherokee_tlv_send_segment(hdev, i, + MAX_SIZE_PER_TLV_SEGMENT, buffer); + if (ret < 0) + return -EIO; + } + + if (remain_size) { + buffer = data + total_segment * MAX_SIZE_PER_TLV_SEGMENT; + ret = qca_btsoc_tlv_send_segment(hdev, total_segment, + remain_size, buffer); if (ret < 0) return -EIO; } @@ -282,7 +371,7 @@ static int rome_tlv_download_request(struct hci_dev *hdev, } static int rome_download_firmware(struct hci_dev *hdev, - struct rome_config *config) + struct qca_config *config) { const struct firmware *fw; int ret; @@ -309,7 +398,46 @@ static int rome_download_firmware(struct hci_dev *hdev, return ret; } -int qca_set_bdaddr_rome(struct hci_dev *hdev, const bdaddr_t *bdaddr) +static int cherokee_download_firmware(struct hci_dev *hdev, + struct qca_config *config) +{ + const struct firmware *fw; + bool dwnd_flag = true; + int ret; + + BT_INFO("%s: wcn3990 Downloading %s", hdev->name, config->fwname); + + ret = request_firmware(&fw, config->fwname, &hdev->dev); + if (ret) { + BT_ERR("%s: Failed to request file: %s (%d)", hdev->name, + config->fwname, ret); + return ret; + } + + cherokee_tlv_check_data(config, fw, &dwnd_flag); + /* check whether the download flag is set.if bit is enabled + * terminate the ram patch download. As we are not supporting, + * receiving of response from soc for every segment sent. + * We look for response from soc for last segment. + */ + if (dwnd_flag == true && config->type == TLV_TYPE_PATCH) { + BT_ERR("%s: btsoc download flag enabled", hdev->name); + return -EOPNOTSUPP; + } + + ret = cherokee_tlv_download_request(hdev, fw); + if (ret) { + BT_ERR("%s: Failed to download file: %s (%d)", hdev->name, + config->fwname, ret); + } + + release_firmware(fw); + + return ret; +} + + +int qca_btsoc_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr) { struct sk_buff *skb; u8 cmd[9]; @@ -332,12 +460,12 @@ int qca_set_bdaddr_rome(struct hci_dev *hdev, const bdaddr_t *bdaddr) return 0; } -EXPORT_SYMBOL_GPL(qca_set_bdaddr_rome); +EXPORT_SYMBOL_GPL(qca_btsoc_set_bdaddr); int qca_uart_setup_rome(struct hci_dev *hdev, uint8_t baudrate) { u32 rome_ver = 0; - struct rome_config config; + struct qca_config config; int err; BT_DBG("%s: ROME setup on UART", hdev->name); @@ -345,7 +473,7 @@ int qca_uart_setup_rome(struct hci_dev *hdev, uint8_t baudrate) config.user_baud_rate = baudrate; /* Get ROME version information */ - err = rome_patch_ver_req(hdev, &rome_ver); + err = qca_btsoc_patch_ver_req(hdev, &rome_ver); if (err < 0 || rome_ver == 0) { BT_ERR("%s: Failed to get version 0x%x", hdev->name, err); return err; @@ -374,7 +502,7 @@ int qca_uart_setup_rome(struct hci_dev *hdev, uint8_t baudrate) } /* Perform HCI reset */ - err = rome_reset(hdev); + err = qca_btsoc_reset(hdev); if (err < 0) { BT_ERR("%s: Failed to run HCI_RESET (%d)", hdev->name, err); return err; @@ -386,6 +514,111 @@ int qca_uart_setup_rome(struct hci_dev *hdev, uint8_t baudrate) } EXPORT_SYMBOL_GPL(qca_uart_setup_rome); +int qca_btsoc_patch_ver_req(struct hci_dev *hdev, u32 *soc_version) +{ + struct sk_buff *skb; + struct edl_event_hdr *edl; + struct qca_btsoc_version *ver; + char cmd; + int err = 0; + + BT_DBG("%s: BTSOC Patch Version Request", hdev->name); + + cmd = EDL_PATCH_VER_REQ_CMD; + skb = __hci_cmd_sync_ev(hdev, EDL_PATCH_CMD_OPCODE, EDL_PATCH_CMD_LEN, + &cmd, HCI_VENDOR_PKT, HCI_INIT_TIMEOUT); + if (IS_ERR(skb)) { + err = PTR_ERR(skb); + BT_ERR("%s: Failed to read version of ROME (%d)", hdev->name, + err); + return err; + } + + if (skb->len != sizeof(*edl) + sizeof(*ver)) { + BT_ERR("%s: Version size mismatch len %d", hdev->name, + skb->len); + err = -EILSEQ; + goto out; + } + + edl = (struct edl_event_hdr *)(skb->data); + if (!edl) { + BT_ERR("%s: TLV with no header", hdev->name); + err = -EILSEQ; + goto out; + } + + if (edl->cresp != EDL_CMD_REQ_RES_EVT || + edl->rtype != EDL_APP_VER_RES_EVT) { + BT_ERR("%s: Wrong packet received %d %d", hdev->name, + edl->cresp, edl->rtype); + err = -EIO; + goto out; + } + + ver = (struct qca_btsoc_version *)(edl->data); + + BT_DBG("%s: Product:0x%08x", hdev->name, le32_to_cpu(ver->product_id)); + BT_DBG("%s: Patch :0x%08x", hdev->name, le16_to_cpu(ver->patch_ver)); + BT_DBG("%s: ROM :0x%08x", hdev->name, le16_to_cpu(ver->btsoc_ver)); + BT_DBG("%s: SOC :0x%08x", hdev->name, le32_to_cpu(ver->soc_id)); + + /* BTSOC chipset version can be decided by patch and SoC + * version, combination with upper 2 bytes from SoC + * and lower 2 bytes from patch will be used. + */ + *soc_version = (le32_to_cpu(ver->soc_id) << 16) | + (le16_to_cpu(ver->btsoc_ver) & 0x0000ffff); + +out: + kfree_skb(skb); + + return err; +} +EXPORT_SYMBOL_GPL(qca_btsoc_patch_ver_req); + +int qca_uart_setup_cherokee(struct hci_dev *hdev, uint8_t baudrate, + u32 *cherokee_ver) +{ + struct qca_config config; + int err; + + BT_DBG("%s: wcn3990 setup on UART", hdev->name); + config.user_baud_rate = baudrate; + + /* Download rampatch file */ + config.type = TLV_TYPE_PATCH; + snprintf(config.fwname, sizeof(config.fwname), "qca/rampatch_%08x.tlv", + *cherokee_ver); + err = cherokee_download_firmware(hdev, &config); + if (err < 0) { + BT_ERR("%s: Failed to download patch (%d)", hdev->name, err); + return err; + } + + /* Download NVM configuration */ + config.type = TLV_TYPE_NVM; + snprintf(config.fwname, sizeof(config.fwname), "qca/nvm_%08x.bin", + *cherokee_ver); + err = cherokee_download_firmware(hdev, &config); + if (err < 0) { + BT_ERR("%s: Failed to download NVM (%d)", hdev->name, err); + return err; + } + + /* Perform HCI reset */ + err = qca_btsoc_reset(hdev); + if (err < 0) { + BT_ERR("%s: Failed to run HCI_RESET (%d)", hdev->name, err); + return err; + } + + BT_INFO("%s: wcn3990 setup on UART is completed", hdev->name); + + return 0; +} +EXPORT_SYMBOL_GPL(qca_uart_setup_cherokee); + MODULE_AUTHOR("Ben Young Tae Kim <ytkim@qca.qualcomm.com>"); MODULE_DESCRIPTION("Bluetooth support for Qualcomm Atheros family ver " VERSION); MODULE_VERSION(VERSION); diff --git a/drivers/bluetooth/btqca.h b/drivers/bluetooth/btqca.h index 65e994b..7608423 100644 --- a/drivers/bluetooth/btqca.h +++ b/drivers/bluetooth/btqca.h @@ -1,7 +1,7 @@ /* * Bluetooth supports for Qualcomm Atheros ROME chips * - * Copyright (c) 2015 The Linux Foundation. All rights reserved. + * Copyright (c) 2015, 2018 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 @@ -37,6 +37,9 @@ #define EDL_TAG_ID_HCI (17) #define EDL_TAG_ID_DEEP_SLEEP (27) +#define CHEROKEE_POWERON_PULSE (0xFC) +#define CHEROKEE_POWEROFF_PULSE (0xC0) + enum qca_bardrate { QCA_BAUDRATE_115200 = 0, QCA_BAUDRATE_57600, @@ -66,7 +69,7 @@ enum rome_tlv_type { TLV_TYPE_NVM }; -struct rome_config { +struct qca_config { u8 type; char fwname[64]; uint8_t user_baud_rate; @@ -78,13 +81,14 @@ struct edl_event_hdr { __u8 data[0]; } __packed; -struct rome_version { +struct qca_btsoc_version { __le32 product_id; __le16 patch_ver; - __le16 rome_ver; + __le16 btsoc_ver; __le32 soc_id; } __packed; + struct tlv_seg_resp { __u8 result; } __packed; @@ -102,6 +106,21 @@ struct tlv_type_patch { __le32 entry; } __packed; +struct cherokee_tlv_type_patch { + __le32 total_size; + __le32 data_length; + __u8 format_version; + __u8 signature; + __u8 dwnd_cfg; + __le16 reserved1; + __le16 product_id; + __le16 rom_build; + __le16 patch_version; + __le16 reserved2; + __le32 entry; +} __packed; + + struct tlv_type_nvm { __le16 tag_id; __le16 tag_len; @@ -115,14 +134,21 @@ struct tlv_type_hdr { __u8 data[0]; } __packed; +int qca_btsoc_cleanup(struct hci_dev *hdev); +int btqca_power_setup(bool on); + #if IS_ENABLED(CONFIG_BT_QCA) -int qca_set_bdaddr_rome(struct hci_dev *hdev, const bdaddr_t *bdaddr); +int qca_btsoc_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr); int qca_uart_setup_rome(struct hci_dev *hdev, uint8_t baudrate); +int qca_uart_setup_cherokee(struct hci_dev *hdev, uint8_t baudrate, + u32 *cherokee_ver); +int qca_btsoc_patch_ver_req(struct hci_dev *hdev, u32 *soc_version); #else -static inline int qca_set_bdaddr_rome(struct hci_dev *hdev, const bdaddr_t *bdaddr) +static inline int qca_btsoc_set_bdaddr(struct hci_dev *hdev, + const bdaddr_t *bdaddr) { return -EOPNOTSUPP; } @@ -132,4 +158,15 @@ static inline int qca_uart_setup_rome(struct hci_dev *hdev, int speed) return -EOPNOTSUPP; } +static inline int qca_uart_setup_cherokee(struct hci_dev *hdev, + uint8_t baudrate, u32 *cherokee_ver) +{ + return -EOPNOTSUPP; +} + +static int qca_btsoc_patch_ver_req(struct hci_dev *hdev, u32 *cherokee_version) +{ + return -EOPNOTSUPP; +} + #endif diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c index b7547e6..1343e63 100644 --- a/drivers/bluetooth/hci_qca.c +++ b/drivers/bluetooth/hci_qca.c @@ -5,7 +5,7 @@ * protocol extension to H4. * * Copyright (C) 2007 Texas Instruments, Inc. - * Copyright (c) 2010, 2012 The Linux Foundation. All rights reserved. + * Copyright (c) 2010, 2012, 2018 The Linux Foundation. All rights reserved. * * Acknowledgements: * This file is based on hci_ll.c, which was... @@ -35,9 +35,15 @@ #include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/serdev.h> - #include <net/bluetooth/bluetooth.h> #include <net/bluetooth/hci_core.h> +#include <asm-generic/delay.h> +#include <linux/tty.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/regulator/consumer.h> +#include <linux/of_device.h> #include "hci_uart.h" #include "btqca.h" @@ -84,12 +90,12 @@ struct qca_data { struct hci_uart *hu; struct sk_buff *rx_skb; struct sk_buff_head txq; - struct sk_buff_head tx_wait_q; /* HCI_IBS wait queue */ - spinlock_t hci_ibs_lock; /* HCI_IBS state lock */ - u8 tx_ibs_state; /* HCI_IBS transmit side power state*/ - u8 rx_ibs_state; /* HCI_IBS receive side power state */ - bool tx_vote; /* Clock must be on for TX */ - bool rx_vote; /* Clock must be on for RX */ + struct sk_buff_head tx_wait_q; /* HCI_IBS wait queue */ + spinlock_t hci_ibs_lock; /* HCI_IBS state lock */ + u8 tx_ibs_state; /* HCI_IBS transmit side power state*/ + u8 rx_ibs_state; /* HCI_IBS receive side power state */ + bool tx_vote; /* Clock must be on for TX */ + bool rx_vote; /* Clock must be on for RX */ struct timer_list tx_idle_timer; u32 tx_idle_delay; struct timer_list wake_retrans_timer; @@ -119,12 +125,49 @@ struct qca_data { u64 votes_off; }; +enum btqca_soc_t { + BTQCA_INVALID = -1, + BTQCA_AR3002, + BTQCA_ROME, + BTQCA_CHEROKEE +}; + struct qca_serdev { struct hci_uart serdev_hu; struct gpio_desc *bt_en; struct clk *susclk; + enum btqca_soc_t btsoc_type; +}; + +/* + * voltage regulator information required for configuring the + * QCA bluetooth chipset + */ +struct btqca_vreg { + const char *name; + unsigned int min_v; + unsigned int max_v; + unsigned int load_ua; }; +struct btqca_vreg_data { + enum btqca_soc_t soc_type; + struct btqca_vreg *vregs; + size_t num_vregs; +}; + +/* + * Platform data for the QCA bluetooth power driver. + */ +struct btqca_power { + struct device *dev; + struct btqca_vreg_data *vreg_data; + struct regulator_bulk_data *vreg_bulk; + bool vreg_status; +}; + +static struct btqca_power *qca; + static void __serial_clock_on(struct tty_struct *tty) { /* TODO: Some chipset requires to enable UART clock on client @@ -462,9 +505,11 @@ static int qca_open(struct hci_uart *hu) if (hu->serdev) { serdev_device_open(hu->serdev); - qcadev = serdev_device_get_drvdata(hu->serdev); - gpiod_set_value_cansleep(qcadev->bt_en, 1); + if (qcadev->btsoc_type == BTQCA_CHEROKEE) + btqca_power_setup(true); + else + gpiod_set_value_cansleep(qcadev->bt_en, 1); } BT_DBG("HCI_UART_QCA open, tx_idle_delay=%u, wake_retrans=%u", @@ -553,7 +598,10 @@ static int qca_close(struct hci_uart *hu) serdev_device_close(hu->serdev); qcadev = serdev_device_get_drvdata(hu->serdev); - gpiod_set_value_cansleep(qcadev->bt_en, 0); + if (qcadev->btsoc_type == BTQCA_CHEROKEE) + qca_btsoc_cleanup(hu->hdev); + else + gpiod_set_value_cansleep(qcadev->bt_en, 0); } kfree_skb(qca->rx_skb); @@ -873,6 +921,8 @@ static uint8_t qca_get_baudrate_value(int speed) return QCA_BAUDRATE_2000000; case 3000000: return QCA_BAUDRATE_3000000; + case 3200000: + return QCA_BAUDRATE_3200000; case 3500000: return QCA_BAUDRATE_3500000; default: @@ -887,7 +937,7 @@ static int qca_set_baudrate(struct hci_dev *hdev, uint8_t baudrate) struct sk_buff *skb; u8 cmd[] = { 0x01, 0x48, 0xFC, 0x01, 0x00 }; - if (baudrate > QCA_BAUDRATE_3000000) + if (baudrate > QCA_BAUDRATE_3200000) return -EINVAL; cmd[4] = baudrate; @@ -924,62 +974,263 @@ static inline void host_set_baudrate(struct hci_uart *hu, unsigned int speed) hci_uart_set_baudrate(hu, speed); } +static int qca_send_poweron_cmd(struct hci_dev *hdev) +{ + struct hci_uart *hu = hci_get_drvdata(hdev); + struct qca_data *qca = hu->priv; + struct sk_buff *skb; + u8 cmd; + + BT_DBG("%s sending power on command to btsoc", hdev->name); + /* By sending 0xFC host is trying to power up the soc */ + cmd = CHEROKEE_POWERON_PULSE; + skb = bt_skb_alloc(sizeof(cmd), GFP_ATOMIC); + if (!skb) { + BT_ERR("Failed to allocate memory for skb packet"); + return -ENOMEM; + } + + skb_put_data(skb, &cmd, sizeof(cmd)); + hci_skb_pkt_type(skb) = HCI_COMMAND_PKT; + + skb_queue_tail(&qca->txq, skb); + hci_uart_tx_wakeup(hu); + + /* Wait for 100 us for soc to settle down */ + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(usecs_to_jiffies(100)); + set_current_state(TASK_INTERRUPTIBLE); + + return 0; +} + +static int qca_send_poweroff_cmd(struct hci_dev *hdev) +{ + struct hci_uart *hu = hci_get_drvdata(hdev); + struct qca_data *qca = hu->priv; + struct sk_buff *skb; + u8 cmd; + + BT_DBG("%s sending power off command to btsoc", hdev->name); + /* By sending 0xC0 host is trying to power off the soc */ + cmd = CHEROKEE_POWEROFF_PULSE; + skb = bt_skb_alloc(sizeof(cmd), GFP_ATOMIC); + if (!skb) { + BT_ERR("Failed to allocate memory for skb packet"); + return -ENOMEM; + } + + skb_put_data(skb, &cmd, sizeof(cmd)); + hci_skb_pkt_type(skb) = HCI_COMMAND_PKT; + + skb_queue_tail(&qca->txq, skb); + hci_uart_tx_wakeup(hu); + + /* Wait for 100 us for soc to settle down */ + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(usecs_to_jiffies(100)); + set_current_state(TASK_INTERRUPTIBLE); + + return 0; +} + +static int qca_serdev_open(struct hci_uart *hu) +{ + int ret = 0; + + if (hu->serdev) + serdev_device_open(hu->serdev); + else { + BT_ERR("%s:open operation not supported", hu->hdev->name); + ret = 1; + } + + return ret; +} + +int qca_btsoc_cleanup(struct hci_dev *hdev) +{ + struct hci_uart *hu = hci_get_drvdata(hdev); + + /* change host baud rate before sending power off command */ + host_set_baudrate(hu, 2400); + /* send 0xC0 command to btsoc before turning off regulators */ + qca_send_poweroff_cmd(hdev); + /* turn off btsoc */ + return btqca_power_setup(false); +} + +static int qca_serdev_close(struct hci_uart *hu) +{ + int ret = 0; + + if (hu->serdev) + serdev_device_close(hu->serdev); + else { + BT_ERR("%s:close operation not supported", hu->hdev->name); + ret = 1; + } + + return ret; +} + static int qca_setup(struct hci_uart *hu) { struct hci_dev *hdev = hu->hdev; struct qca_data *qca = hu->priv; + struct qca_serdev *qcadev; unsigned int speed, qca_baudrate = QCA_BAUDRATE_115200; int ret; + unsigned int soc_ver; + + qcadev = serdev_device_get_drvdata(hu->serdev); + + switch (qcadev->btsoc_type) { + case BTQCA_CHEROKEE: + + BT_INFO("%s:setting up wcn3990", hdev->name); + /* Patch downloading has to be done without IBS mode */ + clear_bit(STATE_IN_BAND_SLEEP_ENABLED, &qca->flags); + /* Setup initial baudrate */ + speed = 0; + if (hu->init_speed) + speed = hu->init_speed; + else if (hu->proto->init_speed) + speed = hu->proto->init_speed; + + if (speed) + host_set_baudrate(hu, speed); + else { + BT_ERR("%s:initial speed %u", hdev->name, speed); + return -1; + } - BT_INFO("%s: ROME setup", hdev->name); + /* clear flow control- for sync cmd*/ + hci_uart_set_flow_control(hu, true); + /* send poweron command to btsoc */ + ret = qca_send_poweron_cmd(hdev); + if (ret) { + BT_ERR("%s:sending sync command failed", hdev->name); + return ret; + } + + /* close port */ + ret = qca_serdev_close(hu); + if (ret) + return ret; + /* reopen port */ + ret = qca_serdev_open(hu); + if (ret) + return ret; + /* Setup initial baudrate */ + speed = 0; + if (hu->init_speed) + speed = hu->init_speed; + else if (hu->proto->init_speed) + speed = hu->proto->init_speed; + if (speed) + host_set_baudrate(hu, speed); + else { + BT_ERR("%s:initial speed %u", hdev->name, speed); + return -1; + } - /* Patch downloading has to be done without IBS mode */ - clear_bit(STATE_IN_BAND_SLEEP_ENABLED, &qca->flags); + /* Enable flow control */ + hci_uart_set_flow_control(hu, false); + /* wait until flow control settled */ + mdelay(100); - /* Setup initial baudrate */ - speed = 0; - if (hu->init_speed) - speed = hu->init_speed; - else if (hu->proto->init_speed) - speed = hu->proto->init_speed; + ret = qca_btsoc_patch_ver_req(hdev, &soc_ver); + if (ret < 0 || soc_ver == 0) { + BT_ERR("%s: Failed to get version 0x%x", hdev->name, + ret); + return ret; + } - if (speed) - host_set_baudrate(hu, speed); + BT_INFO("%s:wcn3990 controller version 0x%08x", hdev->name, + soc_ver); - /* Setup user speed if needed */ - speed = 0; - if (hu->oper_speed) - speed = hu->oper_speed; - else if (hu->proto->oper_speed) + /* clear flow control */ + hci_uart_set_flow_control(hu, true); + /* set operating speed */ speed = hu->proto->oper_speed; + if (speed) { + qca_baudrate = qca_get_baudrate_value(speed); + BT_INFO("%s: Set UART speed to %d", hdev->name, speed); + ret = qca_set_baudrate(hdev, qca_baudrate); + if (ret) { + BT_ERR("%s:Failed to change the baud rate(%d)", + hdev->name, ret); + return ret; + } + if (speed) + host_set_baudrate(hu, speed); + else { + BT_ERR("%s:Error in setting operator speed:%u", + hdev->name, speed); + return -1; + } + } - if (speed) { - qca_baudrate = qca_get_baudrate_value(speed); + /* Set flow control */ + hci_uart_set_flow_control(hu, false); + /*Setup patch and NVM configurations */ + ret = qca_uart_setup_cherokee(hdev, qca_baudrate, &soc_ver); + if (!ret) { + set_bit(STATE_IN_BAND_SLEEP_ENABLED, &qca->flags); + qca_debugfs_init(hdev); + } - BT_INFO("%s: Set UART speed to %d", hdev->name, speed); - ret = qca_set_baudrate(hdev, qca_baudrate); - if (ret) { - BT_ERR("%s: Failed to change the baud rate (%d)", - hdev->name, ret); - return ret; + /* Setup wcn3990 bdaddr */ + hu->hdev->set_bdaddr = qca_btsoc_set_bdaddr; + + return ret; + + default: + BT_INFO("%s: ROME setup", hdev->name); + /* Patch downloading has to be done without IBS mode */ + clear_bit(STATE_IN_BAND_SLEEP_ENABLED, &qca->flags); + /* Setup initial baudrate */ + speed = 0; + if (hu->init_speed) + speed = hu->init_speed; + else if (hu->proto->init_speed) + speed = hu->proto->init_speed; + if (speed) + hci_uart_set_baudrate(hu, speed); + + /* Setup user speed if needed */ + speed = 0; + if (hu->oper_speed) + speed = hu->oper_speed; + else if (hu->proto->oper_speed) + speed = hu->proto->oper_speed; + + if (speed) { + qca_baudrate = qca_get_baudrate_value(speed); + + BT_INFO("%s: Set UART speed to %d", hdev->name, speed); + ret = qca_set_baudrate(hdev, qca_baudrate); + if (ret) { + BT_ERR("%s:Failed to change the baud rate(%d)", + hdev->name, ret); + return ret; + } + host_set_baudrate(hu, speed); } - host_set_baudrate(hu, speed); - } - /* Setup patch / NVM configurations */ - ret = qca_uart_setup_rome(hdev, qca_baudrate); - if (!ret) { - set_bit(STATE_IN_BAND_SLEEP_ENABLED, &qca->flags); - qca_debugfs_init(hdev); - } else if (ret == -ENOENT) { - /* No patch/nvm-config found, run with original fw/config */ - ret = 0; - } + /* Setup patch / NVM configurations */ + ret = qca_uart_setup_rome(hdev, qca_baudrate); + if (!ret) { + set_bit(STATE_IN_BAND_SLEEP_ENABLED, &qca->flags); + qca_debugfs_init(hdev); + } - /* Setup bdaddr */ - hu->hdev->set_bdaddr = qca_set_bdaddr_rome; + /* Setup bdaddr */ + hu->hdev->set_bdaddr = qca_btsoc_set_bdaddr; - return ret; + return ret; + } } static struct hci_uart_proto qca_proto = { @@ -997,42 +1248,142 @@ static int qca_setup(struct hci_uart *hu) .dequeue = qca_dequeue, }; +static const struct btqca_vreg_data cherokee_data = { + .soc_type = BTQCA_CHEROKEE, + .vregs = (struct btqca_vreg []) { + { "vddio", 1352000, 1352000, 0 }, + { "vddxtal", 1904000, 2040000, 0 }, + { "vddcore", 1800000, 1800000, 1 }, + { "vddpa", 130400, 1304000, 1 }, + { "vddldo", 3000000, 3312000, 1 }, + { "vddpwd", 3312000, 3600000, 0 }, + }, + .num_vregs = 6, +}; + +int btqca_power_setup(bool on) +{ + int ret = 0; + int i; + struct btqca_vreg *vregs; + + if (!qca || !qca->vreg_data || !qca->vreg_bulk) + return -EINVAL; + vregs = qca->vreg_data->vregs; + + BT_DBG("on: %d", on); + + /* turn on if regualtors are off */ + if (on == true && qca->vreg_status == false) { + qca->vreg_status = true; + for (i = 0; i < qca->vreg_data->num_vregs; i++) { + regulator_set_voltage(qca->vreg_bulk[i].consumer, + vregs[i].min_v, + vregs[i].max_v); + + if (vregs[i].load_ua) + regulator_set_load(qca->vreg_bulk[i].consumer, + vregs[i].load_ua); + + regulator_enable(qca->vreg_bulk[i].consumer); + } + } else if (on == false && qca->vreg_status == true) { + qca->vreg_status = false; + for (i = 0; i < qca->vreg_data->num_vregs; i++) { + regulator_disable(qca->vreg_bulk[i].consumer); + regulator_set_voltage(qca->vreg_bulk[i].consumer, + 0, vregs[i].max_v); + if (vregs[i].load_ua) + regulator_set_load(qca->vreg_bulk[i].consumer, + 0); + } + } + + return ret; +} + +static int init_regulators(struct btqca_power *qca, + const struct btqca_vreg *vregs, + size_t num_vregs) +{ + int i; + + qca->vreg_bulk = devm_kzalloc(qca->dev, num_vregs * + sizeof(struct regulator_bulk_data), + GFP_KERNEL); + if (!qca->vreg_bulk) + return -ENOMEM; + + for (i = 0; i < num_vregs; i++) + qca->vreg_bulk[i].supply = vregs[i].name; + + return devm_regulator_bulk_get(qca->dev, num_vregs, qca->vreg_bulk); +} + static int qca_serdev_probe(struct serdev_device *serdev) { struct qca_serdev *qcadev; - int err; + struct btqca_vreg_data *data; + int err = 0; qcadev = devm_kzalloc(&serdev->dev, sizeof(*qcadev), GFP_KERNEL); if (!qcadev) return -ENOMEM; qcadev->serdev_hu.serdev = serdev; + data = of_device_get_match_data(&serdev->dev); + if (data && data->soc_type == BTQCA_CHEROKEE) + qcadev->btsoc_type = BTQCA_CHEROKEE; + else + qcadev->btsoc_type = BTQCA_ROME; + serdev_device_set_drvdata(serdev, qcadev); + if (qcadev->btsoc_type == BTQCA_CHEROKEE) { + qca = kzalloc(sizeof(struct btqca_power), GFP_KERNEL); + if (!qca) + return -ENOMEM; + + qca->dev = &serdev->dev; + qca->vreg_data = data; + err = init_regulators(qca, data->vregs, data->num_vregs); + if (err) { + BT_ERR("Failed to init regualtors:%d", err); + kfree(qca); + } - qcadev->bt_en = devm_gpiod_get(&serdev->dev, "enable", - GPIOD_OUT_LOW); - if (IS_ERR(qcadev->bt_en)) { - dev_err(&serdev->dev, "failed to acquire enable gpio\n"); - return PTR_ERR(qcadev->bt_en); - } + /* set voltage regulator status as false */ + qca->vreg_status = false; + err = hci_uart_register_device(&qcadev->serdev_hu, &qca_proto); + if (err) { + BT_ERR("wcn3990 serdev registration failed"); + kfree(qca); + } + } else { + qcadev->bt_en = devm_gpiod_get(&serdev->dev, "enable", + GPIOD_OUT_LOW); + if (IS_ERR(qcadev->bt_en)) { + dev_err(&serdev->dev, "failed to acquire enable gpio\n"); + return PTR_ERR(qcadev->bt_en); + } - qcadev->susclk = devm_clk_get(&serdev->dev, NULL); - if (IS_ERR(qcadev->susclk)) { - dev_err(&serdev->dev, "failed to acquire clk\n"); - return PTR_ERR(qcadev->susclk); - } + qcadev->susclk = devm_clk_get(&serdev->dev, NULL); + if (IS_ERR(qcadev->susclk)) { + dev_err(&serdev->dev, "failed to acquire clk\n"); + return PTR_ERR(qcadev->susclk); + } - err = clk_set_rate(qcadev->susclk, SUSCLK_RATE_32KHZ); - if (err) - return err; + err = clk_set_rate(qcadev->susclk, SUSCLK_RATE_32KHZ); + if (err) + return err; - err = clk_prepare_enable(qcadev->susclk); - if (err) - return err; + err = clk_prepare_enable(qcadev->susclk); + if (err) + return err; - err = hci_uart_register_device(&qcadev->serdev_hu, &qca_proto); - if (err) - clk_disable_unprepare(qcadev->susclk); + err = hci_uart_register_device(&qcadev->serdev_hu, &qca_proto); + if (err) + clk_disable_unprepare(qcadev->susclk); + } return err; } @@ -1042,12 +1393,16 @@ static void qca_serdev_remove(struct serdev_device *serdev) struct qca_serdev *qcadev = serdev_device_get_drvdata(serdev); hci_uart_unregister_device(&qcadev->serdev_hu); - - clk_disable_unprepare(qcadev->susclk); + if (qcadev->btsoc_type == BTQCA_CHEROKEE) { + btqca_power_setup(false); + kfree(qca); + } else + clk_disable_unprepare(qcadev->susclk); } static const struct of_device_id qca_bluetooth_of_match[] = { { .compatible = "qcom,qca6174-bt" }, + { .compatible = "qca,wcn3990-bt", .data = &cherokee_data}, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, qca_bluetooth_of_match); diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index c10cb79..b932ec50 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1418,7 +1418,8 @@ struct sk_buff *__hci_cmd_sync(struct hci_dev *hdev, u16 opcode, u32 plen, const void *param, u32 timeout); struct sk_buff *__hci_cmd_sync_ev(struct hci_dev *hdev, u16 opcode, u32 plen, const void *param, u8 event, u32 timeout); - +int __hci_cmd_no_event(struct hci_dev *hdev, u16 opcode, u32 plen, + const void *param); int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen, const void *param); void hci_send_acl(struct hci_chan *chan, struct sk_buff *skb, __u16 flags); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 97ef85e..8addd97 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3370,7 +3370,7 @@ int hci_unregister_cb(struct hci_cb *cb) } EXPORT_SYMBOL(hci_unregister_cb); -static void hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb) +void hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb) { int err; diff --git a/net/bluetooth/hci_request.c b/net/bluetooth/hci_request.c index f7d6ba6..1ffee59a 100644 --- a/net/bluetooth/hci_request.c +++ b/net/bluetooth/hci_request.c @@ -114,6 +114,29 @@ void hci_req_sync_cancel(struct hci_dev *hdev, int err) } } +/* Queue a frame to an asynchronous transfer to btdevice, + * with out any event from btdevice. + */ +int __hci_cmd_no_event(struct hci_dev *hdev, u16 opcode, u32 plen, + const void *param) +{ + struct sk_buff *skb; + + BT_DBG("%s opcode 0x%4.4x plen %d", hdev->name, opcode, plen); + + skb = hci_prepare_cmd(hdev, opcode, plen, param); + if (!skb) { + BT_ERR("%s no memory for command (opcode 0x%4.4x)", + hdev->name, opcode); + return -ENOMEM; + } + + hci_send_frame(hdev, skb); + + return 0; +} +EXPORT_SYMBOL(__hci_cmd_no_event); + struct sk_buff *__hci_cmd_sync_ev(struct hci_dev *hdev, u16 opcode, u32 plen, const void *param, u8 event, u32 timeout) { diff --git a/net/bluetooth/hci_request.h b/net/bluetooth/hci_request.h index dde77bd..a855edd 100644 --- a/net/bluetooth/hci_request.h +++ b/net/bluetooth/hci_request.h @@ -128,3 +128,5 @@ static inline u16 eir_append_le16(u8 *eir, u16 eir_len, u8 type, u16 data) return eir_len; } + +void hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb);
- Add support to set voltage/current of various regualtors to power up/down BT QCA chip wcn3990. - Add firmware download support for BT QCA chip wcn3990. Signed-off-by: Balakrishna Godavarthi <bgodavar@codeaurora.org> --- drivers/bluetooth/btqca.c | 393 +++++++++++++++++++++++------- drivers/bluetooth/btqca.h | 49 +++- drivers/bluetooth/hci_qca.c | 501 +++++++++++++++++++++++++++++++++------ include/net/bluetooth/hci_core.h | 3 +- net/bluetooth/hci_core.c | 2 +- net/bluetooth/hci_request.c | 23 ++ net/bluetooth/hci_request.h | 2 + 7 files changed, 812 insertions(+), 161 deletions(-)