/** * @brief Transfers firmware to card * * @param priv A Pointer to bt_private structure * @param fw A Pointer to fw image * @param fw_len fw image len * @return BT_STATUS_SUCCESS/BT_STATUS_FAILURE or other error no. */ static int sd_init_fw_dpc(bt_private *priv, u8 *fw, int fw_len) { struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card; u8 *firmware = fw; int firmwarelen = fw_len; u8 base0; u8 base1; int ret = BT_STATUS_SUCCESS; int offset; void *tmpfwbuf = NULL; int tmpfwbufsz; u8 *fwbuf; u16 len; int txlen = 0; int tx_blocks = 0; int i = 0; int tries = 0; u8 sq_read_base_address_a0_reg = SQ_READ_BASE_ADDRESS_A0_REG; u8 sq_read_base_address_a1_reg = SQ_READ_BASE_ADDRESS_A1_REG; u8 crc_buffer = 0; u8 *header_crc_fw = NULL; u8 header_crc_fw_len = 0; header_crc_fw = fw_crc_header_rb_2; header_crc_fw_len = FW_CRC_HEADER_RB2; ENTER(); PRINTM(INFO, "BT: Downloading FW image (%d bytes)\n", firmwarelen); tmpfwbufsz = BT_UPLD_SIZE + DMA_ALIGNMENT; tmpfwbuf = kzalloc(tmpfwbufsz, GFP_KERNEL); if (!tmpfwbuf) { PRINTM(ERROR, "BT: Unable to allocate buffer for firmware. Terminating download\n"); ret = BT_STATUS_FAILURE; goto done; } /* Ensure aligned firmware buffer */ fwbuf = (u8 *)ALIGN_ADDR(tmpfwbuf, DMA_ALIGNMENT); if (!(priv->fw_crc_check) ) { /* CRC check not required, use custom header first */ firmware = header_crc_fw; firmwarelen = header_crc_fw_len; crc_buffer = 1; } /* Perform firmware data transfer */ offset = 0; do { /* The host polls for the DN_LD_CARD_RDY and CARD_IO_READY bits */ ret = sd_poll_card_status(priv, CARD_IO_READY | DN_LD_CARD_RDY); if (ret < 0) { PRINTM(FATAL, "BT: FW download with helper poll status timeout @ %d\n", offset); goto done; } if (!crc_buffer) /* More data? */ if (offset >= firmwarelen) break; for (tries = 0; tries < MAX_POLL_TRIES; tries++) { base0 = sdio_readb(card->func, sq_read_base_address_a0_reg, &ret); if (ret) { PRINTM(WARN, "Dev BASE0 register read failed:" " base0=0x%04X(%d). Terminating download\n", base0, base0); ret = BT_STATUS_FAILURE; goto done; } base1 = sdio_readb(card->func, sq_read_base_address_a1_reg, &ret); if (ret) { PRINTM(WARN, "Dev BASE1 register read failed:" " base1=0x%04X(%d). Terminating download\n", base1, base1); ret = BT_STATUS_FAILURE; goto done; } len = (((u16) base1) << 8) | base0; if (len != 0) break; udelay(10); } if (len == 0) break; else if (len > BT_UPLD_SIZE) { PRINTM(FATAL, "BT: FW download failure @ %d, invalid length %d\n", offset, len); ret = BT_STATUS_FAILURE; goto done; } txlen = len; if (len & BIT(0)) { i++; if (i > MAX_WRITE_IOMEM_RETRY) { PRINTM(FATAL, "BT: FW download failure @ %d, over max retry count\n", offset); ret = BT_STATUS_FAILURE; goto done; } PRINTM(ERROR, "BT: FW CRC error indicated by the helper:" " len = 0x%04X, txlen = %d\n", len, txlen); len &= ~BIT(0); PRINTM(ERROR, "BT: retry: %d, offset %d\n", i, offset); /* Setting this to 0 to resend from same offset */ txlen = 0; } else { i = 0; /* Set blocksize to transfer - checking for last block */ if (firmwarelen - offset < txlen) txlen = firmwarelen - offset; PRINTM(INFO, "."); tx_blocks = (txlen + SD_BLOCK_SIZE_FW_DL - 1) / SD_BLOCK_SIZE_FW_DL; /* Copy payload to buffer */ memcpy(fwbuf, &firmware[offset], txlen); } /* Send data */ ret = sdio_writesb(card->func, priv->bt_dev.ioport, fwbuf, tx_blocks * SD_BLOCK_SIZE_FW_DL); if (ret < 0) { PRINTM(ERROR, "BT: FW download, write iomem (%d) failed @ %d\n", i, offset); sdio_writeb(card->func, 0x04, CONFIGURATION_REG, &ret); if (ret) PRINTM(ERROR, "write ioreg failed (CFG)\n"); } offset += txlen; if (crc_buffer) { if (offset >= header_crc_fw_len) { /* Custom header download complete, restore original FW */ offset = 0; firmware = fw; firmwarelen = fw_len; crc_buffer = 0; } } } while (TRUE); PRINTM(MSG, "BT: FW download over, size %d bytes\n", offset); ret = BT_STATUS_SUCCESS; done: kfree(tmpfwbuf); LEAVE(); return ret; }
/*----------------------------------------------------------------------------*/ BOOL kalDevPortWrite ( IN P_GLUE_INFO_T prGlueInfo, IN UINT_16 u2Port, IN UINT_16 u2Len, IN PUINT_8 pucBuf, IN UINT_16 u2ValidInBufSize ) { P_GL_HIF_INFO_T prHifInfo = NULL; PUINT_8 pucSrc = NULL; int count = u2Len; int ret = 0; int bNum = 0; #if (MTK_WCN_HIF_SDIO == 0) struct sdio_func *prSdioFunc = NULL; #endif #if DBG //printk(KERN_INFO DRV_NAME"++kalDevPortWrite++ buf:0x%p, port:0x%x, length:%d\n", pucBuf, u2Port, u2Len); #endif ASSERT(prGlueInfo); prHifInfo = &prGlueInfo->rHifInfo; ASSERT(pucBuf); pucSrc = pucBuf; ASSERT(u2Len <= u2ValidInBufSize); #if (MTK_WCN_HIF_SDIO == 0) prSdioFunc = prHifInfo->func; ASSERT(prSdioFunc->cur_blksize > 0); if (!in_interrupt) { sdio_claim_host(prSdioFunc); } /* Split buffer into multiple single block to workaround hifsys */ while (count >= prSdioFunc->cur_blksize) { count -= prSdioFunc->cur_blksize; bNum++; } if (count > 0 && bNum > 0) { bNum++; } if (bNum > 0) { // block mode ret = sdio_writesb(prSdioFunc, u2Port, pucSrc, prSdioFunc->cur_blksize * bNum); #ifdef CONFIG_X86 /* ENE workaround */ { int tmp; sdio_writel(prSdioFunc, 0x0, SDIO_X86_WORKAROUND_WRITE_MCR, &tmp); } #endif } else { // byte mode if(count & 0x3L) { int aligned_count = (count+3) & (~0x3L); ret = sdio_writesb(prSdioFunc, u2Port, pucSrc, aligned_count); } else { ret = sdio_writesb(prSdioFunc, u2Port, pucSrc, count); } } if (!in_interrupt) { sdio_release_host(prSdioFunc); } #else /* Split buffer into multiple single block to workaround hifsys */ while (count >= ((prGlueInfo->rHifInfo).prFuncInfo->blk_sz)) { count -= ((prGlueInfo->rHifInfo).prFuncInfo->blk_sz); bNum++; } if (count > 0 && bNum > 0) { bNum++; } if (bNum > 0) { // block mode ret = mtk_wcn_hif_sdio_write_buf(prGlueInfo->rHifInfo.cltCtx, u2Port, (PUINT32) pucSrc, ((prGlueInfo->rHifInfo).prFuncInfo->blk_sz) * bNum); } else { // byte mode ret = mtk_wcn_hif_sdio_write_buf(prGlueInfo->rHifInfo.cltCtx, u2Port, (PUINT32) pucSrc, count); } #endif if (ret) { kalSendAeeWarning(HIF_SDIO_ERR_TITLE_STR, HIF_SDIO_ERR_DESC_STR "sdio_writesb() reports error: %x", ret); DBGLOG(HAL, ERROR, ("sdio_writesb() reports error: %x", ret)); } return (ret) ? FALSE : TRUE; } /* end of kalDevPortWrite() */
/** * @brief This function sends data to the card. * * @param priv A pointer to bt_private structure * @param payload A pointer to the data/cmd buffer * @param nb Length of data/cmd * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE */ int sbi_host_to_card(bt_private *priv, u8 *payload, u16 nb) { struct sdio_mmc_card *card = priv->bt_dev.card; struct m_dev *m_dev = &(priv->bt_dev.m_dev[BT_SEQ]); int ret = BT_STATUS_SUCCESS; int buf_block_len; int blksz; int i = 0; u8 *buf = NULL; void *tmpbuf = NULL; int tmpbufsz; ENTER(); if (!card || !card->func) { PRINTM(ERROR, "BT: card or function is NULL!\n"); LEAVE(); return BT_STATUS_FAILURE; } buf = payload; blksz = SD_BLOCK_SIZE; buf_block_len = (nb + blksz - 1) / blksz; /* Allocate buffer and copy payload */ if ((t_ptr)payload & (DMA_ALIGNMENT - 1)) { tmpbufsz = buf_block_len * blksz + DMA_ALIGNMENT; tmpbuf = kzalloc(tmpbufsz, GFP_KERNEL); if (!tmpbuf) { LEAVE(); return BT_STATUS_FAILURE; } /* Ensure 8-byte aligned CMD buffer */ buf = (u8 *)ALIGN_ADDR(tmpbuf, DMA_ALIGNMENT); memcpy(buf, payload, nb); } sdio_claim_host(card->func); #define MAX_WRITE_IOMEM_RETRY 2 do { /* Transfer data to card */ ret = sdio_writesb(card->func, priv->bt_dev.ioport, buf, buf_block_len * blksz); if (ret < 0) { i++; PRINTM(ERROR, "BT: host_to_card, write iomem (%d) failed: %d\n", i, ret); sdio_writeb(card->func, HOST_WO_CMD53_FINISH_HOST, CONFIGURATION_REG, &ret); udelay(20); ret = BT_STATUS_FAILURE; if (i > MAX_WRITE_IOMEM_RETRY) goto exit; } else { PRINTM(DATA, "BT: SDIO Blk Wr %s: len=%d\n", m_dev->name, nb); DBG_HEXDUMP(DAT_D, "BT: SDIO Blk Wr", payload, nb); } } while (ret == BT_STATUS_FAILURE); priv->bt_dev.tx_dnld_rdy = FALSE; exit: sdio_release_host(card->func); kfree(tmpbuf); LEAVE(); return ret; }
static A_STATUS __HIFReadWrite(HIF_DEVICE *device, A_UINT32 address, A_UCHAR *buffer, A_UINT32 length, A_UINT32 request, void *context) { A_UINT8 opcode; A_STATUS status = A_OK; int ret; A_UINT8 *tbuffer; A_BOOL bounced = FALSE; AR_DEBUG_ASSERT(device != NULL); AR_DEBUG_ASSERT(device->func != NULL); AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: Device: 0x%p, buffer:0x%p (addr:0x%X)\n", device, buffer, address)); do { if (request & HIF_EXTENDED_IO) { /*AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: Command type: CMD53\n")); */ } else { AR_DEBUG_PRINTF(ATH_DEBUG_ERROR, ("AR6000: Invalid command type: 0x%08x\n", request)); status = A_EINVAL; break; } if (request & HIF_BLOCK_BASIS) { /* round to whole block length size */ length = (length / HIF_MBOX_BLOCK_SIZE) * HIF_MBOX_BLOCK_SIZE; AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: Block mode (BlockLen: %d)\n", length)); } else if (request & HIF_BYTE_BASIS) { AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: Byte mode (BlockLen: %d)\n", length)); } else { AR_DEBUG_PRINTF(ATH_DEBUG_ERROR, ("AR6000: Invalid data mode: 0x%08x\n", request)); status = A_EINVAL; break; } #if 0 /* useful for checking register accesses */ if (length & 0x3) { A_PRINTF(KERN_ALERT"AR6000: HIF (%s) is not a multiple of 4 bytes, addr:0x%X, len:%d\n", request & HIF_WRITE ? "write":"read", address, length); } #endif if (request & HIF_WRITE) { if ((address >= HIF_MBOX_START_ADDR(0)) && (address <= HIF_MBOX_END_ADDR(3))) { AR_DEBUG_ASSERT(length <= HIF_MBOX_WIDTH); /* * Mailbox write. Adjust the address so that the last byte * falls on the EOM address. */ address += (HIF_MBOX_WIDTH - length); } } if (request & HIF_FIXED_ADDRESS) { opcode = CMD53_FIXED_ADDRESS; AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: Address mode: Fixed 0x%X\n", address)); } else if (request & HIF_INCREMENTAL_ADDRESS) { opcode = CMD53_INCR_ADDRESS; AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: Address mode: Incremental 0x%X\n", address)); } else { AR_DEBUG_PRINTF(ATH_DEBUG_ERROR, ("AR6000: Invalid address mode: 0x%08x\n", request)); status = A_EINVAL; break; } if (request & HIF_WRITE) { #if HIF_USE_DMA_BOUNCE_BUFFER if (BUFFER_NEEDS_BOUNCE(buffer)) { AR_DEBUG_ASSERT(device->dma_buffer != NULL); tbuffer = device->dma_buffer; /* copy the write data to the dma buffer */ AR_DEBUG_ASSERT(length <= HIF_DMA_BUFFER_SIZE); memcpy(tbuffer, buffer, length); bounced = TRUE; } else { tbuffer = buffer; } #else tbuffer = buffer; #endif if (opcode == CMD53_FIXED_ADDRESS) { ret = sdio_writesb(device->func, address, tbuffer, length); AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: writesb ret=%d address: 0x%X, len: %d, 0x%X\n", ret, address, length, *(int *)tbuffer)); } else { ret = sdio_memcpy_toio(device->func, address, tbuffer, length); AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: writeio ret=%d address: 0x%X, len: %d, 0x%X\n", ret, address, length, *(int *)tbuffer)); } } else if (request & HIF_READ) { #if HIF_USE_DMA_BOUNCE_BUFFER if (BUFFER_NEEDS_BOUNCE(buffer)) { AR_DEBUG_ASSERT(device->dma_buffer != NULL); AR_DEBUG_ASSERT(length <= HIF_DMA_BUFFER_SIZE); tbuffer = device->dma_buffer; bounced = TRUE; } else { tbuffer = buffer; } #else tbuffer = buffer; #endif if (opcode == CMD53_FIXED_ADDRESS) { ret = sdio_readsb(device->func, tbuffer, address, length); AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: readsb ret=%d address: 0x%X, len: %d, 0x%X\n", ret, address, length, *(int *)tbuffer)); } else { ret = sdio_memcpy_fromio(device->func, tbuffer, address, length); AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: readio ret=%d address: 0x%X, len: %d, 0x%X\n", ret, address, length, *(int *)tbuffer)); } #if HIF_USE_DMA_BOUNCE_BUFFER if (bounced) { /* copy the read data from the dma buffer */ memcpy(buffer, tbuffer, length); } #endif } else { AR_DEBUG_PRINTF(ATH_DEBUG_ERROR, ("AR6000: Invalid direction: 0x%08x\n", request)); status = A_EINVAL; break; } if (ret) { AR_DEBUG_PRINTF(ATH_DEBUG_ERROR, ("AR6000: SDIO bus operation failed! MMC stack returned : %d \n", ret)); status = A_ERROR; } } while (FALSE); return status; }
int sif_io_sync(struct esp_pub *epub, u32 addr, u8 *buf, u32 len, u32 flag) { int err = 0; u8 * ibuf = NULL; bool need_ibuf = false; struct esp_sdio_ctrl *sctrl = NULL; struct sdio_func *func = NULL; if (epub == NULL || buf == NULL) { ESSERT(0); err = -EINVAL; goto _exit; } sctrl = (struct esp_sdio_ctrl *)epub->sif; func = sctrl->func; if (func == NULL) { ESSERT(0); err = -EINVAL; goto _exit; } if (bad_buf(buf)) { esp_dbg(ESP_DBG_TRACE, "%s dst 0x%08x, len %d badbuf\n", __func__, addr, len); need_ibuf = true; ibuf = sctrl->dma_buffer; } else { ibuf = buf; } if (flag & SIF_BLOCK_BASIS) { /* round up for block data transcation */ } if (flag & SIF_TO_DEVICE) { esp_dbg(ESP_DBG_TRACE, "%s to addr 0x%08x, len %d \n", __func__, addr, len); if (need_ibuf) memcpy(ibuf, buf, len); sdio_claim_host(func); if (flag & SIF_FIXED_ADDR) err = sdio_writesb(func, addr, ibuf, len); else if (flag & SIF_INC_ADDR) { err = sdio_memcpy_toio(func, addr, ibuf, len); } sif_platform_check_r1_ready(epub); sdio_release_host(func); } else if (flag & SIF_FROM_DEVICE) { esp_dbg(ESP_DBG_TRACE, "%s from addr 0x%08x, len %d \n", __func__, addr, len); sdio_claim_host(func); if (flag & SIF_FIXED_ADDR) err = sdio_readsb(func, ibuf, addr, len); else if (flag & SIF_INC_ADDR) { err = sdio_memcpy_fromio(func, ibuf, addr, len); } sdio_release_host(func); if (!err && need_ibuf) memcpy(buf, ibuf, len); } _exit: return err; }
/** * @brief This function downloads firmware image to the card. * * @param priv A pointer to bt_private structure * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE */ int sd_download_firmware_w_helper(bt_private * priv) { struct sdio_mmc_card *card = (struct sdio_mmc_card *) priv->bt_dev.card; const struct firmware *fw_firmware = NULL; u8 *firmware = NULL; int firmwarelen; u8 base0; u8 base1; int ret = BT_STATUS_SUCCESS; int offset; void *tmpfwbuf = NULL; int tmpfwbufsz; u8 *fwbuf; u16 len; int txlen = 0; int tx_blocks = 0; int i = 0; int tries = 0; #ifdef FW_DOWNLOAD_SPEED u32 tv1, tv2; #endif char *cur_fw_name = NULL; ENTER(); if (fw_name == NULL) /* Check revision ID */ switch (priv->adapter->chip_rev) { case SD8787_W0: case SD8787_W1: cur_fw_name = SD8787_W1_FW_NAME; break; case SD8787_A0: case SD8787_A1: cur_fw_name = SD8787_AX_FW_NAME; break; default: cur_fw_name = DEFAULT_FW_NAME; break; } else cur_fw_name = fw_name; if ((ret = request_firmware(&fw_firmware, cur_fw_name, priv->hotplug_device)) < 0) { PRINTM(FATAL, "request_firmware() failed, error code = %#x\n", ret); goto done; } if (fw_firmware) { firmware = (u8 *) fw_firmware->data; firmwarelen = fw_firmware->size; } else { PRINTM(MSG, "No firmware image found! Terminating download\n"); ret = BT_STATUS_FAILURE; goto done; } PRINTM(INFO, "Downloading FW image (%d bytes)\n", firmwarelen); #ifdef FW_DOWNLOAD_SPEED tv1 = get_utimeofday(); #endif #ifdef PXA3XX_DMA_ALIGN tmpfwbufsz = ALIGN_SZ(BT_UPLD_SIZE, PXA3XX_DMA_ALIGNMENT); #else /* PXA3XX_DMA_ALIGN */ tmpfwbufsz = BT_UPLD_SIZE; #endif tmpfwbuf = kmalloc(tmpfwbufsz, GFP_KERNEL); if (!tmpfwbuf) { PRINTM(ERROR, "Unable to allocate buffer for firmware. Terminating download\n"); ret = BT_STATUS_FAILURE; goto done; } memset(tmpfwbuf, 0, tmpfwbufsz); #ifdef PXA3XX_DMA_ALIGN /* Ensure 8-byte aligned firmware buffer */ fwbuf = (u8 *) ALIGN_ADDR(tmpfwbuf, PXA3XX_DMA_ALIGNMENT); #else /* PXA3XX_DMA_ALIGN */ fwbuf = (u8 *) tmpfwbuf; #endif /* Perform firmware data transfer */ offset = 0; do { /* The host polls for the DN_LD_CARD_RDY and CARD_IO_READY bits */ ret = sd_poll_card_status(priv, CARD_IO_READY | DN_LD_CARD_RDY); if (ret < 0) { PRINTM(FATAL, "FW download with helper poll status timeout @ %d\n", offset); goto done; } /* More data? */ if (offset >= firmwarelen) break; for (tries = 0; tries < MAX_POLL_TRIES; tries++) { base0 = sdio_readb(card->func, SQ_READ_BASE_ADDRESS_A0_REG, &ret); if (ret) { PRINTM(WARN, "Dev BASE0 register read failed:" " base0=0x%04X(%d). Terminating download\n", base0, base0); ret = BT_STATUS_FAILURE; goto done; } base1 = sdio_readb(card->func, SQ_READ_BASE_ADDRESS_A1_REG, &ret); if (ret) { PRINTM(WARN, "Dev BASE1 register read failed:" " base1=0x%04X(%d). Terminating download\n", base1, base1); ret = BT_STATUS_FAILURE; goto done; } len = (((u16) base1) << 8) | base0; if (len != 0) break; udelay(10); } if (len == 0) break; else if (len > BT_UPLD_SIZE) { PRINTM(FATAL, "FW download failure @ %d, invalid length %d\n", offset, len); ret = BT_STATUS_FAILURE; goto done; } txlen = len; if (len & BIT(0)) { i++; if (i > MAX_WRITE_IOMEM_RETRY) { PRINTM(FATAL, "FW download failure @ %d, over max retry count\n", offset); ret = BT_STATUS_FAILURE; goto done; } PRINTM(ERROR, "FW CRC error indicated by the helper:" " len = 0x%04X, txlen = %d\n", len, txlen); len &= ~BIT(0); /* Setting this to 0 to resend from same offset */ txlen = 0; } else { i = 0; /* Set blocksize to transfer - checking for last block */ if (firmwarelen - offset < txlen) txlen = firmwarelen - offset; PRINTM(INFO, "."); tx_blocks = (txlen + SD_BLOCK_SIZE_FW_DL - 1) / SD_BLOCK_SIZE_FW_DL; /* Copy payload to buffer */ memcpy(fwbuf, &firmware[offset], txlen); } /* Send data */ ret = sdio_writesb(card->func, priv->bt_dev.ioport, fwbuf, tx_blocks * SD_BLOCK_SIZE_FW_DL); if (ret < 0) { PRINTM(ERROR, "FW download, write iomem (%d) failed @ %d\n", i, offset); sdio_writeb(card->func, 0x04, CONFIGURATION_REG, &ret); if (ret) PRINTM(ERROR, "write ioreg failed (CFG)\n"); } offset += txlen; } while (TRUE); PRINTM(INFO, "\nFW download over, size %d bytes\n", offset); ret = BT_STATUS_SUCCESS; done: #ifdef FW_DOWNLOAD_SPEED tv2 = get_utimeofday(); PRINTM(INFO, "FW: %ld.%03ld.%03ld ", tv1 / 1000000, (tv1 % 1000000) / 1000, tv1 % 1000); PRINTM(INFO, " -> %ld.%03ld.%03ld ", tv2 / 1000000, (tv2 % 1000000) / 1000, tv2 % 1000); tv2 -= tv1; PRINTM(INFO, " == %ld.%03ld.%03ld\n", tv2 / 1000000, (tv2 % 1000000) / 1000, tv2 % 1000); #endif if (tmpfwbuf) kfree(tmpfwbuf); if (fw_firmware) release_firmware(fw_firmware); LEAVE(); return ret; }
int wlan_prog_helper(const uint8_t * firmware, int size) { int ret; uint8_t status; uint8_t *chunk_buffer; uint32_t chunk_size; uint64_t startTime; bufferPrintf("wlan: programming firmware helper...\r\n"); chunk_buffer = (uint8_t*) memalign(64, 4); if (!chunk_buffer) { ret = -1; goto release_fw; } ret = sdio_set_block_size(1, 32); if (ret) goto release; while (size) { startTime = timer_get_system_microtime(); while (TRUE) { status = sdio_readb(1, IF_SDIO_STATUS, &ret); if (ret) goto release; if ((status & IF_SDIO_IO_RDY) && (status & IF_SDIO_DL_RDY)) break; if(has_elapsed(startTime, 1000 * 1000)) { ret = -1; goto release; } udelay(1000); } if(size > 60) chunk_size = 60; else chunk_size = size; *((uint32_t*)chunk_buffer) = chunk_size; memcpy(chunk_buffer + 4, firmware, chunk_size); //bufferPrintf("wlan: sending %d bytes chunk\r\n", chunk_size); ret = sdio_writesb(1, ioport, chunk_buffer, 64); if (ret) goto release; firmware += chunk_size; size -= chunk_size; } /* an empty block marks the end of the transfer */ memset(chunk_buffer, 0, 4); ret = sdio_writesb(1, ioport, chunk_buffer, 64); if (ret) goto release; bufferPrintf("wlan: waiting for helper to boot\r\n"); /* wait for the helper to boot by looking at the size register */ startTime = timer_get_system_microtime(); while (TRUE) { uint16_t req_size; req_size = sdio_readb(1, IF_SDIO_RD_BASE, &ret); if (ret) goto release; req_size |= sdio_readb(1, IF_SDIO_RD_BASE + 1, &ret) << 8; if (ret) goto release; if (req_size != 0) break; if(has_elapsed(startTime, 1000 * 1000)) { ret = -1; goto release; } udelay(10000); } ret = 0; bufferPrintf("wlan: helper has booted!\r\n"); release: free(chunk_buffer); release_fw: if (ret) bufferPrintf("wlan: failed to load helper firmware\r\n"); return ret; }
int wlan_prog_real(const uint8_t* firmware, size_t size) { int ret; uint8_t status; uint8_t *chunk_buffer; uint32_t chunk_size; size_t req_size; uint64_t startTime; bufferPrintf("wlan: programming firmware...\r\n"); chunk_buffer = (uint8_t*) memalign(512, 4); if (!chunk_buffer) { ret = -1; goto release_fw; } ret = sdio_set_block_size(1, 32); if (ret) goto release; while (size) { startTime = timer_get_system_microtime(); while (1) { status = sdio_readb(1, IF_SDIO_STATUS, &ret); if (ret) goto release; if ((status & IF_SDIO_IO_RDY) && (status & IF_SDIO_DL_RDY)) break; if(has_elapsed(startTime, 1000 * 1000)) { ret = -1; goto release; } udelay(1000); } req_size = sdio_readb(1, IF_SDIO_RD_BASE, &ret); if (ret) goto release; req_size |= sdio_readb(1, IF_SDIO_RD_BASE + 1, &ret) << 8; if (ret) goto release; //bufferPrintf("wlan: firmware helper wants %d bytes\r\n", (int)req_size); if (req_size == 0) { bufferPrintf("wlan: firmware helper gave up early\r\n"); ret = -1; goto release; } if (req_size & 0x01) { bufferPrintf("wlan: firmware helper signalled error\r\n"); ret = -1; goto release; } if (req_size > size) req_size = size; while (req_size) { if(req_size > 512) chunk_size = 512; else chunk_size = req_size; memcpy(chunk_buffer, firmware, chunk_size); //bufferPrintf("wlan: sending %d bytes (%d bytes) chunk\r\n", // chunk_size, (chunk_size + 31) / 32 * 32); int to_send; to_send = chunk_size / 32; to_send *= 32; if(to_send < chunk_size) to_send += 32; ret = sdio_writesb(1, ioport, chunk_buffer, to_send); if (ret) goto release; firmware += chunk_size; size -= chunk_size; req_size -= chunk_size; } } ret = 0; bufferPrintf("wlan: waiting for firmware to boot\r\n"); /* wait for the firmware to boot */ startTime = timer_get_system_microtime(); while (TRUE) { uint16_t scratch; scratch = wlan_read_scratch(&ret); if (ret) goto release; if (scratch == IF_SDIO_FIRMWARE_OK) break; if(has_elapsed(startTime, 1000 * 1000)) { ret = -1; goto release; } udelay(10000); } ret = 0; bufferPrintf("wlan: firmware booted!\r\n"); release: free(chunk_buffer); release_fw: if (ret) bufferPrintf("wlan: failed to load firmware\r\n"); return ret; }