S32K3XX Progmem dataflash with littlefs support

This commit is contained in:
Peter van der Perk 2022-12-23 14:24:31 +01:00 committed by Xiang Xiao
parent c8cf27e848
commit aac9eb57e8
7 changed files with 596 additions and 0 deletions

View File

@ -600,6 +600,14 @@ config S32K3XX_FS26
Engineering development purpose only, not for use in production.
Please refer to the FS26 Datasheet.
config S32K3XX_PROGMEM
bool "PROGMEM"
default n
select ARCH_HAVE_PROGMEM
---help---
Use the Data Flash data memory as a
Memory-Technology-Device (MTD).
endmenu # S32K3XX Peripheral Selection
menu "S32K3XX eMIOS PWM Configuration"

View File

@ -76,6 +76,10 @@ ifeq ($(CONFIG_S32K3XX_QSPI),y)
CHIP_CSRCS += s32k3xx_qspi.c
endif
ifeq ($(CONFIG_S32K3XX_PROGMEM),y)
CHIP_CSRCS += s32k3xx_progmem.c
endif
ifeq ($(CONFIG_BUILD_PROTECTED),y)
CHIP_CSRCS += s32k3xx_userspace.c
endif

View File

@ -118,6 +118,12 @@
#define S32K3XX_PFLASH_PFCBLK3_LOCKMASTER_SS1_OFFSET (0x04b8) /* Block 3 Lock Master Super Sector 1 (PFCBLK3_LOCKMASTER_SS1) */
#define S32K3XX_PFLASH_PFCBLK3_LOCKMASTER_SS2_OFFSET (0x04bc) /* Block 3 Lock Master Super Sector 2 (PFCBLK3_LOCKMASTER_SS2) */
/* Flash Management Unit Register Offsets ***********************************/
#define S32K3XX_FMU_MCR_OFFSET (0x0000) /* Module Configuration (MCR) */
#define S32K3XX_FMU_MCRS_OFFSET (0x0004) /* Module Configuration Status (MCRS) */
#define S32K3XX_FMU_PD_OFFSET (0x0100) /* Program data (PD) */
/* PFLASH Register Addresses ************************************************/
#define S32K3XX_PFLASH_PFCR0 (S32K3XX_PFLASH_BASE + S32K3XX_PFLASH_PFCR0_OFFSET)
@ -203,6 +209,12 @@
#define S32K3XX_PFLASH_PFCBLK3_LOCKMASTER_SS1 (S32K3XX_PFLASH_BASE + S32K3XX_PFLASH_PFCBLK3_LOCKMASTER_SS1_OFFSET)
#define S32K3XX_PFLASH_PFCBLK3_LOCKMASTER_SS2 (S32K3XX_PFLASH_BASE + S32K3XX_PFLASH_PFCBLK3_LOCKMASTER_SS2_OFFSET)
/* Flash Management Unit Register Addresses *********************************/
#define S32K3XX_FMU_MCR (S32K3XX_FMU_BASE + S32K3XX_FMU_MCR_OFFSET)
#define S32K3XX_FMU_MCRS (S32K3XX_FMU_BASE + S32K3XX_FMU_MCRS_OFFSET)
#define S32K3XX_FMU_PD(n) (S32K3XX_FMU_BASE + S32K3XX_FMU_PD_OFFSET + (n * 4))
/* PFLASH Register Bitfield Definitions *************************************/
/* Platform Flash Memory Configuration Register 0 (PFCR0) */
@ -347,4 +359,33 @@
#define PFLASH_PFCBLK_LOCKMASTER_SS_SHIFT (0) /* Bits 0-31: Block n Lock Master Super Sector m (LOCKMASTER_SS) */
#define PFLASH_PFCBLK_LOCKMASTER_SS_MASK (0xffffffff << PFLASH_PFCBLK_LOCKMASTER_SS_SHIFT)
/* Flash Management Unit Bitfield Definitions *******************************/
/* Module Configuration (MCR) */
#define FMU_MCR_EHV_SHIFT (0) /* Bit 0: Enable High Voltage (EHV) */
#define FMU_MCR_EHV_MASK (0x01 << FMU_MCR_EHV_SHIFT)
#define FMU_MCR_ERS_SHIFT (4) /* Bit 4: Erase (ERS) */
#define FMU_MCR_ERS_MASK (0x01 << FMU_MCR_ERS_SHIFT)
#define FMU_MCR_ESS_SHIFT (5) /* Bit 5: Erase Size Select (ESS) */
#define FMU_MCR_ESS_MASK (0x01 << FMU_MCR_ESS_SHIFT)
#define FMU_MCR_PGM_SHIFT (8) /* Bit 8: Program (PGM) */
#define FMU_MCR_PGM_MASK (0x01 << FMU_MCR_PGM_SHIFT)
#define FMU_MCR_WDIE_SHIFT (12) /* Bit 12: Watch Dog Interrupt Enable (WDIE) */
#define FMU_MCR_WDIE_MASK (0x01 << FMU_MCR_WDIE_SHIFT)
#define FMU_MCR_PECIE_SHIFT (15) /* Bit 15: Program/Erase Complete Interrupt Enable (PECIE) */
#define FMU_MCR_PECIE_MASK (0x01 << FMU_MCR_PECIE_SHIFT)
#define FMU_MCR_PEID_SHIFT (16) /* Bit 16-23: Program and Erase Master/Domain ID (PEID) */
#define FMU_MCR_PEID_MASK (0xFF << FMU_MCR_PEID_SHIFT)
#define FMU_MCR_PEID(n) ((n & FMU_MCR_PEID_MASK) >> FMU_MCR_PEID_SHIFT)
#define FMU_MCRS_PEG_SHIFT (14) /* Bit 14: Program/Erase Good (PEG) */
#define FMU_MCRS_PEG_MASK (0x01 << FMU_MCRS_PEG_SHIFT)
#define FMU_MCRS_DONE_SHIFT (15) /* Bit 15: State machine status (DONE) */
#define FMU_MCRS_DONE_MASK (0x01 << FMU_MCRS_DONE_SHIFT)
#define FMU_MCRS_PES_SHIFT (16) /* Bit 16: Program and erase Protection error (PEP) */
#define FMU_MCRS_PES_MASK (0x01 << FMU_MCRS_PES_SHIFT)
#define FMU_MCRS_PEP_SHIFT (17) /* Bit 17: Program and erase sequence error (PES) */
#define FMU_MCRS_PEP_MASK (0x01 << FMU_MCRS_PEP_SHIFT)
#endif /* __ARCH_ARM_SRC_S32K3XX_HARDWARE_S32K3XX_PFLASH_H */

View File

@ -0,0 +1,423 @@
/****************************************************************************
* arch/arm/src/s32k3xx/s32k3xx_progmem.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/* Copyright 2022 NXP */
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <stdint.h>
#include <assert.h>
#include <errno.h>
#include "hardware/s32k3xx_pflash.h"
#include "hardware/s32k3xx_xrdc.h"
#include "s32k3xx_config.h"
#include "s32k3xx_progmem.h"
#include "arm_internal.h"
#include <arch/board/board.h> /* Include last: has dependencies */
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define min(a,b) \
({ __typeof__ (a) _a = (a); \
__typeof__ (b) _b = (b); \
_a < _b ? _a : _b; })
/****************************************************************************
* Private Functions
****************************************************************************/
uint32_t get_sector(uint32_t address)
{
if ((address >= S32K3XX_PROGMEM_START_ADDR) &&
(address <= S32K3XX_PROGMEM_END_ADDR))
{
/* The address is from the data sectors */
return ((address - (uint32_t)S32K3XX_PROGMEM_START_ADDR) /
S32K3XX_PROGMEM_SECTOR_SIZE);
}
}
void data_sector_lock(uint32_t sector, uint32_t lock)
{
uint32_t regval;
regval = getreg32(S32K3XX_PFLASH_PFCBLK4_SPELOCK);
if (lock)
{
regval |= (1 << sector);
}
else
{
regval &= ~(1 << sector);
}
putreg32(regval, S32K3XX_PFLASH_PFCBLK4_SPELOCK);
}
uint32_t execute_flash_sequence(uint32_t mask)
{
uint32_t regval;
uint32_t status;
/* Set Mask bit */
regval = getreg32(S32K3XX_FMU_MCR);
regval |= mask;
putreg32(regval, S32K3XX_FMU_MCR);
/* Set EHV bit to start program operation */
regval |= FMU_MCR_EHV_MASK;
putreg32(regval, S32K3XX_FMU_MCR);
/* Wait for MCRS Done */
do
{
regval = getreg32(S32K3XX_FMU_MCRS);
}
while ((regval & FMU_MCRS_DONE_MASK) != FMU_MCRS_DONE_MASK);
regval = getreg32(S32K3XX_FMU_MCR);
regval &= ~FMU_MCR_EHV_MASK;
putreg32(regval, S32K3XX_FMU_MCR);
status = getreg32(S32K3XX_FMU_MCRS);
regval &= ~mask;
putreg32(regval, S32K3XX_FMU_MCR);
return status;
}
void execute_init_sequence(uint32_t addr)
{
uint8_t domain_id = getreg8(S32K3XX_XRDC_HWCFG1);
do
{
putreg32(addr, S32K3XX_PFLASH_PFCPGM_PEADR_L);
}
while (FMU_MCR_PEID(getreg32(S32K3XX_FMU_MCR)) != domain_id);
putreg32((FMU_MCRS_PES_SHIFT | FMU_MCRS_PEP_SHIFT), S32K3XX_FMU_MCRS);
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: up_progmem_neraseblocks
*
* Description:
* Return number of erase blocks
*
****************************************************************************/
size_t up_progmem_neraseblocks(void)
{
return S32K3XX_PROGMEM_SECTOR_COUNT;
}
/****************************************************************************
* Name: up_progmem_isuniform
*
* Description:
* Is program memory uniform or page size differs?
*
****************************************************************************/
bool up_progmem_isuniform(void)
{
return true;
}
/****************************************************************************
* Name: up_progmem_pagesize
*
* Description:
* Return read/write page size
*
****************************************************************************/
size_t up_progmem_pagesize(size_t page)
{
return (size_t)S32K3XX_PROGMEM_PAGE_SIZE;
}
/****************************************************************************
* Name: up_progmem_erasesize
*
* Description:
* Return erase block size
*
****************************************************************************/
size_t up_progmem_erasesize(size_t block)
{
return (size_t)S32K3XX_PROGMEM_BLOCK_SECTOR_SIZE;
}
/****************************************************************************
* Name: up_progmem_getpage
*
* Description:
* Address to read/write page conversion
*
* Input Parameters:
* addr - Address with or without flash offset (absolute or aligned to
* page0)
*
* Returned Value:
* Page or negative value on error. The following errors are reported
* (errno is not set!):
*
* -EFAULT: On invalid address
*
****************************************************************************/
ssize_t up_progmem_getpage(size_t addr)
{
if (addr >= S32K3XX_PROGMEM_START_ADDR)
{
addr -= S32K3XX_PROGMEM_START_ADDR;
}
return (size_t)(addr / S32K3XX_PROGMEM_PAGE_SIZE);
}
/****************************************************************************
* Name: up_progmem_getaddress
*
* Description:
* Read/write page to address conversion
*
* Input Parameters:
* page - page index
*
* Returned Value:
* Base address of given page, SIZE_MAX if page index is not valid.
*
****************************************************************************/
size_t up_progmem_getaddress(size_t page)
{
return (size_t)(S32K3XX_PROGMEM_START_ADDR
+ (page * S32K3XX_PROGMEM_PAGE_SIZE));
}
/****************************************************************************
* Name: up_progmem_eraseblock
*
* Description:
* Erase selected block.
*
* Input Parameters:
* block - The erase block index to be erased.
*
* Returned Value:
* block size or negative value on error. The following errors are
* reported (errno is not set!):
*
* -EFAULT: On invalid page
* -EIO: On unsuccessful erase
* -EROFS: On access to write protected area
* -EACCES: Insufficient permissions (read/write protected)
* -EPERM: If operation is not permitted due to some other constraints
* (i.e. some internal block is not running etc.)
*
****************************************************************************/
ssize_t up_progmem_eraseblock(size_t block)
{
uint32_t dest;
dest = (block * S32K3XX_PROGMEM_BLOCK_SECTOR_SIZE +
S32K3XX_PROGMEM_START_ADDR);
data_sector_lock(get_sector(dest), 0);
execute_init_sequence(dest);
putreg32(0x0, S32K3XX_FMU_PD(0));
execute_flash_sequence(FMU_MCR_ERS_MASK);
putreg32(0xffffffff, S32K3XX_FMU_PD(0));
return (ssize_t)S32K3XX_PROGMEM_BLOCK_SECTOR_SIZE;
}
/****************************************************************************
* Name: up_progmem_ispageerased
*
* Description:
* Checks whether page is erased
*
* Input Parameters:
* page - The erase page index to be checked.
*
* Returned Value:
* Returns number of bytes NOT erased or negative value on error. If it
* returns zero then complete page is erased.
*
* The following errors are reported:
* -EFAULT: On invalid page
*
****************************************************************************/
ssize_t up_progmem_ispageerased(size_t page)
{
const uint8_t *p;
int i;
if (page >= S32K3XX_PROGMEM_PAGE_COUNT)
{
return -EFAULT;
}
p = (const uint8_t *)up_progmem_getaddress(page);
for (i = 0; i < S32K3XX_PROGMEM_PAGE_SIZE; i++)
{
if (p[i] != S32K3XX_PROGMEM_ERASEDVAL)
{
break;
}
}
return (ssize_t)(S32K3XX_PROGMEM_PAGE_SIZE - i);
}
/****************************************************************************
* Name: up_progmem_write
*
* Description:
* Program data at given address
*
* Note: this function is not limited to single page and nor it requires
* the address be aligned inside the page boundaries.
*
* Input Parameters:
* addr - Address with or without flash offset
* buf - Pointer to buffer
* count - Number of bytes to write
*
* Returned Value:
* Bytes written or negative value on error. The following errors are
* reported (errno is not set!)
*
* EINVAL: If count is not aligned with the flash boundaries (i.e.
* some MCU's require per half-word or even word access)
* EFAULT: On invalid address
* EIO: On unsuccessful write, do note when this occurs the complete
* flash sector is deemed to be unreadable and a read will most
* likely result in a hard fault.
* EROFS: On access to write protected area
* EACCES: Insufficient permissions (read/write protected)
* EPERM: If operation is not permitted due to some other constraints
* (i.e. some internal block is not running etc.)
*
****************************************************************************/
ssize_t up_progmem_write(size_t addr, const void *buf, size_t count)
{
uint32_t i;
uint32_t dest;
uint32_t offset;
uint32_t *p_offset;
uint32_t *p_src;
size_t words_to_write;
if (count % S32K3XX_PROGMEM_DFLASH_WRITE_UNIT_SIZE != 0)
{
return -EINVAL;
}
offset = addr % S32K3XX_PROGMEM_WRITE_SIZE;
dest = addr - offset;
words_to_write = ((count + offset) / 4);
if (offset > 0)
{
p_offset = (uint32_t *)(dest);
}
p_src = (uint32_t *)(buf);
data_sector_lock(get_sector(dest), 0);
while (words_to_write > 0)
{
/* Destination address */
execute_init_sequence(dest);
for (i = 0; i < min(32, words_to_write); i++)
{
if (offset > 0)
{
putreg32(*p_offset++, S32K3XX_FMU_PD(i));
offset = offset - 4;
}
else
{
putreg32(*p_src++, S32K3XX_FMU_PD(i));
}
}
words_to_write = words_to_write - i;
execute_flash_sequence(FMU_MCR_PGM_MASK);
}
return count;
}
/****************************************************************************
* Name: up_progmem_erasestate
*
* Description:
* Return value of erase state.
*
****************************************************************************/
uint8_t up_progmem_erasestate(void)
{
return S32K3XX_PROGMEM_ERASEDVAL;
}
void s32k3xx_progmem_init(void)
{
/* Disable D-Flash ECC for back-to-back flash writes */
putreg32(PFLASH_PFCR4_DERR_SUP, S32K3XX_PFLASH_PFCR4);
}

View File

@ -0,0 +1,77 @@
/****************************************************************************
* arch/arm/src/s32k3xx/s32k3xx_progmem.h
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/* Copyright 2022 NXP */
#ifndef __ARCH_ARM_SRC_S32K3XX_S32K3XX_PROGMEM_H
#define __ARCH_ARM_SRC_S32K3XX_S32K3XX_PROGMEM_H
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <nuttx/compiler.h>
#include <sys/types.h>
#include <stdint.h>
#include <stdbool.h>
#include "arm_internal.h"
#include "s32k3xx_config.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define DFLASH_SIZE 128
/* Base address of the flash segment used for progmem. */
#define S32K3XX_PROGMEM_START_ADDR 0x10000000
#define S32K3XX_PROGMEM_END_ADDR 0x1003FFFF
#define S32K3XX_PROGMEM_BLOCK_COUNT 1
#define S32K3XX_PROGMEM_BLOCK_SIZE DFLASH_SIZE * 1024
#define S32K3XX_PROGMEM_BLOCK_SECTOR_SIZE 8192
#define S32K3XX_PROGMEM_PAGE_SIZE 8
#define S32K3XX_PROGMEM_WRITE_SIZE 128
#define S32K3XX_PROGMEM_SECTOR_SIZE 8192
#define S32K3XX_PROGMEM_SECTOR_COUNT S32K3XX_PROGMEM_BLOCK_SIZE / S32K3XX_PROGMEM_BLOCK_SECTOR_SIZE
#define S32K3XX_PROGMEM_PAGE_COUNT (S32K3XX_PROGMEM_BLOCK_SIZE / S32K3XX_PROGMEM_PAGE_SIZE)
#define S32K3XX_PROGMEM_DFLASH_WRITE_UNIT_SIZE 4
#define S32K3XX_PROGMEM_ERASEDVAL (0xffu)
/****************************************************************************
* Public Function Prototypes
****************************************************************************/
void s32k3xx_progmem_init(void);
#endif /* __ARCH_ARM_SRC_S32K3XX_S32K3XX_PROGMEM_H */

View File

@ -28,6 +28,7 @@ CONFIG_BOARD_LOOPSPERMSEC=14539
CONFIG_BUILTIN=y
CONFIG_CANUTILS_CANDUMP=y
CONFIG_CANUTILS_CANSEND=y
CONFIG_ETH0_PHY_TJA1103=y
CONFIG_EXAMPLES_BUTTONS=y
CONFIG_EXAMPLES_HELLO=y
CONFIG_EXAMPLES_TELNETD=y
@ -55,6 +56,7 @@ CONFIG_LPUART2_SERIAL_CONSOLE=y
CONFIG_MM_REGIONS=2
CONFIG_MTD=y
CONFIG_MTD_MX25RXX=y
CONFIG_MTD_PROGMEM=y
CONFIG_MX25RXX_LXX=y
CONFIG_NET=y
CONFIG_NETDEV_IFINDEX=y
@ -100,6 +102,7 @@ CONFIG_S32K3XX_LPUART14=y
CONFIG_S32K3XX_LPUART1=y
CONFIG_S32K3XX_LPUART2=y
CONFIG_S32K3XX_LPUART9=y
CONFIG_S32K3XX_PROGMEM=y
CONFIG_S32K3XX_QSPI=y
CONFIG_S32K3XX_TJA1153=y
CONFIG_SCHED_LPWORK=y

View File

@ -163,6 +163,46 @@ int s32k3xx_bringup(void)
}
#endif
#ifdef CONFIG_S32K3XX_PROGMEM
struct mtd_dev_s *mtd;
mtd = progmem_initialize();
if (mtd == NULL)
{
syslog(LOG_ERR, "ERROR: progmem_initialize() failed\n");
}
ret = register_mtddriver("/dev/progmem0", mtd, 0755, NULL);
if (ret != OK)
{
_err("register_mtddriver() failed: %d\n", ret);
}
# ifdef CONFIG_FS_LITTLEFS
else
{
_info("register_mtddriver() succesful\n");
ret = nx_mount("/dev/progmem0", "/mnt/progmem", "littlefs", 0, NULL);
if (ret < 0)
{
ret = nx_mount("/dev/progmem0", "/mnt/progmem", "littlefs", 0,
"forceformat");
if (ret < 0)
{
_err("nx_mount() failed: %d\n", ret);
}
else
{
_info("nx_mount() succesful\n");
}
}
}
# endif
#endif
#ifdef HAVE_MX25L
/* Create an instance of the S32K3XX QSPI device driver */