// SPDX-License-Identifier: GPL-2.0-only /* * This file is part of wl1271 * * Copyright (C) 2008-2010 Nokia Corporation * * Contact: Luciano Coelho <luciano.coelho@nokia.com>
*/
if (ret != 5) {
wl1271_warning("fw version incorrect value");
memset(wl->chip.fw_ver, 0, sizeof(wl->chip.fw_ver));
ret = -EINVAL; goto out;
}
ret = wlcore_identify_fw(wl); if (ret < 0) goto out;
out: return ret;
}
staticint wlcore_validate_fw_ver(struct wl1271 *wl)
{ unsignedint *fw_ver = wl->chip.fw_ver; unsignedint *min_ver = (wl->fw_type == WL12XX_FW_TYPE_MULTI) ?
wl->min_mr_fw_ver : wl->min_sr_fw_ver; char min_fw_str[32] = ""; int off = 0; int i;
/* the chip must be exactly equal */ if ((min_ver[FW_VER_CHIP] != WLCORE_FW_VER_IGNORE) &&
(min_ver[FW_VER_CHIP] != fw_ver[FW_VER_CHIP])) goto fail;
/* the firmware type must be equal */ if ((min_ver[FW_VER_IF_TYPE] != WLCORE_FW_VER_IGNORE) &&
(min_ver[FW_VER_IF_TYPE] != fw_ver[FW_VER_IF_TYPE])) goto fail;
/* the project number must be equal */ if ((min_ver[FW_VER_SUBTYPE] != WLCORE_FW_VER_IGNORE) &&
(min_ver[FW_VER_SUBTYPE] != fw_ver[FW_VER_SUBTYPE])) goto fail;
/* the API version must be greater or equal */ if ((min_ver[FW_VER_MAJOR] != WLCORE_FW_VER_IGNORE) &&
(min_ver[FW_VER_MAJOR] > fw_ver[FW_VER_MAJOR])) goto fail;
/* if the API version is equal... */ if (((min_ver[FW_VER_MAJOR] == WLCORE_FW_VER_IGNORE) ||
(min_ver[FW_VER_MAJOR] == fw_ver[FW_VER_MAJOR])) && /* ...the minor must be greater or equal */
((min_ver[FW_VER_MINOR] != WLCORE_FW_VER_IGNORE) &&
(min_ver[FW_VER_MINOR] > fw_ver[FW_VER_MINOR]))) goto fail;
return 0;
fail: for (i = 0; i < NUM_FW_VER && off < sizeof(min_fw_str); i++) if (min_ver[i] == WLCORE_FW_VER_IGNORE)
off += snprintf(min_fw_str + off, sizeof(min_fw_str) - off, "*."); else
off += snprintf(min_fw_str + off, sizeof(min_fw_str) - off, "%u.", min_ver[i]);
wl1271_error("Your WiFi FW version (%u.%u.%u.%u.%u) is invalid.\n" "Please use at least FW %s\n" "You can get the latest firmwares at:\n" "git://git.ti.com/wilink8-wlan/wl18xx_fw.git",
fw_ver[FW_VER_CHIP], fw_ver[FW_VER_IF_TYPE],
fw_ver[FW_VER_MAJOR], fw_ver[FW_VER_SUBTYPE],
fw_ver[FW_VER_MINOR], min_fw_str); return -EINVAL;
}
staticint wlcore_boot_static_data(struct wl1271 *wl)
{ struct wl1271_static_data *static_data;
size_t len = sizeof(*static_data) + wl->static_data_priv_len; int ret;
static_data = kmalloc(len, GFP_KERNEL); if (!static_data) {
ret = -ENOMEM; goto out;
}
ret = wlcore_read(wl, wl->cmd_box_addr, static_data, len, false); if (ret < 0) goto out_free;
ret = wlcore_boot_parse_fw_ver(wl, static_data); if (ret < 0) goto out_free;
ret = wlcore_validate_fw_ver(wl); if (ret < 0) goto out_free;
ret = wlcore_handle_static_data(wl, static_data); if (ret < 0) goto out_free;
if (wl->nvs == NULL) {
wl1271_error("NVS file is needed during boot"); return -ENODEV;
}
if (pdev_data && pdev_data->family)
nvs_name = pdev_data->family->nvs_name;
if (wl->quirks & WLCORE_QUIRK_LEGACY_NVS) { struct wl1271_nvs_file *nvs =
(struct wl1271_nvs_file *)wl->nvs; /* * FIXME: the LEGACY NVS image support (NVS's missing the 5GHz * band configurations) can be removed when those NVS files stop * floating around.
*/ if (wl->nvs_len == sizeof(struct wl1271_nvs_file) ||
wl->nvs_len == WL1271_INI_LEGACY_NVS_FILE_SIZE) { if (nvs->general_params.dual_mode_select)
wl->enable_11a = true;
}
if (wl->nvs_len != sizeof(struct wl1271_nvs_file) &&
(wl->nvs_len != WL1271_INI_LEGACY_NVS_FILE_SIZE ||
wl->enable_11a)) {
wl1271_error("%s size is not as expected: %zu != %zu",
nvs_name, wl->nvs_len, sizeof(struct wl1271_nvs_file));
kfree(wl->nvs);
wl->nvs = NULL;
wl->nvs_len = 0; return -EILSEQ;
}
/* only the first part of the NVS needs to be uploaded */
nvs_len = sizeof(nvs->nvs);
nvs_ptr = (u8 *) nvs->nvs;
} else { struct wl128x_nvs_file *nvs = (struct wl128x_nvs_file *)wl->nvs;
if (wl->nvs_len == sizeof(struct wl128x_nvs_file)) { if (nvs->general_params.dual_mode_select)
wl->enable_11a = true;
} else {
wl1271_error("%s size is not as expected: %zu != %zu",
nvs_name, wl->nvs_len, sizeof(struct wl128x_nvs_file));
kfree(wl->nvs);
wl->nvs = NULL;
wl->nvs_len = 0; return -EILSEQ;
}
/* only the first part of the NVS needs to be uploaded */
nvs_len = sizeof(nvs->nvs);
nvs_ptr = (u8 *)nvs->nvs;
}
/* update current MAC address to NVS */
nvs_ptr[11] = wl->addresses[0].addr[0];
nvs_ptr[10] = wl->addresses[0].addr[1];
nvs_ptr[6] = wl->addresses[0].addr[2];
nvs_ptr[5] = wl->addresses[0].addr[3];
nvs_ptr[4] = wl->addresses[0].addr[4];
nvs_ptr[3] = wl->addresses[0].addr[5];
/* * Layout before the actual NVS tables: * 1 byte : burst length. * 2 bytes: destination address. * n bytes: data to burst copy. * * This is ended by a 0 length, then the NVS tables.
*/
/* FIXME: Do we need to check here whether the LSB is 1? */ while (nvs_ptr[0]) {
burst_len = nvs_ptr[0];
dest_addr = (nvs_ptr[1] & 0xfe) | ((u32)(nvs_ptr[2] << 8));
/* * Due to our new wl1271_translate_reg_addr function, * we need to add the register partition start address * to the destination
*/
dest_addr += wl->curr_part.reg.start;
/* We move our pointer to the data */
nvs_ptr += 3;
for (i = 0; i < burst_len; i++) { if (nvs_ptr + 3 >= (u8 *) wl->nvs + nvs_len) goto out_badnvs;
/* * We've reached the first zero length, the first NVS table * is located at an aligned offset which is at least 7 bytes further. * NOTE: The wl->nvs->nvs element must be first, in order to * simplify the casting, we assume it is at the beginning of * the wl->nvs structure.
*/
nvs_ptr = (u8 *)wl->nvs +
ALIGN(nvs_ptr - (u8 *)wl->nvs + 7, 4);
if (nvs_ptr >= (u8 *) wl->nvs + nvs_len) goto out_badnvs;
nvs_len -= nvs_ptr - (u8 *)wl->nvs;
/* Now we must set the partition correctly */
ret = wlcore_set_partition(wl, &wl->ptable[PART_WORK]); if (ret < 0) return ret;
/* Copy the NVS tables to a new block to ensure alignment */
nvs_aligned = kmemdup(nvs_ptr, nvs_len, GFP_KERNEL); if (!nvs_aligned) return -ENOMEM;
/* And finally we upload the NVS tables */
ret = wlcore_write_data(wl, REG_CMD_MBOX_ADDRESS, nvs_aligned, nvs_len, false);
kfree(nvs_aligned); return ret;
out_badnvs:
wl1271_error("nvs data is malformed"); return -EILSEQ;
}
EXPORT_SYMBOL_GPL(wlcore_boot_upload_nvs);
int wlcore_boot_run_firmware(struct wl1271 *wl)
{ int loop, ret;
u32 chip_id, intr;
/* Make sure we have the boot partition */
ret = wlcore_set_partition(wl, &wl->ptable[PART_BOOT]); if (ret < 0) return ret;
ret = wl1271_boot_set_ecpu_ctrl(wl, ECPU_CONTROL_HALT); if (ret < 0) return ret;
ret = wlcore_read_reg(wl, REG_CHIP_ID_B, &chip_id); if (ret < 0) return ret;
wl1271_debug(DEBUG_BOOT, "chip id after firmware boot: 0x%x", chip_id);
if (chip_id != wl->chip.id) {
wl1271_error("chip id doesn't match after firmware boot"); return -EIO;
}
/* wait for init to complete */
loop = 0; while (loop++ < INIT_LOOP) {
udelay(INIT_LOOP_DELAY);
ret = wlcore_read_reg(wl, REG_INTERRUPT_NO_CLEAR, &intr); if (ret < 0) return ret;
if (intr == 0xffffffff) {
wl1271_error("error reading hardware complete " "init indication"); return -EIO;
} /* check that ACX_INTR_INIT_COMPLETE is enabled */ elseif (intr & WL1271_ACX_INTR_INIT_COMPLETE) {
ret = wlcore_write_reg(wl, REG_INTERRUPT_ACK,
WL1271_ACX_INTR_INIT_COMPLETE); if (ret < 0) return ret; break;
}
}
if (loop > INIT_LOOP) {
wl1271_error("timeout waiting for the hardware to " "complete initialization"); return -EIO;
}
/* get hardware config command mail box */
ret = wlcore_read_reg(wl, REG_COMMAND_MAILBOX_PTR, &wl->cmd_box_addr); if (ret < 0) return ret;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.