Select Git revision
Forked from
card10 / firmware
Source project has a limited visibility.
flc.c 17.59 KiB
/**
* @file flc.h
* @brief Flash Controler driver.
* @details This driver can be used to operate on the embedded flash memory.
*/
/* ****************************************************************************
* Copyright (C) 2016 Maxim Integrated Products, Inc., All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES
* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Except as contained in this notice, the name of Maxim Integrated
* Products, Inc. shall not be used except as stated in the Maxim Integrated
* Products, Inc. Branding Policy.
*
* The mere transfer of this software does not imply any licenses
* of trade secrets, proprietary technology, copyrights, patents,
* trademarks, maskwork rights, or any other form of intellectual
* property whatsoever. Maxim Integrated Products, Inc. retains all
* ownership rights.
*
* $Date: 2018-10-15 21:49:29 +0000 (Mon, 15 Oct 2018) $
* $Revision: 38520 $
*
*************************************************************************** */
/* **** Includes **** */
#include <string.h>
#include "mxc_config.h"
#include "mxc_assert.h"
#include "mxc_sys.h"
#include "flc.h"
#include "flc_regs.h"
#include "mcr_regs.h" // For ECCEN registers.
/**
* @ingroup flc
* @{
*/
/* **** Definitions **** */
/* **** Globals **** */
/* **** Functions **** */
//******************************************************************************
#if IAR_PRAGMAS
#pragma section=".flashprog"
#else
__attribute__((section(".flashprog")))
#endif
static int busy_flc(mxc_flc_regs_t *flc)
{
return (flc->cn & (MXC_F_FLC_CN_WR | MXC_F_FLC_CN_ME | MXC_F_FLC_CN_PGE));
}
//******************************************************************************
#if IAR_PRAGMAS
#pragma section=".flashprog"
#else
__attribute__((section(".flashprog")))
#endif
static int prepare_flc(mxc_flc_regs_t *flc)
{
/* Check if the flash controller is busy */
if (busy_flc(flc)) {
return E_BUSY;
}
// Set flash clock divider to generate a 1MHz clock from the APB clock
flc->clkdiv = SystemCoreClock / 1000000;
/* Clear stale errors */
if (flc->intr & MXC_F_FLC_INTR_AF) {
flc->intr &= ~MXC_F_FLC_INTR_AF;
}
/* Unlock flash */
flc->cn = (flc->cn & ~MXC_F_FLC_CN_UNLOCK) | MXC_S_FLC_CN_UNLOCK_UNLOCKED;
return E_NO_ERROR;
}
//******************************************************************************
#if IAR_PRAGMAS
#pragma section=".flashprog"
#else
__attribute__((section(".flashprog")))
#endif
// Length is number of 32-bit words
static int verify_data(uint32_t address, uint32_t length, uint32_t * data)
{
volatile uint32_t * ptr;
for (ptr = (uint32_t*)address; ptr < (((uint32_t*)(address)) + length); ptr++, data++) {
if (*ptr != *data) {
return E_BAD_STATE;
}
}
return E_NO_ERROR;
}
//******************************************************************************
#if IAR_PRAGMAS
// IAR memory section declaration for the in-system flash programming functions to be loaded in RAM.
#pragma section=".flashprog"
#else
__attribute__((section(".flashprog")))
#endif
int FLC_Init(const sys_cfg_t *sys_cfg)
{
return SYS_FLC_Init(sys_cfg);
}
//******************************************************************************
#if IAR_PRAGMAS
// IAR memory section declaration for the in-system flash programming functions to be loaded in RAM.
#pragma section=".flashprog"
#else
__attribute__((section(".flashprog")))
#endif
int FLC_Busy(void)
{
uint32_t flc_cn = 0;
int i;
mxc_flc_regs_t *flc;
for (i = 0; i < MXC_FLC_INSTANCES; i++) {
flc = MXC_FLC_GET_FLC(i);
flc_cn = busy_flc(flc);
if (flc_cn != 0) {
break;
}
}
return flc_cn;
}
//******************************************************************************
#if IAR_PRAGMAS
#pragma section=".flashprog"
#else
__attribute__((section(".flashprog")))
#endif
int FLC_MassErase(void)
{
int err,i;
mxc_flc_regs_t *flc;
for (i=0; i<MXC_FLC_INSTANCES; i++) {
flc = MXC_FLC_GET_FLC(i);
if ((err = prepare_flc(flc)) != E_NO_ERROR) {
return err;
}
/* Write mass erase code */
flc->cn = (flc->cn & ~MXC_F_FLC_CN_ERASE_CODE) | MXC_S_FLC_CN_ERASE_CODE_ERASEALL;
/* Issue mass erase command */
flc->cn |= MXC_F_FLC_CN_ME;
/* Wait until flash operation is complete */
while (busy_flc(flc));
/* Lock flash */
flc->cn &= ~MXC_F_FLC_CN_UNLOCK;
/* Check access violations */
if (flc->intr & MXC_F_FLC_INTR_AF) {
flc->intr &= ~MXC_F_FLC_INTR_AF;
return E_BAD_STATE;
}
// Flush cache
SYS_Flash_Operation();
}
return E_NO_ERROR;
}
//******************************************************************************
#if IAR_PRAGMAS
#pragma section=".flashprog"
#else
__attribute__((section(".flashprog")))
#endif
int FLC_PageErase(uint32_t address)
{
int err;
uint32_t addr;
mxc_flc_regs_t *flc = NULL;
// Get FLC Instance
if ((err = SYS_FLC_GetByAddress(&flc, address)) != E_NO_ERROR) {
return err;
}
if ((err = SYS_FLC_GetPhysicalAddress(address, &addr)) < E_NO_ERROR) {
return err;
}
if ((err = prepare_flc(flc)) != E_NO_ERROR) {
return err;
}
// Align address on page boundary
address = address - (address % MXC_FLASH_PAGE_SIZE);
/* Write page erase code */
flc->cn = (flc->cn & ~MXC_F_FLC_CN_ERASE_CODE) | MXC_S_FLC_CN_ERASE_CODE_ERASEPAGE;
/* Issue page erase command */
flc->addr = addr;
flc->cn |= MXC_F_FLC_CN_PGE;
/* Wait until flash operation is complete */
while (FLC_Busy());
/* Lock flash */
flc->cn &= ~MXC_F_FLC_CN_UNLOCK;
/* Check access violations */
if (flc->intr & MXC_F_FLC_INTR_AF) {
flc->intr &= ~MXC_F_FLC_INTR_AF;
return E_BAD_STATE;
}
// Flush the cache
SYS_Flash_Operation();
return E_NO_ERROR;
}
//******************************************************************************
#if IAR_PRAGMAS
#pragma section=".flashprog"
#else
__attribute__((section(".flashprog")))
#endif
int FLC_MultiPageErase(uint32_t start, uint32_t end)
{
int retval;
uint32_t addr;
// Align start and end on page boundaries
start = start - (start % MXC_FLASH_PAGE_SIZE);
end = end - (end % MXC_FLASH_PAGE_SIZE);
for (addr = start; addr <= end; addr += MXC_FLASH_PAGE_SIZE) {
retval = FLC_PageErase(addr);
if (retval != E_NO_ERROR) {
return retval;
}
}
return E_NO_ERROR;
}
//******************************************************************************
#if IAR_PRAGMAS
#pragma section=".flashprog"
#else
__attribute__((section(".flashprog")))
#endif
int FLC_Erase(uint32_t start, uint32_t end, uint32_t *buffer, unsigned length)
{
int retval;
uint32_t start_align, start_len, end_align, end_len;
MXC_ASSERT(buffer);
// Align start and end on page boundaries, calculate length of data to buffer
start_align = start - (start % MXC_FLASH_PAGE_SIZE);
start_len = (start % MXC_FLASH_PAGE_SIZE);
end_align = end - (end % MXC_FLASH_PAGE_SIZE);
end_len = MXC_FLASH_PAGE_SIZE - (end % MXC_FLASH_PAGE_SIZE);
// Make sure the length of buffer is sufficient
if ((length < start_len) || (length < end_len)) {
return E_BAD_PARAM;
}
// Start and end address are in the same page
if (start_align == end_align) {
if (length < (start_len + end_len)) {
return E_BAD_PARAM;
}
// Buffer first page data and last page data, erase and write
memcpy(buffer, (void*)start_align, start_len);
memcpy(&buffer[start_len], (void*)end, end_len);
retval = FLC_PageErase(start_align);
if (retval != E_NO_ERROR) {
return retval;
}
retval = FLC_Write(start_align, start_len, buffer);
if (retval != E_NO_ERROR) {
return retval;
}
retval = FLC_Write(end, end_len, &buffer[start_len]);
if (retval != E_NO_ERROR) {
return retval;
}
return E_NO_ERROR;
}
// Buffer, erase, and write the data in the first page
memcpy(buffer, (void*)start_align, start_len);
retval = FLC_PageErase(start_align);
if (retval != E_NO_ERROR) {
return retval;
}
retval = FLC_Write(start_align, start_len, buffer);
if (retval != E_NO_ERROR) {
return retval;
}
// Buffer, erase, and write the data in the last page
memcpy(buffer, (void*)end, end_len);
retval = FLC_PageErase(end_align);
if (retval != E_NO_ERROR) {
return retval;
}
retval = FLC_Write(end, end_len, buffer);
if (retval != E_NO_ERROR) {
return retval;
}
// Erase the remaining pages. MultiPageErase will not erase if start is greater than end.
return FLC_MultiPageErase((start_align + MXC_FLASH_PAGE_SIZE), (end_align - MXC_FLASH_PAGE_SIZE));
}
//******************************************************************************
#if IAR_PRAGMAS
#pragma section=".flashprog"
#else
__attribute__((section(".flashprog")))
#endif
// make sure to disable ICC with ICC_Disable(); before Running this function
int FLC_Write32(uint32_t address, uint32_t data)
{
int err, i=0;
uint32_t addr, byte;
mxc_flc_regs_t *flc = NULL;
uint32_t emptyFlash = 0xffffffff;
volatile uint32_t * ptr;
uint32_t current_data[4] = {0,0,0,0};
if ((MXC_MCR->eccen & MXC_F_MCR_ECCEN_FL0ECCEN)
|| (MXC_MCR->eccen & MXC_F_MCR_ECCEN_FL1ECCEN)) {
return E_BAD_STATE;
}
// Address checked if it is byte addressable
if (address & 0x3) {
return E_BAD_PARAM;
}
if ((err=verify_data(address,1,&emptyFlash))!=E_NO_ERROR) {
return err;
}
// Get byte idx within 128-bit word
byte = (address & 0xf);
// Align address to 128-bit word
address = address & 0xfffffff0;
// Get FLC Instance
if ((err = SYS_FLC_GetByAddress(&flc, address)) != E_NO_ERROR) {
return err;
}
if ((err = SYS_FLC_GetPhysicalAddress(address, &addr)) < E_NO_ERROR) {
return err;
}
if ((err = prepare_flc(flc)) != E_NO_ERROR) {
return err;
}
// Get current data stored in flash
for (ptr = (uint32_t*)address; ptr < (uint32_t*)(address + 16); ptr++, i++) {
current_data[i] = *ptr;
}
// write the data
flc->addr = addr;
if (byte < 4) {
current_data[0] = data;
} else if (byte < 8) {
current_data[1] = data;
} else if (byte < 12) {
current_data[2] = data;
} else {
current_data[3] = data;
}
return FLC_Write128(address,current_data);
}
//******************************************************************************
#if IAR_PRAGMAS
#pragma section=".flashprog"
#else
__attribute__((section(".flashprog")))
#endif
// make sure to disable ICC with ICC_Disable(); before Running this function
int FLC_Write128(uint32_t address, uint32_t *data)
{
int err;
mxc_flc_regs_t *flc = NULL;
uint32_t addr;
// Address checked if it is 128-bit aligned
if (address & 0xF) {
return E_BAD_PARAM;
}
// Get FLC Instance
if ((err = SYS_FLC_GetByAddress(&flc, address)) != E_NO_ERROR) {
return err;
}
if ((err = SYS_FLC_GetPhysicalAddress(address, &addr)) < E_NO_ERROR) {
return err;
}
if ((err = prepare_flc(flc)) != E_NO_ERROR) {
return err;
}
// write 128-bits
flc->cn &= ~MXC_F_FLC_CN_WDTH;
// write the data
flc->addr = addr;
flc->data[0] = data[0];
flc->data[1] = data[1];
flc->data[2] = data[2];
flc->data[3] = data[3];
flc->cn |= MXC_F_FLC_CN_WR;
/* Wait until flash operation is complete */
while (busy_flc(flc));
/* Lock flash */
flc->cn &= ~MXC_F_FLC_CN_UNLOCK;
/* Check access violations */
if (flc->intr & MXC_F_FLC_INTR_AF) {
flc->intr &= ~MXC_F_FLC_INTR_AF;
return E_BAD_STATE;
}
// Flush the cache
SYS_Flash_Operation();
if ((err=verify_data(address,4,data))!=E_NO_ERROR) {
return err;
}
return E_NO_ERROR;
}
//******************************************************************************
#if IAR_PRAGMAS
#pragma section=".flashprog"
#else
__attribute__((section(".flashprog")))
#endif
// make sure to disable ICC with ICC_Disable(); before Running this function
int FLC_Write(uint32_t address, uint32_t length, uint32_t *buffer)
{
int err;
uint32_t bytes_written;
uint8_t current_data[4];
uint32_t *current_data_32 = (uint32_t *)current_data;
// Align the address to a word boundary and read/write if we have to
if (address & 0x3) {
// Figure out how many bytes we have to write to round up the address
bytes_written = 4 - (address & 0x3);
// Save the data currently in the flash
memcpy(current_data, (void*)(address & (~0x3)), 4);
// Modify current_data to insert the data from buffer
memcpy(¤t_data[4-bytes_written], buffer, bytes_written);
// Write the modified data
if ((err = FLC_Write32(address - (address % 4), *current_data_32)) != E_NO_ERROR) {
return err;
}
address += bytes_written;
length -= bytes_written;
//Align the uint32_t buffer with the new address
uint8_t *buffer_unaligned = (uint8_t *)buffer;
buffer_unaligned += bytes_written;
buffer = (uint32_t *)buffer_unaligned;
}
// Align the address to a 4-word (128bit) boundary
while ( (length >= 4) && ((address & 0xF) != 0) ) {
if ((err = FLC_Write32(address, *buffer)) != E_NO_ERROR) {
return err;
}
address += 4;
length -= 4;
buffer += 1;
}
if (length >= 16) {
while (length >= 16) {
if ((err = FLC_Write128(address, buffer)) != E_NO_ERROR) {
return err;
}
address += 16;
length -= 16;
buffer += 4;
}
}
while (length >= 4) {
if ((err = FLC_Write32(address, *buffer)) != E_NO_ERROR) {
return err;
}
address += 4;
length -= 4;
buffer += 1;
}
if (length > 0) {
// Save the data currently in the flash
memcpy(current_data, (void*)(address), 4);
// Modify current_data to insert the data from buffer
memcpy(current_data, buffer, length);
if ((err = FLC_Write32(address, *current_data_32)) != E_NO_ERROR) {
return err;
}
}
return E_NO_ERROR;
}
int FLC_EnableInt(uint32_t mask)
{
mask &= (MXC_F_FLC_INTR_DONEIE | MXC_F_FLC_INTR_AFIE);
if (!mask) {
/* No bits set? Wasn't something we can enable. */
return E_BAD_PARAM;
}
/* Apply enables and write back, preserving the flags */
MXC_FLC0->intr |= mask;
return E_NO_ERROR;
}
int FLC_DisableInt(uint32_t mask)
{
mask &= (MXC_F_FLC_INTR_DONEIE | MXC_F_FLC_INTR_AFIE);
if (!mask) {
/* No bits set? Wasn't something we can disable. */
return E_BAD_PARAM;
}
/* Apply disables and write back, preserving the flags */
MXC_FLC0->intr &= ~mask;
return E_NO_ERROR;
}
int FLC_GetFlags(void)
{
return (MXC_FLC0->intr & (MXC_F_FLC_INTR_DONE | MXC_F_FLC_INTR_AF));
}
int FLC_ClearFlags(uint32_t mask)
{
mask &= (MXC_F_FLC_INTR_DONE | MXC_F_FLC_INTR_AF);
if (!mask) {
/* No bits set? Wasn't something we can clear. */
return E_BAD_PARAM;
}
/* Both flags are write zero clear */
MXC_FLC0->intr ^= mask;
return E_NO_ERROR;
}
int FLC_UnlockInfoBlock(uint32_t address)
{
int err;
mxc_flc_regs_t *flc = NULL;
if ((address < MXC_INFO_MEM_BASE) || (address >= (MXC_INFO_MEM_BASE +(MXC_INFO_MEM_SIZE * 2)))) {
return E_BAD_PARAM;
}
if ((err = SYS_FLC_GetByAddress(&flc, address)) != E_NO_ERROR) {
return err;
}
flc->acntl = 0x3a7f5ca3;
flc->acntl = 0xa1e34f20;
flc->acntl = 0x9608b2c1;
return E_NO_ERROR;
}
int FLC_LockInfoBlock(uint32_t address)
{
int err;
mxc_flc_regs_t *flc = NULL;
if ((address < MXC_INFO_MEM_BASE) || (address >= (MXC_INFO_MEM_BASE +(MXC_INFO_MEM_SIZE * 2)))) {
return E_BAD_PARAM;
}
if ((err = SYS_FLC_GetByAddress(&flc, address)) != E_NO_ERROR) {
return err;
}
flc->acntl = 0xDEADBEEF;
return E_NO_ERROR;
}
/**@} end of group flc */