blob: ac97c0210c14d6ef6fa28f088dca58fabf4a682f [file] [log] [blame]
/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */
/*
* common/rtk_fw.c
*
* Copyright (C) 2020 Amlogic, Inc. All rights reserved.
*
*/
/******************************************************************************
* Copyright (c) 2009-2015 by Hisi.
* All rights reserved.
*
* Create by Chenzetian. 2015-11-16
*
******************************************************************************/
#include <common.h>
#include <command.h>
#include <asm/processor.h>
#include <linux/compiler.h>
#include <linux/ctype.h>
#include <asm/byteorder.h>
#include <asm/unaligned.h>
#include <malloc.h>
#include <usb.h>
#include <rtk_fw.h>
#define USB_ERROR(fmt, args...) printf(fmt, ##args)
#define USB_DEBUG(fmt, args...) printf(fmt, ##args)
#define CHIP_TYPE_RTL8723BU "fw_8723b_b.txt"
#define CHIP_TYPE_RTL8723DU "fw_8723du_c.txt"
#define CHIP_TYPE_RTL8822BU "fw_8822b_b.txt"
#define CODE_MAXSIZE_24K (1024*24) //24K
#define CODE_MAXSIZE_40K (1024*40) //40K
#define code_maxsize 23639//56 * 10K
int fw_8723bu=0;
int fw_8723du=0;
int fw_8822bu=0;
//patch txt formate: total length(2 bytes) || fw patch || config patch
static unsigned char lu8Code_RTL8723BU[CODE_MAXSIZE_24K] = {
#include CHIP_TYPE_RTL8723BU
};
static unsigned char lu8Code_RTL8723DU[CODE_MAXSIZE_40K] = {
#include CHIP_TYPE_RTL8723DU
};
static unsigned char lu8Code_RTL8822BU[code_maxsize] = {
#include CHIP_TYPE_RTL8822BU
};
firmware_info *firmware_info_init(struct usb_device *dev)
{
struct usb_device *udev = dev;
firmware_info *fw_info;
USB_DEBUG("%s[%d]: start.\n", __func__, __LINE__);
fw_info = malloc(sizeof(*fw_info));
memset(fw_info,0,sizeof(*fw_info));
if (!fw_info)
return NULL;
if (fw_8723bu) {
fw_info->fw_data = malloc(CODE_MAXSIZE_24K);
memset(fw_info->fw_data,0,CODE_MAXSIZE_24K);
}
if (fw_8723du) {
fw_info->fw_data = malloc(CODE_MAXSIZE_40K);
memset(fw_info->fw_data,0,CODE_MAXSIZE_40K);
}
if (fw_8822bu) {
fw_info->fw_data = malloc(code_maxsize);
memset(fw_info->fw_data,0,code_maxsize);
//USB_DEBUG("zengqiu %s[%d]: start.\n", __func__, __LINE__);
}
if (!fw_info->fw_data) {
free(fw_info);
return NULL;
}
fw_info->send_pkt = malloc(PKT_LEN);
memset(fw_info->send_pkt,0,PKT_LEN);
if (!fw_info->send_pkt) {
free(fw_info->fw_data);
free(fw_info);
return NULL;
}
fw_info->rcv_pkt = malloc(PKT_LEN);
memset(fw_info->rcv_pkt,0,PKT_LEN);
if (!fw_info->rcv_pkt) {
free(fw_info->fw_data);
free(fw_info->send_pkt);
free(fw_info);
return NULL;
}
fw_info->patch_entry = NULL;
fw_info->intf = NULL;
fw_info->udev = udev;
fw_info->pipe_in = usb_rcvintpipe(fw_info->udev, INTR_EP);
fw_info->pipe_out = usb_sndctrlpipe(fw_info->udev, CTRL_EP);
fw_info->cmd_hdr = (struct hci_command_hdr *)(fw_info->send_pkt);
fw_info->evt_hdr = (struct hci_event_hdr *)(fw_info->rcv_pkt);
fw_info->cmd_cmp = (struct hci_ev_cmd_complete *)(fw_info->rcv_pkt + EVT_HDR_LEN);
fw_info->req_para = fw_info->send_pkt + CMD_HDR_LEN;
fw_info->rsp_para = fw_info->rcv_pkt + EVT_HDR_LEN + CMD_CMP_LEN;
return fw_info;
}
void firmware_info_destroy(firmware_info *fw_info)
{
free(fw_info->fw_data);
free(fw_info->rcv_pkt);
free(fw_info->send_pkt);
free(fw_info);
}
int load_firmware(firmware_info *fw_info, unsigned char **buff)
{
unsigned int size;
if (fw_8723bu) {
size = lu8Code_RTL8723BU[0] | (lu8Code_RTL8723BU[1] << 8);
USB_DEBUG("%s,size is %d\n",__func__, size);
if (size >= CODE_MAXSIZE_24K) {
USB_ERROR("%s,size is %d firmware is larger than %d\n",__func__, size, CODE_MAXSIZE_24K);
return -1;
}
memcpy(*buff, &lu8Code_RTL8723BU[2],size);
fw_8723bu = 0;
return size;
}
if (fw_8723du) {
size = lu8Code_RTL8723DU[0] | (lu8Code_RTL8723DU[1] << 8);
USB_DEBUG("%s,size is %d\n",__func__, size);
if (size >= CODE_MAXSIZE_40K) {
USB_ERROR("%s,size is %d firmware is larger than %d\n",__func__, size, CODE_MAXSIZE_40K);
return -1;
}
memcpy(*buff, &lu8Code_RTL8723DU[2],size);
fw_8723du= 0;
return size;
}
if (fw_8822bu) {
size = lu8Code_RTL8822BU[0] | (lu8Code_RTL8822BU[1] << 8);
USB_DEBUG("%s,size is %d\n",__func__, size);
memcpy(*buff, &lu8Code_RTL8822BU[2],size);
fw_8822bu = 0;
return size;
}
return 0;
}
int send_hci_cmd(firmware_info *fw_info)
{
int ret_val;
ret_val = usb_control_msg(
fw_info->udev, fw_info->pipe_out,
0, USB_TYPE_CLASS, 0, 0,
(void *)(fw_info->send_pkt),
fw_info->pkt_len, MSG_TO);
return ret_val;
}
int rcv_hci_evt(firmware_info *fw_info)
{
int /*ret_len = 0,*/ ret_val = 0;
int i;
while (1) {
for (i = 0; i < 5; i++) {
ret_val = usb_submit_int_msg(
fw_info->udev, fw_info->pipe_in,
(void *)(fw_info->rcv_pkt), PKT_LEN,
MSG_TO);
if (ret_val >= 0)
break;
}
/*USB_DEBUG("fw_info->rcv_pkt : 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x \n",
fw_info->rcv_pkt[0],
fw_info->rcv_pkt[1],
fw_info->rcv_pkt[2],
fw_info->rcv_pkt[3],
fw_info->rcv_pkt[4],
fw_info->rcv_pkt[5],
fw_info->rcv_pkt[6]);*/
if (ret_val < 0)
return ret_val;
if (CMD_CMP_EVT == fw_info->evt_hdr->evt) {
if (fw_info->cmd_hdr->opcode == fw_info->cmd_cmp->opcode)
return 0;
}
}
}
int get_firmware(firmware_info *fw_info)
{
/* patch_info *patch_entry = fw_info->patch_entry; */
fw_info->fw_len = load_firmware(fw_info, &fw_info->fw_data);
USB_DEBUG("%s[%d]: fw_len=%d\n", __func__, __LINE__, fw_info->fw_len);
if (fw_info->fw_len <= 0)
return -1;
return 0;
}
int reset_controller(firmware_info* fw_info)
{
int ret_val;
USB_DEBUG("reset_controller \n");
if (!fw_info)
return -ENODEV;
fw_info->cmd_hdr->opcode = cpu_to_le16(HCI_VENDOR_FORCE_RESET_AND_PATCHABLE);
fw_info->cmd_hdr->plen = 0;
fw_info->pkt_len = CMD_HDR_LEN;
ret_val = send_hci_cmd(fw_info);
if (ret_val < 0) {
USB_DEBUG("%s: Failed to send hci cmd 0x%04x, errno %d \n",
__func__, fw_info->cmd_hdr->opcode, ret_val);
return ret_val;
}
mdelay(200);
USB_DEBUG("%s: Wait fw reset for 200ms \n",__func__);
return ret_val;
}
int download_data(firmware_info *fw_info)
{
download_cp *cmd_para;
download_rp *evt_para;
unsigned char *pcur;
int pkt_len, frag_num, frag_len;
int i, ret_val;
int ncmd = 1, step = 1;
//USB_DEBUG("%s: start\n", __func__);
cmd_para = (download_cp *)fw_info->req_para;
evt_para = (download_rp *)fw_info->rsp_para;
pcur = fw_info->fw_data;
pkt_len = CMD_HDR_LEN + sizeof(download_cp);
frag_num = fw_info->fw_len / PATCH_SEG_MAX + 1;
frag_len = PATCH_SEG_MAX;
cmd_para->index = 0;
for (i = 0; i < frag_num; i++) {
if (i == (frag_num - 1)) {
cmd_para->index |= DATA_END;
frag_len = fw_info->fw_len % PATCH_SEG_MAX;
pkt_len -= (PATCH_SEG_MAX - frag_len);
}
fw_info->cmd_hdr->opcode = cpu_to_le16(DOWNLOAD_OPCODE);
fw_info->cmd_hdr->plen = sizeof(unsigned char) + frag_len;
fw_info->pkt_len = pkt_len;
memcpy(cmd_para->data, pcur, frag_len);
if (step > 0) {
ret_val = send_hci_cmd(fw_info);
if (ret_val < 0) {
//USB_ERROR("%s: Failed to send frag num %d\n", __func__, cmd_para->index);
return ret_val;
} else
//USB_DEBUG("%s: Send frag num %d\n", __func__, cmd_para->index);
if (--step > 0 && i < frag_num - 1) {
//USB_DEBUG("%s: Continue to send frag num %d\n", __func__, cmd_para->index + 1);
pcur += PATCH_SEG_MAX;
continue;
}
}
while (ncmd > 0) {
ret_val = rcv_hci_evt(fw_info);
if (ret_val < 0) {
//USB_ERROR("%s: rcv_hci_evt err %d\n", __func__, ret_val);
return ret_val;
} else {
//USB_DEBUG("%s: Receive acked frag num %d\n", __func__, evt_para->index);
ncmd--;
}
if (0 != evt_para->status) {
//USB_ERROR("%s: Receive acked frag num %d, err status %d\n",
// __func__, ret_val, evt_para->status);
return -1;
}
if ((evt_para->index & DATA_END) || (evt_para->index == frag_num - 1)) {
//USB_DEBUG("%s: Receive last acked index %d\n", __func__, evt_para->index);
goto end;
}
}
cmd_para->index ++;
if (cmd_para->index == 0x80)
cmd_para->index = 1;
ncmd = step = fw_info->cmd_cmp->ncmd;
pcur += PATCH_SEG_MAX;
//USB_DEBUG("%s: HCI command packet num %d\n", __func__, ncmd);
}
/*
* It is tricky that Host cannot receive DATA_END index from BT
* controller, at least for 8723au. We are doomed if failed.
*/
end:
// USB_DEBUG("%s: done, sent %d frag pkts, received %d frag events\n",
// __func__, cmd_para->index, evt_para->index);
return fw_info->fw_len;
}
int set_wakeup_device(firmware_info* fw_info)
{
struct rtk_eversion_evt *ever_evt;
int ret_val;
static unsigned char bdaddr[7] = {0x00, 0xd7, 0xe3,0x3e,0xf8, 0xe6,0xa0};
if (!fw_info)
return -ENODEV;
fw_info->cmd_hdr->opcode = cpu_to_le16(HCI_VENDOR_SET_WAKE_UP_DEVICE);
fw_info->cmd_hdr->plen = 7;
memcpy(fw_info->req_para, bdaddr, 7);
fw_info->pkt_len = CMD_HDR_LEN + 7;
ret_val = send_hci_cmd(fw_info);
if (ret_val < 0) {
USB_ERROR("%s: Failed to send hci cmd 0x%04x, errno %d\n",
__func__, fw_info->cmd_hdr->opcode, ret_val);
return ret_val;
}
ret_val = rcv_hci_evt(fw_info);
if (ret_val < 0) {
USB_ERROR("%s: Failed to receive hci event, errno %d\n",
__func__, ret_val);
return ret_val;
}
ever_evt = (struct rtk_eversion_evt *)(fw_info->rsp_para);
USB_DEBUG("%s: status %d, eversion %d", __func__, ever_evt->status, ever_evt->version);
return ret_val;
}
int check_chip_fw_version(firmware_info* fw_info)
{
struct rtk_localversion_evt *ever_evt;
int ret_val;
if (!fw_info)
return -ENODEV;
fw_info->cmd_hdr->opcode = cpu_to_le16(HCI_VENDOR_READ_LMP_VERISION);
fw_info->cmd_hdr->plen = 0;
fw_info->pkt_len = CMD_HDR_LEN;
ret_val = send_hci_cmd(fw_info);
if (ret_val < 0) {
USB_ERROR("%s: Failed to send hci cmd 0x%04x, errno %d\n",
__func__, fw_info->cmd_hdr->opcode, ret_val);
return ret_val;
}
ret_val = rcv_hci_evt(fw_info);
if (ret_val < 0) {
USB_ERROR("%s: Failed to receive hci event, errno %d\n",
__func__, ret_val);
return ret_val;
}
ever_evt = (struct rtk_localversion_evt *)(fw_info->rsp_para);
USB_DEBUG("%s: status %x \n", __func__, ever_evt->status);
USB_DEBUG("%s: hci_version %x \n", __func__, ever_evt->hci_version);
USB_DEBUG("%s: hci_revision %x \n", __func__, ever_evt->hci_revision);
USB_DEBUG("%s: lmp_version %x \n", __func__, ever_evt->lmp_version);
USB_DEBUG("%s: lmp_subversion %x \n", __func__, ever_evt->lmp_subversion);
USB_DEBUG("%s: lmp_manufacture %x \n", __func__, ever_evt->lmp_manufacture);
return ret_val;
}
#if 0 // just fro reference
void load_rtl_firmware(void){
firmware_info *fw_info;
int ret_val,i;
struct usb_device *dev;
usb_init();
/* scan all USB Devices */
dev = NULL;
for (i=0;i<USB_MAX_DEVICE;i++) {
dev=usb_get_dev_index(i); /* get device */
if (dev == NULL)
return;
if (dev->devnum != -1) {
if (!strcmp(dev->mf,"Realtek") && !strcmp(dev->prod,"Bluetooth Radio")) {
/* Download BT Fireware */
printf("Now Download BT Firmware.\n");
fw_info = firmware_info_init(dev);
get_firmware(fw_info);
ret_val = download_data(fw_info);
if (ret_val > 0)
printf("Download fw patch done, fw len %d\n",ret_val);
mdelay(10);
set_wakeup_device(fw_info);
// set_bt_wakeup(fw_info);
firmware_info_destroy(fw_info);
}
}
}
}
#endif
struct usb_device * get_rtl_dev(void){
int i;
struct usb_device *dev;
/* scan all USB Devices */
dev = NULL;
for (i = 0; i < USB_MAX_DEVICE; i++) {
dev=usb_get_dev_index(i); /* get device */
if (dev == NULL) {
printf("usb dev is null!!!!\n");
return NULL;
}
printf("i=%d dev->devnum:%d dev->mf:%s dev->prod:%s\n", i, dev->devnum, dev->mf, dev->prod);
printf("vid is %x, pid is %x \n",le16_to_cpu(dev->descriptor.idVendor), le16_to_cpu(dev->descriptor.idProduct));
/* AML change for haier to match 8723bu bt */
if (dev->devnum != -1) {
if (le16_to_cpu(dev->descriptor.idVendor) == 0x0bda
&& le16_to_cpu(dev->descriptor.idProduct) == 0xb720){
fw_8723bu = 1;
return dev;
}
if (le16_to_cpu(dev->descriptor.idVendor) == 0x0bda
&& le16_to_cpu(dev->descriptor.idProduct) == 0xd723){
fw_8723du = 1;
return dev;
}
if (le16_to_cpu(dev->descriptor.idVendor) == 0x0bda
&& le16_to_cpu(dev->descriptor.idProduct) == 0xb82c){
fw_8822bu = 1;
return dev;
}
}
}
return NULL;
}
int load_rtl_firmware_dev(struct usb_device *dev)
{
#if 0
char *reboot_mode = getenv("reboot_mode");
if (reboot_mode == NULL)
return 0;
if (reboot_mode&& strcmp("cold_boot", reboot_mode))
return 0;
#endif
firmware_info *fw_info;
int ret_val=-1;
/* Download BT Fireware */
USB_DEBUG("Now Download RTK BT Firmware. (version 20181220i_1)\n");
fw_info = firmware_info_init(dev);
if (NULL == fw_info)
return -1;
ret_val = get_firmware(fw_info);
printf("++++MB:{%s}[%d]...(get_firmware = [%d]).\n", __FUNCTION__, __LINE__, ret_val);
if (-1 == ret_val) {
printf("++++MB:{%s}[%d]...get firmware, errno %d \n", __FUNCTION__, __LINE__, ret_val);
return ret_val;
}
ret_val = check_chip_fw_version(fw_info);
printf("++++MB:{%s}[%d]...(check_chip_fw_version = [%d]).\n", __FUNCTION__, __LINE__, ret_val);
if (ret_val < 0) {
printf("++++MB:{%s}[%d]...Failed to check chip fw version, errno %d \n", __FUNCTION__, __LINE__, ret_val);
return ret_val;
}
ret_val = reset_controller(fw_info);
printf("++++MB:{%s}[%d]...(reset_controller = [%d]).\n", __FUNCTION__, __LINE__, ret_val);
if (ret_val < 0) {
printf("++++MB:{%s}[%d]...(reset controller error = [%d]).\n", __FUNCTION__, __LINE__, ret_val);
return ret_val;
}
ret_val = download_data(fw_info);
printf("++++MB:{%s}[%d]...(download_data = [%d]).\n", __FUNCTION__, __LINE__, ret_val);
if (ret_val > 0)
printf("Download fw patch done, fw len %d\n",ret_val);
else
{
firmware_info_destroy(fw_info);
return ret_val;
}
mdelay(10);
ret_val = set_wakeup_device(fw_info);
printf("++++MB:{%s}[%d]...(set_wakeup_device = [%d]).\n", __FUNCTION__, __LINE__, ret_val);
//set_bt_wakeup(fw_info);
firmware_info_destroy(fw_info);
return ret_val;
}