/* * Copyright (c) 2022, Fuzhou Rockchip Electronics Co., Ltd * * 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 * (at your option) any later version. */ #include #include #include #include "rk960.h" #include "hwio.h" #include "hwbus.h" #include "debug.h" #include "bh.h" #include "fwio.h" //#include "fw_data.h" struct io_cmd { u32 id; u32 length; u32 addr; u32 code_copy_addr; }; static s8 rk960_atoi(u8 * s) { int num = 0, flag = 0; int i; for (i = 0; i <= strlen(s); i++) { if (s[i] >= '0' && s[i] <= '9') num = num * 10 + s[i] - '0'; else if (s[0] == '-' && i == 0) flag = 1; else break; } if (flag == 1) num = num * -1; return num; } static int rk960_comma_del(u8 * s, int size) { int i, num = 0; for (i = 0; i < size; i++) { if (s[i] == ',') { s[i] = 0; num++; } } return num; } #define RK960_TXPWR_COMP_GET(n) \ if (rk960_comma_del(p, strlen(p)) != 13) { \ result = -1; \ break; \ } \ for (i = 0; i < 14; i++) { \ hw_priv->txpwr_comp_tbl[i][n] = rk960_atoi(p); \ p += strlen(p) + 1; \ } int rk960_sdd_parse(struct rk960_common *hw_priv, u8 * sdd, int size) { u8 *line = sdd; u8 *p; int offset = 0; int result = 0; int i; int flag = 0; if (!sdd || !size) return 0; while (1) { if (offset + 2 >= size) break; if (sdd[offset] == 0xd && sdd[offset + 1] == 0xa) { sdd[offset] = 0; sdd[offset + 1] = 0; // get a line to parse if (line[0] == '#') { // } else if (strstr(line, "txpwr_dsss_comp=") != NULL) { p = line + strlen("txpwr_dsss_comp="); RK960_TXPWR_COMP_GET(0); flag |= BIT(0); } else if (strstr(line, "txpwr_cck_comp=") != NULL) { p = line + strlen("txpwr_cck_comp="); RK960_TXPWR_COMP_GET(1); flag |= BIT(1); } else if (strstr(line, "txpwr_bpsk_12_comp=") != NULL) { p = line + strlen("txpwr_bpsk_12_comp="); RK960_TXPWR_COMP_GET(2); flag |= BIT(2); } else if (strstr(line, "txpwr_bpsk_34_comp=") != NULL) { p = line + strlen("txpwr_bpsk_34_comp="); RK960_TXPWR_COMP_GET(3); flag |= BIT(3); } else if (strstr(line, "txpwr_qpsk_12_comp=") != NULL) { p = line + strlen("txpwr_qpsk_12_comp="); RK960_TXPWR_COMP_GET(4); flag |= BIT(4); } else if (strstr(line, "txpwr_qpsk_34_comp=") != NULL) { p = line + strlen("txpwr_qpsk_34_comp="); RK960_TXPWR_COMP_GET(5); flag |= BIT(5); } else if (strstr(line, "txpwr_16qam_12_comp=") != NULL) { p = line + strlen("txpwr_16qam_12_comp="); RK960_TXPWR_COMP_GET(6); flag |= BIT(6); } else if (strstr(line, "txpwr_16qam_34_comp=") != NULL) { p = line + strlen("txpwr_16qam_34_comp="); RK960_TXPWR_COMP_GET(7); flag |= BIT(7); } else if (strstr(line, "txpwr_64qam_23_comp=") != NULL) { p = line + strlen("txpwr_64qam_23_comp="); RK960_TXPWR_COMP_GET(8); flag |= BIT(8); } else if (strstr(line, "txpwr_64qam_34_comp=") != NULL) { p = line + strlen("txpwr_64qam_34_comp="); RK960_TXPWR_COMP_GET(9); flag |= BIT(9); } else if (strstr(line, "txpwr_64qam_56_comp=") != NULL) { p = line + strlen("txpwr_64qam_56_comp="); RK960_TXPWR_COMP_GET(10); flag |= BIT(10); } line = sdd + offset + 2; } offset += 1; } if (flag != 0x7ff) return -1; return result; } void rk960_free_firmware_buf(struct firmware_info *fw_info) { RK960_DEBUG_FW("%s\n", __func__); #ifdef FW_LOADER_FROM_FOPEN if (fw_info->loder_data) vfree(fw_info->loder_data); fw_info->loder_data = NULL; if (fw_info->fw_data) vfree(fw_info->fw_data); fw_info->fw_data = NULL; if (fw_info->fw_rfcal_data) vfree(fw_info->fw_rfcal_data); fw_info->fw_rfcal_data = NULL; if (fw_info->sdd_data) vfree(fw_info->sdd_data); fw_info->sdd_data = NULL; #else if (fw_info->loder_data_r) release_firmware(fw_info->loder_data_r); if (fw_info->fw_data_r) release_firmware(fw_info->fw_data_r); if (fw_info->fw_rfcal_data_r) release_firmware(fw_info->fw_rfcal_data_r); if (fw_info->sdd_data_r) release_firmware(fw_info->sdd_data_r); #endif if (fw_info->buf_data) kfree(fw_info->buf_data); #ifdef FW_DOWNLOAD_CHECK if (fw_info->fw_data_check) vfree(fw_info->fw_data_check); fw_info->fw_data_check = NULL; #endif if (fw_info->fw_start_data) kfree(fw_info->fw_start_data); fw_info->fw_start_data = NULL; } int rk960_alloc_firmware_buf(struct firmware_info *fw_info) { RK960_DEBUG_FW("%s\n", __func__); #ifdef FW_LOADER_FROM_FOPEN fw_info->loder_data = vmalloc(MAX_LOADER_DATA_SIZE); if (fw_info->loder_data == NULL) { RK960_ERROR_FW("alloc loder1_data failed\n"); return -ENOMEM; } fw_info->fw_data = vmalloc(MAX_FW_DATA_SIZE); if (fw_info->fw_data == NULL) { RK960_ERROR_FW("alloc fw_data failed\n"); return -ENOMEM; } fw_info->fw_rfcal_data = vmalloc(MAX_FW_RFCAL_DATA_SIZE); if (fw_info->fw_rfcal_data == NULL) { RK960_ERROR_FW("alloc fw_rfcal_data failed\n"); return -ENOMEM; } fw_info->sdd_data = vmalloc(MAX_SDD_BUF_SIZE); if (fw_info->sdd_data == NULL) { RK960_ERROR_FW("alloc sdd_data failed\n"); return -ENOMEM; } #endif fw_info->buf_data = kmalloc(fw_info->buf_size, GFP_KERNEL); if (fw_info->buf_data == NULL) { RK960_ERROR_FW("alloc buf_data failed\n"); return -ENOMEM; } #ifdef FW_DOWNLOAD_CHECK fw_info->fw_data_check = vmalloc(MAX_FW_DATA_SIZE); if (fw_info->fw_data_check == NULL) { RK960_ERROR_FW("alloc fw_data_check failed\n"); return -ENOMEM; } #endif fw_info->fw_start_data = kmalloc(16, GFP_KERNEL); if (fw_info->fw_start_data == NULL) { RK960_ERROR_FW("alloc fw_start_data failed\n"); return -1; } return 0; } static const char *const fw_path[] = { "/etc/firmware", "/vendor/etc/firmware", "/lib/firmware", "/system/etc/firmware" }; static int rk960_read_firmware_file(struct firmware_info *fw_info, char *name, u8 * buf, int *len, int max_size, int append) { int i, find = 0; char path[64]; struct file *file; int read, size = 1024; u8 *buf_start = buf; RK960_DEBUG_FW("%s: %s\n", __func__, name); for (i = 0; i < ARRAY_SIZE(fw_path); i++) { if (!fw_path[i][0]) continue; sprintf(path, "%s/%s", fw_path[i], name); //pr_info("%s\n", path); file = filp_open(path, O_RDONLY, 0); if (IS_ERR(file)) continue; find = 1; break; } if (!find) { RK960_ERROR_FW("%s: can't find %s\n", __func__, path); return -ENOENT; } RK960_DEBUG_FW("%s: find %s\n", __func__, path); if (rk960_get_file_size(path) > max_size) { RK960_ERROR_FW("%s: file(%s) size exceed fw buf(%d)\n", __func__, path, max_size); return -ENOMEM; } *len = 0; while (1) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) read = kernel_read(file, buf, size, &file->f_pos); #else read = kernel_read(file, file->f_pos, buf, size); #endif if (read <= 0) break; #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) file->f_pos += read; #endif buf += read; *len += read; } filp_close(file, NULL); *len = ALIGN(*len, 4); if (append) { fw_info->useful_code_size = *len; //We copy the data with this address whichbe crashed by LOADER's start CMD. memcpy(buf_start + *len, buf_start + 0x10000, 16); *len += 16; } if (*len > max_size) { RK960_ERROR_FW("%s file exceed max size %d(%d)\n", name, *len, max_size); return -ENOMEM; } return 0; } int rk960_get_firmware_from_open(struct firmware_info *fw_info) { int ret; RK960_INFO_FW("firmware and patch file from open\n"); ret = rk960_read_firmware_file(fw_info, "rk960_wifi_loader.bin", fw_info->loder_data, &fw_info->loder_size, MAX_LOADER_DATA_SIZE, 0); if ((ret < 0) || (fw_info->loder_size <= 0)) { //RK960_INFO_FW("no rk960_wifi_loader.bin file\n"); } fw_info->loder_size = ALIGN(fw_info->loder_size, 1024); ret = rk960_read_firmware_file(fw_info, "rk960_wifi.bin", fw_info->fw_data, &fw_info->fw_size, MAX_FW_DATA_SIZE, 0); if ((ret < 0) || (fw_info->fw_size <= 0)) { if (ret == -ENOENT) { ret = rk960_read_firmware_file(fw_info, "rk960_fw.bin", fw_info->fw_data, &fw_info->fw_size, MAX_FW_DATA_SIZE, 0); if ((ret < 0) || (fw_info->fw_size <= 0)) return -ENOENT; } else { return -ENOENT; } } ret = rk960_read_firmware_file(fw_info, "rk960_wifi_rf.bin", fw_info->fw_rfcal_data, &fw_info->fw_rfcal_size, MAX_FW_RFCAL_DATA_SIZE, 0); if ((ret < 0) || (fw_info->fw_rfcal_size <= 0)) { //RK960_INFO_FW("no rk960_wifi_rfcal.bin file\n"); } fw_info->fw_size = ALIGN(fw_info->fw_size, 1024); fw_info->fw_rfcal_size = ALIGN(fw_info->fw_rfcal_size, 1024); ret = rk960_read_firmware_file(fw_info, "rk960_sdd.txt", fw_info->sdd_data, &fw_info->sdd_size, MAX_SDD_BUF_SIZE, 0); if ((ret < 0) || (fw_info->sdd_size <= 0)) { //RK960_INFO_FW("no rk960_sdd.txt file\n"); } return 0; } static int rk960_get_firmware_info(struct rk960_common *priv, struct firmware_info *fw_info) { int ret = 0; if (fw_info->fw_saved) return 0; #ifdef FW_LOADER_FROM_FOPEN ret = rk960_get_firmware_from_open(fw_info); #else ret = request_firmware(&fw_info->fw_data_r, "rk960_wifi.bin", priv->pdev); if (ret) { ret = request_firmware(&fw_info->fw_data_r, "rk960_fw.bin", priv->pdev); if (ret) { RK960_ERROR_FW("Can't load firmware file %s.\n", "rk960_wifi.bin"); goto firmware_release; } else { RK960_INFO_FW("%s: loaded firmware %s\n", __func__, "rk960_fw.bin"); } } else { RK960_INFO_FW("%s: loaded firmware %s\n", __func__, "rk960_wifi.bin"); } ret = request_firmware(&fw_info->loder_data_r, "rk960_wifi_loader.bin", priv->pdev); if (ret) { RK960_ERROR_FW("Can't load firmware file %s.\n", "rk960_wifi_loader.bin"); } else { RK960_INFO_FW("%s: loaded firmware %s\n", __func__, "rk960_wifi_loader.bin"); } ret = request_firmware(&fw_info->fw_rfcal_data_r, "rk960_wifi_rf.bin", priv->pdev); if (ret) { RK960_ERROR_FW("Can't load firmware file %s.\n", "rk960_wifi_rf.bin"); } else { RK960_INFO_FW("%s: loaded firmware %s\n", __func__, "rk960_wifi_rf.bin"); } ret = request_firmware(&fw_info->sdd_data_r, "rk960_sdd.txt", priv->pdev); if (ret) { RK960_ERROR_FW("Can't load firmware file %s.\n", "rk960_sdd.txt"); } else { RK960_INFO_FW("%s: loaded firmware %s\n", __func__, "rk960_sdd.txt"); } return 0; firmware_release: if (fw_info->loder_data_r) release_firmware(fw_info->loder_data_r); if (fw_info->fw_data_r) release_firmware(fw_info->fw_data_r); if (fw_info->fw_rfcal_data_r) release_firmware(fw_info->fw_rfcal_data_r); if (fw_info->sdd_data_r) release_firmware(fw_info->sdd_data_r); #endif return ret; } static int rk960_get_rom_version(struct rk960_common *priv) { int ret; struct firmware_info *fw = &priv->firmware; memset(fw->buf_data, 0, 16); ret = rk960_reg_read(priv, SDIO_ROM_VER_ADDR, fw->buf_data, 10); if (ret) { RK960_ERROR_FW("%s: read rom_ver failed (%d)\n", __func__, ret); return -1; } RK960_INFO_FW("%s: rom_ver:", __func__); print_hex_dump(KERN_INFO, " ", DUMP_PREFIX_NONE, 16, 1, fw->buf_data, 10, 1); return 0; } int rk960_download_fw(struct rk960_common *priv, int start_fw) { int ret = -ENOENT; struct firmware_info *fw = &priv->firmware; int cnt, left_size, write_size, sdio_cmd_addr, write_addr_block_size; struct io_cmd *scmd; int i; bool is_big_fw; int loder_size = 0; unsigned char *loder_data = NULL; int fw_rfcal_size = 0; unsigned char *fw_rfcal_data = NULL; int fw_size = 0; unsigned char *fw_data = NULL; #ifdef FW_LOADER_FROM_FOPEN fw_size = fw->fw_size; #else fw_size = fw->fw_data_r->size; #endif is_big_fw = fw_size > 64 * 1024 ? true : false; if (priv->chip_id == RK960_DEVICE_ID_D) { if (is_big_fw) sdio_cmd_addr = SDIO_CMD_ADDR_VER_D; else sdio_cmd_addr = SDIO_CMD_ADDR_VER_ROM; write_addr_block_size = 512; } else { sdio_cmd_addr = SDIO_CMD_ADDR_VER_ABC; write_addr_block_size = 1; } RK960_DEBUG_FW("%s: chip id %x block size %d buf size %d\n", __func__, priv->chip_id, write_addr_block_size, fw->buf_size); scmd = (struct io_cmd *)fw->fw_start_data; #ifdef FW_LOADER_FROM_FOPEN loder_size = fw->loder_size; loder_data = fw->loder_data; #else if (fw->loder_data_r) { loder_size = fw->loder_data_r->size; loder_data = (unsigned char *)fw->loder_data_r->data; } #endif //download loader if (!is_big_fw && loder_size) { RK960_INFO_FW("%s: start download loader size: %d\n", __func__, loder_size); cnt = ((loder_size - 1) / fw->buf_size) + 1; left_size = loder_size; for (i = 0; i < cnt; i++) { if (left_size >= fw->buf_size) { write_size = fw->buf_size; } else if (left_size > 0) { write_size = left_size; } else { break; } memset(fw->buf_data, 0, fw->buf_size); memcpy(fw->buf_data, loder_data + i * fw->buf_size, write_size); ret = rk960_reg_write(priv, SDIO_LOADER_VCT_ADDR / write_addr_block_size, fw->buf_data, ALIGN(write_size, 1024)); if (ret) { RK960_ERROR_FW ("%s: rk960_reg_write failed (%d), write_size = %d\n", __func__, ret, write_size); goto fail; } left_size -= write_size; } //send start CMD to reboot scmd->id = SDIO_START_CMD_ID; scmd->length = 4; scmd->addr = SDIO_LOADER_VCT_ADDR; scmd->code_copy_addr = fw->useful_code_size; ret = rk960_reg_write(priv, sdio_cmd_addr, (void *)scmd, sizeof(struct io_cmd)); if (ret) { RK960_ERROR_FW("%s: start loader failed (%d)\n", __func__, ret); goto fail; } msleep(10); rk960_get_rom_version(priv); } #ifdef FW_LOADER_FROM_FOPEN fw_rfcal_size = fw->fw_rfcal_size; fw_rfcal_data = fw->fw_rfcal_data; #else if (fw->fw_rfcal_data_r) { fw_rfcal_size = fw->fw_rfcal_data_r->size; fw_rfcal_data = (unsigned char *)fw->fw_rfcal_data_r->data; } #endif //download rfcal fw if (!is_big_fw && fw_rfcal_size) { u16 ctrl_reg; int to_count = 500; RK960_INFO_FW("%s: start download rfcal firmware size: %d\n", __func__, fw_rfcal_size); cnt = ((fw_rfcal_size - 1) / fw->buf_size) + 1; left_size = fw_rfcal_size; for (i = 0; i < cnt; i++) { if (left_size >= fw->buf_size) { write_size = fw->buf_size; } else if (left_size > 0) { write_size = left_size; } else { break; } memset(fw->buf_data, 0, fw->buf_size); memcpy(fw->buf_data, fw_rfcal_data + i * fw->buf_size, write_size); ret = rk960_reg_write(priv, (i * fw->buf_size) / write_addr_block_size, fw->buf_data, ALIGN(write_size, 1024)); if (ret) { RK960_ERROR_FW ("%s: rk960_reg_write failed (%d), write_size = %d\n", __func__, ret, write_size); goto fail; } left_size -= write_size; } //send start CMD to reboot scmd->id = SDIO_START_CMD_ID; scmd->length = 4; scmd->addr = SDIO_FIRMWARE_VCT_ADDR; scmd->code_copy_addr = fw->useful_code_size; ret = rk960_reg_write(priv, sdio_cmd_addr, (void *)scmd, sizeof(struct io_cmd)); if (ret) { RK960_ERROR_FW("%s: start rfcal fw failed (%d)\n", __func__, ret); goto fail; } // wait for rfcal complete while (to_count--) { rk960_bh_read_ctrl_reg(priv, &ctrl_reg); if (ctrl_reg) { RK960_INFO_FW("rfcal complete %x\n", ctrl_reg); __rk960_clear_irq(priv); break; } msleep(10); } if (to_count <= 0) { RK960_ERROR_FW("rfcal failed\n"); ret = -1; goto fail; } } /*ret = rk960_sdio_irq_subscribe(priv->hwbus_priv); if (ret) { goto fail; }*/ #ifdef FW_LOADER_FROM_FOPEN fw_size = fw->fw_size; fw_data = fw->fw_data; #else fw_size = fw->fw_data_r->size; fw_data = (unsigned char *)fw->fw_data_r->data; #endif //download fw and read back RK960_INFO_FW("%s: start download firmware size: %d\n", __func__, fw_size); cnt = ((fw_size - 1) / fw->buf_size) + 1; left_size = fw_size; for (i = 0; i < cnt; i++) { if (left_size >= fw->buf_size) { write_size = fw->buf_size; } else if (left_size > 0) { write_size = left_size; } else { break; } memset(fw->buf_data, 0, fw->buf_size); memcpy(fw->buf_data, fw_data + i * fw->buf_size, write_size); ret = rk960_reg_write(priv, (i * fw->buf_size) / write_addr_block_size, fw->buf_data, ALIGN(write_size, 1024)); if (ret) { RK960_ERROR_FW ("%s: rk960_reg_write failed (%d), write_size = %d\n", __func__, ret, write_size); goto fail; } #ifdef FW_DOWNLOAD_CHECK ret = rk960_reg_read(priv, (i * fw->buf_size) / write_addr_block_size, fw->buf_data, ALIGN(write_size, 1024)); if (ret) { RK960_ERROR_FW("%s: read fw failed (%d)\n", __func__, ret); goto fail; } memcpy(fw->fw_data_check + i * fw->buf_size, fw->buf_data, write_size); #endif left_size -= write_size; } #ifdef FW_DOWNLOAD_CHECK //check read back's fw data ret = memcmp(fw_data, fw->fw_data_check, fw_size); if (ret && !priv->fw_error_processing) { RK960_ERROR_FW("%s: check downloaded fw failed\n", __func__); for (i = 0; i < fw_size; i++) { if (fw->fw_data_check[i] != fw_data[i]) { RK960_ERROR_FW ("Addr: %d, data: 0x%02x, bad: 0x%02x\n", i, fw_data[i], fw->fw_data_check[i]); } } ret = -ENOENT; goto fail; } else { RK960_INFO_FW("%s: check downloaded fw ok.\n", __func__); } #endif //send start CMD to reboot scmd->id = SDIO_START_CMD_ID; scmd->length = 4; scmd->addr = SDIO_FIRMWARE_VCT_ADDR; scmd->code_copy_addr = fw->useful_code_size; if (start_fw) { ret = rk960_reg_write(priv, sdio_cmd_addr, (void *)scmd, sizeof(struct io_cmd)); if (ret) { RK960_ERROR_FW("%s: start fw failed (%d)\n", __func__, ret); goto fail; } //mdelay(20); } else { priv->fw_scmd = scmd; priv->sdio_cmd_addr = sdio_cmd_addr; } RK960_INFO_FW("%s: download firmware success\n", __func__); return 0; fail: return ret; } int rk960_start_fw(struct rk960_common *priv) { int ret; struct io_cmd *scmd = priv->fw_scmd; int sdio_cmd_addr = priv->sdio_cmd_addr; ret = rk960_reg_write(priv, sdio_cmd_addr, (void *)scmd, sizeof(struct io_cmd)); if (ret) { RK960_ERROR_FW("%s: start fw failed (%d)\n", __func__, ret); return -1; } RK960_INFO_FW("%s: start fw success\n", __func__); //mdelay(20); return 0; } /* return 0 means fw download success, otherwise fail */ int rk960_load_firmware(struct rk960_common *priv) { struct firmware_info *fw_info; int ret; fw_info = &priv->firmware; if (priv->chip_id == RK960_DEVICE_ID_D) { fw_info->buf_size = MAX_FW_BUF_SIZE_SMALL; } else { fw_info->buf_size = MAX_FW_BUF_SIZE_BIG; } if (rk960_alloc_firmware_buf(fw_info) != 0) { RK960_ERROR_FW("%s: rk960_alloc_firmware_buf failed\n", __func__); ret = -ENOENT; goto fw_out; } /* get info of firmware and rompatch */ if (rk960_get_firmware_info(priv, fw_info)) { RK960_ERROR_FW("%s: get firmeware error!.\n", __func__); ret = -ENOENT; goto fw_out; } #ifdef FW_LOADER_FROM_FOPEN if (rk960_sdd_parse(priv, fw_info->sdd_data, fw_info->sdd_size)) { #else if (rk960_sdd_parse(priv, fw_info->sdd_data_r ? (unsigned char *)fw_info->sdd_data_r->data:NULL, fw_info->sdd_data_r ? fw_info->sdd_data_r->size:0)) { #endif RK960_ERROR_FW("%s: sdd parse error!.\n", __func__); ret = -ENOENT; goto fw_out; } //fw_info->fw_saved = 1; #ifdef SUPPORT_FWCR if (priv->fw_hotboot) return 0; #endif ret = rk960_download_fw(priv, 0); if (!ret) return ret; fw_out: rk960_free_firmware_buf(fw_info); return ret; }