-
Home
-
doc
-
support
-
driver
-
nvm
nvm_pic32.c
View on Github
/**
* @file nvm_pic32.c
* @author Sebastien CAUX (sebcaux)
* @copyright UniSwarm 2023
*
* @date January 11, 2023, 10:56 AM
*
* @brief NVM (Non Volatile Memory) support drivers for PIC32MK,
* PIC32MX, PIC32MZDA, PIC32MZEC and PIC32MZEF
*
* Implementation based on Microchip documents DS60001121G and DS60001193B:
* https://ww1.microchip.com/downloads/en/DeviceDoc/60001121g.pdf
* https://ww1.microchip.com/downloads/en/DeviceDoc/60001193B.pdf
*/
#include "nvm.h"
#include <archi.h>
#include <string.h>
static void _nvm_processOperation(void);
typedef enum
{
NVM_OP_NOP = 0x0,
NVM_OP_SINGLE_DOUBLE_WORD_PROGRAM = 0x1,
NVM_OP_QUAD_DOUBLE_WORD_PROGRAM = 0x2,
NVM_OP_ROW_PROGRAM = 0x3,
NVM_OP_PAGE_ERASE = 0x4,
NVM_OP_PROGRAM_ERASE = 0x7,
} NVM_OPERATION;
static void _nvm_processOperation(void)
{
// disable interrupts
uint32_t int_flag = disable_interrupt();
#ifdef DMACON
// disable DMA
uint32_t dma_flag = DMACONbits.SUSPEND;
if (dma_flag == 0)
{
DMACONbits.SUSPEND = 1;
while ((DMACONbits.DMABUSY))
{
;
}
}
#endif // DMACON
// <ATOMIC BLOCK
NVMKEY = 0x0U;
NVMKEY = 0xAA996655U;
NVMKEY = 0x556699AAU;
NVMCONSET = _NVMCON_WR_MASK;
// >ATOMIC BLOCK
#ifdef DMACON
// restore DMA
if (dma_flag == 0)
{
DMACONbits.SUSPEND = 0;
}
#endif // DMACON
// restore interrupts
if ((int_flag & 0x00000001) != 0)
{
enable_interrupt();
}
}
ssize_t nvm_read(nvm_addr addr, char *data, size_t size)
{
memcpy(data, KVA0_TO_KVA1(addr), size);
return size;
}
int nvm_writeWords(nvm_addr addr, const uint32_t *data)
{
const uint32_t *ptrData = data;
// Set data, physical address and operation
#if (NVM_WORD_COUNT == 1)
NVMDATA = *(ptrData++);
#else
NVMDATA0 = *(ptrData++);
NVMDATA1 = *(ptrData++);
# if (NVM_WORD_COUNT > 2)
NVMDATA2 = *(ptrData++);
NVMDATA3 = *(ptrData++);
# endif
#endif
NVMADDR = KVA_TO_PA(addr);
NVMCONbits.WREN = 0;
NVMCONbits.NVMOP = NVM_OP_QUAD_DOUBLE_WORD_PROGRAM;
// Enable write
NVMCONbits.WREN = 1;
// Launch write
_nvm_processOperation();
// Wait for finished
while (NVMCONbits.WR != 0)
{
;
}
if ((NVMCON & (_NVMCON_WRERR_MASK | _NVMCON_LVDERR_MASK)) != 0)
{
return -1;
}
return NVM_WORD_COUNT;
}
int nvm_writeRow(nvm_addr addr, const uint32_t *data)
{
// Set data, physical address and operation
NVMSRCADDR = (uint32_t)KVA_TO_PA(data);
NVMADDR = KVA_TO_PA(addr);
NVMCONbits.WREN = 0;
NVMCONbits.NVMOP = NVM_OP_ROW_PROGRAM;
// Enable write
NVMCONbits.WREN = 1;
// Launch write
_nvm_processOperation();
// Do not wait for finished, too long operation
// pool WR outsite
return 0;
}
ssize_t nvm_erasePage(nvm_addr addr)
{
// Set physical address and operation
NVMADDR = KVA_TO_PA(addr);
NVMCONbits.WREN = 0;
NVMCONbits.NVMOP = NVM_OP_PAGE_ERASE;
// Enable write
NVMCONbits.WREN = 1;
// Launch write
_nvm_processOperation();
// Do not wait for finished, too long operation
// pool WR outsite
return 0;
}
bool nvm_ready(void)
{
return (NVMCONbits.WR != 0);
}
void nvm_waitForReady(void)
{
while (NVMCONbits.WR != 0)
{
;
}
}