/**************************************************************************** * 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 ****************************************************************************/ #if CONFIG_S32K3XX_PROGMEM_SIZE > DFLASH_SIZE # error Progmem size too big #endif #if CONFIG_S32K3XX_PROGMEM_SIZE % 8 != 0 # error Progmem must be a multiple of 8 #endif #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) { DEBUGASSERT(((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; } /* Address with or without flash offset */ addr = ((addr % S32K3XX_PROGMEM_START_ADDR) + S32K3XX_PROGMEM_START_ADDR); 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)); } dest += 4; } 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); }