blob: cb9cb2f83711c0eaffda58db7a36f20c6fbdacc8 [file] [log] [blame]
/*
* -------------------------------------------
* CC3220 SDK - v0.10.00.00
* -------------------------------------------
*
* Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Texas Instruments Incorporated nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
//*****************************************************************************
//
// flash.c
//
// Driver for programming the on-chip flash.
//
//*****************************************************************************
//*****************************************************************************
//
//! \addtogroup flash_api
//! @{
//
//*****************************************************************************
#include "inc/hw_types.h"
#include "inc/hw_flash_ctrl.h"
#include "inc/hw_memmap.h"
#include "inc/hw_ints.h"
#include "inc/hw_gprcm.h"
#include "inc/hw_hib1p2.h"
#include "inc/hw_hib3p3.h"
#include "inc/hw_common_reg.h"
#include "inc/hw_stack_die_ctrl.h"
#include "debug.h"
#include "flash.h"
#include "utils.h"
#include "interrupt.h"
#define HAVE_WRITE_BUFFER 1
//*****************************************************************************
//
// An array that maps the specified memory bank to the appropriate Flash
// Memory Protection Program Enable (FMPPE) register.
//
//*****************************************************************************
static const unsigned long g_pulFMPPERegs[] =
{
FLASH_FMPPE0,
FLASH_FMPPE1,
FLASH_FMPPE2,
FLASH_FMPPE3,
FLASH_FMPPE4,
FLASH_FMPPE5,
FLASH_FMPPE6,
FLASH_FMPPE7,
FLASH_FMPPE8,
FLASH_FMPPE9,
FLASH_FMPPE10,
FLASH_FMPPE11,
FLASH_FMPPE12,
FLASH_FMPPE13,
FLASH_FMPPE14,
FLASH_FMPPE15
};
//*****************************************************************************
//
// An array that maps the specified memory bank to the appropriate Flash
// Memory Protection Read Enable (FMPRE) register.
//
//*****************************************************************************
static const unsigned long g_pulFMPRERegs[] =
{
FLASH_FMPRE0,
FLASH_FMPRE1,
FLASH_FMPRE2,
FLASH_FMPRE3,
FLASH_FMPRE4,
FLASH_FMPRE5,
FLASH_FMPRE6,
FLASH_FMPRE7,
FLASH_FMPRE8,
FLASH_FMPRE9,
FLASH_FMPRE10,
FLASH_FMPRE11,
FLASH_FMPRE12,
FLASH_FMPRE13,
FLASH_FMPRE14,
FLASH_FMPRE15,
};
//*****************************************************************************
//
//! Flash Disable
//!
//! This function Disables the internal Flash.
//!
//! \return None.
//
//*****************************************************************************
void
FlashDisable()
{
//
// Wait for Flash Busy to get cleared
//
while((HWREG(GPRCM_BASE + GPRCM_O_TOP_DIE_ENABLE)
& GPRCM_TOP_DIE_ENABLE_FLASH_BUSY))
{
}
//
// Assert reset
//
HWREG(HIB1P2_BASE + HIB1P2_O_PORPOL_SPARE) = 0xFFFF0000;
//
// 50 usec Delay Loop
//
UtilsDelay((50*80)/3);
//
// Disable TDFlash
//
HWREG(GPRCM_BASE + GPRCM_O_TOP_DIE_ENABLE) = 0x0;
//
// 50 usec Delay Loop
//
UtilsDelay((50*80)/3);
HWREG(HIB1P2_BASE + HIB1P2_O_BGAP_DUTY_CYCLING_EXIT_CFG) = 0x1;
//
// 50 usec Delay Loop
//
UtilsDelay((50*80)/3);
}
//*****************************************************************************
//
//! Erases a block of flash.
//!
//! \param ulAddress is the start address of the flash block to be erased.
//!
//! This function will erase a 2 kB block of the on-chip flash. After erasing,
//! the block will be filled with 0xFF bytes. Read-only and execute-only
//! blocks cannot be erased.
//!
//! This function will not return until the block has been erased.
//!
//! \return Returns 0 on success, or -1 if an invalid block address was
//! specified or the block is write-protected.
//
//*****************************************************************************
long
FlashErase(unsigned long ulAddress)
{
//
// Check the arguments.
//
ASSERT(!(ulAddress & (FLASH_CTRL_ERASE_SIZE - 1)));
//
// Clear the flash access and error interrupts.
//
HWREG(FLASH_CONTROL_BASE + FLASH_CTRL_O_FCMISC)
= (FLASH_CTRL_FCMISC_AMISC | FLASH_CTRL_FCMISC_VOLTMISC |
FLASH_CTRL_FCMISC_ERMISC);
// Erase the block.
//
HWREG(FLASH_CONTROL_BASE + FLASH_CTRL_O_FMA) = ulAddress;
HWREG(FLASH_CONTROL_BASE + FLASH_CTRL_O_FMC)
= FLASH_CTRL_FMC_WRKEY | FLASH_CTRL_FMC_ERASE;
//
// Wait until the block has been erased.
//
while(HWREG(FLASH_CONTROL_BASE + FLASH_CTRL_O_FMC) & FLASH_CTRL_FMC_ERASE)
{
}
//
// Return an error if an access violation or erase error occurred.
//
if(HWREG(FLASH_CONTROL_BASE + FLASH_CTRL_O_FCRIS)
& (FLASH_CTRL_FCRIS_ARIS | FLASH_CTRL_FCRIS_VOLTRIS |
FLASH_CTRL_FCRIS_ERRIS))
{
return(-1);
}
//
// Success.
//
return(0);
}
//*****************************************************************************
//
//! Erases a block of flash but does not wait for completion.
//!
//! \param ulAddress is the start address of the flash block to be erased.
//!
//! This function will erase a 2 kB block of the on-chip flash. After erasing,
//! the block will be filled with 0xFF bytes. Read-only and execute-only
//! blocks cannot be erased.
//!
//! This function will return immediately after commanding the erase operation.
//! Applications making use of the function can determine completion state by
//! using a flash interrupt handler or by polling FlashIntStatus.
//!
//! \return None.
//
//*****************************************************************************
void
FlashEraseNonBlocking(unsigned long ulAddress)
{
//
// Check the arguments.
//
ASSERT(!(ulAddress & (FLASH_CTRL_ERASE_SIZE - 1)));
//
// Clear the flash access and error interrupts.
//
HWREG(FLASH_CONTROL_BASE + FLASH_CTRL_O_FCMISC) =
(FLASH_CTRL_FCMISC_AMISC | FLASH_CTRL_FCMISC_VOLTMISC |
FLASH_CTRL_FCMISC_ERMISC);
//
// Command the flash controller to erase the block.
//
HWREG(FLASH_CONTROL_BASE + FLASH_CTRL_O_FMA) = ulAddress;
HWREG(FLASH_CONTROL_BASE + FLASH_CTRL_O_FMC) = FLASH_CTRL_FMC_WRKEY | FLASH_CTRL_FMC_ERASE;
}
//*****************************************************************************
//
//! Erases a complele flash at shot.
//!
//! This function erases a complele flash at shot
//!
//! \return Returns 0 on success, or -1 if the block is write-protected.
//
//*****************************************************************************
long
FlashMassErase()
{
//
// Clear the flash access and error interrupts.
//
HWREG(FLASH_CONTROL_BASE + FLASH_CTRL_O_FCMISC) =
(FLASH_CTRL_FCMISC_AMISC | FLASH_CTRL_FCMISC_VOLTMISC |
FLASH_CTRL_FCMISC_ERMISC);
//
// Command the flash controller for mass erase.
//
HWREG(FLASH_CONTROL_BASE + FLASH_CTRL_O_FMC) =
FLASH_CTRL_FMC_WRKEY | FLASH_CTRL_FMC_MERASE1;
//
// Wait until mass erase completes.
//
while(HWREG(FLASH_CONTROL_BASE + FLASH_CTRL_O_FMC) & FLASH_CTRL_FMC_MERASE1)
{
}
//
// Return an error if an access violation or erase error occurred.
//
if(HWREG(FLASH_CONTROL_BASE + FLASH_CTRL_O_FCRIS)
& (FLASH_CTRL_FCRIS_ARIS | FLASH_CTRL_FCRIS_VOLTRIS |
FLASH_CTRL_FCRIS_ERRIS))
{
return -1;
}
//
// Success.
//
return 0;
}
//*****************************************************************************
//
//! Erases a complele flash at shot but does not wait for completion.
//!
//!
//! This function will not return until the Flash has been erased.
//!
//! \return None.
//
//*****************************************************************************
void
FlashMassEraseNonBlocking()
{
//
// Clear the flash access and error interrupts.
//
HWREG(FLASH_CONTROL_BASE + FLASH_CTRL_O_FCMISC) =
(FLASH_CTRL_FCMISC_AMISC | FLASH_CTRL_FCMISC_VOLTMISC |
FLASH_CTRL_FCMISC_ERMISC);
//
// Command the flash controller for mass erase.
//
HWREG(FLASH_CONTROL_BASE + FLASH_CTRL_O_FMC) =
FLASH_CTRL_FMC_WRKEY | FLASH_CTRL_FMC_MERASE1;
}
//*****************************************************************************
//
//! Programs flash.
//!
//! \param pulData is a pointer to the data to be programmed.
//! \param ulAddress is the starting address in flash to be programmed. Must
//! be a multiple of four.
//! \param ulCount is the number of bytes to be programmed. Must be a multiple
//! of four.
//!
//! This function will program a sequence of words into the on-chip flash.
//! Each word in a page of flash can only be programmed one time between an
//! erase of that page; programming a word multiple times will result in an
//! unpredictable value in that word of flash.
//!
//! Since the flash is programmed one word at a time, the starting address and
//! byte count must both be multiples of four. It is up to the caller to
//! verify the programmed contents, if such verification is required.
//!
//! This function will not return until the data has been programmed.
//!
//! \return Returns 0 on success, or -1 if a programming error is encountered.
//
//*****************************************************************************
long
FlashProgram(unsigned long *pulData, unsigned long ulAddress,
unsigned long ulCount)
{
//
// Check the arguments.
//
ASSERT(!(ulAddress & 3));
ASSERT(!(ulCount & 3));
//
// Clear the flash access and error interrupts.
//
HWREG(FLASH_CONTROL_BASE + FLASH_CTRL_O_FCMISC)
= (FLASH_CTRL_FCMISC_AMISC | FLASH_CTRL_FCMISC_VOLTMISC |
FLASH_CTRL_FCMISC_INVDMISC | FLASH_CTRL_FCMISC_PROGMISC);
//
// See if this device has a write buffer.
//
#if HAVE_WRITE_BUFFER
{
//
// Loop over the words to be programmed.
//
while(ulCount)
{
//
// Set the address of this block of words. for 1 MB
//
HWREG(FLASH_CONTROL_BASE + FLASH_CTRL_O_FMA) = ulAddress & ~(0x7F);
//
// Loop over the words in this 32-word block.
//
while(((ulAddress & 0x7C) ||
(HWREG(FLASH_CONTROL_BASE + FLASH_CTRL_O_FWBVAL) == 0)) &&
(ulCount != 0))
{
//
// Write this word into the write buffer.
//
HWREG(FLASH_CONTROL_BASE + FLASH_CTRL_O_FWBN
+ (ulAddress & 0x7C)) = *pulData++;
ulAddress += 4;
ulCount -= 4;
}
//
// Program the contents of the write buffer into flash.
//
HWREG(FLASH_CONTROL_BASE + FLASH_CTRL_O_FMC2)
= FLASH_CTRL_FMC2_WRKEY | FLASH_CTRL_FMC2_WRBUF;
//
// Wait until the write buffer has been programmed.
//
while(HWREG(FLASH_CONTROL_BASE + FLASH_CTRL_O_FMC2) & FLASH_CTRL_FMC2_WRBUF)
{
}
}
}
#else
{
//
// Loop over the words to be programmed.
//
while(ulCount)
{
//
// Program the next word.
//
HWREG(FLASH_CONTROL_BASE + FLASH_CTRL_O_FMA) = ulAddress;
HWREG(FLASH_CONTROL_BASE + FLASH_CTRL_O_FMD) = *pulData;
HWREG(FLASH_CONTROL_BASE + FLASH_CTRL_O_FMC) = FLASH_CTRL_FMC_WRKEY | FLASH_CTRL_FMC_WRITE;
//
// Wait until the word has been programmed.
//
while(HWREG(FLASH_CONTROL_BASE + FLASH_CTRL_O_FMC) & FLASH_CTRL_FMC_WRITE)
{
}
//
// Increment to the next word.
//
pulData++;
ulAddress += 4;
ulCount -= 4;
}
}
#endif
//
// Return an error if an access violation occurred.
//
if(HWREG(FLASH_CONTROL_BASE + FLASH_CTRL_O_FCRIS) & (FLASH_CTRL_FCRIS_ARIS | FLASH_CTRL_FCRIS_VOLTRIS |
FLASH_CTRL_FCRIS_INVDRIS | FLASH_CTRL_FCRIS_PROGRIS))
{
return(-1);
}
//
// Success.
//
return(0);
}
//*****************************************************************************
//
//! Programs flash but does not poll for completion.
//!
//! \param pulData is a pointer to the data to be programmed.
//! \param ulAddress is the starting address in flash to be programmed. Must
//! be a multiple of four.
//! \param ulCount is the number of bytes to be programmed. Must be a multiple
//! of four.
//!
//! This function will start programming one or more words into the on-chip
//! flash and return immediately. The number of words that can be programmed
//! in a single call depends the part on which the function is running. For
//! parts without support for a flash write buffer, only a single word may be
//! programmed on each call to this function (\e ulCount must be 1). If a
//! write buffer is present, up to 32 words may be programmed on condition
//! that the block being programmed does not straddle a 32 word address
//! boundary. For example, wherease 32 words can be programmed if the address
//! passed is 0x100 (a multiple of 128 bytes or 32 words), only 31 words could
//! be programmed at 0x104 since attempting to write 32 would cross the 32
//! word boundary at 0x180.
//!
//! Since the flash is programmed one word at a time, the starting address and
//! byte count must both be multiples of four. It is up to the caller to
//! verify the programmed contents, if such verification is required.
//!
//! This function will return immediately after commanding the erase operation.
//! Applications making use of the function can determine completion state by
//! using a flash interrupt handler or by polling FlashIntStatus.
//!
//! \return 0 if the write was started successfully, -1 if there was an error.
//
//*****************************************************************************
long
FlashProgramNonBlocking(unsigned long *pulData, unsigned long ulAddress,
unsigned long ulCount)
{
//
// Check the arguments.
//
ASSERT(!(ulAddress & 3));
ASSERT(!(ulCount & 3));
//
// Clear the flash access and error interrupts.
//
HWREG(FLASH_CONTROL_BASE + FLASH_CTRL_O_FCMISC)
= (FLASH_CTRL_FCMISC_AMISC | FLASH_CTRL_FCMISC_VOLTMISC |
FLASH_CTRL_FCMISC_INVDMISC | FLASH_CTRL_FCMISC_PROGMISC);
//
// See if this device has a write buffer.
//
#if HAVE_WRITE_BUFFER
{
//
// Make sure the address/count specified doesn't straddle a 32 word
// boundary.
//
if(((ulAddress + (ulCount - 1)) & ~0x7F) != (ulAddress & ~0x7F))
{
return(-1);
}
//
// Loop over the words to be programmed.
//
while(ulCount)
{
//
// Set the address of this block of words.
//
HWREG(FLASH_CONTROL_BASE + FLASH_CTRL_O_FMA) = ulAddress & ~(0x7F);
//
// Loop over the words in this 32-word block.
//
while(((ulAddress & 0x7C) || (HWREG(FLASH_CONTROL_BASE + FLASH_CTRL_O_FWBVAL) == 0)) &&
(ulCount != 0))
{
//
// Write this word into the write buffer.
//
HWREG(FLASH_CONTROL_BASE + FLASH_CTRL_O_FWBN + (ulAddress & 0x7C)) = *pulData++;
ulAddress += 4;
ulCount -= 4;
}
//
// Program the contents of the write buffer into flash.
//
HWREG(FLASH_CONTROL_BASE + FLASH_CTRL_O_FMC2) = FLASH_CTRL_FMC2_WRKEY | FLASH_CTRL_FMC2_WRBUF;
}
}
#else
{
//
// We don't have a write buffer so we can only write a single word.
//
if(ulCount > 1)
{
return(-1);
}
//
// Write a single word.
//
HWREG(FLASH_CONTROL_BASE + FLASH_CTRL_O_FMA) = ulAddress;
HWREG(FLASH_CONTROL_BASE + FLASH_CTRL_O_FMD) = *pulData;
HWREG(FLASH_CONTROL_BASE + FLASH_CTRL_O_FMC) = FLASH_CTRL_FMC_WRKEY | FLASH_CTRL_FMC_WRITE;
}
#endif
//
// Success.
//
return(0);
}
//*****************************************************************************
//
//! Gets the protection setting for a block of flash.
//!
//! \param ulAddress is the start address of the flash block to be queried.
//!
//! This function gets the current protection for the specified 2-kB block
//! of flash. Each block can be read/write, read-only, or execute-only.
//! Read/write blocks can be read, executed, erased, and programmed. Read-only
//! blocks can be read and executed. Execute-only blocks can only be executed;
//! processor and debugger data reads are not allowed.
//!
//! \return Returns the protection setting for this block. See
//! FlashProtectSet() for possible values.
//
//*****************************************************************************
tFlashProtection
FlashProtectGet(unsigned long ulAddress)
{
unsigned long ulFMPRE, ulFMPPE;
unsigned long ulBank;
//
// Check the argument.
//
ASSERT(!(ulAddress & (FLASH_PROTECT_SIZE - 1)));
//
// Calculate the Flash Bank from Base Address, and mask off the Bank
// from ulAddress for subsequent reference.
//
ulBank = (((ulAddress / FLASH_PROTECT_SIZE) / 32) % 16);
ulAddress &= ((FLASH_PROTECT_SIZE * 32) - 1);
//
// Read the appropriate flash protection registers for the specified
// flash bank.
//
ulFMPRE = HWREG(g_pulFMPRERegs[ulBank]);
ulFMPPE = HWREG(g_pulFMPPERegs[ulBank]);
//
// Check the appropriate protection bits for the block of memory that
// is specified by the address.
//
switch((((ulFMPRE >> (ulAddress / FLASH_PROTECT_SIZE)) &
FLASH_FMP_BLOCK_0) << 1) |
((ulFMPPE >> (ulAddress / FLASH_PROTECT_SIZE)) & FLASH_FMP_BLOCK_0))
{
//
// This block is marked as execute only (that is, it can not be erased
// or programmed, and the only reads allowed are via the instruction
// fetch interface).
//
case 0:
case 1:
{
return(FlashExecuteOnly);
}
//
// This block is marked as read only (that is, it can not be erased or
// programmed).
//
case 2:
{
return(FlashReadOnly);
}
//
// This block is read/write; it can be read, erased, and programmed.
//
case 3:
default:
{
return(FlashReadWrite);
}
}
}
//*****************************************************************************
//
//! Registers an interrupt handler for the flash interrupt.
//!
//! \param pfnHandler is a pointer to the function to be called when the flash
//! interrupt occurs.
//!
//! This sets the handler to be called when the flash interrupt occurs. The
//! flash controller can generate an interrupt when an invalid flash access
//! occurs, such as trying to program or erase a read-only block, or trying to
//! read from an execute-only block. It can also generate an interrupt when a
//! program or erase operation has completed. The interrupt will be
//! automatically enabled when the handler is registered.
//!
//! \sa IntRegister() for important information about registering interrupt
//! handlers.
//!
//! \return None.
//
//*****************************************************************************
void
FlashIntRegister(void (*pfnHandler)(void))
{
//
// Register the interrupt handler, returning an error if an error occurs.
//
IntRegister(INT_FLASH, pfnHandler);
//
// Enable the flash interrupt.
//
IntEnable(INT_FLASH);
}
//*****************************************************************************
//
//! Unregisters the interrupt handler for the flash interrupt.
//!
//! This function will clear the handler to be called when the flash interrupt
//! occurs. This will also mask off the interrupt in the interrupt controller
//! so that the interrupt handler is no longer called.
//!
//! \sa IntRegister() for important information about registering interrupt
//! handlers.
//!
//! \return None.
//
//*****************************************************************************
void
FlashIntUnregister(void)
{
//
// Disable the interrupt.
//
IntDisable(INT_FLASH);
//
// Unregister the interrupt handler.
//
IntUnregister(INT_FLASH);
}
//*****************************************************************************
//
//! Enables individual flash controller interrupt sources.
//!
//! \param ulIntFlags is a bit mask of the interrupt sources to be enabled.
//! Can be any of the \b FLASH_CTRL_PROGRAM or \b FLASH_CTRL_ACCESS values.
//!
//! Enables the indicated flash controller interrupt sources. Only the sources
//! that are enabled can be reflected to the processor interrupt; disabled
//! sources have no effect on the processor.
//!
//! \return None.
//
//*****************************************************************************
void
FlashIntEnable(unsigned long ulIntFlags)
{
//
// Enable the specified interrupts.
//
HWREG(FLASH_CONTROL_BASE + FLASH_CTRL_O_FCIM) |= ulIntFlags;
}
//*****************************************************************************
//
//! Disables individual flash controller interrupt sources.
//!
//! \param ulIntFlags is a bit mask of the interrupt sources to be disabled.
//! Can be any of the \b FLASH_CTRL_PROGRAM or \b FLASH_CTRL_ACCESS values.
//!
//! Disables the indicated flash controller interrupt sources. Only the
//! sources that are enabled can be reflected to the processor interrupt;
//! disabled sources have no effect on the processor.
//!
//! \return None.
//
//*****************************************************************************
void
FlashIntDisable(unsigned long ulIntFlags)
{
//
// Disable the specified interrupts.
//
HWREG(FLASH_CONTROL_BASE + FLASH_CTRL_O_FCIM) &= ~(ulIntFlags);
}
//*****************************************************************************
//
//! Gets the current interrupt status.
//!
//! \param bMasked is false if the raw interrupt status is required and true if
//! the masked interrupt status is required.
//!
//! This returns the interrupt status for the flash controller. Either the raw
//! interrupt status or the status of interrupts that are allowed to reflect to
//! the processor can be returned.
//!
//! \return The current interrupt status, enumerated as a bit field of
//! \b FLASH_CTRL_PROGRAM and \b FLASH_CTRL_ACCESS.
//
//*****************************************************************************
unsigned long
FlashIntStatus(tBoolean bMasked)
{
//
// Return either the interrupt status or the raw interrupt status as
// requested.
//
if(bMasked)
{
return(HWREG(FLASH_CONTROL_BASE + FLASH_CTRL_O_FCMISC));
}
else
{
return(HWREG(FLASH_CONTROL_BASE + FLASH_CTRL_O_FCRIS));
}
}
//*****************************************************************************
//
//! Clears flash controller interrupt sources.
//!
//! \param ulIntFlags is the bit mask of the interrupt sources to be cleared.
//! Can be any of the \b FLASH_CTRL_PROGRAM or \b FLASH_CTRL_AMISC values.
//!
//! The specified flash controller interrupt sources are cleared, so that they
//! no longer assert. This must be done in the interrupt handler to keep it
//! from being called again immediately upon exit.
//!
//! \note Because there is a write buffer in the Cortex-M3 processor, it may
//! take several clock cycles before the interrupt source is actually cleared.
//! Therefore, it is recommended that the interrupt source be cleared early in
//! the interrupt handler (as opposed to the very last action) to avoid
//! returning from the interrupt handler before the interrupt source is
//! actually cleared. Failure to do so may result in the interrupt handler
//! being immediately reentered (because the interrupt controller still sees
//! the interrupt source asserted).
//!
//! \return None.
//
//*****************************************************************************
void
FlashIntClear(unsigned long ulIntFlags)
{
//
// Clear the flash interrupt.
//
HWREG(FLASH_CONTROL_BASE + FLASH_CTRL_O_FCMISC) = ulIntFlags;
}
//*****************************************************************************
//
// Close the Doxygen group.
//! @}
//
//*****************************************************************************