From 01ee9b3abcca8cd2b58463a39ebe5811ee0b1cf2 Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Tue, 1 May 2018 14:49:16 -0600 Subject: [PATCH] arch/arm/src/lpc17xx: This commit adds a configurable option to allow using a part of LPC17xx's internal flash memory as a Memory Technology Device via NuttX' mtd_progmem infrastructure. Tested with LPC1769. --- arch/arm/src/lpc17xx/Kconfig | 23 ++ arch/arm/src/lpc17xx/Make.defs | 4 + arch/arm/src/lpc17xx/lpc17_progmem.c | 418 +++++++++++++++++++++++++++ arch/arm/src/lpc17xx/lpc17_progmem.h | 139 +++++++++ 4 files changed, 584 insertions(+) create mode 100644 arch/arm/src/lpc17xx/lpc17_progmem.c create mode 100644 arch/arm/src/lpc17xx/lpc17_progmem.h diff --git a/arch/arm/src/lpc17xx/Kconfig b/arch/arm/src/lpc17xx/Kconfig index bef6ef884c..800ad55bbb 100644 --- a/arch/arm/src/lpc17xx/Kconfig +++ b/arch/arm/src/lpc17xx/Kconfig @@ -352,6 +352,14 @@ config LPC17_EEPROM default n depends on ARCH_FAMILY_LPC177X || ARCH_FAMILY_LPC178X +config LPC17_PROGMEM + bool PROGMEM + default n + select ARCH_HAVE_PROGMEM + ---help--- + Use a part of LPC17xx's internal flash memory as a + Memory-Technology-Device (MTD). + endmenu menu "External Memory Configuration" @@ -1006,3 +1014,18 @@ config LPC17_USBHOST_REGDEBUG also CONFIG_DEBUG_USB_INFO. endmenu + +menu "Memory-Technolgy-Device Options (PROGMEM)" + depends on LPC17_PROGMEM + +config LPC17_PROGMEM_NSECTORS + int "Size in 32 kB sectors" + default 2 + ---help--- + Number of 32 kB sectors to use as an MTD-device. This driver + will allocate the last n sectors. Only 32 kB sectors are + supported, so this works only on devices with 128 kB, 256 kB and + 512 kB of flash. The maximum number of sectors for these + devices is 2, 6 and 14, respectively. Default is 2. + +endmenu diff --git a/arch/arm/src/lpc17xx/Make.defs b/arch/arm/src/lpc17xx/Make.defs index c164c95f13..a2e3494f66 100644 --- a/arch/arm/src/lpc17xx/Make.defs +++ b/arch/arm/src/lpc17xx/Make.defs @@ -184,3 +184,7 @@ endif ifeq ($(CONFIG_LPC17_TMR0),y) CHIP_CSRCS += lpc17_timer.c endif + +ifeq ($(CONFIG_MTD_PROGMEM),y) +CHIP_CSRCS += lpc17_progmem.c +endif diff --git a/arch/arm/src/lpc17xx/lpc17_progmem.c b/arch/arm/src/lpc17xx/lpc17_progmem.c new file mode 100644 index 0000000000..3e3e53a7ad --- /dev/null +++ b/arch/arm/src/lpc17xx/lpc17_progmem.c @@ -0,0 +1,418 @@ +/****************************************************************************** + * arch/arm/src/lpc17xx/lpc17_progmem.c + * + * Copyright (C) 2018 Michael Jung. All rights reserved. + * Author: Michael Jung + * + * 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 NuttX 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 OWNER 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. + * + ******************************************************************************/ + +/****************************************************************************** + * See NXP UM10360 LPC176x/5x User manual, Rev 4.1, Chapter 32: LPC176x/5x + * Flash memory interface and programming. + ******************************************************************************/ + +/****************************************************************************** +* Included Files +******************************************************************************/ + +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include "lpc17_progmem.h" + +/****************************************************************************** + * Private Function Prototypes + ******************************************************************************/ + +static void lpc17_iap(void *in, void *out); +static uint32_t lpc17_iap_prepare_sector_for_write_operation(uint32_t sector); +static uint32_t lpc17_iap_erase_sector(uint32_t sector); +static uint32_t lpc17_iap_copy_ram_to_flash(void *flash, const void *ram, + size_t count); + +/****************************************************************************** + * Private Functions + ******************************************************************************/ + +/****************************************************************************** + * Name: lpc17_iap + * + * Description (from UM10360): + * For in-application programming the IAP routine should be called with a word + * pointer in register r0 pointing to memory (RAM) containing command code and + * parameters. The result from the IAP command is returned in the table + * pointed to by register r1. The user can reuse the command table for the + * result by passing the same pointer in registers r0 and r1. + * + ******************************************************************************/ + +static void lpc17_iap(FAR void *in, FAR void *out) +{ + irqstate_t flags; + + flags = enter_critical_section(); + + ((void (*)(FAR void *, FAR void *))LPC17_IAP_ENTRY_ADDR)(in, out); + + leave_critical_section(flags); +} + +/****************************************************************************** + * Name: lpc17_iap_prepare_sector_for_write_operation + * + * Description (from UM10360): + * This command must be executed before executing "Copy RAM to Flash" or + * "Erase Sector(s)" command. Successful execution of the "Copy RAM to Flash" + * or "Erase Sector(s)" command causes relevant sectors to be protected again. + * To prepare a single sector use the same "Start" and "End" sector numbers. + * + ******************************************************************************/ + +static uint32_t lpc17_iap_prepare_sector_for_write_operation(uint32_t sector) +{ + uint32_t inout[3]; + + inout[0] = LPC17_IAP_CMD_PREPARE_SECTORS_FOR_WRITE_OPERATION; + inout[1] = sector; + inout[2] = sector; + + lpc17_iap(inout, inout); + + return inout[0]; +} + +/****************************************************************************** + * Name: lpc17_iap_erase_sector + * + * Description (from UM10360): + * This command is used to erase a sector or multiple sectors of on-chip flash + * memory. To erase a single sector use the same "Start" and "End" sector + * numbers. + * + ******************************************************************************/ + +static uint32_t lpc17_iap_erase_sector(uint32_t sector) +{ + uint32_t inout[4]; + + inout[0] = LPC17_IAP_CMD_ERASE_SECTORS; + inout[1] = sector; + inout[2] = sector; + inout[3] = LPC17_CCLK / 1000; + + lpc17_iap(inout, inout); + + return inout[0]; +} + +/****************************************************************************** + * Name: lpc17_iap_copy_ram_to_flash + * + * Description (from UM10360): + * This command is used to program the flash memory. The affected sectors + * should be prepared first by calling "Prepare Sector for Write Operation" + * command. The affected sectors are automatically protected again once the + * copy command is successfully executed. + * + ******************************************************************************/ + +static uint32_t lpc17_iap_copy_ram_to_flash(void *flash, const void *ram, + size_t count) +{ + uint32_t inout[5]; + + inout[0] = LPC17_IAP_CMD_COPY_RAM_TO_FLASH; + inout[1] = (uint32_t)flash; + inout[2] = (uint32_t)ram; + inout[3] = (uint32_t)count; + inout[4] = LPC17_CCLK / 1000; + + lpc17_iap(inout, inout); + + return inout[0]; +} + +/****************************************************************************** + * Public Functions + ******************************************************************************/ + +/****************************************************************************** + * Name: up_progmem_npages + * + * Description: + * Return number of erase pages + * + ******************************************************************************/ + +size_t up_progmem_npages(void) +{ + return CONFIG_LPC17_PROGMEM_NSECTORS; +} + +/****************************************************************************** + * 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)LPC17_PROGMEM_PAGE_SIZE; +} + +/****************************************************************************** + * Name: up_progmem_erasesize + * + * Description: + * Return erase page size + * + ******************************************************************************/ + +size_t up_progmem_erasesize(size_t page) +{ + return (size_t)LPC17_PROGMEM_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 >= LPC17_PROGMEM_START_ADDR) + { + addr -= LPC17_PROGMEM_START_ADDR; + } + + return (size_t)(addr / LPC17_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)(LPC17_PROGMEM_START_ADDR + page * LPC17_PROGMEM_PAGE_SIZE); +} + +/****************************************************************************** + * Name: up_progmem_erasepage + * + * Description: + * Erase selected page. + * + * Input Parameters: + * page - The erase page index to be erased. + * + * Returned Value: + * Page 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_erasepage(size_t page) +{ + uint32_t rc; + + if (page >= CONFIG_LPC17_PROGMEM_NSECTORS) + { + return -EFAULT; + } + + rc = lpc17_iap_prepare_sector_for_write_operation((uint32_t)page + + LPC17_PROGMEM_START_SECTOR); + if (rc != LPC17_IAP_RC_CMD_SUCCESS) + { + return -EIO; + } + + rc = lpc17_iap_erase_sector((uint32_t)page + LPC17_PROGMEM_START_SECTOR); + + if (rc != LPC17_IAP_RC_CMD_SUCCESS) + { + return -EIO; + } + + return (ssize_t)LPC17_PROGMEM_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 >= CONFIG_LPC17_PROGMEM_NSECTORS) + { + return -EFAULT; + } + + p = (const uint8_t *)up_progmem_getaddress(page); + + for (i = 0; i < LPC17_PROGMEM_SECTOR_SIZE; i++) + { + if (p[i] != 0xffu) + { + break; + } + } + + return (ssize_t)(LPC17_PROGMEM_SECTOR_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 (absolute or aligned to page0) + * 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 + * 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, FAR const void *buf, size_t count) +{ + size_t page; + uint32_t rc; + + if (count % LPC17_PROGMEM_PAGE_SIZE) + { + return -EINVAL; + } + + page = up_progmem_getpage(addr) / LPC17_PROGMEM_PAGES_PER_SECTOR + + LPC17_PROGMEM_START_SECTOR; + + rc = lpc17_iap_prepare_sector_for_write_operation((uint32_t)page); + + if (rc != LPC17_IAP_RC_CMD_SUCCESS) + { + return -EIO; + } + + rc = lpc17_iap_copy_ram_to_flash((void *)addr, buf, count); + + if (rc != LPC17_IAP_RC_CMD_SUCCESS) + { + return -EIO; + } + + return count; +} diff --git a/arch/arm/src/lpc17xx/lpc17_progmem.h b/arch/arm/src/lpc17xx/lpc17_progmem.h new file mode 100644 index 0000000000..fd826273c3 --- /dev/null +++ b/arch/arm/src/lpc17xx/lpc17_progmem.h @@ -0,0 +1,139 @@ +/****************************************************************************** + * arch/arm/src/lpc17xx/lpc17_progmem.c + * + * Copyright (C) 2018 Michael Jung. All rights reserved. + * Author: Michael Jung + * + * 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 NuttX 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 OWNER 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. + * + ******************************************************************************/ + +#ifndef __ARCH_ARM_SRC_LPC17XX_LPC17_PROGMEM_H +#define __ARCH_ARM_SRC_LPC17XX_LPC17_PROGMEM_H + +/****************************************************************************** + * See NXP UM10360 LPC176x/5x User manual, Rev 4.1, Chapter 32: LPC176x/5x + * Flash memory interface and programming. + * + * The first 16 flash sectors (aka erase blocks) are 4kB in size, followed by + * up to 14 sectors of 32 kB. This progmem driver supports just 32 kB sectors. + * + * Flash write access is provided by an "In Application Programming" service + * function stored in boot loader firmware. Individual write accesses must be + * 256 byte in size and must be aligned to a 256 byte boundary. + * + ******************************************************************************/ + +/****************************************************************************** +* Included Files +******************************************************************************/ + +#include + +/****************************************************************************** + * Pre-processor Definitions + ******************************************************************************/ + +/* The first 16 sectors are 4kB in size and thus not supported as progmem. */ + +#define LPC17_FLASH_NUM_4K_SECTORS 16 + +/* The number of 32kB sectors depends on the target device's flash size */ + +#define LPC17_FLASH_NUM_32K_SECTORS \ + ((LPC17_FLASH_SIZE - LPC17_FLASH_NUM_4K_SECTORS * 4096) / 32768) + +/* The number of 32kB sectors to be used for progmem is configurable. The + * sectors at the end of the flash are used for progmem, the rest is left + * for code and data. */ + +#define LPC17_PROGMEM_START_SECTOR \ + (LPC17_FLASH_NUM_4K_SECTORS + LPC17_FLASH_NUM_32K_SECTORS - \ + CONFIG_LPC17_PROGMEM_NSECTORS) + +/* Base address of the flash segment used for progmem. */ + +#define LPC17_PROGMEM_START_ADDR \ + (LPC17_FLASH_NUM_4K_SECTORS * 4096 + \ + (LPC17_PROGMEM_START_SECTOR - LPC17_FLASH_NUM_4K_SECTORS) * 32768) + +/* Size of the flash segment used for progmem. */ + +#define LPC17_PROGMEM_SIZE (CONFIG_LPC17_PROGMEM_NSECTORS * 32768) + +/* Size of a read/write page. */ + +#define LPC17_PROGMEM_PAGE_SIZE 256 + +/* Total number of read/write pages. */ + +#define LPC17_PROGMEM_NUM_PAGES (LPC17_PROGMEM_SIZE / LPC17_PROGMEM_PAGE_SIZE) + +/* Size of an erase page. This driver only supports the 32kB sectors. */ + +#define LPC17_PROGMEM_SECTOR_SIZE 32768 + +/* Number of read/write pages per erase page. */ + +#define LPC17_PROGMEM_PAGES_PER_SECTOR \ + (LPC17_PROGMEM_SECTOR_SIZE / LPC17_PROGMEM_PAGE_SIZE) + +/* LPC17 entry point for In-Application-Programming boot rom service function */ + +#define LPC17_IAP_ENTRY_ADDR 0x1fff1ff1 + +/* The IAP Commands required for progmem */ + +#define LPC17_IAP_CMD_PREPARE_SECTORS_FOR_WRITE_OPERATION 50 +#define LPC17_IAP_CMD_COPY_RAM_TO_FLASH 51 +#define LPC17_IAP_CMD_ERASE_SECTORS 52 + +/* IAP return codes */ + +#define LPC17_IAP_RC_CMD_SUCCESS 0 +#define LPC17_IAP_RC_INVALID_CMD 1 +#define LPC17_IAP_RC_SCR_ADDR_ERROR 2 +#define LPC17_IAP_RC_DST_ADDR_ERROR 3 +#define LPC17_IAP_RC_SRC_ADDR_NOT_MAPPED 4 +#define LPC17_IAP_RC_DST_ADDR_NOT_MAPPED 5 +#define LPC17_IAP_RC_COUNT_ERROR 6 +#define LPC17_IAP_RC_INVALID_SECTOR 7 +#define LPC17_IAP_RC_SECTOR_NOT_BLANK 8 +#define LPC17_IAP_RC_SECTOR_NOT_PREPARED_FOR_WRITE_OPERATION 9 +#define LPC17_IAP_RC_COMPARE_ERROR 10 +#define LPC17_IAP_RC_BUSY 11 +#define LPC17_IAP_RC_PARAM_ERROR 12 +#define LPC17_IAP_RC_ADDR_ERROR 13 +#define LPC17_IAP_RC_ADDR_NOT_MAPPED 14 +#define LPC17_IAP_RC_CMD_LOCKED 15 +#define LPC17_IAP_RC_INVALID_CODE 16 +#define LPC17_IAP_RC_INVALID_BAUD_RATE 17 +#define LPC17_IAP_RC_INVALID_STOP_BIT 18 +#define LPC17_IAP_RC_CODE_READ_PROTECTION_ENABLED 19 + +#endif /* __ARCH_ARM_SRC_LPC17XX_LPC17_PROGMEM_H */