text: //*****************************************************************************
  //
  // spi_flash.c - Driver for a SPI flash that supports the "Intel" SPI flash
  //               command set, capable of utilizing Bi-SPI and Quad-SPI.
  //
  // Copyright (c) 2012-2013 Texas Instruments Incorporated.  All rights reserved.
  // Software License Agreement
  // 
  // Texas Instruments (TI) is supplying this software for use solely and
  // exclusively on TI's microcontroller products. The software is owned by
  // TI and/or its suppliers, and is protected under applicable copyright
  // laws. You may not combine this software with "viral" open-source
  // software in order to form a larger program.
  // 
  // THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS.
  // NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT
  // NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  // A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY
  // CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
  // DAMAGES, FOR ANY REASON WHATSOEVER.
  // 
  // This is part of revision 2.0.1.11577 of the Tiva Utility Library.
  //
  //*****************************************************************************
  
  #include <stdint.h>
  #include <stdbool.h>
  #include <stdint.h>
  #include "inc/hw_ssi.h"
  #include "inc/hw_types.h"
  #include "inc/hw_udma.h"
  #include "driverlib/rom.h"
  #include "driverlib/rom_map.h"
  #include "driverlib/ssi.h"
  #include "driverlib/udma.h"
  #include "utils/spi_flash.h"
  
  //*****************************************************************************
  //
  //! \addtogroup spi_flash_api
  //! @{
  //
  //*****************************************************************************
  
  //*****************************************************************************
  //
  // The commands that can be sent to the SPI flash.  This is the "generic"
  // command set that is supported by a wide number of SPI flashes.
  //
  //*****************************************************************************
  #define CMD_WRSR                0x01        // Write status register
  #define CMD_PP                  0x02        // Page program
  #define CMD_READ                0x03        // Read data
  #define CMD_WRDI                0x04        // Disable writes
  #define CMD_RDSR                0x05        // Read status register
  #define CMD_WREN                0x06        // Enable writes
  #define CMD_FREAD               0x0b        // Fast read data
  #define CMD_SE                  0x20        // Sector erase (4K)
  #define CMD_DREAD               0x3b        // 1 in 2 out read data
  #define CMD_BE32                0x52        // Block erase (32K)
  #define CMD_QREAD               0x6b        // 1 in 4 out read data
  #define CMD_RDID                0x9f        // Read JEDEC ID
  #define CMD_CE                  0xc7        // Chip erase
  #define CMD_BE64                0xd8        // Block erase (64K)
  
  //*****************************************************************************
  //
  // The states for the SPI flash interrupt handler state machine.
  //
  //*****************************************************************************
  #define STATE_IDLE              0
  #define STATE_CMD               1
  #define STATE_ADDR1             2
  #define STATE_ADDR2             3
  #define STATE_ADDR3             4
  #define STATE_READ_DUMMY        5
  #define STATE_READ_DATA_SETUP   6
  #define STATE_READ_DATA         7
  #define STATE_READ_DATA_DMA     8
  #define STATE_READ_DATA_END     9
  #define STATE_WRITE_DATA_SETUP  10
  #define STATE_WRITE_DATA        11
  #define STATE_WRITE_DATA_DMA    12
  #define STATE_WRITE_DATA_END    13
  
  //*****************************************************************************
  //
  //! Handles SSI module interrupts for the SPI flash driver.
  //!
  //! \param pState is a pointer to the SPI flash driver instance data.
  //!
  //! This function handles SSI module interrupts that are generated as a result
  //! of SPI flash driver operations.  This must be called by the application in
  //! response to the SSI module interrupt when using the SPIFlashxxxNonBlocking
  //! APIs.
  //!
  //! \return Returns \b SPI_FLASH_IDLE if there is no transfer in progress,
  //! \b SPI_FLASH_WORKING is the requested transfer is still in progress, or
  //! \b SPI_FLASH_DONE if the requested transfer has completed.
  //
  //*****************************************************************************
  uint32_t
  SPIFlashIntHandler(tSPIFlashState *pState)
  {
      uint32_t ui32Data, ui32Count;
  
      //
      // Set the write count to four.  This is the maximum number of bytes that
      // will be written into the SSI transmit FIFO in the interrupt handler.
      // Writing more might be possible but makes the latency of handling future
      // SSI interrupt critical to preventing receive FIFO overruns.
      //
      ui32Count = 4;
  
      //
      // Get the set of asserted and unmasked SSI module interrupts.  Only some
      // of these are directly handled; the others are implicitly handled via the
      // operation of the state machine.
      //
      ui32Data = HWREG(pState->ui32Base + SSI_O_MIS);
  
      //
      // See if the uDMA transmit complete interrupt has asserted.
      //
      if(ui32Data & SSI_MIS_DMATXMIS)
      {
          //
          // Determine the size of the uDMA transfer based on the number of bytes
          // left to write.
          //
          if(pState->ui32WriteCount > 1024)
          {
              //
              // There are more than 1024 bytes left to transfer, so the uDMA
              // transfer that just completed was for a full 1024 bytes.
              //
              pState->ui32WriteCount -= 1024;
  
              //
              // If a page program is being performed, then the data buffer
              // pointer needs to be incremented as well.
              //
              if(pState->ui16Cmd == CMD_PP)
              {
                  //
                  // Increment the data buffer pointer.
                  //
                  pState->pui8Buffer += 1024;
  
                  //
                  // See if there is more than one byte left to transfer.
                  //
                  if(pState->ui32WriteCount > 1)
                  {
                      //
                      // Configure the uDMA to transmit the next portion of the
                      // data buffer.
                      //
                      uDMAChannelTransferSet(pState->ui32TxChannel,
                                             UDMA_MODE_BASIC,
                                             pState->pui8Buffer,
                                             (void *)(pState->ui32Base +
                                                      SSI_O_DR),
                                             (pState->ui32WriteCount > 1024) ?
                                             1024 : pState->ui32WriteCount - 1);
  
                      //
                      // Enable the uDMA transmit channel.
                      //
                      uDMAChannelEnable(pState->ui32TxChannel);
                  }
              }
          }
          else
          {
              //
              // There are 1024 or less bytes left to transfer, so the uDMA
              // transfer that just copmleted was for one less than the remaining
              // transfer count.  If a page program is being performed, then the
              // data buffer pointer needs to be incremented.
              //
              if(pState->ui16Cmd == CMD_PP)
              {
                  pState->pui8Buffer += (pState->ui32WriteCount - 1);
              }
  
              //
              // Set the remaining transfer count to 1.  The final byte will be
              // transferred with PIO since the end of frame flag needs to be set
              // first.
              //
              pState->ui32WriteCount = 1;
          }
  
          //
          // Clear the uDMA transmit complete interrupt.
          //
          HWREG(pState->ui32Base + SSI_O_ICR) = SSI_ICR_DMATXIC;
      }
  
      //
      // See if the uDMA receive complete interrupt has asserted.
      //
      if(ui32Data & SSI_MIS_DMARXMIS)
      {
          //
          // Determine the size of the uDMA transfer based on the number of bytes
          // left to read.
          //
          if(pState->ui32ReadCount >= 1024)
          {
              //
              // There are 1024 or more bytes left to transfer, so the uDMA
              // transfer that just completed was for a full 1024 bytes.
              //
              pState->ui32ReadCount -= 1024;
              if(pState->ui32WriteCount != 0)
              {
                  pState->ui32WriteCount -= 1024;
              }
  
              //
              // The data buffer pointer needs to be incremented as well.
              //
              pState->pui8Buffer += 1024;
  
              //
              // See if there is additional data to transfer.
              //
              if(pState->ui32ReadCount != 0)
              {
                  //
                  // Configure the transmit uDMA if there is more than one byte
                  // left to write.
                  //
                  if(pState->ui32WriteCount > 1)
                  {
                      //
                      // Configure the uDMA to transmit the next portion of the
                      // data buffer.
                      //
                      uDMAChannelTransferSet(pState->ui32TxChannel,
                                             UDMA_MODE_BASIC, pState->pui8Buffer,
                                             (void *)(pState->ui32Base +
                                                      SSI_O_DR),
                                             (pState->ui32WriteCount > 1024) ?
                                             1024 : pState->ui32WriteCount - 1);
  
                      //
                      // Enable the uDMA transmit channel.
                      //
                      uDMAChannelEnable(pState->ui32TxChannel);
                  }
  
                  //
                  // Configure the uDMA to receive the next portion of the data
                  // buffer.
                  //
                  uDMAChannelTransferSet(pState->ui32RxChannel, UDMA_MODE_BASIC,
                                         (void *)(pState->ui32Base + SSI_O_DR),
                                         pState->pui8Buffer,
                                         (pState->ui32ReadCount >= 1024) ?
                                         1024 : pState->ui32ReadCount);
  
                  //
                  // Enable the uDMA receive channel.
                  //
                  uDMAChannelEnable(pState->ui32RxChannel);
  
                  //
                  // If this is the final receive uDMA buffer and there is a
                  // transmit uDMA buffer associated, enable the DMA transmit
                  // interrupt.
                  //
                  if((pState->ui32ReadCount <= 1024) &&
                     (pState->ui32WriteCount > 1))
                  {
                      HWREG(pState->ui32Base + SSI_O_ICR) = SSI_ICR_DMATXIC;
                      HWREG(pState->ui32Base + SSI_O_IM) = SSI_IM_DMATXIM;
                  }
              }
          }
          else
          {
              //
              // There are less than 1024 bytes left to transfer, so the uDMA
              // transfer that copmleted was for the remaining transfer count.
              //
              pState->ui32ReadCount = 0;
          }
  
          //
          // Clear the uDMA receive complete interrupt.
          //
          HWREG(pState->ui32Base + SSI_O_ICR) = SSI_ICR_DMARXIC;
      }
  
      //
      // Drain the receive FIFO is not using uDMA.
      //
      if(!pState->bUseDMA)
      {
          //
          // Loop while there is more data in the receive FIFO and more data to
          // be read.
          //
          while((pState->ui32ReadCount != 0) &&
                (MAP_SSIDataGetNonBlocking(pState->ui32Base, &ui32Data) != 0))
          {
              //
              // Save this byte into the data buffer.
              //
              *(pState->pui8Buffer)++ = ui32Data & 0xff;
  
              //
              // Decrement the read count.
              //
              pState->ui32ReadCount--;
          }
      }
  
      //
      // The SPI flash state machine.  Loop forever; the state machine will
      // explicitly return to the caller when there is no further work that can
      // be done without stalling.
      //
      while(1)
      {
          //
          // Determine the current state.
          //
          switch(pState->ui16State)
          {
              //
              // The state machine is idle.
              //
              case STATE_IDLE:
              {
                  //
                  // Return indicating that the state machine is idle.  This
                  // should never happen since no further interrupts should occur
                  // once the transfer has completed and the state machine goes
                  // into the idle state.
                  //
                  return(SPI_FLASH_IDLE);
              }
  
              //
              // The state machine is in the command state.
              //
              case STATE_CMD:
              {
                  //
                  // Set the SSI module into write-only mode.
                  //
                  MAP_SSIAdvModeSet(pState->ui32Base, SSI_ADV_MODE_WRITE);
  
                  //
                  // Attempt to write the command byte into the FIFO.
                  //
                  if(ui32Count == 0)
                  {
                      return(SPI_FLASH_WORKING);
                  }
                  if(MAP_SSIDataPutNonBlocking(pState->ui32Base,
                                               pState->ui16Cmd) == 0)
                  {
                      //
                      // The command byte could not be written, so return
                      // indicating that the transfer is still in progress.
                      //
                      return(SPI_FLASH_WORKING);
                  }
                  else
                  {
                      //
                      // The command byte has been written, so move to the first
                      // address byte state.
                      //
                      pState->ui16State = STATE_ADDR1;
  
                      //
                      // Decrement the count of bytes that have been written.
                      //
                      ui32Count--;
                  }
  
                  //
                  // Done with this state.
                  //
                  break;
              }
  
              //
              // The state machine is in the first address byte state.
              //
              case STATE_ADDR1:
              {
                  //
                  // Attempt to write the first address byte into the FIFO.
                  //
                  if(ui32Count == 0)
                  {
                      return(SPI_FLASH_WORKING);
                  }
                  if(MAP_SSIDataPutNonBlocking(pState->ui32Base,
                                               (pState->ui32Addr >> 16) &
                                               0xff) == 0)
                  {
                      //
                      // The first address byte could not be written, so return
                      // indicating that the transfer is still in progress.
                      //
                      return(SPI_FLASH_WORKING);
                  }
                  else
                  {
                      //
                      // The first address byte has been written, so move to the
                      // second address byte state.
                      //
                      pState->ui16State = STATE_ADDR2;
  
                      //
                      // Decrement the count of bytes that have been written.
                      //
                      ui32Count--;
                  }
  
                  //
                  // Done with this state.
                  //
                  break;
              }
  
              //
              // The state machine is in the second address byte state.
              //
              case STATE_ADDR2:
              {
                  //
                  // Attempt to write the second address byte into the FIFO.
                  //
                  if(ui32Count == 0)
                  {
                      return(SPI_FLASH_WORKING);
                  }
                  if(MAP_SSIDataPutNonBlocking(pState->ui32Base,
                                               (pState->ui32Addr >> 8) & 0xff) ==
                     0)
                  {
                      //
                      // The second address byte could not be written, so return
                      // indicating that the transfer is still in progress.
                      //
                      return(SPI_FLASH_WORKING);
                  }
                  else
                  {
                      //
                      // The second address byte has been written, so move to the
                      // third address byte state.
                      //
                      pState->ui16State = STATE_ADDR3;
  
                      //
                      // Decrement the count of bytes that have been written.
                      //
                      ui32Count--;
                  }
  
                  //
                  // Done with this state.
                  //
                  break;
              }
  
              //
              // The state machine is in the third address byte state.
              //
              case STATE_ADDR3:
              {
                  //
                  // Attempt to write the third address byte into the FIFO.
                  //
                  if(ui32Count == 0)
                  {
                      return(SPI_FLASH_WORKING);
                  }
                  if(MAP_SSIDataPutNonBlocking(pState->ui32Base,
                                               pState->ui32Addr & 0xff) == 0)
                  {
                      //
                      // The third address byte could not be written, so return
                      // indicating that the transfer is still in progress.
                      //
                      return(SPI_FLASH_WORKING);
                  }
                  else
                  {
                      //
                      // The third address byte has been written, so determine
                      // the next state based on the command byte.
                      //
                      if(pState->ui16Cmd == CMD_PP)
                      {
                          //
                          // A page program is being performed, so move to the
                          // write data setup state.
                          //
                          pState->ui16State = STATE_WRITE_DATA_SETUP;
                      }
                      else if(pState->ui16Cmd == CMD_READ)
                      {
                          //
                          // A read is being performed, so move to the read data
                          // setup state.
                          //
                          pState->ui16State = STATE_READ_DATA_SETUP;
                      }
                      else
                      {
                          //
                          // The other forms of read (fast read, dual read, and
                          // quad read) all require a dummy byte.  Move to the
                          // dummy byte state.
                          //
                          pState->ui16State = STATE_READ_DUMMY;
                      }
  
                      //
                      // Decrement the count of bytes that have been written.
                      //
                      ui32Count--;
                  }
  
                  //
                  // Done with this state.
                  //
                  break;
              }
  
              //
              // The state machine is in the dummy byte state.
              //
              case STATE_READ_DUMMY:
              {
                  //
                  // Attempt to write the dummy byte into the FIFO.
                  //
                  if(ui32Count == 0)
                  {
                      return(SPI_FLASH_WORKING);
                  }
                  if(MAP_SSIDataPutNonBlocking(pState->ui32Base, 0) == 0)
                  {
                      //
                      // THe dummy byte could not be written, so return
                      // indicating that the transfer is still in progress.
                      //
                      return(SPI_FLASH_WORKING);
                  }
                  else
                  {
                      //
                      // The dummy byte has been written, so move to the read
                      // data setup state.
                      //
                      pState->ui16State = STATE_READ_DATA_SETUP;
  
                      //
                      // Decrement the count of bytes that have been written.
                      //
                      ui32Count--;
                  }
  
                  //
                  // Done with this state.
                  //
                  break;
              }
  
              //
              // The state machine is in the read data setup state.
              //
              case STATE_READ_DATA_SETUP:
              {
                  //
                  // Set the SSI module into the appropriate mode based on the
                  // command byte.
                  //
                  if(pState->ui16Cmd == CMD_DREAD)
                  {
                      //
                      // Bi-SPI read mode is used for the dual read command.
                      //
                      MAP_SSIAdvModeSet(pState->ui32Base, SSI_ADV_MODE_BI_READ);
                  }
                  else if(pState->ui16Cmd == CMD_QREAD)
                  {
                      //
                      // Quad-SPI read mode is used for the quad read command.
                      //
                      MAP_SSIAdvModeSet(pState->ui32Base,
                                        SSI_ADV_MODE_QUAD_READ);
                  }
                  else
                  {
                      //
                      // Advanced read/write mode is used for the read and fast
                      // read commands.
                      //
                      MAP_SSIAdvModeSet(pState->ui32Base,
                                        SSI_ADV_MODE_READ_WRITE);
                  }
  
                  //
                  // See if a single byte is being transferred.
                  //
                  if(pState->ui32ReadCount == 1)
                  {
                      //
                      // Disable the use of uDMA.
                      //
                      pState->bUseDMA = false;
  
                      //
                      // Move to the read data end state to transfer the single
                      // byte.  This uses PIO even if uDMA has been requested.
                      //
                      pState->ui16State = STATE_READ_DATA_END;
                  }
  
                  //
                  // See if uDMA has been requested for this transfer.
                  //
                  else if(!pState->bUseDMA || (pState->ui32ReadCount < 4))
                  {
                      //
                      // Disable the use of uDMA.
                      //
                      pState->bUseDMA = false;
  
                      //
                      // Move to the read data state.
                      //
                      pState->ui16State = STATE_READ_DATA;
                  }
  
                  //
                  // This transfer should use uDMA.
                  //
                  else
                  {
                      //
                      // If the transfer is larger than 1024 bytes, enable the
                      // uDMA receive complete interrupt which will be used to
                      // move to the next block of the transfer.  Otherwise,
                      // enable the uDMA transmit complete interrupt which will
                      // be used to complete the transaction.
                      //
                      if(pState->ui32ReadCount > 1024)
                      {
                          HWREG(pState->ui32Base + SSI_O_IM) = SSI_IM_DMARXIM;
                      }
                      else
                      {
                          HWREG(pState->ui32Base + SSI_O_IM) = SSI_IM_DMATXIM;
                      }
  
                      //
                      // Disable the uDMA channels.
                      //
                      HWREG(UDMA_ENACLR) = ((1 << pState->ui32TxChannel) |
                                            (1 << pState->ui32RxChannel));
  
                      //
                      // Configure the attributes for the transmit uDMA channel.
                      //
                      HWREG(UDMA_USEBURSTSET) = ((1 << pState->ui32TxChannel) |
                                                 (1 << pState->ui32RxChannel));
                      HWREG(UDMA_ALTCLR) = ((1 << pState->ui32TxChannel) |
                                            (1 << pState->ui32RxChannel));
                      HWREG(UDMA_PRIOCLR) = 1 << pState->ui32TxChannel;
                      HWREG(UDMA_PRIOSET) = 1 << pState->ui32RxChannel;
                      HWREG(UDMA_REQMASKCLR) = ((1 << pState->ui32TxChannel) |
                                                (1 << pState->ui32RxChannel));
  
                      //
                      // Configure the control parameters of the uDMA channels.
                      //
                      uDMAChannelControlSet(pState->ui32TxChannel,
                                            UDMA_SRC_INC_NONE |
                                            UDMA_DST_INC_NONE |
                                            UDMA_SIZE_8 | UDMA_ARB_2);
                      uDMAChannelControlSet(pState->ui32RxChannel,
                                            UDMA_SRC_INC_NONE |
                                            UDMA_DST_INC_8 |
                                            UDMA_SIZE_8 | UDMA_ARB_4);
  
                      //
                      // Configure the uDMA receive channel to transfer the first
                      // portion of the data buffer.
                      //
                      uDMAChannelTransferSet(pState->ui32RxChannel,
                                             UDMA_MODE_BASIC,
                                             (void *)(pState->ui32Base +
                                                      SSI_O_DR),
                                             pState->pui8Buffer,
                                             (pState->ui32ReadCount >= 1024) ?
                                             1024 : pState->ui32ReadCount);
  
                      //
                      // Enable the uDMA receive channel.
                      //
                      uDMAChannelEnable(pState->ui32RxChannel);
  
                      //
                      // Configure the uDMA channel to transfer the dummy bytes
                      // for the first portion of the data buffer.  The last
                      // dummy byte will not be included since it must be treated
                      // special.
                      //
                      uDMAChannelTransferSet(pState->ui32TxChannel,
                                             UDMA_MODE_BASIC,
                                             pState->pui8Buffer,
                                             (void *)(pState->ui32Base +
                                                      SSI_O_DR),
                                             (pState->ui32WriteCount > 1024) ?
                                             1024 : pState->ui32WriteCount - 1);
  
                      //
                      // Enable the uDMA transmit channel.
                      //
                      uDMAChannelEnable(pState->ui32TxChannel);
  
                      //
                      // Clear any previously pending uDMA completion interrupt.
                      //
                      HWREG(pState->ui32Base + SSI_O_ICR) = SSI_ICR_DMARXIC;
  
                      //
                      // Enable uDMA transmit and receive in the SSI module.
                      //
                      MAP_SSIDMAEnable(pState->ui32Base,
                                       SSI_DMA_TX | SSI_DMA_RX);
  
                      //
                      // Move to the uDMA data read state.
                      //
                      pState->ui16State = STATE_READ_DATA_DMA;
                  }
  
                  //
                  // Done with this state.
                  //
                  break;
              }
  
              //
              // The state machine is in the read data state.
              //
              case STATE_READ_DATA:
              {
                  //
                  // Loop while there is more than one byte left to write.
                  //
                  while(pState->ui32WriteCount != 1)
                  {
                      //
                      // Dummy bytes are written into the FIFO in order to
                      // trigger the read operation.  Attempt to write another
                      // dummy byte into the FIFO.
                      //
                      if(ui32Count == 0)
                      {
                          return(SPI_FLASH_WORKING);
                      }
                      if(MAP_SSIDataPutNonBlocking(pState->ui32Base, 0) == 0)
                      {
                          //
                          // The dummy byte could not be written, so return
                          // indicating that the transfer is still in progress.
                          //
                          return(SPI_FLASH_WORKING);
                      }
  
                      //
                      // Decrement the count of dummy bytes to write.
                      //
                      pState->ui32WriteCount--;
  
                      //
                      // Decrement the count of bytes that have been written.
                      //
                      ui32Count--;
                  }
  
                  //
                  // Move to the read data end state.
                  //
                  pState->ui16State = STATE_READ_DATA_END;
  
                  //
                  // Done with this state.
                  //
                  break;
              }
  
              //
              // The state machine is in the uDMA read data state.
              //
              case STATE_READ_DATA_DMA:
              {
                  //
                  // See if the write count is greater than one.
                  //
                  if(pState->ui32WriteCount > 1)
                  {
                      //
                      // Return indicating that the transfer is still in
                      // progress.
                      //
                      return(SPI_FLASH_WORKING);
                  }
  
                  //
                  // Disable uDMA transmit in the SSI module.
                  //
                  MAP_SSIDMADisable(pState->ui32Base, SSI_DMA_TX);
  
                  //
                  // Enable the uDMA receive done and FIFO transmit interrupt.
                  //
                  HWREG(pState->ui32Base + SSI_O_IM) =
                      SSI_IM_DMARXIM | SSI_IM_TXIM;
  
                  //
                  // Move to the read data end state.
                  //
                  pState->ui16State = STATE_READ_DATA_END;
  
                  //
                  // Done with this state.
                  //
                  break;
              }
  
              //
              // The state machine is in the data read end state.
              //
              case STATE_READ_DATA_END:
              {
                  //
                  // See if the final dummy byte still needs to be written.
                  //
                  if(pState->ui32WriteCount != 0)
                  {
                      //
                      // Attempt to write the final dummy byte into the FIFO and
                      // mark it as the end of the frame.
                      //
                      if(ui32Count == 0)
                      {
                          return(SPI_FLASH_WORKING);
                      }
                      if(MAP_SSIAdvDataPutFrameEndNonBlocking(pState->ui32Base,
                                                              0) == 0)
                      {
                          //
                          // The dummy byte could not be written, so return
                          // indicating that the transfer is still in progress.
                          //
                          return(SPI_FLASH_WORKING);
                      }
  
                      //
                      // The write portion of the transfer has completed.
                      //
                      pState->ui32WriteCount = 0;
  
                      //
                      // Disable the transmit interrupt now that the write
                      // write portion of the transfer has completed.
                      //
                      HWREG(pState->ui32Base + SSI_O_IM) &= ~(SSI_IM_TXIM);
                  }
  
                  //
                  // Return indicating that the transfer is still in progress if
                  // there are still data bytes to be read.
                  //
                  if(pState->ui32ReadCount != 0)
                  {
                      return(SPI_FLASH_WORKING);
                  }
  
                  //
                  // Disable uDMA receive in the SSI module.
                  //
                  MAP_SSIDMADisable(pState->ui32Base, SSI_DMA_RX);
  
                  //
                  // The transfer is complete, so disable all interrupts.
                  //
                  HWREG(pState->ui32Base + SSI_O_IM) = 0;
  
                  //
                  // Move to the idle state.
                  //
                  pState->ui16State = STATE_IDLE;
  
                  //
                  // Return indicating that the transfer has completed.
                  //
                  return(SPI_FLASH_DONE);
              }
  
              //
              // The state machine is in the write data setup state.
              //
              case STATE_WRITE_DATA_SETUP:
              {
                  //
                  // See if a single data byte is being transferred.
                  //
                  if(pState->ui32WriteCount == 1)
                  {
                      //
                      // Disable the use of uDMA.
                      //
                      pState->bUseDMA = false;
  
                      //
                      // Move to the write data end state to transfer the single
                      // byte.  This uses PIO even if uDMA has been requested.
                      //
                      pState->ui16State = STATE_WRITE_DATA_END;
                  }
  
                  //
                  // See if uDMA has been requested for this transfer.
                  //
                  else if(!pState->bUseDMA || (pState->ui32WriteCount < 4))
                  {
                      //
                      // Disable the use of uDMA.
                      //
                      pState->bUseDMA = false;
  
                      //
                      // uDMA is not being used, so move to the write data state.
                      //
                      pState->ui16State = STATE_WRITE_DATA;
                  }
  
                  //
                  // This transfer should use uDMA.
                  //
                  else
                  {
                      //
                      // Enable the uDMA transmit complete interrupt.
                      //
                      HWREG(pState->ui32Base + SSI_O_IM) = SSI_IM_DMATXIM;
  
                      //
                      // Disable the transmit uDMA channel.
                      //
                      HWREG(UDMA_ENACLR) = 1 << pState->ui32TxChannel;
  
                      //
                      // Configure the attributes for the transmit uDMA channel.
                      //
                      HWREG(UDMA_USEBURSTSET) = 1 << pState->ui32TxChannel;
                      HWREG(UDMA_ALTCLR) = 1 << pState->ui32TxChannel;
                      HWREG(UDMA_PRIOCLR) = 1 << pState->ui32TxChannel;
                      HWREG(UDMA_REQMASKCLR) = 1 << pState->ui32TxChannel;
  
                      //
                      // Configure the control parameters of the uDMA channel.
                      //
                      uDMAChannelControlSet(pState->ui32TxChannel,
                                            UDMA_SRC_INC_8 |
                                            UDMA_DST_INC_NONE |
                                            UDMA_SIZE_8 | UDMA_ARB_4);
  
                      //
                      // Configure the uDMA channel to transfer the next portion
                      // of the data buffer.  The last byte in the buffer will
                      // not be included since it must be treated special.
                      //
                      uDMAChannelTransferSet(pState->ui32TxChannel,
                                             UDMA_MODE_BASIC,
                                             pState->pui8Buffer,
                                             (void *)(pState->ui32Base +
                                                      SSI_O_DR),
                                             (pState->ui32WriteCount > 1024) ?
                                             1024 : pState->ui32WriteCount - 1);
  
                      //
                      // Enable the uDMA transmit channel.
                      //
                      uDMAChannelEnable(pState->ui32TxChannel);
                      HWREG(pState->ui32Base + SSI_O_ICR) = SSI_ICR_DMATXIC;
  
                      //
                      // Enable uDMA in the SSI module.
                      //
                      MAP_SSIDMAEnable(pState->ui32Base, SSI_DMA_TX);
  
                      //
                      // Move to the uDMA data write state.
                      //
                      pState->ui16State = STATE_WRITE_DATA_DMA;
                  }
  
                  //
                  // Done with this state.
                  //
                  break;
              }
  
              //
              // The state machine is in the write data state.
              //
              case STATE_WRITE_DATA:
              {
                  //
                  // Loop while there is more than one byte left to write.
                  //
                  while(pState->ui32WriteCount != 1)
                  {
                      //
                      // Attempt to write the next data byte into the FIFO.
                      //
                      if(ui32Count == 0)
                      {
                          return(SPI_FLASH_WORKING);
                      }
                      if(MAP_SSIDataPutNonBlocking(pState->ui32Base,
                                                   *(pState->pui8Buffer)) == 0)
                      {
                          //
                          // The next data byte could not be written, so return
                          // indicating that the transfer is still in progress.
                          //
                          return(SPI_FLASH_WORKING);
                      }
  
                      //
                      // Increment the buffer pointer and decrement the byte
                      // count.
                      //
                      pState->pui8Buffer++;
                      pState->ui32WriteCount--;
  
                      //
                      // Decrement the count of bytes that have been written.
                      //
                      ui32Count--;
                  }
  
                  //
                  // Move to the write data end state.
                  //
                  pState->ui16State = STATE_WRITE_DATA_END;
  
                  //
                  // Done with this state.
                  //
                  break;
              }
  
              //
              // The state machine is in the uDMA write data state.
              //
              case STATE_WRITE_DATA_DMA:
              {
                  //
                  // See if the write count is greater than one.
                  //
                  if(pState->ui32WriteCount > 1)
                  {
                      //
                      // Return indicating that the transfer is still in
                      // progress.
                      //
                      return(SPI_FLASH_WORKING);
                  }
  
                  //
                  // Disable uDMA in the SSI module.
                  //
                  MAP_SSIDMADisable(pState->ui32Base, SSI_DMA_TX);
  
                  //
                  // Disable the uDMA transmit complete interrupt and enable the
                  // FIFO interrupt.
                  //
                  HWREG(pState->ui32Base + SSI_O_IM) = SSI_IM_TXIM;
  
                  //
                  // Move to the write data end state.
                  //
                  pState->ui16State = STATE_WRITE_DATA_END;
  
                  //
                  // Done with this state.
                  //
                  break;
              }
  
              //
              // The state machine is in the write data end state.
              //
              case STATE_WRITE_DATA_END:
              {
                  //
                  // Attempt to write the final data byte into the FIFO.
                  //
                  if(MAP_SSIAdvDataPutFrameEndNonBlocking(pState->ui32Base,
                                                        *(pState->pui8Buffer)) ==
                     0)
                  {
                      //
                      // The final data byte could not be written, so return
                      // indicating that the transfer is still in progress.
                      //
                      return(SPI_FLASH_WORKING);
                  }
  
                  //
                  // The transfer is complete, so disable all interrupts.
                  //
                  HWREG(pState->ui32Base + SSI_O_IM) = 0;
  
                  //
                  // Move to the idle state.
                  //
                  pState->ui16State = STATE_IDLE;
  
                  //
                  // Return indicating that the transfer has completed.
                  //
                  return(SPI_FLASH_DONE);
              }
          }
      }
  }
  
  //*****************************************************************************
  //
  //! Initializes the SPI flash driver.
  //!
  //! \param ui32Base is the SSI module base address.
  //! \param ui32Clock is the rate of the clock supplied to the SSI module.
  //! \param ui32BitRate is the SPI clock rate.
  //!
  //! This function configures the SSI module for use by the SPI flash driver.
  //! The SSI module will be placed into the correct mode of operation to allow
  //! communication with the SPI flash.  This function must be called prior to
  //! calling the remaining SPI flash driver APIs.  It can be called at a later
  //! point to reconfigure the SSI module, such as to increase the SPI clock rate
  //! once it has been determined that it is safe to use a higher speed clock.
  //!
  //! It is the responsibility of the caller to enable the SSI module and
  //! configure the pins that it will utilize.
  //!
  //! \return None.
  //
  //*****************************************************************************
  void
  SPIFlashInit(uint32_t ui32Base, uint32_t ui32Clock, uint32_t ui32BitRate)
  {
      //
      // Configure the SPI module.
      //
      MAP_SSIConfigSetExpClk(ui32Base, ui32Clock, SSI_FRF_MOTO_MODE_0,
                             SSI_MODE_MASTER, ui32BitRate, 8);
  
      //
      // Enable the advanced mode of operation, defaulting to read/write mode.
      //
      MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_READ_WRITE);
  
      //
      // Enable the frame hold feature.
      //
      MAP_SSIAdvFrameHoldEnable(ui32Base);
  
      //
      // Enable the SPI module.
      //
      MAP_SSIEnable(ui32Base);
  }
  
  //*****************************************************************************
  //
  //! Writes the SPI flash status register.
  //!
  //! \param ui32Base is the SSI module base address.
  //! \param ui8Status is the value to write to the status register.
  //!
  //! This function writes the SPI flash status register.  This uses the 0x01 SPI
  //! flash command.
  //!
  //! \return None.
  //
  //*****************************************************************************
  void
  SPIFlashWriteStatus(uint32_t ui32Base, uint8_t ui8Status)
  {
      //
      // Set the SSI module into write-only mode.
      //
      MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_WRITE);
  
      //
      // Send the write status register command.
      //
      MAP_SSIDataPut(ui32Base, CMD_WRSR);
  
      //
      // Send the new status register value, marking this byte as the end of the
      // frame.
      //
      MAP_SSIAdvDataPutFrameEnd(ui32Base, ui8Status);
  }
  
  //*****************************************************************************
  //
  //! Programs the SPI flash.
  //!
  //! \param ui32Base is the SSI module base address.
  //! \param ui32Addr is the SPI flash address to be programmed.
  //! \param pui8Data is a pointer to the data to be programmed.
  //! \param ui32Count is the number of bytes to be programmed.
  //!
  //! This function programs data into the SPI flash, using PIO mode.  This
  //! function will not return until the entire program command has been written
  //! into the SSI transmit FIFO.  This uses the 0x02 SPI flash command.
  //!
  //! \return None.
  //
  //*****************************************************************************
  void
  SPIFlashPageProgram(uint32_t ui32Base, uint32_t ui32Addr,
                      const uint8_t *pui8Data, uint32_t ui32Count)
  {
      //
      // Set the SSI module into write-only mode.
      //
      MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_WRITE);
  
      //
      // Send the page program command.
      //
      MAP_SSIDataPut(ui32Base, CMD_PP);
  
      //
      // Send the address of the first byte to program.
      //
      MAP_SSIDataPut(ui32Base, (ui32Addr >> 16) & 0xff);
      MAP_SSIDataPut(ui32Base, (ui32Addr >> 8) & 0xff);
      MAP_SSIDataPut(ui32Base, ui32Addr & 0xff);
  
      //
      // Loop while there is more than one data byte left to be sent.
      //
      while(ui32Count-- != 1)
      {
          //
          // Send the next data byte.
          //
          MAP_SSIDataPut(ui32Base, *pui8Data++);
      }
  
      //
      // Send the last data byte, marking it as the end of the frame.
      //
      MAP_SSIAdvDataPutFrameEnd(ui32Base, *pui8Data);
  }
  
  //*****************************************************************************
  //
  //! Programs the SPI flash in the background.
  //!
  //! \param pState is a pointer to the SPI flash state structure.
  //! \param ui32Base is the SSI module base address.
  //! \param ui32Addr is the SPI flash address to be programmed.
  //! \param pui8Data is a pointer to the data to be programmed.
  //! \param ui32Count is the number of bytes to be programmed.
  //! \param bUseDMA is \b true if uDMA should be used and \b false otherwise.
  //! \param ui32TxChannel is the uDMA channel to be used for writing to the SSI
  //! module.
  //!
  //! This function programs data into the SPI flash, using either interrupts or
  //! uDMA to transfer the data.  This function will return immediately and send
  //! the data in the background.  In order for this to complete successfully,
  //! several conditions must be satisfied:
  //!
  //! - Prior to calling this function:
  //!   - The SSI module must be enabled in SysCtl.
  //!   - The SSI pins must be configured for use by the SSI module.
  //!   - The SSI module interrupt must be enabled in NVIC.
  //!   - The uDMA module must be enabled in SysCtl and the control table set (if
  //!     using uDMA).
  //!   - The uDMA channels must be assigned to the SSI module.
  //!
  //! - After calling this function:
  //!   - The interrupt handler for the SSI module must call
  //!     SPIFlashIntHandler(), passing the same pState structure pointer that
  //!     was supplied to this function.
  //!   - No other SPI flash operation can be called until this operation has
  //!     completed.
  //!
  //! Completion of the programming operation is indicated when
  //! SPIFlashIntHandler() returns \b SPI_FLASH_DONE.
  //!
  //! Like SPIFlashPageProgram(), this uses the 0x02 SPI flash command.
  //!
  //! \return None.
  //
  //*****************************************************************************
  void
  SPIFlashPageProgramNonBlocking(tSPIFlashState *pState, uint32_t ui32Base,
                                 uint32_t ui32Addr, const uint8_t *pui8Data,
                                 uint32_t ui32Count, bool bUseDMA,
                                 uint32_t ui32TxChannel)
  {
      //
      // Save the parameters of this program operation to the state structure.
      //
      pState->ui32Base = ui32Base;
      pState->ui16Cmd = CMD_PP;
      pState->ui16State = STATE_CMD;
      pState->ui32Addr = ui32Addr;
      pState->pui8Buffer = (uint8_t *)pui8Data;
      pState->ui32ReadCount = 0;
      pState->ui32WriteCount = ui32Count;
      pState->bUseDMA = bUseDMA;
      pState->ui32TxChannel = ui32TxChannel & 0x1f;
  
      //
      // Enable the SSI transmit interrupt.  This will start the transfer.  If
      // uDMA is being used, the uDMA-related interrupt will be enabled at the
      // appropriate time by the interrupt handler.
      //
      HWREG(ui32Base + SSI_O_ICR) = SSI_ICR_DMATXIC;
      HWREG(ui32Base + SSI_O_IM) = SSI_IM_TXIM;
  }
  
  //*****************************************************************************
  //
  //! Reads data from the SPI flash.
  //!
  //! \param ui32Base is the SSI module base address.
  //! \param ui32Addr is the SPI flash address to read.
  //! \param pui8Data is a pointer to the data buffer to into which to read the
  //! data.
  //! \param ui32Count is the number of bytes to read.
  //!
  //! This function reads data from the SPI flash, using PIO mode.  This function
  //! will not return until the read has completed.  This uses the 0x03 SPI flash
  //! command.
  //!
  //! \return None.
  //
  //*****************************************************************************
  void
  SPIFlashRead(uint32_t ui32Base, uint32_t ui32Addr, uint8_t *pui8Data,
               uint32_t ui32Count)
  {
      uint32_t ui32Trash;
  
      //
      // Drain any residual data from the receive FIFO.
      //
      while(MAP_SSIDataGetNonBlocking(ui32Base, &ui32Trash) != 0)
      {
      }
  
      //
      // Set the SSI module into write-only mode.
      //
      MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_WRITE);
  
      //
      // Send the read command.
      //
      MAP_SSIDataPut(ui32Base, CMD_READ);
  
      //
      // Send the address of the first byte to read.
      //
      MAP_SSIDataPut(ui32Base, (ui32Addr >> 16) & 0xff);
      MAP_SSIDataPut(ui32Base, (ui32Addr >> 8) & 0xff);
      MAP_SSIDataPut(ui32Base, ui32Addr & 0xff);
  
      //
      // Set the SSI module into read/write mode.  In this mode, dummy writes are
      // required in order to make the transfer occur; the SPI flash will ignore
      // the data.
      //
      MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_READ_WRITE);
  
      //
      // See if there is a single byte to be read.
      //
      if(ui32Count == 1)
      {
          //
          // Perform a single dummy write, marking it as the end of the frame.
          //
          MAP_SSIAdvDataPutFrameEnd(ui32Base, 0);
      }
      else
      {
          //
          // Perform a dummy write to prime the loop.
          //
          MAP_SSIDataPut(ui32Base, 0);
  
          //
          // Loop while there is more than one byte left to be read.
          //
          while(--ui32Count != 1)
          {
              //
              // Perform a dummy write to keep the transmit FIFO from going
              // empty.
              //
              MAP_SSIDataPut(ui32Base, 0);
  
              //
              // Read the next data byte from the receive FIFO and place it into
              // the data buffer.
              //
              MAP_SSIDataGet(ui32Base, &ui32Addr);
              *pui8Data++ = ui32Addr & 0xff;
          }
  
          //
          // Perform the final dummy write, marking it as the end of the frame.
          //
          MAP_SSIAdvDataPutFrameEnd(ui32Base, 0);
  
          //
          // Read the next data byte from the receive FIFO and place it into the
          // data buffer.
          //
          MAP_SSIDataGet(ui32Base, &ui32Addr);
          *pui8Data++ = ui32Addr & 0xff;
      }
  
      //
      // Read the final data byte from the receive FIFO and place it into the
      // data buffer.
      //
      MAP_SSIDataGet(ui32Base, &ui32Addr);
      *pui8Data++ = ui32Addr & 0xff;
  }
  
  //*****************************************************************************
  //
  //! Reads data from the SPI flash in the background.
  //!
  //! \param pState is a pointer to the SPI flash state structure.
  //! \param ui32Base is the SSI module base address.
  //! \param ui32Addr is the SPI flash address to read.
  //! \param pui8Data is a pointer to the data buffer to into which to read the
  //! data.
  //! \param ui32Count is the number of bytes to read.
  //! \param bUseDMA is \b true if uDMA should be used and \b false otherwise.
  //! \param ui32TxChannel is the uDMA channel to be used for writing to the SSI
  //! module.
  //! \param ui32RxChannel is the uDMA channel to be used for reading from the
  //! SSI module.
  //!
  //! This function reads data from the SPI flash, using either interrupts or
  //! uDMA to transfer the data.  This function will return immediately and read
  //! the data in the background.  In order for this to complete successfully,
  //! several conditions must be satisfied:
  //!
  //! - Prior to calling this function:
  //!   - The SSI module must be enabled in SysCtl.
  //!   - The SSI pins must be configured for use by the SSI module.
  //!   - The SSI module interrupt must be enabled in NVIC.
  //!   - The uDMA module must be enabled in SysCtl and the control table set (if
  //!     using uDMA).
  //!   - The uDMA channels must be assigned to the SSI module.
  //!
  //! - After calling this function:
  //!   - The interrupt handler for the SSI module must call
  //!     SPIFlashIntHandler(), passing the same pState structure pointer that
  //!     was supplied to this function.
  //!   - No other SPI flash operation can be called until this operation has
  //!     completed.
  //!
  //! Completion of the read operation is indicated when SPIFlashIntHandler()
  //! returns \b SPI_FLASH_DONE.
  //!
  //! Like SPIFlashRead(), this uses the 0x03 SPI flash command.
  //!
  //! \return None.
  //
  //*****************************************************************************
  void
  SPIFlashReadNonBlocking(tSPIFlashState *pState, uint32_t ui32Base,
                          uint32_t ui32Addr, uint8_t *pui8Data,
                          uint32_t ui32Count, bool bUseDMA,
                          uint32_t ui32TxChannel, uint32_t ui32RxChannel)
  {
      uint32_t ui32Trash;
  
      //
      // Drain any residual data from the receive FIFO.
      //
      while(MAP_SSIDataGetNonBlocking(ui32Base, &ui32Trash) != 0)
      {
      }
  
      //
      // Save the parameters of this read operation to the state structure.
      //
      pState->ui32Base = ui32Base;
      pState->ui16Cmd = CMD_READ;
      pState->ui16State = STATE_CMD;
      pState->ui32Addr = ui32Addr;
      pState->pui8Buffer = pui8Data;
      pState->ui32ReadCount = ui32Count;
      pState->ui32WriteCount = ui32Count;
      pState->bUseDMA = bUseDMA;
      pState->ui32TxChannel = ui32TxChannel & 0x1f;
      pState->ui32RxChannel = ui32RxChannel & 0x1f;
  
      //
      // Enable the SSI transmit and receive interrupts.  This will start the
      // transfer.  If uDMA is being used, the uDMA-related interrupts will be
      // enabled at the appropriate time by the interrupt handler.
      //
      HWREG(ui32Base + SSI_O_ICR) = SSI_ICR_DMATXIC | SSI_ICR_DMARXIC;
      HWREG(ui32Base + SSI_O_IM) = SSI_IM_TXIM | SSI_IM_RXIM | SSI_IM_RTIM;
  }
  
  //*****************************************************************************
  //
  //! Disables SPI flash write operations.
  //!
  //! \param ui32Base is the SSI module base address.
  //!
  //! This function sets the SPI flash to disallow program and erase operations.
  //! This uses the 0x04 SPI flash command.
  //!
  //! \return None.
  //
  //*****************************************************************************
  void
  SPIFlashWriteDisable(uint32_t ui32Base)
  {
      //
      // Set the SSI module into write-only mode.
      //
      MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_WRITE);
  
      //
      // Send the write disable command, marking this byte as the end of the
      // frame.
      //
      MAP_SSIAdvDataPutFrameEnd(ui32Base, CMD_WRDI);
  }
  
  //*****************************************************************************
  //
  //! Reads the SPI flash status register.
  //!
  //! \param ui32Base is the SSI module base address.
  //!
  //! This function reads the SPI flash status register.  This uses the 0x05 SPI
  //! flash command.
  //!
  //! \return Returns the value of the SPI flash status register.
  //
  //*****************************************************************************
  uint8_t
  SPIFlashReadStatus(uint32_t ui32Base)
  {
      uint32_t ui32Data;
  
      //
      // Drain any residual data from the receive FIFO.
      //
      while(MAP_SSIDataGetNonBlocking(ui32Base, &ui32Data) != 0)
      {
      }
  
      //
      // Set the SSI module into write-only mode.
      //
      MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_WRITE);
  
      //
      // Send the write status register command.
      //
      MAP_SSIDataPut(ui32Base, CMD_RDSR);
  
      //
      // Set the SSI module into read/write mode.  In this mode, dummy writes are
      // required in order to make the transfer occur; the SPI flash will ignore
      // the data.
      //
      MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_READ_WRITE);
  
      //
      // Perform a single dummy write, marking it as the end of the frame.
      //
      MAP_SSIAdvDataPutFrameEnd(ui32Base, 0);
  
      //
      // Read the value of the status register.
      //
      MAP_SSIDataGet(ui32Base, &ui32Data);
  
      //
      // Return the status register value.
      //
      return(ui32Data & 0xff);
  }
  
  //*****************************************************************************
  //
  //! Enables SPI flash write operations.
  //!
  //! \param ui32Base is the SSI module base address.
  //!
  //! This function sets the SPI flash to allow program and erase operations.
  //! This must be done prior to each SPI flash program or erase operation; the
  //! SPI flash will automatically disable program and erase operations once a
  //! program or erase operation has completed.  This uses the 0x06 SPI flash
  //! command.
  //!
  //! \return None.
  //
  //*****************************************************************************
  void
  SPIFlashWriteEnable(uint32_t ui32Base)
  {
      //
      // Set the SSI module into write-only mode.
      //
      MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_WRITE);
  
      //
      // Send the write enable command, marking this byte as the end of the
      // frame.
      //
      MAP_SSIAdvDataPutFrameEnd(ui32Base, CMD_WREN);
  }
  
  //*****************************************************************************
  //
  //! Reads data from the SPI flash using the fast read command.
  //!
  //! \param ui32Base is the SSI module base address.
  //! \param ui32Addr is the SPI flash address to read.
  //! \param pui8Data is a pointer to the data buffer to into which to read the
  //! data.
  //! \param ui32Count is the number of bytes to read.
  //!
  //! This function reads data from the SPI flash with the fast read command,
  //! using PIO mode.  The fast read command allows the SPI flash to be read at
  //! a higher SPI clock rate because of the addition of a dummy cycle during the
  //! command setup.  This function will not return until the read has completed.
  //! This uses the 0x0b SPI flash command.
  //!
  //! \return None.
  //
  //*****************************************************************************
  void
  SPIFlashFastRead(uint32_t ui32Base, uint32_t ui32Addr, uint8_t *pui8Data,
                   uint32_t ui32Count)
  {
      uint32_t ui32Trash;
  
      //
      // Drain any residual data from the receive FIFO.
      //
      while(MAP_SSIDataGetNonBlocking(ui32Base, &ui32Trash) != 0)
      {
      }
  
      //
      // Set the SSI module into write-only mode.
      //
      MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_WRITE);
  
      //
      // Send the fast read command.
      //
      MAP_SSIDataPut(ui32Base, CMD_FREAD);
  
      //
      // Send the address of the first byte to read.
      //
      MAP_SSIDataPut(ui32Base, (ui32Addr >> 16) & 0xff);
      MAP_SSIDataPut(ui32Base, (ui32Addr >> 8) & 0xff);
      MAP_SSIDataPut(ui32Base, ui32Addr & 0xff);
  
      //
      // Send a dummy byte.
      //
      MAP_SSIDataPut(ui32Base, 0);
  
      //
      // Set the SSI module into read/write mode.  In this mode, dummy writes are
      // required in order to make the transfer occur; the SPI flash will ignore
      // the data.
      //
      MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_READ_WRITE);
  
      //
      // See if there is a single byte to be read.
      //
      if(ui32Count == 1)
      {
          //
          // Perform a single dummy write, marking it as the end of the frame.
          //
          MAP_SSIAdvDataPutFrameEnd(ui32Base, 0);
      }
      else
      {
          //
          // Perform a dummy write to prime the loop.
          //
          MAP_SSIDataPut(ui32Base, 0);
  
          //
          // Loop while there is more than one byte left to be read.
          //
          while(--ui32Count != 1)
          {
              //
              // Perform a dummy write to keep the transmit FIFO from going
              // empty.
              //
              MAP_SSIDataPut(ui32Base, 0);
  
              //
              // Read the next data byte from the receive FIFO and place it into
              // the data buffer.
              //
              MAP_SSIDataGet(ui32Base, &ui32Addr);
              *pui8Data++ = ui32Addr & 0xff;
          }
  
          //
          // Perform the final dummy write, marking it as the end of the frame.
          //
          MAP_SSIAdvDataPutFrameEnd(ui32Base, 0);
  
          //
          // Read the next data byte from the receive FIFO and place it into the
          // data buffer.
          //
          MAP_SSIDataGet(ui32Base, &ui32Addr);
          *pui8Data++ = ui32Addr & 0xff;
      }
  
      //
      // Read the final data byte from the receive FIFO and place it into the
      // data buffer.
      //
      MAP_SSIDataGet(ui32Base, &ui32Addr);
      *pui8Data++ = ui32Addr & 0xff;
  }
  
  //*****************************************************************************
  //
  //! Reads data from the SPI flash using the fast read command in the
  //! background.
  //!
  //! \param pState is a pointer to the SPI flash state structure.
  //! \param ui32Base is the SSI module base address.
  //! \param ui32Addr is the SPI flash address to read.
  //! \param pui8Data is a pointer to the data buffer to into which to read the
  //! data.
  //! \param ui32Count is the number of bytes to read.
  //! \param bUseDMA is \b true if uDMA should be used and \b false otherwise.
  //! \param ui32TxChannel is the uDMA channel to be used for writing to the SSI
  //! module.
  //! \param ui32RxChannel is the uDMA channel to be used for reading from the
  //! SSI module.
  //!
  //! This function reads data from the SPI flash with the fast read command,
  //! using either interrupts or uDMA to transfer the data.  The fast read
  //! command allows the SPI flash to be read at a higher SPI clock rate because
  //! of the addition of a dummy cycle during the command setup.  This function
  //! will return immediately and read the data in the background.  In order for
  //! this to complete successfully, several conditions must be satisfied:
  //!
  //! - Prior to calling this function:
  //!   - The SSI module must be enabled in SysCtl.
  //!   - The SSI pins must be configured for use by the SSI module.
  //!   - The SSI module interrupt must be enabled in NVIC.
  //!   - The uDMA module must be enabled in SysCtl and the control table set (if
  //!     using uDMA).
  //!   - The uDMA channels must be assigned to the SSI module.
  //!
  //! - After calling this function:
  //!   - The interrupt handler for the SSI module must call
  //!     SPIFlashIntHandler(), passing the same pState structure pointer that
  //!     was supplied to this function.
  //!   - No other SPI flash operation can be called until this operation has
  //!     completed.
  //!
  //! Completion of the read operation is indicated when SPIFlashIntHandler()
  //! returns \b SPI_FLASH_DONE.
  //!
  //! Like SPIFlashFastRead(), this uses the 0x0b SPI flash command.
  //!
  //! \return None.
  //
  //*****************************************************************************
  void
  SPIFlashFastReadNonBlocking(tSPIFlashState *pState, uint32_t ui32Base,
                              uint32_t ui32Addr, uint8_t *pui8Data,
                              uint32_t ui32Count, bool bUseDMA,
                              uint32_t ui32TxChannel, uint32_t ui32RxChannel)
  {
      uint32_t ui32Trash;
  
      //
      // Drain any residual data from the receive FIFO.
      //
      while(MAP_SSIDataGetNonBlocking(ui32Base, &ui32Trash) != 0)
      {
      }
  
      //
      // Save the parameters of this read operation to the state structure.
      //
      pState->ui32Base = ui32Base;
      pState->ui16Cmd = CMD_FREAD;
      pState->ui16State = STATE_CMD;
      pState->ui32Addr = ui32Addr;
      pState->pui8Buffer = pui8Data;
      pState->ui32ReadCount = ui32Count;
      pState->ui32WriteCount = ui32Count;
      pState->bUseDMA = bUseDMA;
      pState->ui32TxChannel = ui32TxChannel & 0x1f;
      pState->ui32RxChannel = ui32RxChannel & 0x1f;
  
      //
      // Enable the SSI transmit and receive interrupts.  This will start the
      // transfer.  If uDMA is being used, the uDMA-related interrupts will be
      // enabled at the appropriate time by the interrupt handler.
      //
      HWREG(ui32Base + SSI_O_ICR) = SSI_ICR_DMATXIC | SSI_ICR_DMARXIC;
      HWREG(ui32Base + SSI_O_IM) = SSI_IM_TXIM | SSI_IM_RXIM | SSI_IM_RTIM;
  }
  
  //*****************************************************************************
  //
  //! Erases a 4 KB sector of the SPI flash.
  //!
  //! \param ui32Base is the SSI module base address.
  //! \param ui32Addr is the SPI flash address to erase.
  //!
  //! This function erases a sector of the SPI flash.  Each sector is 4 KB with a
  //! 4 KB alignment; the SPI flash will ignore the lower ten bits of the address
  //! provided.  The sector erase command is issued by this function;
  //! SPIFlashReadStatus() must be used to query the SPI flash to determine when
  //! the sector erase operation has completed.  This uses the 0x20 SPI flash
  //! command.
  //!
  //! \return None.
  //
  //*****************************************************************************
  void
  SPIFlashSectorErase(uint32_t ui32Base, uint32_t ui32Addr)
  {
      //
      // Set the SSI module into write-only mode.
      //
      MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_WRITE);
  
      //
      // Send the sector erase command.
      //
      MAP_SSIDataPut(ui32Base, CMD_SE);
  
      //
      // Send the address of the sector to be erased, marking the last byte of
      // the address as the end of the frame.
      //
      MAP_SSIDataPut(ui32Base, (ui32Addr >> 16) & 0xff);
      MAP_SSIDataPut(ui32Base, (ui32Addr >> 8) & 0xff);
      MAP_SSIAdvDataPutFrameEnd(ui32Base, ui32Addr & 0xff);
  }
  
  //*****************************************************************************
  //
  //! Reads data from the SPI flash using Bi-SPI.
  //!
  //! \param ui32Base is the SSI module base address.
  //! \param ui32Addr is the SPI flash address to read.
  //! \param pui8Data is a pointer to the data buffer to into which to read the
  //! data.
  //! \param ui32Count is the number of bytes to read.
  //!
  //! This function reads data from the SPI flash with Bi-SPI, using PIO mode.
  //! This function will not return until the read has completed.  This uses the
  //! 0x3b SPI flash command.
  //!
  //! \return None.
  //
  //*****************************************************************************
  void
  SPIFlashDualRead(uint32_t ui32Base, uint32_t ui32Addr, uint8_t *pui8Data,
                   uint32_t ui32Count)
  {
      uint32_t ui32Trash;
  
      //
      // Drain any residual data from the receive FIFO.
      //
      while(MAP_SSIDataGetNonBlocking(ui32Base, &ui32Trash) != 0)
      {
      }
  
      //
      // Set the SSI module into write-only mode.
      //
      MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_WRITE);
  
      //
      // Send the dual read command.
      //
      MAP_SSIDataPut(ui32Base, CMD_DREAD);
  
      //
      // Send the address of the first byte to read.
      //
      MAP_SSIDataPut(ui32Base, (ui32Addr >> 16) & 0xff);
      MAP_SSIDataPut(ui32Base, (ui32Addr >> 8) & 0xff);
      MAP_SSIDataPut(ui32Base, ui32Addr & 0xff);
  
      //
      // Send a dummy byte.
      //
      MAP_SSIDataPut(ui32Base, 0);
  
      //
      // Set the SSI module into Bi-SPI read mode.  In this mode, dummy writes
      // are required in order to make the transfer occur; the SSI module will
      // ignore the data (the SPI flash will never see the dummy data since
      // Bi-SPI read mode is a uni-directional input mode).
      //
      MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_BI_READ);
  
      //
      // See if there is a single byte to be read.
      //
      if(ui32Count == 1)
      {
          //
          // Perform a single dummy write, marking it as the end of the frame.
          //
          MAP_SSIAdvDataPutFrameEnd(ui32Base, 0);
      }
      else
      {
          //
          // Perform a dummy write to prime the loop.
          //
          MAP_SSIDataPut(ui32Base, 0);
  
          //
          // Loop while there is more than one byte left to be read.
          //
          while(--ui32Count != 1)
          {
              //
              // Perform a dummy write to keep the transmit FIFO from going
              // empty.
              //
              MAP_SSIDataPut(ui32Base, 0);
  
              //
              // Read the next data byte from the receive FIFO and place it into
              // the data buffer.
              //
              MAP_SSIDataGet(ui32Base, &ui32Addr);
              *pui8Data++ = ui32Addr & 0xff;
          }
  
          //
          // Perform the final dummy write, marking it as the end of the frame.
          //
          MAP_SSIAdvDataPutFrameEnd(ui32Base, 0);
  
          //
          // Read the next data byte from the receive FIFO and place it into the
          // data buffer.
          //
          MAP_SSIDataGet(ui32Base, &ui32Addr);
          *pui8Data++ = ui32Addr & 0xff;
      }
  
      //
      // Read the final data byte from the receive FIFO and place it into the
      // data buffer.
      //
      MAP_SSIDataGet(ui32Base, &ui32Addr);
      *pui8Data++ = ui32Addr & 0xff;
  }
  
  //*****************************************************************************
  //
  //! Reads data from the SPI flash using Bi-SPI in the background.
  //!
  //! \param pState is a pointer to the SPI flash state structure.
  //! \param ui32Base is the SSI module base address.
  //! \param ui32Addr is the SPI flash address to read.
  //! \param pui8Data is a pointer to the data buffer to into which to read the
  //! data.
  //! \param ui32Count is the number of bytes to read.
  //! \param bUseDMA is \b true if uDMA should be used and \b false otherwise.
  //! \param ui32TxChannel is the uDMA channel to be used for writing to the SSI
  //! module.
  //! \param ui32RxChannel is the uDMA channel to be used for reading from the
  //! SSI module.
  //!
  //! This function reads data from the SPI flash with Bi-SPI, using either
  //! interrupts or uDMA to transfer the data.  This function will return
  //! immediately and read the data in the background.  In order for this to
  //! complete successfully, several conditions must be satisfied:
  //!
  //! - Prior to calling this function:
  //!   - The SSI module must be enabled in SysCtl.
  //!   - The SSI pins must be configured for use by the SSI module.
  //!   - The SSI module interrupt must be enabled in NVIC.
  //!   - The uDMA module must be enabled in SysCtl and the control table set (if
  //!     using uDMA).
  //!   - The uDMA channels must be assigned to the SSI module.
  //!
  //! - After calling this function:
  //!   - The interrupt handler for the SSI module must call
  //!     SPIFlashIntHandler(), passing the same pState structure pointer that
  //!     was supplied to this function.
  //!   - No other SPI flash operation can be called until this operation has
  //!     completed.
  //!
  //! Completion of the read operation is indicated when SPIFlashIntHandler()
  //! returns \b SPI_FLASH_DONE.
  //!
  //! Like SPIFLashDualRead(), this uses the 0x3b SPI flash command.
  //!
  //! \return None.
  //
  //*****************************************************************************
  void
  SPIFlashDualReadNonBlocking(tSPIFlashState *pState, uint32_t ui32Base,
                              uint32_t ui32Addr, uint8_t *pui8Data,
                              uint32_t ui32Count, bool bUseDMA,
                              uint32_t ui32TxChannel, uint32_t ui32RxChannel)
  {
      uint32_t ui32Trash;
  
      //
      // Drain any residual data from the receive FIFO.
      //
      while(MAP_SSIDataGetNonBlocking(ui32Base, &ui32Trash) != 0)
      {
      }
  
      //
      // Save the parameters of this read operation to the state structure.
      //
      pState->ui32Base = ui32Base;
      pState->ui16Cmd = CMD_DREAD;
      pState->ui16State = STATE_CMD;
      pState->ui32Addr = ui32Addr;
      pState->pui8Buffer = pui8Data;
      pState->ui32ReadCount = ui32Count;
      pState->ui32WriteCount = ui32Count;
      pState->bUseDMA = bUseDMA;
      pState->ui32TxChannel = ui32TxChannel & 0x1f;
      pState->ui32RxChannel = ui32RxChannel & 0x1f;
  
      //
      // Enable the SSI transmit and receive interrupts.  This will start the
      // transfer.  If uDMA is being used, the uDMA-related interrupts will be
      // enabled at the appropriate time by the interrupt handler.
      //
      HWREG(ui32Base + SSI_O_ICR) = SSI_ICR_DMATXIC | SSI_ICR_DMARXIC;
      HWREG(ui32Base + SSI_O_IM) = SSI_IM_TXIM | SSI_IM_RXIM | SSI_IM_RTIM;
  }
  
  //*****************************************************************************
  //
  //! Erases a 32 KB block of the SPI flash.
  //!
  //! \param ui32Base is the SSI module base address.
  //! \param ui32Addr is the SPI flash address to erase.
  //!
  //! This function erases a 32 KB block of the SPI flash.  Each 32 KB block has
  //! a 32 KB alignment; the SPI flash will ignore the lower 15 bits of the
  //! address provided.  The 32 KB block erase command is issued by this
  //! function; SPIFlashReadStatus() must be used to query the SPI flash to
  //! determine when the 32 KB block erase operation has completed.  This uses
  //! the 0x52 SPI flash command.
  //!
  //! \return None.
  //
  //*****************************************************************************
  void
  SPIFlashBlockErase32(uint32_t ui32Base, uint32_t ui32Addr)
  {
      //
      // Set the SSI module into write-only mode.
      //
      MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_WRITE);
  
      //
      // Send the 32 KB block erase command command.
      //
      MAP_SSIDataPut(ui32Base, CMD_BE32);
  
      //
      // Send the address of the 32 KB block to be erased, marking the last byte
      // of the address as the end of the frame.
      //
      MAP_SSIDataPut(ui32Base, (ui32Addr >> 16) & 0xff);
      MAP_SSIDataPut(ui32Base, (ui32Addr >> 8) & 0xff);
      MAP_SSIAdvDataPutFrameEnd(ui32Base, ui32Addr & 0xff);
  }
  
  //*****************************************************************************
  //
  //! Reads data from the SPI flash using Quad-SPI.
  //!
  //! \param ui32Base is the SSI module base address.
  //! \param ui32Addr is the SPI flash address to read.
  //! \param pui8Data is a pointer to the data buffer to into which to read the
  //! data.
  //! \param ui32Count is the number of bytes to read.
  //!
  //! This function reads data from the SPI flash with Quad-SPI, using PIO mode.
  //! This function will not return until the read has completed.  This uses the
  //! 0x6b SPI flash command.
  //!
  //! \return None.
  //
  //*****************************************************************************
  void
  SPIFlashQuadRead(uint32_t ui32Base, uint32_t ui32Addr, uint8_t *pui8Data,
                   uint32_t ui32Count)
  {
      uint32_t ui32Trash;
  
      //
      // Drain any residual data from the receive FIFO.
      //
      while(MAP_SSIDataGetNonBlocking(ui32Base, &ui32Trash) != 0)
      {
      }
  
      //
      // Set the SSI module into write-only mode.
      //
      MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_WRITE);
  
      //
      // Send the quad read command.
      //
      MAP_SSIDataPut(ui32Base, CMD_QREAD);
  
      //
      // Send the address of the first byte to read.
      //
      MAP_SSIDataPut(ui32Base, (ui32Addr >> 16) & 0xff);
      MAP_SSIDataPut(ui32Base, (ui32Addr >> 8) & 0xff);
      MAP_SSIDataPut(ui32Base, ui32Addr & 0xff);
  
      //
      // Send a dummy byte.
      //
      MAP_SSIDataPut(ui32Base, 0);
  
      //
      // Set the SSI module into Quad-SPI read mode.  In this mode, dummy writes
      // are required in order to make the transfer occur; the SSI module will
      // ignore the data (the SPI flash will never see the dummy data since
      // Quad-SPI read mode is a uni-directional input mode).
      //
      MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_QUAD_READ);
  
      //
      // See if there is a single byte to be read.
      //
      if(ui32Count == 1)
      {
          //
          // Perform a single dummy write, marking it as the end of the frame.
          //
          MAP_SSIAdvDataPutFrameEnd(ui32Base, 0);
      }
      else
      {
          //
          // Perform a dummy write to prime the loop.
          //
          MAP_SSIDataPut(ui32Base, 0);
  
          //
          // Loop while there is more than one byte left to be read.
          //
          while(--ui32Count != 1)
          {
              //
              // Perform a dummy write to keep the transmit FIFO from going
              // empty.
              //
              MAP_SSIDataPut(ui32Base, 0);
  
              //
              // Read the next data byte from the receive FIFO and place it into
              // the data buffer.
              //
              MAP_SSIDataGet(ui32Base, &ui32Addr);
              *pui8Data++ = ui32Addr & 0xff;
          }
  
          //
          // Perform the final dummy write, marking it as the end of the frame.
          //
          MAP_SSIAdvDataPutFrameEnd(ui32Base, 0);
  
          //
          // Read the next data byte from the receive FIFO and place it into the
          // data buffer.
          //
          MAP_SSIDataGet(ui32Base, &ui32Addr);
          *pui8Data++ = ui32Addr & 0xff;
      }
  
      //
      // Read the final data byte from the receive FIFO and place it into the
      // data buffer.
      //
      MAP_SSIDataGet(ui32Base, &ui32Addr);
      *pui8Data++ = ui32Addr & 0xff;
  }
  
  //*****************************************************************************
  //
  //! Reads data from the SPI flash using Quad-SPI in the background.
  //!
  //! \param pState is a pointer to the SPI flash state structure.
  //! \param ui32Base is the SSI module base address.
  //! \param ui32Addr is the SPI flash address to read.
  //! \param pui8Data is a pointer to the data buffer to into which to read the
  //! data.
  //! \param ui32Count is the number of bytes to read.
  //! \param bUseDMA is \b true if uDMA should be used and \b false otherwise.
  //! \param ui32TxChannel is the uDMA channel to be used for writing to the SSI
  //! module.
  //! \param ui32RxChannel is the uDMA channel to be used for reading from the
  //! SSI module.
  //!
  //! This function reads data from the SPI flash with Quad-SPI, using either
  //! interrupts or uDMA to transfer the data.  This function will return
  //! immediately and read the data in the background.  In order for this to
  //! complete successfully, several conditions must be satisfied:
  //!
  //! - Prior to calling this function:
  //!   - The SSI module must be enabled in SysCtl.
  //!   - The SSI pins must be configured for use by the SSI module.
  //!   - The SSI module interrupt must be enabled in NVIC.
  //!   - The uDMA module must be enabled in SysCtl and the control table set (if
  //!     using uDMA).
  //!   - The uDMA channels must be assigned to the SSI module.
  //!
  //! - After calling this function:
  //!   - The interrupt handler for the SSI module must call
  //!     SPIFlashIntHandler(), passing the same pState structure pointer that
  //!     was supplied to this function.
  //!   - No other SPI flash operation can be called until this operation has
  //!     completed.
  //!
  //! Completion of the read operation is indicated when SPIFlashIntHandler()
  //! returns \b SPI_FLASH_DONE.
  //!
  //! Like SPIFlashQuadRead(), this uses the 0x6b SPI flash command.
  //!
  //! \return None.
  //
  //*****************************************************************************
  void
  SPIFlashQuadReadNonBlocking(tSPIFlashState *pState, uint32_t ui32Base,
                              uint32_t ui32Addr, uint8_t *pui8Data,
                              uint32_t ui32Count, bool bUseDMA,
                              uint32_t ui32TxChannel, uint32_t ui32RxChannel)
  {
      uint32_t ui32Trash;
  
      //
      // Drain any residual data from the receive FIFO.
      //
      while(MAP_SSIDataGetNonBlocking(ui32Base, &ui32Trash) != 0)
      {
      }
  
      //
      // Save the parameters of this read operation to the state structure.
      //
      pState->ui32Base = ui32Base;
      pState->ui16Cmd = CMD_QREAD;
      pState->ui16State = STATE_CMD;
      pState->ui32Addr = ui32Addr;
      pState->pui8Buffer = pui8Data;
      pState->ui32ReadCount = ui32Count;
      pState->ui32WriteCount = ui32Count;
      pState->bUseDMA = bUseDMA;
      pState->ui32TxChannel = ui32TxChannel & 0x1f;
      pState->ui32RxChannel = ui32RxChannel & 0x1f;
  
      //
      // Enable the SSI transmit and receive interrupts.  This will start the
      // transfer.  If uDMA is being used, the uDMA-related interrupts will be
      // enabled at the appropriate time by the interrupt handler.
      //
      HWREG(ui32Base + SSI_O_ICR) = SSI_ICR_DMATXIC | SSI_ICR_DMARXIC;
      HWREG(ui32Base + SSI_O_IM) = SSI_IM_TXIM | SSI_IM_RXIM | SSI_IM_RTIM;
  }
  
  //*****************************************************************************
  //
  //! Reads the manufacturer and device IDs from the SPI flash.
  //!
  //! \param ui32Base is the SSI module base address.
  //! \param pui8ManufacturerID is a pointer to the location into which to store
  //! the manufacturer ID.
  //! \param pui16DeviceID is a pointer to the location into which to store the
  //! device ID.
  //!
  //! This function reads the manufacturer and device IDs from the SPI flash.
  //! These values can be used to identify the SPI flash that is attached, as
  //! well as determining if a SPI flash is attached (if the \b SSIRx pin is
  //! pulled up or down, either using the pad's weak pull up/down or using an
  //! external resistor, which will cause the returned IDs to be either all zeros
  //! or all ones if the SPI flash is not attached).  This uses the 0x9f SPI
  //! flash command.
  //!
  //! \return None.
  //
  //*****************************************************************************
  void
  SPIFlashReadID(uint32_t ui32Base, uint8_t *pui8ManufacturerID,
                 uint16_t *pui16DeviceID)
  {
      uint32_t ui32Data1, ui32Data2;
  
      //
      // Drain any residual data from the receive FIFO.
      //
      while(MAP_SSIDataGetNonBlocking(ui32Base, &ui32Data1) != 0)
      {
      }
  
      //
      // Set the SSI module into write-only mode.
      //
      MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_WRITE);
  
      //
      // Send the read ID command.
      //
      MAP_SSIDataPut(ui32Base, CMD_RDID);
  
      //
      // Set the SSI module into read/write mode.  In this mode, dummy writes are
      // required in order to make the transfer occur; the SPI flash will ignore
      // the data.
      //
      MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_READ_WRITE);
  
      //
      // Send three dummy bytes, marking the last as the end of the frame.
      //
      MAP_SSIDataPut(ui32Base, 0);
      MAP_SSIDataPut(ui32Base, 0);
      MAP_SSIAdvDataPutFrameEnd(ui32Base, 0);
  
      //
      // Read the first returned data byte, which contains the manufacturer ID.
      //
      MAP_SSIDataGet(ui32Base, &ui32Data1);
      *pui8ManufacturerID = ui32Data1 & 0xff;
  
      //
      // Read the remaining two data bytes, which contain the device ID.
      //
      MAP_SSIDataGet(ui32Base, &ui32Data1);
      MAP_SSIDataGet(ui32Base, &ui32Data2);
      *pui16DeviceID = ((ui32Data1 & 0xff) << 8) | (ui32Data2 & 0xff);
  }
  
  //*****************************************************************************
  //
  //! Erases the entire SPI flash.
  //!
  //! \param ui32Base is the SSI module base address.
  //!
  //! This command erase the entire SPI flash.  The chip erase command is issued
  //! by this function; SPIFlashReadStatus() must be used to query the SPI flash
  //! to determine when the chip erase operation has completed.  This uses the
  //! 0xc7 SPI flash command.
  //!
  //! \return None.
  //
  //*****************************************************************************
  void
  SPIFlashChipErase(uint32_t ui32Base)
  {
      //
      // Set the SSI module into write-only mode.
      //
      MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_WRITE);
  
      //
      // Send the chip erase command, marking this byte as the end of the frame.
      //
      MAP_SSIAdvDataPutFrameEnd(ui32Base, CMD_CE);
  }
  
  //*****************************************************************************
  //
  //! Erases a 64 KB block of the SPI flash.
  //!
  //! \param ui32Base is the SSI module base address.
  //! \param ui32Addr is the SPI flash address to erase.
  //!
  //! This function erases a 64 KB block of the SPI flash.  Each 64 KB block has
  //! a 64 KB alignment; the SPI flash will ignore the lower 16 bits of the
  //! address provided.  The 64 KB block erase command is issued by this
  //! function; SPIFlashReadStatus() must be used to query the SPI flash to
  //! determine when the 64 KB block erase operation has completed.  This uses
  //! the 0xd8 SPI flash command.
  //!
  //! \return None.
  //
  //*****************************************************************************
  void
  SPIFlashBlockErase64(uint32_t ui32Base, uint32_t ui32Addr)
  {
      //
      // Set the SSI module into write-only mode.
      //
      MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_WRITE);
  
      //
      // Send the 64 KB block erase command command.
      //
      MAP_SSIDataPut(ui32Base, CMD_BE64);
  
      //
      // Send the address of the 64 KB block to be erased, marking the last byte
      // of the address as the end of the frame.
      //
      MAP_SSIDataPut(ui32Base, (ui32Addr >> 16) & 0xff);
      MAP_SSIDataPut(ui32Base, (ui32Addr >> 8) & 0xff);
      MAP_SSIAdvDataPutFrameEnd(ui32Base, ui32Addr & 0xff);
  }
  
  //*****************************************************************************
  //
  // Close the Doxygen group.
  //! @}
  //
  //*****************************************************************************
  