/** 
 *  @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_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;
}
예제 #2
0
/** 
 *  @brief Transfer firmware to card
 *  
 *  @param priv      A Pointer to bt_private structure
 *  @return 	     BT_STATUS_SUCCESS/BT_STATUS_FAILURE or other error no.	
 */
static int
sd_init_fw_dpc(bt_private * priv)
{
    struct sdio_mmc_card *card = (struct sdio_mmc_card *) priv->bt_dev.card;
    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
    u8 crc_buffer = 0;

    ENTER();
    firmware = (u8 *) priv->firmware->data;
    firmwarelen = priv->firmware->size;

    PRINTM(INFO, "BT: Downloading FW image (%d bytes)\n", firmwarelen);

#ifdef FW_DOWNLOAD_SPEED
    tv1 = get_utimeofday();
#endif

    tmpfwbufsz = ALIGN_SZ(BT_UPLD_SIZE, DMA_ALIGNMENT);
    tmpfwbuf = kmalloc(tmpfwbufsz, GFP_KERNEL);
    if (!tmpfwbuf) {
        PRINTM(ERROR,
               "BT: Unable to allocate buffer for firmware. Terminating download\n");
        ret = BT_STATUS_FAILURE;
        goto done;
    }
    memset(tmpfwbuf, 0, tmpfwbufsz);
    /* Ensure 8-byte aligned firmware buffer */
    fwbuf = (u8 *) ALIGN_ADDR(tmpfwbuf, DMA_ALIGNMENT);

    if (!(priv->fw_crc_check)) {
        /* CRC check not required, use custom header first */
        firmware = fw_crc_header;
        firmwarelen = FW_CRC_HEADER;
        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);
            DBG_HEXDUMP(ERROR, "BT: FW block:", fwbuf, len);

            /* 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 >= FW_CRC_HEADER) {
                /* Custom header download complete, restore original FW */
                offset = 0;
                firmware = (u8 *) priv->firmware->data;
                firmwarelen = priv->firmware->size;
                crc_buffer = 0;
            }
        }
    } while (TRUE);

    PRINTM(INFO, "\nBT: FW 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);
    LEAVE();
    return ret;
}