aboutsummaryrefslogtreecommitdiff
path: root/drivers/net/wireless/ti/wlcore/boot.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/ti/wlcore/boot.c')
-rw-r--r--drivers/net/wireless/ti/wlcore/boot.c213
1 files changed, 151 insertions, 62 deletions
diff --git a/drivers/net/wireless/ti/wlcore/boot.c b/drivers/net/wireless/ti/wlcore/boot.c
index 9b98230f84c..77752b03f18 100644
--- a/drivers/net/wireless/ti/wlcore/boot.c
+++ b/drivers/net/wireless/ti/wlcore/boot.c
@@ -33,22 +33,35 @@
#include "rx.h"
#include "hw_ops.h"
-static void wl1271_boot_set_ecpu_ctrl(struct wl1271 *wl, u32 flag)
+static int wl1271_boot_set_ecpu_ctrl(struct wl1271 *wl, u32 flag)
{
u32 cpu_ctrl;
+ int ret;
/* 10.5.0 run the firmware (I) */
- cpu_ctrl = wlcore_read_reg(wl, REG_ECPU_CONTROL);
+ ret = wlcore_read_reg(wl, REG_ECPU_CONTROL, &cpu_ctrl);
+ if (ret < 0)
+ goto out;
/* 10.5.1 run the firmware (II) */
cpu_ctrl |= flag;
- wlcore_write_reg(wl, REG_ECPU_CONTROL, cpu_ctrl);
+ ret = wlcore_write_reg(wl, REG_ECPU_CONTROL, cpu_ctrl);
+
+out:
+ return ret;
}
-static int wlcore_parse_fw_ver(struct wl1271 *wl)
+static int wlcore_boot_parse_fw_ver(struct wl1271 *wl,
+ struct wl1271_static_data *static_data)
{
int ret;
+ strncpy(wl->chip.fw_ver_str, static_data->fw_version,
+ sizeof(wl->chip.fw_ver_str));
+
+ /* make sure the string is NULL-terminated */
+ wl->chip.fw_ver_str[sizeof(wl->chip.fw_ver_str) - 1] = '\0';
+
ret = sscanf(wl->chip.fw_ver_str + 4, "%u.%u.%u.%u.%u",
&wl->chip.fw_ver[0], &wl->chip.fw_ver[1],
&wl->chip.fw_ver[2], &wl->chip.fw_ver[3],
@@ -57,43 +70,106 @@ static int wlcore_parse_fw_ver(struct wl1271 *wl)
if (ret != 5) {
wl1271_warning("fw version incorrect value");
memset(wl->chip.fw_ver, 0, sizeof(wl->chip.fw_ver));
- return -EINVAL;
+ ret = -EINVAL;
+ goto out;
}
ret = wlcore_identify_fw(wl);
if (ret < 0)
- return ret;
+ goto out;
+out:
+ return ret;
+}
+
+static int wlcore_validate_fw_ver(struct wl1271 *wl)
+{
+ unsigned int *fw_ver = wl->chip.fw_ver;
+ unsigned int *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 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; i++)
+ if (min_ver[i] == WLCORE_FW_VER_IGNORE)
+ snprintf(min_fw_str, sizeof(min_fw_str),
+ "%s*.", min_fw_str);
+ else
+ snprintf(min_fw_str, sizeof(min_fw_str),
+ "%s%u.", min_fw_str, 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://github.com/TI-OpenLink/firmwares.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;
}
-static int wlcore_boot_fw_version(struct wl1271 *wl)
+static int 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(sizeof(*static_data), GFP_KERNEL | GFP_DMA);
+ static_data = kmalloc(len, GFP_KERNEL);
if (!static_data) {
- wl1271_error("Couldn't allocate memory for static data!");
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto out;
}
- wl1271_read(wl, wl->cmd_box_addr, static_data, sizeof(*static_data),
- false);
-
- strncpy(wl->chip.fw_ver_str, static_data->fw_version,
- sizeof(wl->chip.fw_ver_str));
+ ret = wlcore_read(wl, wl->cmd_box_addr, static_data, len, false);
+ if (ret < 0)
+ goto out_free;
- kfree(static_data);
+ ret = wlcore_boot_parse_fw_ver(wl, static_data);
+ if (ret < 0)
+ goto out_free;
- /* make sure the string is NULL-terminated */
- wl->chip.fw_ver_str[sizeof(wl->chip.fw_ver_str) - 1] = '\0';
+ ret = wlcore_validate_fw_ver(wl);
+ if (ret < 0)
+ goto out_free;
- ret = wlcore_parse_fw_ver(wl);
+ ret = wlcore_handle_static_data(wl, static_data);
if (ret < 0)
- return ret;
+ goto out_free;
- return 0;
+out_free:
+ kfree(static_data);
+out:
+ return ret;
}
static int wl1271_boot_upload_firmware_chunk(struct wl1271 *wl, void *buf,
@@ -102,6 +178,7 @@ static int wl1271_boot_upload_firmware_chunk(struct wl1271 *wl, void *buf,
struct wlcore_partition_set partition;
int addr, chunk_num, partition_limit;
u8 *p, *chunk;
+ int ret;
/* whal_FwCtrl_LoadFwImageSm() */
@@ -123,7 +200,9 @@ static int wl1271_boot_upload_firmware_chunk(struct wl1271 *wl, void *buf,
memcpy(&partition, &wl->ptable[PART_DOWN], sizeof(partition));
partition.mem.start = dest;
- wlcore_set_partition(wl, &partition);
+ ret = wlcore_set_partition(wl, &partition);
+ if (ret < 0)
+ goto out;
/* 10.1 set partition limit and chunk num */
chunk_num = 0;
@@ -137,7 +216,9 @@ static int wl1271_boot_upload_firmware_chunk(struct wl1271 *wl, void *buf,
partition_limit = chunk_num * CHUNK_SIZE +
wl->ptable[PART_DOWN].mem.size;
partition.mem.start = addr;
- wlcore_set_partition(wl, &partition);
+ ret = wlcore_set_partition(wl, &partition);
+ if (ret < 0)
+ goto out;
}
/* 10.3 upload the chunk */
@@ -146,7 +227,9 @@ static int wl1271_boot_upload_firmware_chunk(struct wl1271 *wl, void *buf,
memcpy(chunk, p, CHUNK_SIZE);
wl1271_debug(DEBUG_BOOT, "uploading fw chunk 0x%p to 0x%x",
p, addr);
- wl1271_write(wl, addr, chunk, CHUNK_SIZE, false);
+ ret = wlcore_write(wl, addr, chunk, CHUNK_SIZE, false);
+ if (ret < 0)
+ goto out;
chunk_num++;
}
@@ -157,10 +240,11 @@ static int wl1271_boot_upload_firmware_chunk(struct wl1271 *wl, void *buf,
memcpy(chunk, p, fw_data_len % CHUNK_SIZE);
wl1271_debug(DEBUG_BOOT, "uploading fw last chunk (%zd B) 0x%p to 0x%x",
fw_data_len % CHUNK_SIZE, p, addr);
- wl1271_write(wl, addr, chunk, fw_data_len % CHUNK_SIZE, false);
+ ret = wlcore_write(wl, addr, chunk, fw_data_len % CHUNK_SIZE, false);
+out:
kfree(chunk);
- return 0;
+ return ret;
}
int wlcore_boot_upload_firmware(struct wl1271 *wl)
@@ -203,9 +287,12 @@ int wlcore_boot_upload_nvs(struct wl1271 *wl)
int i;
u32 dest_addr, val;
u8 *nvs_ptr, *nvs_aligned;
+ int ret;
- if (wl->nvs == NULL)
+ if (wl->nvs == NULL) {
+ wl1271_error("NVS file is needed during boot");
return -ENODEV;
+ }
if (wl->quirks & WLCORE_QUIRK_LEGACY_NVS) {
struct wl1271_nvs_file *nvs =
@@ -298,7 +385,9 @@ int wlcore_boot_upload_nvs(struct wl1271 *wl)
wl1271_debug(DEBUG_BOOT,
"nvs burst write 0x%x: 0x%x",
dest_addr, val);
- wl1271_write32(wl, dest_addr, val);
+ ret = wlcore_write32(wl, dest_addr, val);
+ if (ret < 0)
+ return ret;
nvs_ptr += 4;
dest_addr += 4;
@@ -324,7 +413,9 @@ int wlcore_boot_upload_nvs(struct wl1271 *wl)
nvs_len -= nvs_ptr - (u8 *)wl->nvs;
/* Now we must set the partition correctly */
- wlcore_set_partition(wl, &wl->ptable[PART_WORK]);
+ 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);
@@ -332,11 +423,11 @@ int wlcore_boot_upload_nvs(struct wl1271 *wl)
return -ENOMEM;
/* And finally we upload the NVS tables */
- wlcore_write_data(wl, REG_CMD_MBOX_ADDRESS,
- nvs_aligned, nvs_len, false);
+ ret = wlcore_write_data(wl, REG_CMD_MBOX_ADDRESS, nvs_aligned, nvs_len,
+ false);
kfree(nvs_aligned);
- return 0;
+ return ret;
out_badnvs:
wl1271_error("nvs data is malformed");
@@ -350,11 +441,17 @@ int wlcore_boot_run_firmware(struct wl1271 *wl)
u32 chip_id, intr;
/* Make sure we have the boot partition */
- wlcore_set_partition(wl, &wl->ptable[PART_BOOT]);
+ ret = wlcore_set_partition(wl, &wl->ptable[PART_BOOT]);
+ if (ret < 0)
+ return ret;
- wl1271_boot_set_ecpu_ctrl(wl, ECPU_CONTROL_HALT);
+ ret = wl1271_boot_set_ecpu_ctrl(wl, ECPU_CONTROL_HALT);
+ if (ret < 0)
+ return ret;
- chip_id = wlcore_read_reg(wl, REG_CHIP_ID_B);
+ 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);
@@ -367,7 +464,9 @@ int wlcore_boot_run_firmware(struct wl1271 *wl)
loop = 0;
while (loop++ < INIT_LOOP) {
udelay(INIT_LOOP_DELAY);
- intr = wlcore_read_reg(wl, REG_INTERRUPT_NO_CLEAR);
+ ret = wlcore_read_reg(wl, REG_INTERRUPT_NO_CLEAR, &intr);
+ if (ret < 0)
+ return ret;
if (intr == 0xffffffff) {
wl1271_error("error reading hardware complete "
@@ -376,8 +475,10 @@ int wlcore_boot_run_firmware(struct wl1271 *wl)
}
/* check that ACX_INTR_INIT_COMPLETE is enabled */
else if (intr & WL1271_ACX_INTR_INIT_COMPLETE) {
- wlcore_write_reg(wl, REG_INTERRUPT_ACK,
- WL1271_ACX_INTR_INIT_COMPLETE);
+ ret = wlcore_write_reg(wl, REG_INTERRUPT_ACK,
+ WL1271_ACX_INTR_INIT_COMPLETE);
+ if (ret < 0)
+ return ret;
break;
}
}
@@ -389,20 +490,25 @@ int wlcore_boot_run_firmware(struct wl1271 *wl)
}
/* get hardware config command mail box */
- wl->cmd_box_addr = wlcore_read_reg(wl, REG_COMMAND_MAILBOX_PTR);
+ ret = wlcore_read_reg(wl, REG_COMMAND_MAILBOX_PTR, &wl->cmd_box_addr);
+ if (ret < 0)
+ return ret;
wl1271_debug(DEBUG_MAILBOX, "cmd_box_addr 0x%x", wl->cmd_box_addr);
/* get hardware config event mail box */
- wl->mbox_ptr[0] = wlcore_read_reg(wl, REG_EVENT_MAILBOX_PTR);
- wl->mbox_ptr[1] = wl->mbox_ptr[0] + sizeof(struct event_mailbox);
+ ret = wlcore_read_reg(wl, REG_EVENT_MAILBOX_PTR, &wl->mbox_ptr[0]);
+ if (ret < 0)
+ return ret;
+
+ wl->mbox_ptr[1] = wl->mbox_ptr[0] + wl->mbox_size;
wl1271_debug(DEBUG_MAILBOX, "MBOX ptrs: 0x%x 0x%x",
wl->mbox_ptr[0], wl->mbox_ptr[1]);
- ret = wlcore_boot_fw_version(wl);
+ ret = wlcore_boot_static_data(wl);
if (ret < 0) {
- wl1271_error("couldn't boot firmware");
+ wl1271_error("error getting static data");
return ret;
}
@@ -412,23 +518,6 @@ int wlcore_boot_run_firmware(struct wl1271 *wl)
*/
/* unmask required mbox events */
- wl->event_mask = BSS_LOSE_EVENT_ID |
- REGAINED_BSS_EVENT_ID |
- SCAN_COMPLETE_EVENT_ID |
- ROLE_STOP_COMPLETE_EVENT_ID |
- RSSI_SNR_TRIGGER_0_EVENT_ID |
- PSPOLL_DELIVERY_FAILURE_EVENT_ID |
- SOFT_GEMINI_SENSE_EVENT_ID |
- PERIODIC_SCAN_REPORT_EVENT_ID |
- PERIODIC_SCAN_COMPLETE_EVENT_ID |
- DUMMY_PACKET_EVENT_ID |
- PEER_REMOVE_COMPLETE_EVENT_ID |
- BA_SESSION_RX_CONSTRAINT_EVENT_ID |
- REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID |
- INACTIVE_STA_EVENT_ID |
- MAX_TX_RETRY_EVENT_ID |
- CHANNEL_SWITCH_COMPLETE_EVENT_ID;
-
ret = wl1271_event_unmask(wl);
if (ret < 0) {
wl1271_error("EVENT mask setting failed");
@@ -436,9 +525,9 @@ int wlcore_boot_run_firmware(struct wl1271 *wl)
}
/* set the working partition to its "running" mode offset */
- wlcore_set_partition(wl, &wl->ptable[PART_WORK]);
+ ret = wlcore_set_partition(wl, &wl->ptable[PART_WORK]);
/* firmware startup completed */
- return 0;
+ return ret;
}
EXPORT_SYMBOL_GPL(wlcore_boot_run_firmware);