//------------------------------------------------------------------------------ /// Initializes the Spid structure and the corresponding SPI hardware. /// Always returns 0. /// \param pSpid Pointer to a Spid instance. /// \param pSpiHw Associated SPI peripheral. /// \param spiId SPI peripheral identifier. //------------------------------------------------------------------------------ unsigned char SPID_Configure(Spid *pSpid, AT91S_SPI *pSpiHw, unsigned char spiId) { // Initialize the SPI structure pSpid->pSpiHw = pSpiHw; pSpid->spiId = spiId; pSpid->semaphore = 1; pSpid->pCurrentCommand = 0; // Enable the SPI clock WRITE_PMC(AT91C_BASE_PMC, PMC_PCER, (1 << pSpid->spiId)); // Execute a software reset of the SPI twice WRITE_SPI(pSpiHw, SPI_CR, AT91C_SPI_SWRST); WRITE_SPI(pSpiHw, SPI_CR, AT91C_SPI_SWRST); // Configure SPI in Master Mode with No CS selected !!! WRITE_SPI(pSpiHw, SPI_MR, AT91C_SPI_MSTR | AT91C_SPI_MODFDIS | AT91C_SPI_PCS); // Disable the PDC transfer WRITE_SPI(pSpiHw, SPI_PTCR, AT91C_PDC_RXTDIS | AT91C_PDC_TXTDIS); // Enable the SPI WRITE_SPI(pSpiHw, SPI_CR, AT91C_SPI_SPIEN); // Enable the SPI clock WRITE_PMC(AT91C_BASE_PMC, PMC_PCDR, (1 << pSpid->spiId)); return 0; }
//------------------------------------------------------------------------------ /// The SPI_Handler must be called by the SPI Interrupt Service Routine with the /// corresponding Spi instance. /// The SPI_Handler will unlock the Spi semaphore and invoke the upper application /// callback. /// \param pSpid Pointer to a Spid instance. //------------------------------------------------------------------------------ void SPID_Handler(Spid *pSpid) { SpidCmd *pSpidCmd = pSpid->pCurrentCommand; AT91S_SPI *pSpiHw = pSpid->pSpiHw; volatile unsigned int spiSr; // Read the status register spiSr = READ_SPI(pSpiHw, SPI_SR); if (spiSr & AT91C_SPI_RXBUFF) { // Disable transmitter and receiver WRITE_SPI(pSpiHw, SPI_PTCR, AT91C_PDC_RXTDIS | AT91C_PDC_TXTDIS); // Disable the SPI clock WRITE_PMC(AT91C_BASE_PMC, PMC_PCDR, (1 << pSpid->spiId)); // Disable buffer complete interrupt WRITE_SPI(pSpiHw, SPI_IDR, AT91C_SPI_RXBUFF); // Release the dataflash semaphore pSpid->semaphore++; // Invoke the callback associated with the current command if (pSpidCmd && pSpidCmd->callback) pSpidCmd->callback(0, pSpidCmd->pArgument); // Nothing must be done after. A new DF operation may have been started // in the callback function. } }
//------------------------------------------------------------------------------ /// Initializes a MCI driver instance and the underlying peripheral. /// \param pMci Pointer to a MCI driver instance. /// \param pMciHw Pointer to a MCI peripheral. /// \param mciId MCI peripheral identifier. /// \param mode Slot and type of connected card. //------------------------------------------------------------------------------ void MCI_Init( Mci *pMci, AT91S_MCI *pMciHw, unsigned char mciId, unsigned int mode) { unsigned short clkDiv; SANITY_CHECK(pMci); SANITY_CHECK(pMciHw); SANITY_CHECK((mode == MCI_MMC_SLOTA) || (mode == MCI_MMC_SLOTB) || (mode == MCI_SD_SLOTA) || (mode == MCI_SD_SLOTB)); // Initialize the MCI driver structure pMci->pMciHw = pMciHw; pMci->mciId = mciId; pMci->semaphore = 1; pMci->pCommand = 0; // Enable the MCI clock WRITE_PMC(AT91C_BASE_PMC, PMC_PCER, (1 << mciId)); // Reset the MCI WRITE_MCI(pMciHw, MCI_CR, AT91C_MCI_SWRST); // Disable the MCI WRITE_MCI(pMciHw, MCI_CR, AT91C_MCI_MCIDIS | AT91C_MCI_PWSDIS); // Disable all the interrupts WRITE_MCI(pMciHw, MCI_IDR, 0xFFFFFFFF); // Set the Data Timeout Register WRITE_MCI(pMciHw, MCI_DTOR, DTOR_1MEGA_CYCLES); // Set the Mode Register: 400KHz for MCK = 48MHz (CLKDIV = 58) clkDiv = (BOARD_MCK / (400000 * 2)) - 1; WRITE_MCI(pMciHw, MCI_MR, (clkDiv | (AT91C_MCI_PWSDIV & (0x7 << 8)))); // Set the SDCard Register WRITE_MCI(pMciHw, MCI_SDCR, mode); // Enable the MCI and the Power Saving WRITE_MCI(pMciHw, MCI_CR, AT91C_MCI_MCIEN); // Disable the MCI peripheral clock. WRITE_PMC(AT91C_BASE_PMC, PMC_PCDR, (1 << mciId)); }
//------------------------------------------------------------------------------ /// Starts a SPI master transfer. This is a non blocking function. It will /// return as soon as the transfer is started. /// Returns 0 if the transfer has been started successfully; otherwise returns /// SPID_ERROR_LOCK is the driver is in use, or SPID_ERROR if the command is not /// valid. /// \param pSpid Pointer to a Spid instance. /// \param pCommand Pointer to the SPI command to execute. //------------------------------------------------------------------------------ unsigned char SPID_SendCommand(Spid *pSpid, SpidCmd *pCommand) { AT91S_SPI *pSpiHw = pSpid->pSpiHw; unsigned int spiMr; // Try to get the dataflash semaphore if (pSpid->semaphore == 0) return SPID_ERROR_LOCK; pSpid->semaphore--; // Enable the SPI clock WRITE_PMC(AT91C_BASE_PMC, PMC_PCER, (1 << pSpid->spiId)); // Disable transmitter and receiver WRITE_SPI(pSpiHw, SPI_PTCR, AT91C_PDC_RXTDIS | AT91C_PDC_TXTDIS); // Write to the MR register spiMr = READ_SPI(pSpiHw, SPI_MR); spiMr |= AT91C_SPI_PCS; spiMr &= ~((1 << pCommand->spiCs) << 16); WRITE_SPI(pSpiHw, SPI_MR, spiMr); // Initialize the two SPI PDC buffer WRITE_SPI(pSpiHw, SPI_RPR, (int) pCommand->pCmd); WRITE_SPI(pSpiHw, SPI_RCR, pCommand->cmdSize); WRITE_SPI(pSpiHw, SPI_TPR, (int) pCommand->pCmd); WRITE_SPI(pSpiHw, SPI_TCR, pCommand->cmdSize); WRITE_SPI(pSpiHw, SPI_RNPR, (int) pCommand->pData); WRITE_SPI(pSpiHw, SPI_RNCR, pCommand->dataSize); WRITE_SPI(pSpiHw, SPI_TNPR, (int) pCommand->pData); WRITE_SPI(pSpiHw, SPI_TNCR, pCommand->dataSize); // Initialize the callback pSpid->pCurrentCommand = pCommand; // Enable transmitter and receiver WRITE_SPI(pSpiHw, SPI_PTCR, AT91C_PDC_RXTEN | AT91C_PDC_TXTEN); // Enable buffer complete interrupt WRITE_SPI(pSpiHw, SPI_IER, AT91C_SPI_RXBUFF); return 0; }
//------------------------------------------------------------------------------ /// Close a MCI driver instance and the underlying peripheral. /// \param pMci Pointer to a MCI driver instance. /// \param pMciHw Pointer to a MCI peripheral. /// \param mciId MCI peripheral identifier. //------------------------------------------------------------------------------ void MCI_Close(Mci *pMci) { AT91S_MCI *pMciHw = pMci->pMciHw; SANITY_CHECK(pMci); SANITY_CHECK(pMciHw); // Initialize the MCI driver structure pMci->semaphore = 1; pMci->pCommand = 0; // Disable the MCI peripheral clock. WRITE_PMC(AT91C_BASE_PMC, PMC_PCDR, (1 << pMci->mciId)); // Disable the MCI WRITE_MCI(pMciHw, MCI_CR, AT91C_MCI_MCIDIS); // Disable all the interrupts WRITE_MCI(pMciHw, MCI_IDR, 0xFFFFFFFF); }
//------------------------------------------------------------------------------ /// Starts a MCI transfer. This is a non blocking function. It will return /// as soon as the transfer is started. /// Return 0 if successful; otherwise returns MCI_ERROR_LOCK if the driver is /// already in use. /// \param pMci Pointer to an MCI driver instance. /// \param pCommand Pointer to the command to execute. //------------------------------------------------------------------------------ unsigned char MCI_SendCommand(Mci *pMci, MciCmd *pCommand) { 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_LOG(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_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 | (pCommand->blockSize << 16)); } // DATA transfer from card to host if (pCommand->isRead) { WRITE_MCI(pMciHw, MCI_RPR, (int) pCommand->pData); // If Multiblock command set the BLKR register /* if (pCommand->nbBlock > 1) { WRITE_MCI(pMciHw, MCI_BLKR, pCommand->nbBlock | (pCommand->blockSize << 16)); } else { WRITE_MCI(pMciHw, MCI_BLKR, (pCommand->blockSize << 16)); }*/ // 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 // if (pCommand->blockSize == 0) { { 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; }