From f3d2f532872cfef32843e1c34e4060a4fa03f5b5 Mon Sep 17 00:00:00 2001 From: Vincent Dupont Date: Tue, 13 Mar 2018 14:11:12 +0100 Subject: [PATCH 1/3] drivers: add mtd_flashpage driver This is a MTD wrapper for flashpage internal flash --- drivers/Makefile.dep | 19 +++-- drivers/include/mtd_flashpage.h | 53 ++++++++++++++ drivers/mtd_flashpage/Makefile | 1 + drivers/mtd_flashpage/mtd_flashpage.c | 99 +++++++++++++++++++++++++++ 4 files changed, 166 insertions(+), 6 deletions(-) create mode 100644 drivers/include/mtd_flashpage.h create mode 100644 drivers/mtd_flashpage/Makefile create mode 100644 drivers/mtd_flashpage/mtd_flashpage.c diff --git a/drivers/Makefile.dep b/drivers/Makefile.dep index 7a9773a9802a..dc3993024400 100644 --- a/drivers/Makefile.dep +++ b/drivers/Makefile.dep @@ -342,14 +342,21 @@ ifneq (,$(filter mrf24j40,$(USEMODULE))) FEATURES_REQUIRED += periph_spi endif -ifneq (,$(filter mtd_sdcard,$(USEMODULE))) +ifneq (,$(filter mtd_%,$(USEMODULE))) USEMODULE += mtd - USEMODULE += sdcard_spi -endif -ifneq (,$(filter mtd_spi_nor,$(USEMODULE))) - USEMODULE += mtd - FEATURES_REQUIRED += periph_spi + ifneq (,$(filter mtd_sdcard,$(USEMODULE))) + USEMODULE += sdcard_spi + endif + + ifneq (,$(filter mtd_spi_nor,$(USEMODULE))) + FEATURES_REQUIRED += periph_spi + endif + + ifneq (,$(filter mtd_flashpage,$(USEMODULE))) + FEATURES_REQUIRED += periph_flashpage + FEATURES_REQUIRED += periph_flashpage_raw + endif endif ifneq (,$(filter my9221,$(USEMODULE))) diff --git a/drivers/include/mtd_flashpage.h b/drivers/include/mtd_flashpage.h new file mode 100644 index 000000000000..0c6d4ac8a49f --- /dev/null +++ b/drivers/include/mtd_flashpage.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2018 OTA keys S.A. + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup drivers_mtd_flashpage Flashpage MTD + * @ingroup drivers_storage + * @brief Driver for internal flash devices implementing flashpage interface + * + * @{ + * + * @file + * @brief Interface definition for the flashpage memory driver + * + * @author Vincent Dupont + */ + +#ifndef MTD_FLASHPAGE_H +#define MTD_FLASHPAGE_H + +#include "mtd.h" +#include "periph/flashpage.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** + * @brief Macro helper to initialize a mtd_t with flash-age driver + */ +#define MTD_FLASHPAGE_INIT_VAL(_pages_per_sector) { \ + .driver = &mtd_flashpage_driver, \ + .sector_count = FLASHPAGE_NUMOF, \ + .pages_per_sector = _pages_per_sector,\ + .page_size = FLASHPAGE_SIZE / _pages_per_sector,\ +} + +/** + * @brief Flashpage MTD device operations table + */ +extern const mtd_desc_t mtd_flashpage_driver; + +#ifdef __cplusplus +} +#endif + +#endif /* MTD_FLASHPAGE_H */ +/** @} */ diff --git a/drivers/mtd_flashpage/Makefile b/drivers/mtd_flashpage/Makefile new file mode 100644 index 000000000000..48422e909a47 --- /dev/null +++ b/drivers/mtd_flashpage/Makefile @@ -0,0 +1 @@ +include $(RIOTBASE)/Makefile.base diff --git a/drivers/mtd_flashpage/mtd_flashpage.c b/drivers/mtd_flashpage/mtd_flashpage.c new file mode 100644 index 000000000000..480954226334 --- /dev/null +++ b/drivers/mtd_flashpage/mtd_flashpage.c @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2018 OTA keys S.A. + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup drivers_mtd_flashpage + * @brief Driver for internal flash devices implementing flashpage interface + * + * @{ + * + * @file + * @brief Implementation for the flashpage memory driver + * + * @author Vincent Dupont + * @} + */ + +#include +#include +#include + +#include "cpu_conf.h" +#include "mtd_flashpage.h" +#include "periph/flashpage.h" + +#define MTD_FLASHPAGE_END_ADDR CPU_FLASH_BASE + (FLASHPAGE_NUMOF * FLASHPAGE_SIZE) + +static int _init(mtd_dev_t *dev) +{ + (void)dev; + assert(dev->pages_per_sector * dev->page_size == FLASHPAGE_SIZE); + return 0; +} + +static int _read(mtd_dev_t *dev, void *buf, uint32_t addr, uint32_t size) +{ + assert(addr < MTD_FLASHPAGE_END_ADDR); + (void)dev; + + if (addr % FLASHPAGE_RAW_ALIGNMENT) { + return -EINVAL; + } + + memcpy(buf, (void*)addr, size); + + return size; +} + +static int _write(mtd_dev_t *dev, const void *buf, uint32_t addr, uint32_t size) +{ + (void)dev; + if (addr % FLASHPAGE_RAW_ALIGNMENT) { + return -EINVAL; + } + if ((uintptr_t)buf % FLASHPAGE_RAW_ALIGNMENT) { + return -EINVAL; + } + if (size % FLASHPAGE_RAW_BLOCKSIZE) { + return -EOVERFLOW; + } + if (addr + size > MTD_FLASHPAGE_END_ADDR) { + return -EOVERFLOW; + } + flashpage_write_raw((void *)addr, buf, size); + + return size; +} + +int _erase(mtd_dev_t *dev, uint32_t addr, uint32_t size) +{ + size_t sector_size = dev->page_size * dev->pages_per_sector; + + if (size % sector_size) { + return -EOVERFLOW; + } + if (addr + size > MTD_FLASHPAGE_END_ADDR) { + return - EOVERFLOW; + } + if (addr % sector_size) { + return - EOVERFLOW; + } + for (size_t i = 0; i < size; i += sector_size) { + flashpage_write(flashpage_page((void *)addr), NULL); + } + + return 0; +} + + +const mtd_desc_t mtd_flashpage_driver = { + .init = _init, + .read = _read, + .write = _write, + .erase = _erase, +}; From a05f7c7cac8c1b8c1a6d3de8b2249d6e13029d4f Mon Sep 17 00:00:00 2001 From: Vincent Dupont Date: Tue, 13 Mar 2018 14:11:57 +0100 Subject: [PATCH 2/3] unittests: add mtd_flashpage unittests --- tests/mtd_flashpage/Makefile | 6 ++ tests/mtd_flashpage/main.c | 160 ++++++++++++++++++++++++++++ tests/mtd_flashpage/tests/01-run.py | 18 ++++ 3 files changed, 184 insertions(+) create mode 100644 tests/mtd_flashpage/Makefile create mode 100644 tests/mtd_flashpage/main.c create mode 100755 tests/mtd_flashpage/tests/01-run.py diff --git a/tests/mtd_flashpage/Makefile b/tests/mtd_flashpage/Makefile new file mode 100644 index 000000000000..4ebfdfb69b56 --- /dev/null +++ b/tests/mtd_flashpage/Makefile @@ -0,0 +1,6 @@ +include ../Makefile.tests_common + +USEMODULE += mtd_flashpage +USEMODULE += embunit + +include $(RIOTBASE)/Makefile.include diff --git a/tests/mtd_flashpage/main.c b/tests/mtd_flashpage/main.c new file mode 100644 index 000000000000..6361829443eb --- /dev/null +++ b/tests/mtd_flashpage/main.c @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2016 OTA keys S.A. + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @{ + * + * @file + */ +#include +#include + +#include "embUnit.h" + +#include "mtd.h" +#include "mtd_flashpage.h" + +#define TEST_ADDRESS1 (uint32_t)flashpage_addr(FLASHPAGE_NUMOF - 1) +#define TEST_ADDRESS2 (uint32_t)flashpage_addr(FLASHPAGE_NUMOF - 2) + +static mtd_dev_t _dev = MTD_FLASHPAGE_INIT_VAL(8); +static mtd_dev_t *dev = &_dev; + +static void setup(void) +{ + int ret = mtd_init(dev); + TEST_ASSERT_EQUAL_INT(0, ret); + mtd_erase(dev, TEST_ADDRESS1, dev->pages_per_sector * dev->page_size); + mtd_erase(dev, TEST_ADDRESS2, dev->pages_per_sector * dev->page_size); +} + +static void teardown(void) +{ + mtd_erase(dev, TEST_ADDRESS1, dev->pages_per_sector * dev->page_size); + mtd_erase(dev, TEST_ADDRESS2, dev->pages_per_sector * dev->page_size); +} + +static void test_mtd_init(void) +{ + int ret = mtd_init(dev); + TEST_ASSERT_EQUAL_INT(0, ret); +} + +static void test_mtd_erase(void) +{ + /* Erase last sector */ + int ret = mtd_erase(dev, TEST_ADDRESS1, FLASHPAGE_SIZE); + TEST_ASSERT_EQUAL_INT(0, ret); + + /* Erase with wrong size (less than sector size) */ + ret = mtd_erase(dev, TEST_ADDRESS1, dev->page_size); + TEST_ASSERT_EQUAL_INT(-EOVERFLOW, ret); + + /* Unaligned erase */ + ret = mtd_erase(dev, TEST_ADDRESS1 + dev->page_size, dev->page_size); + TEST_ASSERT_EQUAL_INT(-EOVERFLOW, ret); + + /* Erase 2 last sectors */ + ret = mtd_erase(dev, TEST_ADDRESS2, + FLASHPAGE_SIZE * 2); + TEST_ASSERT_EQUAL_INT(0, ret); + + /* Erase out of memory area */ + ret = mtd_erase(dev, TEST_ADDRESS1, + FLASHPAGE_SIZE * 2); + TEST_ASSERT_EQUAL_INT(-EOVERFLOW, ret); +} + +static void test_mtd_write_erase(void) +{ + const char buf[] = "ABCDEFGHIJKLMNO"; + + /* stm32l0x and stm32l1x erase its flash with 0's */ +#if defined(CPU_FAM_STM32L0) || defined(CPU_FAM_STM32L1) + uint8_t buf_empty[] = {0, 0, 0}; +#else + uint8_t buf_empty[] = {0xff, 0xff, 0xff}; +#endif + char buf_read[sizeof(buf) + sizeof(buf_empty)]; + memset(buf_read, 0, sizeof(buf_read)); + + int ret = mtd_write(dev, buf, TEST_ADDRESS1, sizeof(buf)); + TEST_ASSERT_EQUAL_INT(sizeof(buf), ret); + + ret = mtd_erase(dev, TEST_ADDRESS1, dev->pages_per_sector * dev->page_size); + TEST_ASSERT_EQUAL_INT(0, ret); + + uint8_t expected[sizeof(buf_read)]; +#if defined(CPU_FAM_STM32L0) || defined(CPU_FAM_STM32L1) + memset(expected, 0, sizeof(expected)); +#else + memset(expected, 0xff, sizeof(expected)); +#endif + ret = mtd_read(dev, buf_read, TEST_ADDRESS1, sizeof(buf_read)); + TEST_ASSERT_EQUAL_INT(sizeof(buf_read), ret); + TEST_ASSERT_EQUAL_INT(0, memcmp(expected, buf_read, sizeof(buf_read))); +} + +static void test_mtd_write_read(void) +{ + const char buf[] __attribute__ ((aligned (FLASHPAGE_RAW_ALIGNMENT))) = "ABCDEFGHIJKLMNO"; + + /* stm32l0x and stm32l1x erase its flash with 0's */ +#if defined(CPU_FAM_STM32L0) || defined(CPU_FAM_STM32L1) + uint8_t buf_empty[] = {0, 0, 0}; +#else + uint8_t buf_empty[] = {0xff, 0xff, 0xff}; +#endif + char buf_read[sizeof(buf) + sizeof(buf_empty)]; + memset(buf_read, 0, sizeof(buf_read)); + + /* Basic write / read */ + int ret = mtd_write(dev, buf, TEST_ADDRESS1, sizeof(buf)); + TEST_ASSERT_EQUAL_INT(sizeof(buf), ret); + + ret = mtd_read(dev, buf_read, TEST_ADDRESS1, sizeof(buf_read)); + TEST_ASSERT_EQUAL_INT(sizeof(buf_read), ret); + TEST_ASSERT_EQUAL_INT(0, memcmp(buf, buf_read, sizeof(buf))); + TEST_ASSERT_EQUAL_INT(0, memcmp(buf_empty, buf_read + sizeof(buf), sizeof(buf_empty))); + + ret = mtd_erase(dev, TEST_ADDRESS1, dev->pages_per_sector * dev->page_size); + TEST_ASSERT_EQUAL_INT(0, ret); + + /* Unaligned write / read */ + ret = mtd_write(dev, buf, TEST_ADDRESS1 + sizeof(buf_empty), sizeof(buf)); + TEST_ASSERT_EQUAL_INT(-EINVAL, ret); + + /* Only Cortex-M0 doesn't allow unaligned reads */ +#if defined(CPU_ARCH_CORTEX_M0) + ret = mtd_read(dev, buf_read, TEST_ADDRESS1 + sizeof(buf_empty), sizeof(buf_read)); + TEST_ASSERT_EQUAL_INT(-EINVAL, ret); +#endif +} + +Test *tests_mtd_flashpage_tests(void) +{ + EMB_UNIT_TESTFIXTURES(fixtures) { + new_TestFixture(test_mtd_init), + new_TestFixture(test_mtd_erase), + new_TestFixture(test_mtd_write_erase), + new_TestFixture(test_mtd_write_read), + }; + + EMB_UNIT_TESTCALLER(mtd_flashpage_tests, setup, teardown, fixtures); + + return (Test *)&mtd_flashpage_tests; +} + +int main(void) +{ + TESTS_START(); + TESTS_RUN(tests_mtd_flashpage_tests()); + TESTS_END(); + return 0; +} +/** @} */ diff --git a/tests/mtd_flashpage/tests/01-run.py b/tests/mtd_flashpage/tests/01-run.py new file mode 100755 index 000000000000..5fc8788f24cb --- /dev/null +++ b/tests/mtd_flashpage/tests/01-run.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 + +# Copyright (C) 2017 Freie Universität Berlin +# +# This file is subject to the terms and conditions of the GNU Lesser +# General Public License v2.1. See the file LICENSE in the top level +# directory for more details. + +import sys +from testrunner import run + + +def testfunc(child): + child.expect(r'OK \(\d+ tests\)') + + +if __name__ == "__main__": + sys.exit(run(testfunc)) From 7c8a3eae71d85f8d03d735df48222479afef634e Mon Sep 17 00:00:00 2001 From: Vincent Dupont Date: Wed, 5 Sep 2018 13:52:16 +0200 Subject: [PATCH 3/3] drivers/mtd: improve mtd API doc --- drivers/include/mtd.h | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/include/mtd.h b/drivers/include/mtd.h index 900ecaba1b66..dcf530e550de 100644 --- a/drivers/include/mtd.h +++ b/drivers/include/mtd.h @@ -158,7 +158,7 @@ struct mtd_desc { int mtd_init(mtd_dev_t *mtd); /** - * @brief mtd_read Read data from a MTD device + * @brief Read data from a MTD device * * No alignment is required on @p addr and @p count. * @@ -177,10 +177,11 @@ int mtd_init(mtd_dev_t *mtd); int mtd_read(mtd_dev_t *mtd, void *dest, uint32_t addr, uint32_t count); /** - * @brief mtd_read write data to a MTD device + * @brief Write data to a MTD device * * @p addr + @p count must be inside a page boundary. @p addr can be anywhere - * but the buffer cannot overlap two pages. + * but the buffer cannot overlap two pages. Though some devices might enforce alignement + * on both @p addr and @p buf. * * @param mtd the device to write to * @param[in] src the buffer to write @@ -194,11 +195,12 @@ int mtd_read(mtd_dev_t *mtd, void *dest, uint32_t addr, uint32_t count); * @return -EOVERFLOW if @p addr or @p count are not valid, i.e. outside memory, * or overlapping two pages * @return -EIO if I/O error occured + * @return -EINVAL if parameters are invalid (invalid alignment for instance) */ int mtd_write(mtd_dev_t *mtd, const void *src, uint32_t addr, uint32_t count); /** - * @brief mtd_erase Erase sectors of a MTD device + * @brief Erase sectors of a MTD device * * @p addr must be aligned on a sector boundary. @p count must be a multiple of a sector size. * @@ -216,7 +218,7 @@ int mtd_write(mtd_dev_t *mtd, const void *src, uint32_t addr, uint32_t count); int mtd_erase(mtd_dev_t *mtd, uint32_t addr, uint32_t count); /** - * @brief mtd_power Set power mode on a MTD device + * @brief Set power mode on a MTD device * * @param mtd the device to access * @param[in] power the power mode to set