/** * send command to firmware * * @wl: wl struct * @id: command id * @buf: buffer containing the command, must work with dma * @len: length of the buffer */ int wl1251_cmd_send(struct wl1251 *wl, u16 id, void *buf, size_t len) { struct wl1251_cmd_header *cmd; unsigned long timeout; u32 intr; int ret = 0; cmd = buf; cmd->id = id; cmd->status = 0; WARN_ON(len % 4 != 0); wl1251_mem_write(wl, wl->cmd_box_addr, buf, len); wl1251_reg_write32(wl, ACX_REG_INTERRUPT_TRIG, INTR_TRIG_CMD); timeout = jiffies + msecs_to_jiffies(WL1251_COMMAND_TIMEOUT); intr = wl1251_reg_read32(wl, ACX_REG_INTERRUPT_NO_CLEAR); while (!(intr & WL1251_ACX_INTR_CMD_COMPLETE)) { if (time_after(jiffies, timeout)) { wl1251_error("command complete timeout"); ret = -ETIMEDOUT; goto out; } msleep(1); intr = wl1251_reg_read32(wl, ACX_REG_INTERRUPT_NO_CLEAR); } wl1251_reg_write32(wl, ACX_REG_INTERRUPT_ACK, WL1251_ACX_INTR_CMD_COMPLETE); out: return ret; }
static int wl1251_boot_upload_firmware(struct wl1251 *wl) { int addr, chunk_num, partition_limit; size_t fw_data_len, len; u8 *p, *buf; /* whal_FwCtrl_LoadFwImageSm() */ wl1251_debug(DEBUG_BOOT, "chip id before fw upload: 0x%x", wl1251_reg_read32(wl, CHIP_ID_B)); /* 10.0 check firmware length and set partition */ fw_data_len = (wl->fw[4] << 24) | (wl->fw[5] << 16) | (wl->fw[6] << 8) | (wl->fw[7]); wl1251_debug(DEBUG_BOOT, "fw_data_len %zu chunk_size %d", fw_data_len, CHUNK_SIZE); if ((fw_data_len % 4) != 0) { wl1251_error("firmware length not multiple of four"); return -EIO; } buf = kmalloc(CHUNK_SIZE, GFP_KERNEL); if (!buf) { wl1251_error("allocation for firmware upload chunk failed"); return -ENOMEM; } wl1251_set_partition(wl, WL1251_PART_DOWN_MEM_START, WL1251_PART_DOWN_MEM_SIZE, WL1251_PART_DOWN_REG_START, WL1251_PART_DOWN_REG_SIZE); /* 10.1 set partition limit and chunk num */ chunk_num = 0; partition_limit = WL1251_PART_DOWN_MEM_SIZE; while (chunk_num < fw_data_len / CHUNK_SIZE) { /* 10.2 update partition, if needed */ addr = WL1251_PART_DOWN_MEM_START + (chunk_num + 2) * CHUNK_SIZE; if (addr > partition_limit) { addr = WL1251_PART_DOWN_MEM_START + chunk_num * CHUNK_SIZE; partition_limit = chunk_num * CHUNK_SIZE + WL1251_PART_DOWN_MEM_SIZE; wl1251_set_partition(wl, addr, WL1251_PART_DOWN_MEM_SIZE, WL1251_PART_DOWN_REG_START, WL1251_PART_DOWN_REG_SIZE); } /* 10.3 upload the chunk */ addr = WL1251_PART_DOWN_MEM_START + chunk_num * CHUNK_SIZE; p = wl->fw + FW_HDR_SIZE + chunk_num * CHUNK_SIZE; wl1251_debug(DEBUG_BOOT, "uploading fw chunk 0x%p to 0x%x", p, addr); /* need to copy the chunk for dma */ len = CHUNK_SIZE; memcpy(buf, p, len); wl1251_mem_write(wl, addr, buf, len); chunk_num++; } /* 10.4 upload the last chunk */ addr = WL1251_PART_DOWN_MEM_START + chunk_num * CHUNK_SIZE; p = wl->fw + FW_HDR_SIZE + chunk_num * CHUNK_SIZE; /* need to copy the chunk for dma */ len = fw_data_len % CHUNK_SIZE; memcpy(buf, p, len); wl1251_debug(DEBUG_BOOT, "uploading fw last chunk (%zu B) 0x%p to 0x%x", len, p, addr); wl1251_mem_write(wl, addr, buf, len); kfree(buf); return 0; }