Bluetooth: x86-Rome driver support

Add USB client driver support for Rome USB chip.

Change-Id: I781dcdc913291ab0695ced1b34fde2710049586e
Signed-off-by: Bhasker Neti <bneti@codeaurora.org>
This commit is contained in:
Bhasker Neti 2014-03-30 15:28:58 +05:30
parent 480c71781b
commit f38c173fe1
3 changed files with 154 additions and 17 deletions

View File

@ -1,5 +1,6 @@
/*
* Copyright (c) 2008-2009 Atheros Communications Inc.
* Copyright (c) 2014 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 as published by
@ -32,6 +33,19 @@
#define VERSION "1.0"
#define ATH3K_FIRMWARE "ath3k-1.fw"
#define ROME2_1_USB_RAMPATCH_FILE "ar3k/rampatch_2.1.tlv"
#define ROME2_1_USB_NVM_FILE "ar3k/nvm_tlv_usb_2.1.bin"
#define ROME1_1_USB_RAMPATCH_FILE "ar3k/rampatch_1.1.img"
#define ROME1_1_USB_NVM_FILE "ar3k/nvm_tlv_usb_1.1.bin"
#define ROME2_1_USB_RAMPATCH_HEADER sizeof(struct rome2_1_version)
#define ROME1_1_USB_RAMPATCH_HEADER sizeof(struct rome1_1_version)
#define ROME1_1_USB_NVM_HEADER 0x04
#define ROME2_1_USB_NVM_HEADER 0x04
#define ROME2_1_USB_CHIP_VERSION 0x200
#define ROME1_1_USB_CHIP_VERSION 0x101
#define ATH3K_DNLOAD 0x01
#define ATH3K_GETSTATE 0x05
#define ATH3K_SET_NORMAL_MODE 0x07
@ -57,6 +71,34 @@ struct ath3k_version {
unsigned char reserved[0x07];
};
struct __packed rome1_1_version {
u8 type;
u8 length[3];
u8 sign_ver;
u8 sign_algo;
u8 resv1[2];
u16 product_id;
u16 build_ver;
u16 patch_ver;
u8 resv2[2];
u32 entry_addr;
};
struct __packed rome2_1_version {
u8 type;
u8 length[3];
u32 total_len;
u32 patch_len;
u8 sign_ver;
u8 sign_algo;
u8 resv1[2];
u16 product_id;
u16 build_ver;
u16 patch_ver;
u8 resv2[2];
u32 entry_addr;
};
static struct usb_device_id ath3k_table[] = {
/* Atheros AR3011 */
{ USB_DEVICE(0x0CF3, 0x3000) },
@ -243,8 +285,27 @@ static int ath3k_get_version(struct usb_device *udev,
return ret;
}
int get_rome_version(struct usb_device *udev)
{
struct ath3k_version fw_version;
int ret = 0;
ret = ath3k_get_version(udev, &fw_version);
if (ret < 0) {
BT_ERR("Failed to get Rome Firmware version");
return ret;
}
if ((fw_version.rom_version == ROME2_1_USB_CHIP_VERSION) ||
(fw_version.rom_version == ROME1_1_USB_CHIP_VERSION))
ret = fw_version.rom_version;
else
ret = 0;
return ret;
}
EXPORT_SYMBOL(get_rome_version);
static int ath3k_load_fwfile(struct usb_device *udev,
const struct firmware *firmware)
const struct firmware *firmware, int header_h)
{
u8 *send_buf;
int err, pipe, len, size, count, sent = 0;
@ -258,7 +319,7 @@ static int ath3k_load_fwfile(struct usb_device *udev,
return -ENOMEM;
}
size = min_t(uint, count, FW_HDR_SIZE);
size = min_t(uint, count, header_h);
memcpy(send_buf, firmware->data, size);
pipe = usb_sndctrlpipe(udev, 0);
@ -334,6 +395,8 @@ static int ath3k_load_patch(struct usb_device *udev)
char filename[ATH3K_NAME_LEN] = {0};
const struct firmware *firmware;
struct ath3k_version fw_version, pt_version;
struct rome2_1_version *rome2_1_version;
struct rome1_1_version *rome1_1_version;
int ret;
ret = ath3k_get_state(udev, &fw_state);
@ -352,20 +415,45 @@ static int ath3k_load_patch(struct usb_device *udev)
BT_ERR("Can't get version to change to load ram patch err");
return ret;
}
snprintf(filename, ATH3K_NAME_LEN, "ar3k/AthrBT_0x%08x.dfu",
if (fw_version.rom_version == ROME1_1_USB_CHIP_VERSION) {
BT_DBG("Chip Detected as ROME1.1");
snprintf(filename, ATH3K_NAME_LEN, ROME1_1_USB_RAMPATCH_FILE);
} else if (fw_version.rom_version == ROME2_1_USB_CHIP_VERSION) {
BT_DBG("Chip Detected as ROME2.1");
snprintf(filename, ATH3K_NAME_LEN, ROME2_1_USB_RAMPATCH_FILE);
} else {
BT_DBG("Chip Detected as Ath3k");
snprintf(filename, ATH3K_NAME_LEN, "ar3k/AthrBT_0x%08x.dfu",
fw_version.rom_version);
}
ret = request_firmware(&firmware, filename, &udev->dev);
if (ret < 0) {
BT_ERR("Patch file not found %s", filename);
return ret;
}
pt_version.rom_version = *(int *)(firmware->data + firmware->size - 8);
pt_version.build_version = *(int *)
if (fw_version.rom_version == ROME2_1_USB_CHIP_VERSION) {
rome2_1_version = (struct rome2_1_version *) firmware->data;
pt_version.rom_version = rome2_1_version->build_ver;
pt_version.build_version = rome2_1_version->patch_ver;
BT_DBG("pt_ver.rom2.1_ver : 0x%x", pt_version.rom_version);
BT_DBG("pt_ver.build2.1_ver: 0x%x", pt_version.build_version);
BT_DBG("fw_ver.rom2.1_ver: 0x%x", fw_version.rom_version);
BT_DBG("fw_ver.build2.1_ver: 0x%x", fw_version.build_version);
} else if (fw_version.rom_version == ROME1_1_USB_CHIP_VERSION) {
rome1_1_version = (struct rome1_1_version *) firmware->data;
pt_version.build_version = rome1_1_version->build_ver;
pt_version.rom_version = rome1_1_version->patch_ver;
BT_DBG("pt_ver.rom1.1_ver : 0x%x", pt_version.rom_version);
BT_DBG("pt_ver.build1.1_ver: 0x%x", pt_version.build_version);
BT_DBG("fw_ver.rom1.1_ver: 0x%x", fw_version.rom_version);
BT_DBG("fw_ver.build1.1_ver: 0x%x", fw_version.build_version);
} else {
pt_version.rom_version = *(int *)(firmware->data +
firmware->size - 8);
pt_version.build_version = *(int *)
(firmware->data + firmware->size - 4);
}
if ((pt_version.rom_version != fw_version.rom_version) ||
(pt_version.build_version <= fw_version.build_version)) {
BT_ERR("Patch file version did not match with firmware");
@ -373,7 +461,15 @@ static int ath3k_load_patch(struct usb_device *udev)
return -EINVAL;
}
ret = ath3k_load_fwfile(udev, firmware);
if (fw_version.rom_version == ROME2_1_USB_CHIP_VERSION)
ret = ath3k_load_fwfile(udev, firmware,
ROME2_1_USB_RAMPATCH_HEADER);
else if (fw_version.rom_version == ROME1_1_USB_CHIP_VERSION)
ret = ath3k_load_fwfile(udev, firmware,
ROME1_1_USB_RAMPATCH_HEADER);
else
ret = ath3k_load_fwfile(udev, firmware, FW_HDR_SIZE);
release_firmware(firmware);
return ret;
@ -415,8 +511,13 @@ static int ath3k_load_syscfg(struct usb_device *udev)
break;
}
snprintf(filename, ATH3K_NAME_LEN, "ar3k/ramps_0x%08x_%d%s",
fw_version.rom_version, clk_value, ".dfu");
if (fw_version.rom_version == ROME2_1_USB_CHIP_VERSION)
snprintf(filename, ATH3K_NAME_LEN, ROME2_1_USB_NVM_FILE);
else if (fw_version.rom_version == ROME1_1_USB_CHIP_VERSION)
snprintf(filename, ATH3K_NAME_LEN, ROME1_1_USB_NVM_FILE);
else
snprintf(filename, ATH3K_NAME_LEN, "ar3k/ramps_0x%08x_%d%s",
fw_version.rom_version, clk_value, ".dfu");
ret = request_firmware(&firmware, filename, &udev->dev);
if (ret < 0) {
@ -424,12 +525,36 @@ static int ath3k_load_syscfg(struct usb_device *udev)
return ret;
}
ret = ath3k_load_fwfile(udev, firmware);
if (fw_version.rom_version == ROME2_1_USB_CHIP_VERSION)
ret = ath3k_load_fwfile(udev, firmware, ROME2_1_USB_NVM_HEADER);
else if (fw_version.rom_version == ROME1_1_USB_CHIP_VERSION)
ret = ath3k_load_fwfile(udev, firmware, ROME1_1_USB_NVM_HEADER);
else
ret = ath3k_load_fwfile(udev, firmware, FW_HDR_SIZE);
release_firmware(firmware);
return ret;
}
int rome_download(struct usb_device *udev)
{
int ret;
ret = ath3k_load_patch(udev);
if (ret < 0) {
BT_ERR("Loading patch file failed");
return ret;
}
ret = ath3k_load_syscfg(udev);
if (ret < 0) {
BT_ERR("Loading sysconfig file failed");
return ret;
}
return ret;
}
EXPORT_SYMBOL(rome_download);
static int ath3k_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
@ -441,7 +566,10 @@ static int ath3k_probe(struct usb_interface *intf,
if (intf->cur_altsetting->desc.bInterfaceNumber != 0)
return -ENODEV;
if (get_rome_version(udev)) {
BT_INFO("Rome detected, fw dnld will be triggered from btusb");
return -ENODEV;
}
/* match device ID in ath3k blacklist table */
if (!id->driver_info) {
const struct usb_device_id *match;

View File

@ -4,7 +4,6 @@
*
* Copyright (C) 2005-2008 Marcel Holtmann <marcel@holtmann.org>
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
@ -1332,7 +1331,7 @@ static int btusb_probe(struct usb_interface *intf,
struct usb_endpoint_descriptor *ep_desc;
struct btusb_data *data;
struct hci_dev *hdev;
int i, err;
int i, version, err;
BT_DBG("intf %p id %p", intf, id);
@ -1362,12 +1361,17 @@ static int btusb_probe(struct usb_interface *intf,
if (id->driver_info & BTUSB_ATH3012) {
struct usb_device *udev = interface_to_usbdev(intf);
version = get_rome_version(udev);
BT_INFO("Rome Version: 0x%x", version);
/* Old firmware would otherwise let ath3k driver load
* patch and sysconfig files */
if (le16_to_cpu(udev->descriptor.bcdDevice) <= 0x0001)
if (version)
rome_download(udev);
else if (le16_to_cpu(udev->descriptor.bcdDevice) <= 0x0001) {
BT_INFO("FW for ar3k is yet to be downloaded");
return -ENODEV;
}
}
data = devm_kzalloc(&intf->dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;

View File

@ -1,6 +1,7 @@
/*
BlueZ - Bluetooth protocol stack for Linux
Copyright (C) 2000-2001 Qualcomm Incorporated
Copyright (C) 2014 The Linux Foundation. All rights reserved.
Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
@ -28,6 +29,7 @@
#include <linux/poll.h>
#include <net/sock.h>
#include <linux/seq_file.h>
#include <linux/usb.h>
#ifndef AF_BLUETOOTH
#define AF_BLUETOOTH 31
@ -344,4 +346,7 @@ void sco_exit(void);
void bt_sock_reclassify_lock(struct sock *sk, int proto);
int get_rome_version(struct usb_device *udev);
int rome_download(struct usb_device *udev);
#endif /* __BLUETOOTH_H */