int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len) { struct wl1271_cmd_header *cmd; unsigned long timeout; u32 intr; int ret = 0; cmd = buf; cmd->id = id; cmd->status = 0; WARN_ON(len % 4 != 0); wl1271_spi_mem_write(wl, wl->cmd_box_addr, buf, len); wl1271_reg_write32(wl, ACX_REG_INTERRUPT_TRIG, INTR_TRIG_CMD); timeout = jiffies + msecs_to_jiffies(WL1271_COMMAND_TIMEOUT); intr = wl1271_reg_read32(wl, ACX_REG_INTERRUPT_NO_CLEAR); while (!(intr & WL1271_ACX_INTR_CMD_COMPLETE)) { if (time_after(jiffies, timeout)) { wl1271_error("command complete timeout"); ret = -ETIMEDOUT; goto out; } msleep(1); intr = wl1271_reg_read32(wl, ACX_REG_INTERRUPT_NO_CLEAR); } wl1271_reg_write32(wl, ACX_REG_INTERRUPT_ACK, WL1271_ACX_INTR_CMD_COMPLETE); out: return ret; }
static int wl1271_boot_upload_firmware_chunk(struct wl1271 *wl, void *buf, size_t fw_data_len, u32 dest) { int addr, chunk_num, partition_limit; u8 *p; /* whal_FwCtrl_LoadFwImageSm() */ wl1271_debug(DEBUG_BOOT, "starting firmware upload"); wl1271_debug(DEBUG_BOOT, "fw_data_len %d chunk_size %d", fw_data_len, CHUNK_SIZE); if ((fw_data_len % 4) != 0) { wl1271_error("firmware length not multiple of four"); return -EIO; } wl1271_set_partition(wl, dest, part_table[PART_DOWN].mem.size, part_table[PART_DOWN].reg.start, part_table[PART_DOWN].reg.size); /* 10.1 set partition limit and chunk num */ chunk_num = 0; partition_limit = part_table[PART_DOWN].mem.size; while (chunk_num < fw_data_len / CHUNK_SIZE) { /* 10.2 update partition, if needed */ addr = dest + (chunk_num + 2) * CHUNK_SIZE; if (addr > partition_limit) { addr = dest + chunk_num * CHUNK_SIZE; partition_limit = chunk_num * CHUNK_SIZE + part_table[PART_DOWN].mem.size; /* FIXME: Over 80 chars! */ wl1271_set_partition(wl, addr, part_table[PART_DOWN].mem.size, part_table[PART_DOWN].reg.start, part_table[PART_DOWN].reg.size); } /* 10.3 upload the chunk */ addr = dest + chunk_num * CHUNK_SIZE; p = buf + chunk_num * CHUNK_SIZE; wl1271_debug(DEBUG_BOOT, "uploading fw chunk 0x%p to 0x%x", p, addr); wl1271_spi_mem_write(wl, addr, p, CHUNK_SIZE); chunk_num++; } /* 10.4 upload the last chunk */ addr = dest + chunk_num * CHUNK_SIZE; p = buf + chunk_num * CHUNK_SIZE; wl1271_debug(DEBUG_BOOT, "uploading fw last chunk (%d B) 0x%p to 0x%x", fw_data_len % CHUNK_SIZE, p, addr); wl1271_spi_mem_write(wl, addr, p, fw_data_len % CHUNK_SIZE); return 0; }
static int wl1271_boot_upload_nvs(struct wl1271 *wl) { size_t nvs_len, burst_len; int i; u32 dest_addr, val; u8 *nvs_ptr, *nvs, *nvs_aligned; nvs = wl->nvs; if (nvs == NULL) return -ENODEV; nvs_ptr = nvs; nvs_len = wl->nvs_len; /* Update the device MAC address into the nvs */ nvs[11] = wl->mac_addr[0]; nvs[10] = wl->mac_addr[1]; nvs[6] = wl->mac_addr[2]; nvs[5] = wl->mac_addr[3]; nvs[4] = wl->mac_addr[4]; nvs[3] = wl->mac_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)); /* FIXME: Due to our new wl1271_translate_reg_addr function, we need to add the REGISTER_BASE to the destination */ dest_addr += REGISTERS_BASE; /* We move our pointer to the data */ nvs_ptr += 3; for (i = 0; i < burst_len; i++) { val = (nvs_ptr[0] | (nvs_ptr[1] << 8) | (nvs_ptr[2] << 16) | (nvs_ptr[3] << 24)); wl1271_debug(DEBUG_BOOT, "nvs burst write 0x%x: 0x%x", dest_addr, val); wl1271_reg_write32(wl, dest_addr, val); nvs_ptr += 4; dest_addr += 4; } } /* * We've reached the first zero length, the first NVS table * is 7 bytes further. */ nvs_ptr += 7; nvs_len -= nvs_ptr - nvs; nvs_len = ALIGN(nvs_len, 4); /* FIXME: The driver sets the partition here, but this is not needed, since it sets to the same one as currently in use */ /* Now we must set the partition correctly */ wl1271_set_partition(wl, part_table[PART_WORK].mem.start, part_table[PART_WORK].mem.size, part_table[PART_WORK].reg.start, part_table[PART_WORK].reg.size); /* Copy the NVS tables to a new block to ensure alignment */ nvs_aligned = kmemdup(nvs_ptr, nvs_len, GFP_KERNEL); /* And finally we upload the NVS tables */ /* FIXME: In wl1271, we upload everything at once. No endianness handling needed here?! The ref driver doesn't do anything about it at this point */ wl1271_spi_mem_write(wl, CMD_MBOX_ADDRESS, nvs_aligned, nvs_len); kfree(nvs_aligned); return 0; }