blob: 9f9073e0b1e5828679daca289cb05bdf0bbd833d [file] [log] [blame] [edit]
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* common/rtk_fw.c
*
* Copyright (C) 2020 Amlogic, Inc. All rights reserved.
*
*/
/******************************************************************************
* Copyright (c) 2020-2022 by Hisi.
* All rights reserved.
*
* Create by zengqiu. 2022-1-10
*
******************************************************************************/
#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 <dm/uclass.h>
#include <dm/device.h>
#include <dm/uclass-internal.h>
#include <malloc.h>
#include <usb.h>
#include <rtk_fw.h>
#define USB_ERROR(fmt, args...) {if (log_level) printf(fmt, ##args); }
#define USB_DEBUG(fmt, args...) {if (log_level) printf(fmt, ##args); }
//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_23K] = {
#include CHIP_TYPE_RTL8822BU
};
static unsigned char lu8Code_RTL88X2CU[CODE_MAXSIZE_40K] = {
#include CHIP_TYPE_RTL88X2CU
};
static const bluetooth_dongle usb_dongle[] = {
{0x0bda, 0xb720, "8723bu", CHIP_TYPE_RTL8723BU, CODE_MAXSIZE_24K, lu8Code_RTL8723BU},
{0x0bda, 0xd723, "8723du", CHIP_TYPE_RTL8723DU, CODE_MAXSIZE_40K, lu8Code_RTL8723DU},
{0x0bda, 0xb82c, "8822bu", CHIP_TYPE_RTL8822BU, CODE_MAXSIZE_23K, lu8Code_RTL8822BU},
{0x0bda, 0xc82c, "88x2cu", CHIP_TYPE_RTL88X2CU, CODE_MAXSIZE_40K, lu8Code_RTL88X2CU}
};
static int num = -1;
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;
fw_info->fw_data = malloc(usb_dongle[num].fw_size);
memset(fw_info->fw_data, 0, usb_dongle[num].fw_size);
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;
size = usb_dongle[num].fw_buf[0] | (usb_dongle[num].fw_buf[1] << 8);
USB_DEBUG("%s,%s size is %d\n", __func__, usb_dongle[num].fw_name, size);
if (size > usb_dongle[num].fw_size) {
USB_ERROR("%s,size is %d firmware is larger than %d\n",
__func__, size, usb_dongle[num].fw_size);
num = -1;
return -1;
}
memcpy(*buff, &usb_dongle[num].fw_buf[2], size);
return size;
}
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 (fw_info->evt_hdr->evt == CMD_CMP_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("%s\n", __func__);
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);
if (pkt_len == (CMD_HDR_LEN + sizeof(unsigned char)))
goto end;
}
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;
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;
}
//USB_DEBUG("%s: Receive acked frag num %d\n", __func__, evt_para->index);
ncmd--;
if (evt_para->status != 0) {
//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 = fw_info->cmd_cmp->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\n", __func__, ever_evt->status, ever_evt->version);
return ret_val;
}
int reset_chip_dev(firmware_info *fw_info)
{
int ret_val;
if (!fw_info)
return -ENODEV;
fw_info->cmd_hdr->opcode = cpu_to_le16(HCI_VENDOR_CHIP_RESET);
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;
}
if (!*fw_info->rsp_para)
printf("%s success\n", __func__);
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_VERSION);
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 DEL_CODE // 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)
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
static struct usb_device *qiu_usb_find_device(int devnum)
{
#ifdef CONFIG_DM_USB
struct usb_device *udev;
struct udevice *hub;
struct uclass *uc;
int ret;
/* Device addresses start at 1 */
devnum++;
ret = uclass_get(UCLASS_USB_HUB, &uc);
if (ret)
return NULL;
uclass_foreach_dev(hub, uc) {
struct udevice *dev;
if (!device_active(hub))
continue;
udev = dev_get_parent_priv(hub);
if (udev->devnum == devnum)
return udev;
for (device_find_first_child(hub, &dev);
dev;
device_find_next_child(&dev)) {
if (!device_active(hub))
continue;
udev = dev_get_parent_priv(dev);
if (udev->devnum == devnum)
return udev;
}
}
#else
struct usb_device *udev;
int d;
for (d = 0; d < USB_MAX_DEVICE; d++) {
udev = usb_get_dev_index(d);
if (!udev)
return NULL;
if (udev->devnum == devnum)
return udev;
}
#endif
return NULL;
}
struct usb_device *get_rtl_dev(void)
{
int i = 0;
int size, usb_size;
struct usb_device *dev;
/* scan all USB Devices */
dev = NULL;
for (i = 0; i < USB_MAX_DEVICE; i++) {
dev = qiu_usb_find_device(i);
if (!dev) {
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);
USB_DEBUG("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) {
size = sizeof(usb_dongle) / sizeof(bluetooth_dongle);
for (usb_size = 0; usb_size < size; usb_size++) {
if (le16_to_cpu(dev->descriptor.idVendor ==
usb_dongle[usb_size].idVendor) &&
le16_to_cpu(dev->descriptor.idProduct) ==
usb_dongle[usb_size].idProduct) {
num = usb_size;
return dev;
}
}
}
}
printf("dou't find device\n");
return NULL;
}
int load_rtl_firmware_dev(struct usb_device *dev)
{
#if DEL_CODE
char *reboot_mode = getenv("reboot_mode");
if (!reboot_mode)
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 20221220i_1)\n");
fw_info = firmware_info_init(dev);
if (!fw_info)
return -1;
ret_val = get_firmware(fw_info);
printf("++++ZQ:{%s}[%d]...(get_firmware = [%d]).\n",
__func__, __LINE__, fw_info->fw_len);
if (-1 == ret_val) {
printf("++++ZQ:{%s}[%d]...get firmware, errno %d\n",
__func__, __LINE__, ret_val);
return ret_val;
}
ret_val = reset_chip_dev(fw_info);
if (ret_val < 0) {
printf("++++ZQ:{%s}[%d]...chip fail", __func__, __LINE__);
return ret_val;
}
ret_val = check_chip_fw_version(fw_info);
USB_DEBUG("++++ZQ:{%s}[%d]...(check_chip_fw_version = [%d]).\n",
__func__, __LINE__, ret_val);
if (ret_val < 0) {
printf("++++ZQ:{%s}[%d]...Failed to check chip fw version, errno %d\n",
__func__, __LINE__, ret_val);
return ret_val;
}
ret_val = reset_controller(fw_info);
USB_DEBUG("++++ZQ:{%s}[%d]...(reset_controller = [%d]).\n",
__func__, __LINE__, ret_val);
if (ret_val < 0) {
printf("++++ZQ:{%s}[%d]...(reset controller error = [%d]).\n",
__func__, __LINE__, ret_val);
return ret_val;
}
ret_val = download_data(fw_info);
USB_DEBUG("++++ZQ:{%s}[%d]...(download_data = [%d]).\n",
__func__, __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);
USB_DEBUG("++++ZQ:{%s}[%d]...(set_wakeup_device = [%d]).\n",
__func__, __LINE__, ret_val);
//set_bt_wakeup(fw_info);
firmware_info_destroy(fw_info);
return ret_val;
}