blob: 32f843b546192639d01cb0766fe2f0f60ff4b36c [file] [log] [blame]
/*
* Copyright (c) 2019, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. 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.
* 3. Neither the name of the copyright holder 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 HOLDER 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.
*/
#include "openthread/platform/flash.h"
#include "fsl_device_registers.h"
#include "fsl_flash.h"
#include "openthread-core-config.h"
#include <utils/code_utils.h>
#include "openthread/platform/alarm-milli.h"
#define USE_MEM_COPY_FOR_READ 0
#define NUMBER_OF_INTEGERS 4
#define ONE_READ 1
#define BYTES_IN_ONE_READ (NUMBER_OF_INTEGERS * sizeof(uint32_t))
#define NORMAL_READ_MODE 0
#define ONE_PAGE 1
#define BYTES_ALINGMENT 16
uint8_t pageBuffer[FLASH_PAGE_SIZE] __attribute__((aligned(4))) = {0};
static uint32_t sNvFlashStartAddr;
static uint32_t sNvFlashEndAddr;
static bool mapToNvFlashAddress(uint32_t *aAddress);
static void copyFromFlash(uint8_t *pDst, uint8_t *pSrc, uint32_t cBytes);
static uint32_t blankCheckAndErase(uint8_t *pageAddr);
void otPlatFlashInit(otInstance *aInstance)
{
OT_UNUSED_VARIABLE(aInstance);
extern uint32_t __nv_storage_start_address;
extern uint32_t __nv_storage_end_address;
FLASH_Init(FLASH);
sNvFlashStartAddr = (uint32_t)&__nv_storage_start_address;
sNvFlashEndAddr = (uint32_t)&__nv_storage_end_address;
}
otError utilsFlashErasePage(uint32_t aAddress)
{
otError error = OT_ERROR_INVALID_ARGS;
status_t status;
uint32_t address = aAddress;
/* Map address to NV Flash space and check boundaries */
if (mapToNvFlashAddress(&address))
{
/* If address is aligned to page size */
if ((address % FLASH_PAGE_SIZE) == 0)
{
error = OT_ERROR_NONE;
status = blankCheckAndErase((uint8_t *)address);
otEXPECT_ACTION((status & FLASH_DONE), error = OT_ERROR_FAILED);
}
}
exit:
return error;
}
void otPlatFlashWrite(otInstance *aInstance, uint8_t aSwapIndex, uint32_t aOffset, const void *aData, uint32_t aSize)
{
uint32_t result = 0;
status_t status;
uint32_t address = aOffset;
uint32_t alignAddr;
uint32_t bytes;
OT_UNUSED_VARIABLE(aInstance);
OT_UNUSED_VARIABLE(aSwapIndex);
/* Map address to NV Flash space and check boundaries */
if (mapToNvFlashAddress(&address))
{
/* Check to see if data is written outside NV Flash space */
if ((address + aSize) <= sNvFlashEndAddr)
{
alignAddr = address - (address % FLASH_PAGE_SIZE);
bytes = (address - alignAddr);
result = aSize;
if (bytes)
{
uint32_t unalignedBytes = FLASH_PAGE_SIZE - bytes;
if (unalignedBytes > aSize)
{
unalignedBytes = aSize;
}
copyFromFlash(pageBuffer, (void *)alignAddr, FLASH_PAGE_SIZE);
memcpy(&pageBuffer[bytes], aData, unalignedBytes);
status = blankCheckAndErase((uint8_t *)alignAddr);
otEXPECT_ACTION((status & FLASH_DONE), result = 0);
status = FLASH_Program(FLASH, (uint32_t *)alignAddr, (uint32_t *)pageBuffer, FLASH_PAGE_SIZE);
otEXPECT_ACTION((status & FLASH_DONE), result = 0);
address += unalignedBytes;
/* if size is less than the distance to the end of program block
unalignedBytes has been shrunk , after size is decremented it will become 0 */
aData += unalignedBytes;
aSize -= unalignedBytes;
}
bytes = aSize & ~(FLASH_PAGE_SIZE - 1U);
/* Now dest is on an aligned boundary */
/* bytes is an integer number of program blocks (pages) */
while (bytes)
{
status = blankCheckAndErase((uint8_t *)address);
otEXPECT_ACTION((status & FLASH_DONE), result = 0);
status = FLASH_Program(FLASH, (uint32_t *)address, (uint32_t *)aData, FLASH_PAGE_SIZE);
otEXPECT_ACTION((status & FLASH_DONE), result = 0);
address += FLASH_PAGE_SIZE;
aData += FLASH_PAGE_SIZE;
aSize -= FLASH_PAGE_SIZE;
bytes -= FLASH_PAGE_SIZE;
}
/* dest is still aligned because we have increased it by a multiple of the program block (page) */
if (aSize)
{
status = blankCheckAndErase((uint8_t *)address);
otEXPECT_ACTION((status & FLASH_DONE), result = 0);
status = FLASH_Program(FLASH, (uint32_t *)address, (uint32_t *)aData, aSize);
otEXPECT_ACTION((status & FLASH_DONE), result = 0);
}
}
}
exit:
/* There are times when the result != 0.
* Use this workaround until we replace the flash code with the Packet Data Manager.
*/
if (result)
{
result = 0;
}
}
void otPlatFlashRead(otInstance *aInstance, uint8_t aSwapIndex, uint32_t aOffset, void *aData, uint32_t aSize)
{
uint32_t address = aOffset;
OT_UNUSED_VARIABLE(aInstance);
OT_UNUSED_VARIABLE(aSwapIndex);
/* Map address to NV Flash space and check boundaries */
if (mapToNvFlashAddress(&address))
{
/* Check to see if data is read outside NV Flash space */
if ((address + aSize) <= sNvFlashEndAddr)
{
copyFromFlash(aData, (uint8_t *)address, aSize);
}
}
}
static bool mapToNvFlashAddress(uint32_t *aAddress)
{
bool status = true;
uint32_t address = *aAddress + sNvFlashStartAddr;
if ((address < sNvFlashStartAddr) || (address > sNvFlashEndAddr))
{
status = false;
}
else
{
*aAddress = address;
}
return status;
}
uint32_t blankCheckAndErase(uint8_t *pageAddr)
{
uint32_t status = FLASH_BlankCheck(FLASH, pageAddr, pageAddr + FLASH_PAGE_SIZE - 1);
if (status & FLASH_FAIL)
{
status = FLASH_Erase(FLASH, pageAddr, (pageAddr + FLASH_PAGE_SIZE - 1));
}
else
{
status = FLASH_DONE;
}
return status;
}
static void copyFromFlash(uint8_t *pDst, uint8_t *pSrc, uint32_t cBytes)
{
#if !USE_MEM_COPY_FOR_READ
uint32_t nbOfReads;
uint32_t aligningOffset;
uint32_t temp[NUMBER_OF_INTEGERS];
uint32_t bytesLeft = cBytes;
uint32_t bytesToRead = 0;
/* Flash driver reads 16 bytes in one run, so calculating the number of reads from Flash
is needed */
nbOfReads = cBytes / BYTES_IN_ONE_READ;
if (cBytes % BYTES_IN_ONE_READ)
{
nbOfReads++;
}
/* calculate aligning offset -> the number of bytes from a 16 byte aligned address the
read address is located */
aligningOffset = (uint32_t)pSrc % BYTES_ALINGMENT;
for (uint32_t i = 0; i < nbOfReads; i++)
{
/* Read from Flash */
FLASH_Read(FLASH, (uint8_t *)(pSrc - aligningOffset + i * BYTES_IN_ONE_READ), NORMAL_READ_MODE,
(uint32_t *)temp);
if (0 == i)
{
bytesToRead =
(bytesLeft < BYTES_IN_ONE_READ - aligningOffset) ? bytesLeft : BYTES_IN_ONE_READ - aligningOffset;
/* first read must take into account align offset */
memcpy((void *)pDst, (void *)temp + aligningOffset, bytesToRead);
bytesLeft -= bytesToRead;
pDst += bytesToRead;
}
else
{
bytesToRead = (bytesLeft < BYTES_IN_ONE_READ) ? bytesLeft : BYTES_IN_ONE_READ;
memcpy((void *)pDst, (void *)temp, bytesToRead);
bytesLeft -= bytesToRead;
pDst += bytesToRead;
}
}
#else
while (cBytes)
{
*(pDst) = *(pSrc);
pDst = pDst + 1;
pSrc = pSrc + 1;
cBytes--;
}
#endif
}