MCI_SendCommand
Default mainpageat91libperipheralsmciMCI_SendCommand
Description Source Call Graph
Start Line: 257
unsigned char MCI_SendCommand(Mci *pMci, MciCmd *pMciCmd)
{
    AT91PS_MCI pMciHw = pMci->pMciHw;
    unsigned int mciIer, mciMr;

    SANITY_CHECK(pMci);
    SANITY_CHECK(pMciHw);
    SANITY_CHECK(pCommand);

    // Try to acquire the MCI semaphore
    if (pMci->semaphore == 0) {

        return MCI_ERROR_LOCK;
    }
    pMci->semaphore--;
    // TRACE_DEBUG("MCI_SendCommand %x %d\n\r", READ_MCI(pMciHw, MCI_SR), pCommand->cmd & 0x3f);

    // Command is now being executed
    pMci->pCommand = pCommand;
    pCommand->status = MCI_STATUS_PENDING;

    // Enable the MCI clock
    WRITE_PMC(AT91C_BASE_PMC, PMC_PCER, (1 << pMci->mciId));

    //Disable MCI clock, for multi-block data transfer
    MCI_Enable(pMci, DISABLE);

    // Set PDC data transfer direction
    if(pCommand->blockSize > 0) {
        if(pCommand->isRead) {
            WRITE_MCI(pMciHw, MCI_PTCR, AT91C_PDC_RXTEN);
        }
        else {
            WRITE_MCI(pMciHw, MCI_PTCR, AT91C_PDC_TXTEN);
        }
    }
    // Disable transmitter and receiver
    WRITE_MCI(pMciHw, MCI_PTCR, AT91C_PDC_RXTDIS | AT91C_PDC_TXTDIS);

    mciMr = READ_MCI(pMciHw, MCI_MR) & (~(AT91C_MCI_WRPROOF|AT91C_MCI_RDPROOF|AT91C_MCI_BLKLEN | AT91C_MCI_PDCMODE));

    // Command with DATA stage
    if (pCommand->blockSize > 0) {
        // Enable PDC mode and set block size
        if(pCommand->conTrans != MCI_CONTINUE_TRANSFER) {

            WRITE_MCI(pMciHw, MCI_MR, mciMr | AT91C_MCI_PDCMODE |AT91C_MCI_RDPROOF|AT91C_MCI_WRPROOF|(pCommand->blockSize << 16));
        }

        // DATA transfer from card to host
        if (pCommand->isRead) {
            WRITE_MCI(pMciHw, MCI_RPR, (int) pCommand->pData);

            // Sanity check
            if (pCommand->nbBlock == 0)
                pCommand->nbBlock = 1;
            if ((pCommand->blockSize & 0x3) != 0) {
                WRITE_MCI(pMciHw, MCI_RCR, (pCommand->nbBlock * pCommand->blockSize) / 4 + 1);
            }
            else {
                WRITE_MCI(pMciHw, MCI_RCR, (pCommand->nbBlock * pCommand->blockSize) / 4);
            }

            WRITE_MCI(pMciHw, MCI_PTCR, AT91C_PDC_RXTEN);
            mciIer = AT91C_MCI_ENDRX | STATUS_ERRORS;
            // mciIer = AT91C_MCI_RXBUFF | STATUS_ERRORS;
        }

        // DATA transfer from host to card
        else {
            // Sanity check
            if (pCommand->nbBlock == 0)
                pCommand->nbBlock = 1;
            WRITE_MCI(pMciHw, MCI_TPR, (int) pCommand->pData);
            // Update the PDC counter
            if ((pCommand->blockSize & 0x3) != 0) {
                WRITE_MCI(pMciHw, MCI_TCR, (pCommand->nbBlock * pCommand->blockSize) / 4 + 1);
            }
            else {
                WRITE_MCI(pMciHw, MCI_TCR, (pCommand->nbBlock * pCommand->blockSize) / 4);
            }
            // MCI_BLKE notifies the end of Multiblock command
            mciIer = AT91C_MCI_BLKE | STATUS_ERRORS;
        }
    }
    // No data transfer: stop at the end of the command
    else {
        WRITE_MCI(pMciHw, MCI_MR, mciMr);
        mciIer = AT91C_MCI_CMDRDY | STATUS_ERRORS;
    }
    // Enable MCI clock
    MCI_Enable(pMci, ENABLE);

    // Send the command
    if((pCommand->conTrans != MCI_CONTINUE_TRANSFER)
        || (pCommand->blockSize == 0)) {

        WRITE_MCI(pMciHw, MCI_ARGR, pCommand->arg);
        WRITE_MCI(pMciHw, MCI_CMDR, pCommand->cmd);
    }

    // In case of transmit, the PDC shall be enabled after sending the command
    if ((pCommand->blockSize > 0) && !(pCommand->isRead)) {
        WRITE_MCI(pMciHw, MCI_PTCR, AT91C_PDC_TXTEN);
    }

    // Ignore data error
    mciIer &= ~(AT91C_MCI_UNRE | AT91C_MCI_OVRE \
              | AT91C_MCI_DTOE | AT91C_MCI_DCRCE);

    // Interrupt enable shall be done after PDC TXTEN and RXTEN
    WRITE_MCI(pMciHw, MCI_IER, mciIer);

    return 0;
}