diff --git a/arch/arm/src/s32k3xx/Kconfig b/arch/arm/src/s32k3xx/Kconfig index fee36a2340..461940b616 100644 --- a/arch/arm/src/s32k3xx/Kconfig +++ b/arch/arm/src/s32k3xx/Kconfig @@ -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" diff --git a/arch/arm/src/s32k3xx/Make.defs b/arch/arm/src/s32k3xx/Make.defs index a54bb2c439..56ea64c92a 100644 --- a/arch/arm/src/s32k3xx/Make.defs +++ b/arch/arm/src/s32k3xx/Make.defs @@ -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 diff --git a/arch/arm/src/s32k3xx/hardware/s32k3xx_pflash.h b/arch/arm/src/s32k3xx/hardware/s32k3xx_pflash.h index efb27d8289..78b1731fee 100644 --- a/arch/arm/src/s32k3xx/hardware/s32k3xx_pflash.h +++ b/arch/arm/src/s32k3xx/hardware/s32k3xx_pflash.h @@ -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 */ diff --git a/arch/arm/src/s32k3xx/s32k3xx_progmem.c b/arch/arm/src/s32k3xx/s32k3xx_progmem.c new file mode 100644 index 0000000000..9149d6d31c --- /dev/null +++ b/arch/arm/src/s32k3xx/s32k3xx_progmem.c @@ -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 + +#include +#include +#include + +#include "hardware/s32k3xx_pflash.h" +#include "hardware/s32k3xx_xrdc.h" + +#include "s32k3xx_config.h" +#include "s32k3xx_progmem.h" +#include "arm_internal.h" + +#include /* 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); +} diff --git a/arch/arm/src/s32k3xx/s32k3xx_progmem.h b/arch/arm/src/s32k3xx/s32k3xx_progmem.h new file mode 100644 index 0000000000..fc6a7bfed3 --- /dev/null +++ b/arch/arm/src/s32k3xx/s32k3xx_progmem.h @@ -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 +#include + +#include +#include +#include + +#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 */ diff --git a/boards/arm/s32k3xx/mr-canhubk3/configs/net/defconfig b/boards/arm/s32k3xx/mr-canhubk3/configs/net/defconfig index e5a33c4690..2925290b4d 100644 --- a/boards/arm/s32k3xx/mr-canhubk3/configs/net/defconfig +++ b/boards/arm/s32k3xx/mr-canhubk3/configs/net/defconfig @@ -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 diff --git a/boards/arm/s32k3xx/mr-canhubk3/src/s32k3xx_bringup.c b/boards/arm/s32k3xx/mr-canhubk3/src/s32k3xx_bringup.c index 0350a41efb..a70991882f 100644 --- a/boards/arm/s32k3xx/mr-canhubk3/src/s32k3xx_bringup.c +++ b/boards/arm/s32k3xx/mr-canhubk3/src/s32k3xx_bringup.c @@ -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 */