aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorbarthess <barthess@35acf78f-673a-0410-8e92-d51de3d6d3f4>2014-08-05 08:00:56 +0000
committerbarthess <barthess@35acf78f-673a-0410-8e92-d51de3d6d3f4>2014-08-05 08:00:56 +0000
commit88a09ec74105c34e3eb66eb5ba84f0e448ce7a2a (patch)
treefe8ade2bad038927e12d26ee9e39222dc974fa27
parentadb00b4f8f7d34b0fa026bc8fc083e3464d6c32a (diff)
downloadChibiOS-88a09ec74105c34e3eb66eb5ba84f0e448ce7a2a.tar.gz
ChibiOS-88a09ec74105c34e3eb66eb5ba84f0e448ce7a2a.tar.bz2
ChibiOS-88a09ec74105c34e3eb66eb5ba84f0e448ce7a2a.zip
Added FSMC NAND driver
git-svn-id: svn://svn.code.sf.net/p/chibios/svn/trunk@7123 35acf78f-673a-0410-8e92-d51de3d6d3f4
-rw-r--r--os/hal/hal.mk4
-rw-r--r--os/hal/include/emc.h93
-rw-r--r--os/hal/include/emcnand.h152
-rw-r--r--os/hal/include/hal.h2
-rw-r--r--os/hal/ports/STM32/LLD/emc_lld.c185
-rw-r--r--os/hal/ports/STM32/LLD/emc_lld.h247
-rw-r--r--os/hal/ports/STM32/LLD/emcnand_lld.c555
-rw-r--r--os/hal/ports/STM32/LLD/emcnand_lld.h361
-rw-r--r--os/hal/ports/STM32/STM32F4xx/platform.mk4
-rw-r--r--os/hal/ports/STM32/STM32F4xx/stm32_rcc.h29
-rw-r--r--os/hal/ports/STM32/STM32F4xx/stm32_registry.h12
-rw-r--r--os/hal/src/emc.c128
-rw-r--r--os/hal/src/emcnand.c603
-rw-r--r--os/hal/src/hal.c7
14 files changed, 2379 insertions, 3 deletions
diff --git a/os/hal/hal.mk b/os/hal/hal.mk
index c0c239228..0c46285bf 100644
--- a/os/hal/hal.mk
+++ b/os/hal/hal.mk
@@ -22,7 +22,9 @@ HALSRC = ${CHIBIOS}/os/hal/src/hal.c \
${CHIBIOS}/os/hal/src/spi.c \
${CHIBIOS}/os/hal/src/st.c \
${CHIBIOS}/os/hal/src/uart.c \
- ${CHIBIOS}/os/hal/src/usb.c
+ ${CHIBIOS}/os/hal/src/usb.c \
+ ${CHIBIOS}/os/hal/src/emc.c \
+ ${CHIBIOS}/os/hal/src/emcnand.c
# Required include directories
HALINC = ${CHIBIOS}/os/hal/include
diff --git a/os/hal/include/emc.h b/os/hal/include/emc.h
new file mode 100644
index 000000000..2d5f78d71
--- /dev/null
+++ b/os/hal/include/emc.h
@@ -0,0 +1,93 @@
+/*
+ ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010,
+ 2011,2012,2013 Giovanni Di Sirio.
+
+ This file is part of ChibiOS/RT.
+
+ ChibiOS/RT is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ ChibiOS/RT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+/*
+ Concepts and parts of this file have been contributed by Uladzimir Pylinsky
+ aka barthess.
+ */
+
+/**
+ * @file emc.h
+ * @brief EMC Driver macros and structures.
+ *
+ * @addtogroup EMC
+ * @{
+ */
+
+#ifndef _EMC_H_
+#define _EMC_H_
+
+#if HAL_USE_EMC || defined(__DOXYGEN__)
+
+/*===========================================================================*/
+/* Driver constants. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver pre-compile time settings. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Derived constants and error checks. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver data structures and types. */
+/*===========================================================================*/
+
+/**
+ * @brief Driver state machine possible states.
+ */
+typedef enum {
+ EMC_UNINIT = 0, /**< Not initialized. */
+ EMC_STOP = 1, /**< Stopped. */
+ EMC_READY = 2, /**< Ready. */
+} emcstate_t;
+
+/**
+ * @brief Type of a structure representing a EMC driver.
+ */
+typedef struct EMCDriver EMCDriver;
+
+#include "emc_lld.h"
+
+/*===========================================================================*/
+/* Driver macros. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* External declarations. */
+/*===========================================================================*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ void emcInit(void);
+ void emcObjectInit(EMCDriver *emcp);
+ void emcStart(EMCDriver *emcp, const EMCConfig *config);
+ void emcStop(EMCDriver *emcp);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* HAL_USE_EMC */
+
+#endif /* _EMC_H_ */
+
+/** @} */
diff --git a/os/hal/include/emcnand.h b/os/hal/include/emcnand.h
new file mode 100644
index 000000000..fbb8d48ca
--- /dev/null
+++ b/os/hal/include/emcnand.h
@@ -0,0 +1,152 @@
+/*
+ ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010,
+ 2011,2012,2013 Giovanni Di Sirio.
+
+ This file is part of ChibiOS/RT.
+
+ ChibiOS/RT is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ ChibiOS/RT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+/*
+ Concepts and parts of this file have been contributed by Uladzimir Pylinsky
+ aka barthess.
+ */
+
+/**
+ * @file emcnand.h
+ * @brief EMCNAND Driver macros and structures.
+ *
+ * @addtogroup EMCNAND
+ * @{
+ */
+
+#ifndef _EMCNAND_H_
+#define _EMCNAND_H_
+
+#if HAL_USE_EMCNAND || defined(__DOXYGEN__)
+
+/*===========================================================================*/
+/* Driver constants. */
+/*===========================================================================*/
+
+/*
+ * Standard NAND flash commands
+ */
+#define NAND_CMD_READ0 0x00
+#define NAND_CMD_RNDOUT 0x05
+#define NAND_CMD_PAGEPROG 0x10
+#define NAND_CMD_READ0_CONFIRM 0x30
+#define NAND_CMD_READOOB 0x50
+#define NAND_CMD_ERASE 0x60
+#define NAND_CMD_STATUS 0x70
+#define NAND_CMD_STATUS_MULTI 0x71
+#define NAND_CMD_WRITE 0x80
+#define NAND_CMD_RNDIN 0x85
+#define NAND_CMD_READID 0x90
+#define NAND_CMD_ERASE_CONFIRM 0xD0
+#define NAND_CMD_RESET 0xFF
+
+/*===========================================================================*/
+/* Driver pre-compile time settings. */
+/*===========================================================================*/
+/**
+ * @brief Enables the mutual exclusion APIs on the NAND.
+ */
+#if !defined(EMCNAND_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define EMCNAND_USE_MUTUAL_EXCLUSION FALSE
+#endif
+
+/*===========================================================================*/
+/* Derived constants and error checks. */
+/*===========================================================================*/
+#if EMCNAND_USE_MUTUAL_EXCLUSION && !CH_CFG_USE_MUTEXES && !CH_CFG_USE_SEMAPHORES
+#error "EMCNAND_USE_MUTUAL_EXCLUSION requires CH_CFG_USE_MUTEXES and/or CH_CFG_USE_SEMAPHORES"
+#endif
+
+#if !HAL_USE_EMC
+#error "This driver requires EMC controller"
+#endif
+
+/*===========================================================================*/
+/* Driver data structures and types. */
+/*===========================================================================*/
+
+/**
+ * @brief Driver state machine possible states.
+ */
+typedef enum {
+ EMCNAND_UNINIT = 0, /**< Not initialized. */
+ EMCNAND_STOP = 1, /**< Stopped. */
+ EMCNAND_READY = 2, /**< Ready. */
+ EMCNAND_PROGRAM = 3, /**< Programming in progress. */
+ EMCNAND_ERASE = 4, /**< Erasing in progress. */
+ EMCNAND_WRITE = 5, /**< Writing to NAND buffer. */
+ EMCNAND_READ = 6, /**< Reading from NAND. */
+ EMCNAND_DMA_TX = 7, /**< DMA transmitting. */
+ EMCNAND_DMA_RX = 8, /**< DMA receiving. */
+} emcnandstate_t;
+
+/**
+ * @brief Type of a structure representing a EMCNAND driver.
+ */
+typedef struct EMCNANDDriver EMCNANDDriver;
+
+#include "emcnand_lld.h"
+
+/*===========================================================================*/
+/* Driver macros. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* External declarations. */
+/*===========================================================================*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ void emcnandInit(void);
+ void emcnandObjectInit(EMCNANDDriver *emcnandp);
+ void emcnandStart(EMCNANDDriver *emcnandp, const EMCNANDConfig *config);
+ void emcnandStop(EMCNANDDriver *emcnandp);
+ void emcnandReadPageWhole(EMCNANDDriver *emcnandp, uint32_t block,
+ uint32_t page, uint8_t *data, size_t datalen);
+ uint8_t emcnandWritePageWhole(EMCNANDDriver *emcnandp, uint32_t block,
+ uint32_t page, const uint8_t *data, size_t datalen);
+ void emcnandReadPageData(EMCNANDDriver *emcnandp, uint32_t block,
+ uint32_t page, uint8_t *data, size_t datalen, uint32_t *ecc);
+ uint8_t emcnandWritePageData(EMCNANDDriver *emcnandp, uint32_t block,
+ uint32_t page, const uint8_t *data, size_t datalen, uint32_t *ecc);
+ void emcnandReadPageSpare(EMCNANDDriver *emcnandp, uint32_t block,
+ uint32_t page, uint8_t *spare, size_t sparelen);
+ uint8_t emcnandWritePageSpare(EMCNANDDriver *emcnandp, uint32_t block,
+ uint32_t page, const uint8_t *spare, size_t sparelen);
+ void emcnandMarkBad(EMCNANDDriver *emcnandp, uint32_t block);
+ uint8_t emcnandReadBadMark(EMCNANDDriver *emcnandp,
+ uint32_t block, uint32_t page);
+ uint8_t emcnandErase(EMCNANDDriver *emcnandp, uint32_t block);
+ bool emcnandIsBad(EMCNANDDriver *emcnandp, uint32_t block);
+
+#if EMCNAND_USE_MUTUAL_EXCLUSION
+ void emcnandAcquireBus(EMCNANDDriver *emcnandp);
+ void emcnandReleaseBus(EMCNANDDriver *emcnandp);
+#endif /* EMCNAND_USE_MUTUAL_EXCLUSION */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* HAL_USE_EMCNAND */
+
+#endif /* _EMCNAND_H_ */
+
+/** @} */
diff --git a/os/hal/include/hal.h b/os/hal/include/hal.h
index c29eee17d..162eced73 100644
--- a/os/hal/include/hal.h
+++ b/os/hal/include/hal.h
@@ -64,6 +64,8 @@
#include "st.h"
#include "uart.h"
#include "usb.h"
+#include "emc.h"
+#include "emcnand.h"
/* Complex drivers.*/
#include "mmc_spi.h"
diff --git a/os/hal/ports/STM32/LLD/emc_lld.c b/os/hal/ports/STM32/LLD/emc_lld.c
new file mode 100644
index 000000000..3c6f16c5b
--- /dev/null
+++ b/os/hal/ports/STM32/LLD/emc_lld.c
@@ -0,0 +1,185 @@
+/*
+ ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010,
+ 2011,2012,2013 Giovanni Di Sirio.
+
+ This file is part of ChibiOS/RT.
+
+ ChibiOS/RT is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ ChibiOS/RT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+/*
+ Concepts and parts of this file have been contributed by Uladzimir Pylinsky
+ aka barthess.
+ */
+
+/**
+ * @file emc_lld.c
+ * @brief EMC Driver subsystem low level driver source template.
+ *
+ * @addtogroup EMC
+ * @{
+ */
+
+#include "hal.h"
+
+#if HAL_USE_EMC || defined(__DOXYGEN__)
+
+/*===========================================================================*/
+/* Driver local definitions. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver exported variables. */
+/*===========================================================================*/
+
+/**
+ * @brief EMC1 driver identifier.
+ */
+#if STM32_EMC_USE_FSMC1 || defined(__DOXYGEN__)
+EMCDriver EMCD1;
+#endif
+
+/*===========================================================================*/
+/* Driver local types. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver local variables and types. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver local functions. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver interrupt handlers. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver exported functions. */
+/*===========================================================================*/
+
+/**
+ * @brief Low level EMC driver initialization.
+ *
+ * @notapi
+ */
+void emc_lld_init(void) {
+
+#if STM32_EMC_USE_FSMC1
+ emcObjectInit(&EMCD1);
+
+#if STM32_EMCNAND_USE_EMCNAND1
+ EMCD1.nand1 = (FSMC_NAND_TypeDef *)FSMC_Bank2_R_BASE;
+#endif
+
+#if STM32_EMCNAND_USE_EMCNAND2
+ EMCD1.nand2 = (FSMC_NAND_TypeDef *)FSMC_Bank3_R_BASE;
+#endif
+
+#if STM32_USE_EMC_PCCARD
+ EMCD1.pccard = (FSMC_PCCARD_TypeDef *)FSMC_Bank4_R_BASE;
+#endif
+
+#endif /* STM32_EMC_USE_EMC1 */
+}
+
+/**
+ * @brief Configures and activates the EMC peripheral.
+ *
+ * @param[in] emcp pointer to the @p EMCDriver object
+ *
+ * @notapi
+ */
+void emc_lld_start(EMCDriver *emcp) {
+
+ if (emcp->state == EMC_STOP) {
+ /* Enables the peripheral.*/
+#if STM32_EMC_USE_FSMC1
+ if (&EMCD1 == emcp) {
+ rccResetFSMC();
+ rccEnableFSMC(FALSE);
+ #if STM32_EMC_USE_INT
+ nvicEnableVector(FSMC_IRQn,
+ CORTEX_PRIORITY_MASK(STM32_EMC_FSMC1_IRQ_PRIORITY));
+ #endif /* STM32_EMC_USE_INT */
+ }
+#endif /* PLATFORM_STM32_USE_EMC1 */
+
+ emcp->state = EMC_READY;
+ }
+}
+
+/**
+ * @brief Deactivates the EMC peripheral.
+ *
+ * @param[in] emcp pointer to the @p EMCDriver object
+ *
+ * @notapi
+ */
+void emc_lld_stop(EMCDriver *emcp) {
+
+ if (emcp->state == EMC_READY) {
+ /* Resets the peripheral.*/
+ rccResetFSMC();
+
+ /* Disables the peripheral.*/
+#if STM32_EMC_USE_FSMC1
+ if (&EMCD1 == emcp) {
+ #if STM32_EMC_USE_INT
+ nvicDisableVector(FSMC_IRQn);
+ #endif
+ rccDisableFSMC(FALSE);
+ }
+#endif /* PLATFORM_STM32_USE_EMC1 */
+
+ emcp->state = EMC_STOP;
+ }
+}
+
+#if STM32_EMC_USE_INT
+/**
+ * @brief Serve common interrupt.
+ *
+ * @notapi
+ */
+void emc_lld_serve_interrupt(void) {
+#warning "This functionality untested"
+
+ chDbgPanic("Unrealized");
+}
+
+/**
+ * @brief FSMC shared interrupt handler.
+ *
+ * @notapi
+ */
+CH_IRQ_HANDLER(FSMC_IRQHandler) {
+#warning "This functionality untested"
+
+ CH_IRQ_PROLOGUE();
+
+ if (EMCD1.nand1->SR & FSMC_SR_ISR_MASK){
+ EMCNANDD1.isr_handler(&EMCNANDD1, EMCD1.nand1->SR);
+ }
+ if (EMCD1.nand2->SR & FSMC_SR_ISR_MASK){
+ EMCNANDD2.isr_handler(&EMCNANDD2, EMCD1.nand2->SR);
+ }
+
+ CH_IRQ_EPILOGUE();
+}
+#endif /* STM32_EMC_USE_INT */
+
+#endif /* HAL_USE_EMC */
+
+/** @} */
diff --git a/os/hal/ports/STM32/LLD/emc_lld.h b/os/hal/ports/STM32/LLD/emc_lld.h
new file mode 100644
index 000000000..6df1257ba
--- /dev/null
+++ b/os/hal/ports/STM32/LLD/emc_lld.h
@@ -0,0 +1,247 @@
+/*
+ ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010,
+ 2011,2012,2013 Giovanni Di Sirio.
+
+ This file is part of ChibiOS/RT.
+
+ ChibiOS/RT is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ ChibiOS/RT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+/*
+ Concepts and parts of this file have been contributed by Uladzimir Pylinsky
+ aka barthess.
+ */
+
+/**
+ * @file emc_lld.h
+ * @brief EMC Driver subsystem low level driver header template.
+ *
+ * @addtogroup EMC
+ * @{
+ */
+
+
+#ifndef _EMC_LLD_H_
+#define _EMC_LLD_H_
+
+#if HAL_USE_EMC || defined(__DOXYGEN__)
+
+/*===========================================================================*/
+/* Driver constants. */
+/*===========================================================================*/
+
+/*
+ * Base bank mappings
+ */
+#define FSMC_Bank1_MAP_BASE ((uint32_t) 0x60000000)
+#define FSMC_Bank2_MAP_BASE ((uint32_t) 0x70000000)
+#define FSMC_Bank3_MAP_BASE ((uint32_t) 0x80000000)
+#define FSMC_Bank4_MAP_BASE ((uint32_t) 0x90000000)
+
+/*
+ * Bank 2 (NAND)
+ */
+#define FSMC_Bank2_MAP_COMMON (FSMC_Bank2_MAP_BASE + 0)
+#define FSMC_Bank2_MAP_ATTR (FSMC_Bank2_MAP_BASE + 0x8000000)
+
+#define FSMC_Bank2_MAP_COMMON_DATA (FSMC_Bank2_MAP_COMMON + 0)
+#define FSMC_Bank2_MAP_COMMON_CMD (FSMC_Bank2_MAP_COMMON + 0x10000)
+#define FSMC_Bank2_MAP_COMMON_ADDR (FSMC_Bank2_MAP_COMMON + 0x20000)
+
+#define FSMC_Bank2_MAP_ATTR_DATA (FSMC_Bank2_MAP_ATTR + 0)
+#define FSMC_Bank2_MAP_ATTR_CMD (FSMC_Bank2_MAP_ATTR + 0x10000)
+#define FSMC_Bank2_MAP_ATTR_ADDR (FSMC_Bank2_MAP_ATTR + 0x20000)
+
+/*
+ * Bank 3 (NAND)
+ */
+#define FSMC_Bank3_MAP_COMMON (FSMC_Bank3_MAP_BASE + 0)
+#define FSMC_Bank3_MAP_ATTR (FSMC_Bank3_MAP_BASE + 0x8000000)
+
+#define FSMC_Bank3_MAP_COMMON_DATA (FSMC_Bank3_MAP_COMMON + 0)
+#define FSMC_Bank3_MAP_COMMON_CMD (FSMC_Bank3_MAP_COMMON + 0x10000)
+#define FSMC_Bank3_MAP_COMMON_ADDR (FSMC_Bank3_MAP_COMMON + 0x20000)
+
+#define FSMC_Bank3_MAP_ATTR_DATA (FSMC_Bank3_MAP_ATTR + 0)
+#define FSMC_Bank3_MAP_ATTR_CMD (FSMC_Bank3_MAP_ATTR + 0x10000)
+#define FSMC_Bank3_MAP_ATTR_ADDR (FSMC_Bank3_MAP_ATTR + 0x20000)
+
+/*
+ * Bank 4 (PC card)
+ */
+#define FSMC_Bank4_MAP_COMMON (FSMC_Bank4_MAP_BASE + 0)
+#define FSMC_Bank4_MAP_ATTR (FSMC_Bank4_MAP_BASE + 0x8000000)
+#define FSMC_Bank4_MAP_IO (FSMC_Bank4_MAP_BASE + 0xC000000)
+
+/*
+ * More convenient typedefs than CMSIS has
+ */
+typedef struct {
+ __IO uint32_t PCR; /**< NAND Flash control */
+ __IO uint32_t SR; /**< NAND Flash FIFO status and interrupt */
+ __IO uint32_t PMEM; /**< NAND Flash Common memory space timing */
+ __IO uint32_t PATT; /**< NAND Flash Attribute memory space timing */
+ uint32_t RESERVED0; /**< Reserved, 0x70 */
+ __IO uint32_t ECCR; /**< NAND Flash ECC result registers */
+} FSMC_NAND_TypeDef;
+
+typedef struct {
+ __IO uint32_t PCR; /**< PC Card control */
+ __IO uint32_t SR; /**< PC Card FIFO status and interrupt */
+ __IO uint32_t PMEM; /**< PC Card Common memory space timing */
+ __IO uint32_t PATT; /**< PC Card Attribute memory space timing */
+ __IO uint32_t PIO; /**< PC Card I/O space timing */
+} FSMC_PCCard_TypeDef;
+
+/**
+ * @brief PCR register
+ */
+#define FSMC_PCR_PWAITEN ((uint32_t)0x00000002)
+#define FSMC_PCR_PBKEN ((uint32_t)0x00000004)
+#define FSMC_PCR_PTYP ((uint32_t)0x00000008)
+#define FSMC_PCR_ECCEN ((uint32_t)0x00000040)
+#define FSMC_PCR_PTYP_PCCARD 0
+#define FSMC_PCR_PTYP_NAND FSMC_PCR_PTYP
+
+/**
+ * @brief SR register
+ */
+#define FSMC_SR_IRS ((uint8_t)0x01)
+#define FSMC_SR_ILS ((uint8_t)0x02)
+#define FSMC_SR_IFS ((uint8_t)0x04)
+#define FSMC_SR_IREN ((uint8_t)0x08)
+#define FSMC_SR_ILEN ((uint8_t)0x10)
+#define FSMC_SR_IFEN ((uint8_t)0x20)
+#define FSMC_SR_FEMPT ((uint8_t)0x40)
+#define FSMC_SR_ISR_MASK (FSMC_SR_IRS | FSMC_SR_ILS | FSMC_SR_IFS)
+
+/**
+ * @brief RCR register
+ */
+#define FSMC_BCR_MBKEN ((uint32_t)0x00000001)
+#define FSMC_BCR_MUXEN ((uint32_t)0x00000002)
+#define FSMC_BCR_FACCEN ((uint32_t)0x00000040)
+#define FSMC_BCR_BURSTEN ((uint32_t)0x00000100)
+#define FSMC_BCR_WAITPOL ((uint32_t)0x00000200)
+#define FSMC_BCR_WRAPMOD ((uint32_t)0x00000400)
+#define FSMC_BCR_WAITCFG ((uint32_t)0x00000800)
+#define FSMC_BCR_WREN ((uint32_t)0x00001000)
+#define FSMC_BCR_WAITEN ((uint32_t)0x00002000)
+#define FSMC_BCR_EXTMOD ((uint32_t)0x00004000)
+#define FSMC_BCR_ASYNCWAIT ((uint32_t)0x00008000)
+#define FSMC_BCR_CBURSTRW ((uint32_t)0x00080000)
+
+/*===========================================================================*/
+/* Driver pre-compile time settings. */
+/*===========================================================================*/
+
+/**
+ * @name Configuration options
+ * @{
+ */
+/**
+ * @brief EMC driver enable switch.
+ * @details If set to @p TRUE the support for EMC is included.
+ */
+#if !defined(STM32_EMC_USE_FSMC1) || defined(__DOXYGEN__)
+#define STM32_EMC_USE_FSMC1 FALSE
+#endif
+
+/**
+ * @brief Internal FSMC interrupt enable switch
+ * @details MCUs in 100-pin package has no dedicated interrupt pin for FSMC.
+ * You have to use EXTI module instead to workaround this issue.
+ */
+#if STM32_EMC_EMCNAND_USE_FSMC_INT
+#define STM32_EMC_USE_INT TRUE
+#else
+#define STM32_EMC_USE_INT FALSE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/* Derived constants and error checks. */
+/*===========================================================================*/
+#if !STM32_EMC_USE_FSMC1
+#error "EMC driver activated but no EMC peripheral assigned"
+#endif
+
+/*===========================================================================*/
+/* Driver data structures and types. */
+/*===========================================================================*/
+
+/**
+ * @brief Type of a structure representing an EMC driver.
+ */
+typedef struct EMCDriver EMCDriver;
+
+/**
+ * @brief Driver configuration structure.
+ * @note Empty on this architecture.
+ */
+typedef struct {
+
+} EMCConfig;
+
+/**
+ * @brief Structure representing an EMC driver.
+ */
+struct EMCDriver {
+ /**
+ * @brief Driver state.
+ */
+ emcstate_t state;
+ /**
+ * @brief Current configuration data.
+ */
+ const EMCConfig *config;
+ /* End of the mandatory fields.*/
+#if STM32_EMCNAND_USE_EMCNAND1
+ FSMC_NAND_TypeDef *nand1;
+#endif
+#if STM32_EMCNAND_USE_EMCNAND2
+ FSMC_NAND_TypeDef *nand2;
+#endif
+#if STM32_USE_EMC_PCCARD
+ FSMC_PCCard_TypeDef *pccard;
+#endif
+};
+
+/*===========================================================================*/
+/* Driver macros. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* External declarations. */
+/*===========================================================================*/
+
+#if STM32_EMC_USE_FSMC1 && !defined(__DOXYGEN__)
+extern EMCDriver EMCD1;
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ void emc_lld_init(void);
+ void emc_lld_start(EMCDriver *emcp);
+ void emc_lld_stop(EMCDriver *emcp);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* HAL_USE_EMC */
+
+#endif /* _EMC_LLD_H_ */
+
+/** @} */
diff --git a/os/hal/ports/STM32/LLD/emcnand_lld.c b/os/hal/ports/STM32/LLD/emcnand_lld.c
new file mode 100644
index 000000000..242b47a14
--- /dev/null
+++ b/os/hal/ports/STM32/LLD/emcnand_lld.c
@@ -0,0 +1,555 @@
+/*
+ ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010,
+ 2011,2012,2013 Giovanni Di Sirio.
+
+ This file is part of ChibiOS/RT.
+
+ ChibiOS/RT is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ ChibiOS/RT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+/*
+ Concepts and parts of this file have been contributed by Uladzimir Pylinsky
+ aka barthess.
+ */
+
+/**
+ * @file emcnand_lld.c
+ * @brief EMCNAND Driver subsystem low level driver source template.
+ *
+ * @addtogroup EMCNAND
+ * @{
+ */
+
+#include "hal.h"
+
+#if HAL_USE_EMCNAND || defined(__DOXYGEN__)
+
+/*===========================================================================*/
+/* Driver local definitions. */
+/*===========================================================================*/
+#define EMCNAND_DMA_CHANNEL \
+ STM32_DMA_GETCHANNEL(STM32_EMCNAND_EMCNAND1_DMA_STREAM, \
+ STM32_EMC_DMA_CHN)
+
+/*===========================================================================*/
+/* Driver exported variables. */
+/*===========================================================================*/
+
+/**
+ * @brief EMCNAND1 driver identifier.
+ */
+#if STM32_EMCNAND_USE_EMCNAND1 || defined(__DOXYGEN__)
+EMCNANDDriver EMCNANDD1;
+#endif
+
+/**
+ * @brief EMCNAND2 driver identifier.
+ */
+#if STM32_EMCNAND_USE_EMCNAND2 || defined(__DOXYGEN__)
+EMCNANDDriver EMCNANDD2;
+#endif
+
+/*===========================================================================*/
+/* Driver local types. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver local variables and types. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver local functions. */
+/*===========================================================================*/
+/**
+ * @brief Wakes up the waiting thread.
+ *
+ * @param[in] emcnandp pointer to the @p EMCNANDDriver object
+ * @param[in] msg wakeup message
+ *
+ * @notapi
+ */
+static void wakeup_isr(EMCNANDDriver *emcnandp, msg_t msg){
+
+ osalDbgCheck(emcnandp->thread != NULL);
+
+ if (emcnandp->thread) {
+ thread_t *tp = emcnandp->thread;
+ emcnandp->thread = NULL;
+ tp->p_u.rdymsg = msg;
+ chSchReadyI(tp);
+ }
+}
+
+/**
+ * @brief Put calling thread in suspend and switch driver state
+ *
+ * @param[in] emcnandp pointer to the @p EMCNANDDriver object
+ */
+static void emcnand_lld_suspend_thread(EMCNANDDriver *emcnandp) {
+
+ emcnandp->thread = chThdGetSelfX();
+ chSchGoSleepS(CH_STATE_SUSPENDED);
+}
+
+/**
+ * @brief Caclulate ECCPS register value
+ *
+ * @param[in] emcnandp pointer to the @p EMCNANDDriver object
+ */
+static uint32_t calc_eccps(EMCNANDDriver *emcnandp){
+
+ uint32_t i = 0;
+ uint32_t eccps = emcnandp->config->page_data_size;
+
+ eccps = eccps >> 9;
+ while (eccps > 0){
+ i++;
+ eccps >>= 1;
+ }
+
+ return i << 17;
+}
+
+/*===========================================================================*/
+/* Driver interrupt handlers. */
+/*===========================================================================*/
+
+#if STM32_EMC_USE_INT
+/**
+ * @brief Enable interrupts from FSMC
+ *
+ * @param[in] emcnandp pointer to the @p EMCNANDDriver object
+ *
+ * @notapi
+ */
+static void emcnand_ready_isr_enable(EMCNANDDriver *emcnandp) {
+ emcnandp->nand->SR |= FSMC_SR_IREN;
+ chDbgPanic("Function untested");
+}
+
+/**
+ * @brief Disable interrupts from FSMC
+ *
+ * @param[in] emcnandp pointer to the @p EMCNANDDriver object
+ *
+ * @notapi
+ */
+static void emcnand_ready_isr_disable(EMCNANDDriver *emcnandp) {
+ emcnandp->nand->SR &= ~FSMC_SR_IREN;
+ chDbgPanic("Function untested");
+}
+
+/**
+ * @brief Ready interrupt handler
+ *
+ * @param[in] emcnandp pointer to the @p EMCNANDDriver object
+ * @param[in] flags flags passed from FSMC intrrupt handler
+ *
+ * @notapi
+ */
+static void emcnand_isr_handler (EMCNANDDriver *emcnandp,
+ emcnandflags_t flags){
+ (void)emcnandp;
+ (void)flags;
+
+ chDbgPanic("Unrealized");
+}
+#else /* STM32_EMC_USE_INT */
+/**
+ * @brief Disable interrupts from EXTI
+ *
+ * @param[in] emcnandp pointer to the @p EMCNANDDriver object
+ *
+ * @notapi
+ */
+static void emcnand_ready_isr_enable(EMCNANDDriver *emcnandp) {
+ emcnandp->config->ext_isr_enable();
+}
+
+/**
+ * @brief Enable interrupts from EXTI
+ *
+ * @param[in] emcnandp pointer to the @p EMCNANDDriver object
+ *
+ * @notapi
+ */
+static void emcnand_ready_isr_disable(EMCNANDDriver *emcnandp) {
+ emcnandp->config->ext_isr_disable();
+}
+
+/**
+ * @brief Ready pin interrupt handler.
+ *
+ * @param[in] emcnandp pointer to the @p EMCNANDDriver object
+ *
+ * @notapi
+ */
+static void emcnand_isr_handler(EMCNANDDriver *emcnandp){
+
+ osalSysLockFromISR();
+
+ switch (emcnandp->state){
+ case EMCNAND_READ:
+ emcnandp->state = EMCNAND_DMA_RX;
+ dmaStartMemCopy(emcnandp->dma, emcnandp->dmamode,
+ emcnandp->map_data, emcnandp->rxdata, emcnandp->datalen);
+ /* thread will be woked up from DMA ISR */
+ break;
+
+ case EMCNAND_ERASE:
+ /* NAND reports about erase finish */
+ emcnandp->state = EMCNAND_READY;
+ wakeup_isr(emcnandp, MSG_OK);
+ break;
+
+ case EMCNAND_PROGRAM:
+ /* NAND reports about page programming finish */
+ emcnandp->state = EMCNAND_READY;
+ wakeup_isr(emcnandp, MSG_OK);
+ break;
+
+ default:
+ osalSysHalt("Unhandled case");
+ break;
+ }
+
+ osalSysUnlockFromISR();
+}
+#endif /* STM32_EMC_USE_INT */
+
+/**
+ * @brief DMA RX end IRQ handler.
+ *
+ * @param[in] emcnandp pointer to the @p EMCNANDDriver object
+ * @param[in] flags pre-shifted content of the ISR register
+ *
+ * @notapi
+ */
+static void emcnand_lld_serve_transfer_end_irq(EMCNANDDriver *emcnandp,
+ uint32_t flags) {
+ /* DMA errors handling.*/
+#if defined(STM32_EMCNAND_DMA_ERROR_HOOK)
+ if ((flags & (STM32_DMA_ISR_TEIF | STM32_DMA_ISR_DMEIF)) != 0) {
+ STM32_EMCNAND_DMA_ERROR_HOOK(emcnandp);
+ }
+#else
+ (void)flags;
+#endif
+
+ osalSysLockFromISR();
+
+ dmaStreamDisable(emcnandp->dma);
+
+ switch (emcnandp->state){
+ case EMCNAND_DMA_TX:
+ emcnandp->state = EMCNAND_PROGRAM;
+ emcnandp->map_cmd[0] = NAND_CMD_PAGEPROG;
+ /* thread will be woken from ready_isr() */
+ break;
+
+ case EMCNAND_DMA_RX:
+ emcnandp->state = EMCNAND_READY;
+ emcnandp->rxdata = NULL;
+ emcnandp->datalen = 0;
+ wakeup_isr(emcnandp, MSG_OK);
+ break;
+
+ default:
+ osalSysHalt("Unhandled case");
+ break;
+ }
+
+ osalSysUnlockFromISR();
+}
+
+/*===========================================================================*/
+/* Driver exported functions. */
+/*===========================================================================*/
+
+/**
+ * @brief Low level EMCNAND driver initialization.
+ *
+ * @notapi
+ */
+void emcnand_lld_init(void) {
+
+#if STM32_EMCNAND_USE_EMCNAND1
+ /* Driver initialization.*/
+ emcnandObjectInit(&EMCNANDD1);
+ EMCNANDD1.rxdata = NULL;
+ EMCNANDD1.datalen = 0;
+ EMCNANDD1.thread = NULL;
+ EMCNANDD1.dma = STM32_DMA_STREAM(STM32_EMCNAND_EMCNAND1_DMA_STREAM);
+ EMCNANDD1.nand = (FSMC_NAND_TypeDef *)FSMC_Bank2_R_BASE;
+ EMCNANDD1.map_data = (uint8_t*)FSMC_Bank2_MAP_COMMON_DATA;
+ EMCNANDD1.map_cmd = (uint8_t*)FSMC_Bank2_MAP_COMMON_CMD;
+ EMCNANDD1.map_addr = (uint8_t*)FSMC_Bank2_MAP_COMMON_ADDR;
+#endif /* STM32_EMCNAND_USE_EMCNAND1 */
+
+#if STM32_EMCNAND_USE_EMCNAND2
+ /* Driver initialization.*/
+ #warning "Untested"
+ emcnandObjectInit(&EMCNANDD1);
+ EMCNANDD2.rxdata = NULL;
+ EMCNANDD2.datalen = 0;
+ EMCNANDD2.thread = NULL;
+ EMCNANDD2.dma = STM32_DMA_STREAM(STM32_EMCNAND_EMCNAND2_DMA_STREAM);
+ EMCNANDD2.nand = (FSMC_NAND_TypeDef *)FSMC_Bank3_R_BASE;
+ EMCNANDD2.map_data = (uint8_t*)FSMC_Bank3_MAP_COMMON_DATA;
+ EMCNANDD2.map_cmd = (uint8_t*)FSMC_Bank3_MAP_COMMON_CMD;
+ EMCNANDD2.map_addr = (uint8_t*)FSMC_Bank3_MAP_COMMON_ADDR;
+#endif /* STM32_EMCNAND_USE_EMCNAND2 */
+}
+
+/**
+ * @brief Configures and activates the EMCNAND peripheral.
+ *
+ * @param[in] emcnandp pointer to the @p EMCNANDDriver object
+ *
+ * @notapi
+ */
+void emcnand_lld_start(EMCNANDDriver *emcnandp) {
+
+ bool_t b;
+
+ if (emcnandp->state == EMCNAND_STOP) {
+ b = dmaStreamAllocate(emcnandp->dma,
+ STM32_EMC_FSMC1_IRQ_PRIORITY,
+ (stm32_dmaisr_t)emcnand_lld_serve_transfer_end_irq,
+ (void *)emcnandp);
+ osalDbgAssert(!b, "stream already allocated");
+ emcnandp->dmamode = STM32_DMA_CR_CHSEL(EMCNAND_DMA_CHANNEL) |
+ STM32_DMA_CR_PL(STM32_EMCNAND_EMCNAND1_DMA_PRIORITY) |
+ STM32_DMA_CR_PSIZE_BYTE | STM32_DMA_CR_MSIZE_BYTE |
+ STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE |
+ STM32_DMA_CR_TCIE;
+ /* dmaStreamSetFIFO(emcnandp->dma,
+ STM32_DMA_FCR_DMDIS | EMCNAND_STM32_DMA_FCR_FTH_LVL); */
+ emcnandp->nand->PCR = calc_eccps(emcnandp) | FSMC_PCR_PTYP | FSMC_PCR_PBKEN;
+ emcnandp->nand->PMEM = emcnandp->config->pmem;
+ emcnandp->nand->PATT = emcnandp->config->pmem;
+ emcnandp->isr_handler = emcnand_isr_handler;
+ emcnand_ready_isr_enable(emcnandp);
+ }
+}
+
+/**
+ * @brief Deactivates the EMCNAND peripheral.
+ *
+ * @param[in] emcnandp pointer to the @p EMCNANDDriver object
+ *
+ * @notapi
+ */
+void emcnand_lld_stop(EMCNANDDriver *emcnandp) {
+
+ if (emcnandp->state == EMCNAND_READY) {
+ dmaStreamRelease(emcnandp->dma);
+ emcnandp->nand->PCR &= ~FSMC_PCR_PBKEN;
+ emcnand_ready_isr_disable(emcnandp);
+ emcnandp->isr_handler = NULL;
+ }
+}
+
+/**
+ * @brief Read data from NAND.
+ *
+ * @param[in] emcnandp pointer to the @p EMCNANDDriver object
+ * @param[out] data pointer to data buffer
+ * @param[in] datalen size of data buffer
+ * @param[in] addr pointer to address buffer
+ * @param[in] addrlen length of address
+ * @param[out] ecc pointer to store computed ECC. Ignored when NULL.
+ *
+ * @notapi
+ */
+void emcnand_lld_read_data(EMCNANDDriver *emcnandp, uint8_t *data,
+ size_t datalen, uint8_t *addr, size_t addrlen, uint32_t *ecc){
+
+ emcnandp->state = EMCNAND_READ;
+ emcnandp->rxdata = data;
+ emcnandp->datalen = datalen;
+
+ emcnand_lld_write_cmd (emcnandp, NAND_CMD_READ0);
+ emcnand_lld_write_addr(emcnandp, addr, addrlen);
+ osalSysLock();
+ emcnand_lld_write_cmd (emcnandp, NAND_CMD_READ0_CONFIRM);
+
+ /* Here NAND asserts busy signal and starts transferring from memory
+ array to page buffer. After the end of transmission ready_isr functions
+ starts DMA transfer from page buffer to MCU's RAM.*/
+ osalDbgAssert((emcnandp->nand->PCR & FSMC_PCR_ECCEN) == 0,
+ "State machine broken. ECCEN must be previously disabled.");
+
+ if (NULL != ecc){
+ emcnandp->nand->PCR |= FSMC_PCR_ECCEN;
+ }
+
+ emcnand_lld_suspend_thread(emcnandp);
+ osalSysUnlock();
+
+ /* thread was woken up from DMA ISR */
+ if (NULL != ecc){
+ while (! (emcnandp->nand->SR & FSMC_SR_FEMPT))
+ ;
+ *ecc = emcnandp->nand->ECCR;
+ emcnandp->nand->PCR &= ~FSMC_PCR_ECCEN;
+ }
+}
+
+/**
+ * @brief Write data to NAND.
+ *
+ * @param[in] emcnandp pointer to the @p EMCNANDDriver object
+ * @param[in] data buffer with data to be written
+ * @param[in] datalen size of data buffer
+ * @param[in] addr pointer to address buffer
+ * @param[in] addrlen length of address
+ * @param[out] ecc pointer to store computed ECC. Ignored when NULL.
+ *
+ * @return The operation status reported by NAND IC (0x70 command).
+ *
+ * @notapi
+ */
+uint8_t emcnand_lld_write_data(EMCNANDDriver *emcnandp, const uint8_t *data,
+ size_t datalen, uint8_t *addr, size_t addrlen, uint32_t *ecc){
+
+ emcnandp->state = EMCNAND_WRITE;
+
+ emcnand_lld_write_cmd (emcnandp, NAND_CMD_WRITE);
+ osalSysLock();
+ emcnand_lld_write_addr(emcnandp, addr, addrlen);
+
+ /* Now start DMA transfer to NAND buffer and put thread in sleep state.
+ Tread will we woken up from ready ISR. */
+ emcnandp->state = EMCNAND_DMA_TX;
+ osalDbgAssert((emcnandp->nand->PCR & FSMC_PCR_ECCEN) == 0,
+ "State machine broken. ECCEN must be previously disabled.");
+
+ if (NULL != ecc){
+ emcnandp->nand->PCR |= FSMC_PCR_ECCEN;
+ }
+
+ dmaStartMemCopy(emcnandp->dma, emcnandp->dmamode,
+ data, emcnandp->map_data, datalen);
+
+ emcnand_lld_suspend_thread(emcnandp);
+ osalSysUnlock();
+
+ if (NULL != ecc){
+ while (! (emcnandp->nand->SR & FSMC_SR_FEMPT))
+ ;
+ *ecc = emcnandp->nand->ECCR;
+ emcnandp->nand->PCR &= ~FSMC_PCR_ECCEN;
+ }
+
+ return emcnand_lld_read_status(emcnandp);
+}
+
+/**
+ * @brief Erase block.
+ *
+ * @param[in] emcnandp pointer to the @p EMCNANDDriver object
+ * @param[in] addr pointer to address buffer
+ * @param[in] addrlen length of address
+ *
+ * @return The operation status reported by NAND IC (0x70 command).
+ *
+ * @notapi
+ */
+uint8_t emcnand_lld_erase(EMCNANDDriver *emcnandp,
+ uint8_t *addr, size_t addrlen){
+
+ emcnandp->state = EMCNAND_ERASE;
+
+ emcnand_lld_write_cmd (emcnandp, NAND_CMD_ERASE);
+ emcnand_lld_write_addr(emcnandp, addr, addrlen);
+ osalSysLock();
+ emcnand_lld_write_cmd (emcnandp, NAND_CMD_ERASE_CONFIRM);
+ emcnand_lld_suspend_thread(emcnandp);
+ osalSysUnlock();
+
+ return emcnand_lld_read_status(emcnandp);
+}
+
+/**
+ * @brief Read data from NAND using polling approach.
+ *
+ * @detatils Use this function to read data when no waiting expected. For
+ * Example read status word after 0x70 command
+ *
+ * @param[in] emcnandp pointer to the @p EMCNANDDriver object
+ * @param[out] data pointer to output buffer
+ * @param[in] len length of data to be read
+ *
+ * @notapi
+ */
+void emcnand_lld_polled_read_data(EMCNANDDriver *emcnandp,
+ uint8_t *data, size_t len){
+ size_t i = 0;
+
+ for (i=0; i<len; i++)
+ data[i] = emcnandp->map_data[i];
+}
+
+/**
+ * @brief Send addres to NAND.
+ *
+ * @param[in] emcnandp pointer to the @p EMCNANDDriver object
+ * @param[in] len length of address array
+ * @param[in] addr pointer to address array
+ *
+ * @notapi
+ */
+void emcnand_lld_write_addr(EMCNANDDriver *emcnandp,
+ const uint8_t *addr, size_t len){
+ size_t i = 0;
+
+ for (i=0; i<len; i++)
+ emcnandp->map_addr[i] = addr[i];
+}
+
+/**
+ * @brief Send command to NAND.
+ *
+ * @param[in] emcnandp pointer to the @p EMCNANDDriver object
+ * @param[in] cmd command value
+ *
+ * @notapi
+ */
+void emcnand_lld_write_cmd(EMCNANDDriver *emcnandp, uint8_t cmd){
+ emcnandp->map_cmd[0] = cmd;
+}
+
+/**
+ * @brief Read status byte from NAND.
+ *
+ * @param[in] emcnandp pointer to the @p EMCNANDDriver object
+ *
+ * @return Status byte.
+ *
+ * @notapi
+ */
+uint8_t emcnand_lld_read_status(EMCNANDDriver *emcnandp) {
+
+ uint8_t status[1] = {0x01}; /* presume worse */
+
+ emcnand_lld_write_cmd(emcnandp, NAND_CMD_STATUS);
+ emcnand_lld_polled_read_data(emcnandp, status, 1);
+
+ return status[0];
+}
+
+#endif /* HAL_USE_EMCNAND */
+
+/** @} */
+
diff --git a/os/hal/ports/STM32/LLD/emcnand_lld.h b/os/hal/ports/STM32/LLD/emcnand_lld.h
new file mode 100644
index 000000000..7f83a5723
--- /dev/null
+++ b/os/hal/ports/STM32/LLD/emcnand_lld.h
@@ -0,0 +1,361 @@
+/*
+ ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010,
+ 2011,2012,2013 Giovanni Di Sirio.
+
+ This file is part of ChibiOS/RT.
+
+ ChibiOS/RT is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ ChibiOS/RT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+/*
+ Concepts and parts of this file have been contributed by Uladzimir Pylinsky
+ aka barthess.
+ */
+
+/**
+ * @file emcnand_lld.h
+ * @brief EMCNAND Driver subsystem low level driver header template.
+ *
+ * @addtogroup EMCNAND
+ * @{
+ */
+
+#ifndef _EMCNAND_LLD_H_
+#define _EMCNAND_LLD_H_
+
+#if HAL_USE_EMCNAND || defined(__DOXYGEN__)
+
+/*===========================================================================*/
+/* Driver constants. */
+/*===========================================================================*/
+#define EMCNAND_MIN_PAGE_SIZE 256
+#define EMCNAND_MAX_PAGE_SIZE 8192
+#define EMCNAND_BAD_MAP_END_MARK ((uint16_t)0xFFFF)
+
+/*===========================================================================*/
+/* Driver pre-compile time settings. */
+/*===========================================================================*/
+
+/**
+ * @name Configuration options
+ * @{
+ */
+/**
+ * @brief EMD FSMC1 interrupt priority level setting.
+ */
+#if !defined(STM32_EMC_FSMC1_IRQ_PRIORITY) || defined(__DOXYGEN__)
+#define STM32_EMC_FSMC1_IRQ_PRIORITY 10
+#endif
+
+/**
+ * @brief EMCNAND driver enable switch.
+ * @details If set to @p TRUE the support for EMCNAND1 is included.
+ */
+#if !defined(STM32_EMCNAND_USE_EMCNAND1) || defined(__DOXYGEN__)
+#define STM32_EMCNAND_USE_EMCNAND1 FALSE
+#endif
+
+/**
+ * @brief EMCNAND driver enable switch.
+ * @details If set to @p TRUE the support for EMCNAND2 is included.
+ */
+#if !defined(STM32_EMCNAND_USE_EMCNAND2) || defined(__DOXYGEN__)
+#define STM32_EMCNAND_USE_EMCNAND2 FALSE
+#endif
+
+/**
+ * @brief EMCNAND DMA error hook.
+ * @note The default action for DMA errors is a system halt because DMA
+ * error can only happen because programming errors.
+ */
+#if !defined(STM32_EMCNAND_DMA_ERROR_HOOK) || defined(__DOXYGEN__)
+#define STM32_EMCNAND_DMA_ERROR_HOOK(emcnandp) osalSysHalt("DMA failure")
+#endif
+
+/**
+ * @brief EMCNAND interrupt enable switch.
+ * @details If set to @p TRUE the support for internal FSMC interrupt included.
+ */
+#if !defined(STM32_EMCNAND_USE_INT) || defined(__DOXYGEN__)
+#define STM32_EMCNAND_USE_INT FALSE
+#endif
+
+/**
+* @brief EMCNAND1 DMA priority (0..3|lowest..highest).
+*/
+#if !defined(STM32_EMCNAND_EMCNAND1_DMA_PRIORITY) || defined(__DOXYGEN__)
+#define STM32_EMCNAND_EMCNAND1_DMA_PRIORITY 0
+#endif
+
+/**
+* @brief EMCNAND2 DMA priority (0..3|lowest..highest).
+*/
+#if !defined(STM32_EMCNAND_EMCNAND2_DMA_PRIORITY) || defined(__DOXYGEN__)
+#define STM32_EMCNAND_EMCNAND2_DMA_PRIORITY 0
+#endif
+
+/**
+ * @brief DMA stream used for EMCNAND1 operations.
+ * @note This option is only available on platforms with enhanced DMA.
+ */
+#if !defined(STM32_EMCNAND_EMCNAND1_DMA_STREAM) || defined(__DOXYGEN__)
+#define STM32_EMCNAND_EMCNAND1_DMA_STREAM STM32_DMA_STREAM_ID(2, 6)
+#endif
+
+/**
+ * @brief DMA stream used for EMCNAND2 operations.
+ * @note This option is only available on platforms with enhanced DMA.
+ */
+#if !defined(STM32_EMCNAND_EMCNAND2_DMA_STREAM) || defined(__DOXYGEN__)
+#define STM32_EMCNAND_EMCNAND2_DMA_STREAM STM32_DMA_STREAM_ID(2, 7)
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/* Derived constants and error checks. */
+/*===========================================================================*/
+
+#if !STM32_EMCNAND_USE_EMCNAND1 && !STM32_EMCNAND_USE_EMCNAND2
+#error "EMCNAND driver activated but no EMCNAND peripheral assigned"
+#endif
+
+#if STM32_EMCNAND_USE_EMCNAND1 && !STM32_HAS_EMC
+#error "EMC not present in the selected device"
+#endif
+
+#if !STM32_EMCNAND_USE_INT && !HAL_USE_EXT
+#error "External interrupt controller must be enabled to use this feature"
+#endif
+
+#if STM32_EMCNAND_USE_EMCNAND1 && \
+ !STM32_DMA_IS_VALID_ID(STM32_EMCNAND_EMCNAND1_DMA_STREAM, \
+ STM32_EMC_DMA_MSK)
+#error "invalid DMA stream associated to EMCNAND"
+#endif
+
+#if STM32_EMCNAND_USE_EMCNAND2 && \
+ !STM32_DMA_IS_VALID_ID(STM32_EMCNAND_EMCNAND2_DMA_STREAM, \
+ STM32_EMC_DMA_MSK)
+#error "invalid DMA stream associated to EMCNAND"
+#endif
+
+#if !defined(STM32_DMA_REQUIRED)
+#define STM32_DMA_REQUIRED
+#endif
+
+/*===========================================================================*/
+/* Driver data structures and types. */
+/*===========================================================================*/
+
+/**
+ * @brief NAND driver condition flags type.
+ */
+typedef uint32_t emcnandflags_t;
+
+/**
+ * @brief Type of a structure representing an EMCNAND driver.
+ */
+typedef struct EMCNANDDriver EMCNANDDriver;
+
+#if STM32_EMC_USE_INT
+/**
+ * @brief Type of interrupt handler function
+ */
+typedef void (*emcnandisrhandler_t)
+ (EMCNANDDriver *emcnandp, emcnandflags_t flags);
+#else
+/**
+ * @brief Type of interrupt handler function
+ */
+typedef void (*emcnandisrhandler_t)(EMCNANDDriver *emcnandp);
+
+/**
+ * @brief Type of function switching external interrupts on and off.
+ */
+typedef void (*emcnandisrswitch_t)(void);
+#endif /* STM32_EMC_USE_INT */
+
+/**
+ * @brief Driver configuration structure.
+ * @note It could be empty on some architectures.
+ */
+typedef struct {
+ /**
+ * @brief Pointer to lower level driver.
+ */
+ EMCDriver *emcp;
+ /**
+ * @brief Number of erase blocks in NAND device.
+ */
+ uint32_t blocks;
+ /**
+ * @brief Number of data bytes in page.
+ */
+ uint32_t page_data_size;
+ /**
+ * @brief Number of spare bytes in page.
+ */
+ uint32_t page_spare_size;
+ /**
+ * @brief Number of pages in block.
+ */
+ uint32_t pages_per_block;
+#if EMCNAND_USE_BAD_MAP
+ /**
+ * @brief Pointer to bad block map.
+ * @details One bit per block. Memory for map must be allocated by user.
+ */
+ uint32_t *bb_map;
+#endif /* EMCNAND_USE_BAD_MAP */
+ /**
+ * @brief Number of write cycles for row addressing.
+ */
+ uint8_t rowcycles;
+ /**
+ * @brief Number of write cycles for column addressing.
+ */
+ uint8_t colcycles;
+
+ /* End of the mandatory fields.*/
+ /**
+ * @brief Number of wait cycles. This value will be used both for
+ * PMEM and PATTR registers
+ *
+ * @note For proper calculation procedure please look at AN2784 document
+ * from STMicroelectronics.
+ */
+ uint32_t pmem;
+#if !STM32_EMC_USE_INT
+ /**
+ * @brief Function enabling interrupts from EXTI
+ */
+ emcnandisrswitch_t ext_isr_enable;
+ /**
+ * @brief Function disabling interrupts from EXTI
+ */
+ emcnandisrswitch_t ext_isr_disable;
+#endif /* !STM32_EMC_USE_INT */
+} EMCNANDConfig;
+
+/**
+ * @brief Structure representing an EMCNAND driver.
+ */
+struct EMCNANDDriver {
+ /**
+ * @brief Driver state.
+ */
+ emcnandstate_t state;
+ /**
+ * @brief Current configuration data.
+ */
+ const EMCNANDConfig *config;
+ /**
+ * @brief Array to store bad block map.
+ */
+#if EMCNAND_USE_MUTUAL_EXCLUSION || defined(__DOXYGEN__)
+#if CH_CFG_USE_MUTEXES || defined(__DOXYGEN__)
+ /**
+ * @brief Mutex protecting the bus.
+ */
+ mutex_t mutex;
+#elif CH_CFG_USE_SEMAPHORES
+ semaphore_t semaphore;
+#endif
+#endif /* EMCNAND_USE_MUTUAL_EXCLUSION */
+ /* End of the mandatory fields.*/
+ /**
+ * @brief Function enabling interrupts from FSMC
+ */
+ emcnandisrhandler_t isr_handler;
+ /**
+ * @brief Pointer to current transaction buffer
+ */
+ uint8_t *rxdata;
+ /**
+ * @brief Current transaction length
+ */
+ size_t datalen;
+ /**
+ * @brief DMA mode bit mask.
+ */
+ uint32_t dmamode;
+ /**
+ * @brief DMA channel.
+ */
+ const stm32_dma_stream_t *dma;
+ /**
+ * @brief Thread waiting for I/O completion.
+ */
+ thread_t *thread;
+ /**
+ * @brief Pointer to the FSMC NAND registers block.
+ */
+ FSMC_NAND_TypeDef *nand;
+ /**
+ * @brief Memory mapping for data.
+ */
+ uint8_t *map_data;
+ /**
+ * @brief Memory mapping for commands.
+ */
+ uint8_t *map_cmd;
+ /**
+ * @brief Memory mapping for addresses.
+ */
+ uint8_t *map_addr;
+};
+
+/*===========================================================================*/
+/* Driver macros. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* External declarations. */
+/*===========================================================================*/
+
+#if STM32_EMCNAND_USE_EMCNAND1 && !defined(__DOXYGEN__)
+extern EMCNANDDriver EMCNANDD1;
+#endif
+
+#if STM32_EMCNAND_USE_EMCNAND2 && !defined(__DOXYGEN__)
+extern EMCNANDDriver EMCNANDD2;
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ void emcnand_lld_init(void);
+ void emcnand_lld_start(EMCNANDDriver *emcnandp);
+ void emcnand_lld_stop(EMCNANDDriver *emcnandp);
+ uint8_t emcnand_lld_write_data(EMCNANDDriver *emcnandp, const uint8_t *data,
+ size_t datalen, uint8_t *addr, size_t addrlen, uint32_t *ecc);
+ void emcnand_lld_read_data(EMCNANDDriver *emcnandp, uint8_t *data,
+ size_t datalen, uint8_t *addr, size_t addrlen, uint32_t *ecc);
+ void emcnand_lld_polled_read_data(EMCNANDDriver *emcnandp, uint8_t *data,
+ size_t len);
+ uint8_t emcnand_lld_erase(EMCNANDDriver *emcnandp, uint8_t *addr,
+ size_t addrlen);
+ void emcnand_lld_write_addr(EMCNANDDriver *emcnandp,
+ const uint8_t *addr, size_t len);
+ void emcnand_lld_write_cmd(EMCNANDDriver *emcnandp, uint8_t cmd);
+ uint8_t emcnand_lld_read_status(EMCNANDDriver *emcnandp);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* HAL_USE_EMCNAND */
+
+#endif /* _EMCNAND_LLD_H_ */
+
+/** @} */
diff --git a/os/hal/ports/STM32/STM32F4xx/platform.mk b/os/hal/ports/STM32/STM32F4xx/platform.mk
index c9b74b45c..00dd5ccb4 100644
--- a/os/hal/ports/STM32/STM32F4xx/platform.mk
+++ b/os/hal/ports/STM32/STM32F4xx/platform.mk
@@ -20,7 +20,9 @@ PLATFORMSRC = ${CHIBIOS}/os/hal/ports/common/ARMCMx/nvic.c \
${CHIBIOS}/os/hal/ports/STM32/LLD/TIMv1/pwm_lld.c \
${CHIBIOS}/os/hal/ports/STM32/LLD/TIMv1/st_lld.c \
${CHIBIOS}/os/hal/ports/STM32/LLD/USARTv1/serial_lld.c \
- ${CHIBIOS}/os/hal/ports/STM32/LLD/USARTv1/uart_lld.c
+ ${CHIBIOS}/os/hal/ports/STM32/LLD/USARTv1/uart_lld.c \
+ ${CHIBIOS}/os/hal/ports/STM32/LLD/emc_lld.c \
+ ${CHIBIOS}/os/hal/ports/STM32/LLD/emcnand_lld.c
# Required include directories
PLATFORMINC = ${CHIBIOS}/os/hal/ports/common/ARMCMx \
diff --git a/os/hal/ports/STM32/STM32F4xx/stm32_rcc.h b/os/hal/ports/STM32/STM32F4xx/stm32_rcc.h
index 5d66fffa5..fa9fe14bd 100644
--- a/os/hal/ports/STM32/STM32F4xx/stm32_rcc.h
+++ b/os/hal/ports/STM32/STM32F4xx/stm32_rcc.h
@@ -1382,6 +1382,35 @@
* @api
*/
#define rccResetLTDC() rccResetAPB2(RCC_APB2RSTR_LTDCRST)
+
+/**
+ * @name FSMC peripherals specific RCC operations
+ * @{
+ */
+/**
+ * @brief Enables the FSMS peripheral clock.
+ *
+ * @param[in] lp low power enable flag
+ *
+ * @api
+ */
+#define rccEnableFSMC(lp) rccEnableAHB3(RCC_AHB3ENR_FSMCEN, lp)
+
+/**
+ * @brief Disables the FSMC peripheral clock.
+ *
+ * @param[in] lp low power enable flag
+ *
+ * @api
+ */
+#define rccDisableFSMC(lp) rccDisableAHB3(RCC_AHB3ENR_FSMCEN, lp)
+
+/**
+ * @brief Resets the FSMC peripheral.
+ *
+ * @api
+ */
+#define rccResetFSMC() rccResetAHB3(RCC_AHB3RSTR_FSMCRST)
/** @} */
/*===========================================================================*/
diff --git a/os/hal/ports/STM32/STM32F4xx/stm32_registry.h b/os/hal/ports/STM32/STM32F4xx/stm32_registry.h
index 78a8d783a..816ae0353 100644
--- a/os/hal/ports/STM32/STM32F4xx/stm32_registry.h
+++ b/os/hal/ports/STM32/STM32F4xx/stm32_registry.h
@@ -325,6 +325,18 @@
#else /* defined(STM32F401xx) */
#define STM32_HAS_OTG2 FALSE
#endif /* defined(STM32F401xx) */
+
+/* EMC attributes.*/
+#define STM32_HAS_EMC TRUE
+#define STM32_EMC_DMA_MSK (STM32_DMA_STREAM_ID_MSK(2, 0) | \
+ STM32_DMA_STREAM_ID_MSK(2, 1) | \
+ STM32_DMA_STREAM_ID_MSK(2, 2) | \
+ STM32_DMA_STREAM_ID_MSK(2, 3) | \
+ STM32_DMA_STREAM_ID_MSK(2, 4) | \
+ STM32_DMA_STREAM_ID_MSK(2, 5) | \
+ STM32_DMA_STREAM_ID_MSK(2, 6) | \
+ STM32_DMA_STREAM_ID_MSK(2, 7))
+#define STM32_EMC_DMA_CHN 0x03010201
/** @} */
#endif /* _STM32_REGISTRY_H_ */
diff --git a/os/hal/src/emc.c b/os/hal/src/emc.c
new file mode 100644
index 000000000..61948b5d2
--- /dev/null
+++ b/os/hal/src/emc.c
@@ -0,0 +1,128 @@
+/*
+ ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010,
+ 2011,2012,2013 Giovanni Di Sirio.
+
+ This file is part of ChibiOS/RT.
+
+ ChibiOS/RT is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ ChibiOS/RT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+/*
+ Concepts and parts of this file have been contributed by Uladzimir Pylinsky
+ aka barthess.
+ */
+
+/**
+ * @file emc.c
+ * @brief EMC Driver code.
+ *
+ * @addtogroup EMC
+ * @{
+ */
+
+#include "hal.h"
+
+#if HAL_USE_EMC || defined(__DOXYGEN__)
+
+/*===========================================================================*/
+/* Driver local definitions. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver exported variables. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver local types. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver local variables. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver local functions. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver exported functions. */
+/*===========================================================================*/
+
+/**
+ * @brief EMC Driver initialization.
+ * @note This function is implicitly invoked by @p halInit(), there is
+ * no need to explicitly initialize the driver.
+ *
+ * @init
+ */
+void emcInit(void) {
+
+ emc_lld_init();
+}
+
+/**
+ * @brief Initializes the standard part of a @p EMCDriver structure.
+ *
+ * @param[out] emcp pointer to the @p EMCDriver object
+ *
+ * @init
+ */
+void emcObjectInit(EMCDriver *emcp) {
+
+ emcp->state = EMC_STOP;
+ emcp->config = NULL;
+}
+
+/**
+ * @brief Configures and activates the EMC peripheral.
+ *
+ * @param[in] emcp pointer to the @p EMCDriver object
+ * @param[in] config pointer to the @p EMCConfig object
+ *
+ * @api
+ */
+void emcStart(EMCDriver *emcp, const EMCConfig *config) {
+
+ osalDbgCheck((emcp != NULL) && (config != NULL));
+
+ osalSysLock();
+ osalDbgAssert((emcp->state == EMC_STOP) || (emcp->state == EMC_READY),
+ "invalid state");
+ emcp->config = config;
+ emc_lld_start(emcp);
+ emcp->state = EMC_READY;
+ osalSysUnlock();
+}
+
+/**
+ * @brief Deactivates the EMC peripheral.
+ *
+ * @param[in] emcp pointer to the @p EMCDriver object
+ *
+ * @api
+ */
+void emcStop(EMCDriver *emcp) {
+
+ osalDbgCheck(emcp != NULL);
+
+ osalSysLock();
+ osalDbgAssert((emcp->state == EMC_STOP) || (emcp->state == EMC_READY),
+ "invalid state");
+ emc_lld_stop(emcp);
+ emcp->state = EMC_STOP;
+ osalSysUnlock();
+}
+
+#endif /* HAL_USE_EMC */
+
+/** @} */
diff --git a/os/hal/src/emcnand.c b/os/hal/src/emcnand.c
new file mode 100644
index 000000000..f68a4b6e7
--- /dev/null
+++ b/os/hal/src/emcnand.c
@@ -0,0 +1,603 @@
+/*
+ ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010,
+ 2011,2012,2013 Giovanni Di Sirio.
+
+ This file is part of ChibiOS/RT.
+
+ ChibiOS/RT is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ ChibiOS/RT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+/*
+ Concepts and parts of this file have been contributed by Uladzimir Pylinsky
+ aka barthess.
+ */
+
+/**
+ * @file emcnand.c
+ * @brief EMCNAND Driver code.
+ *
+ * @addtogroup EMCNAND
+ * @{
+ */
+
+#include "hal.h"
+
+#if HAL_USE_EMCNAND || defined(__DOXYGEN__)
+
+#include "string.h" /* for memset */
+
+/*===========================================================================*/
+/* Driver local definitions. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver exported variables. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver local types. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver local variables. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver local functions. */
+/*===========================================================================*/
+
+/**
+ * @brief Check page size.
+ *
+ * @param[in] page_data_size size of page data area
+ *
+ * @notapi
+ */
+static void pagesize_check(size_t page_data_size){
+
+ /* Page size out of bounds.*/
+ osalDbgCheck((page_data_size >= EMCNAND_MIN_PAGE_SIZE) &&
+ (page_data_size <= EMCNAND_MAX_PAGE_SIZE));
+
+ /* Page size must be power of 2.*/
+ osalDbgCheck(((page_data_size - 1) & page_data_size) == 0);
+}
+
+/**
+ * @brief Translate block-page-offset scheme to NAND internal address.
+ *
+ * @param[in] cfg pointer to the @p EMCNANDConfig from
+ * corresponding NAND driver
+ * @param[in] block block number
+ * @param[in] page page number related to begin of block
+ * @param[in] offset data offset related to begin of page
+ * @param[out] addr buffer to store calculated address
+ * @param[in] addr_len length of address buffer
+ *
+ * @notapi
+ */
+static void calc_addr(const EMCNANDConfig *cfg,
+ uint32_t block, uint32_t page, uint32_t offset,
+ uint8_t *addr, size_t addr_len){
+ size_t i = 0;
+ uint32_t row = 0;
+
+ /* Incorrect buffer length.*/
+ osalDbgCheck(cfg->rowcycles + cfg->colcycles == addr_len);
+ osalDbgCheck((block < cfg->blocks) && (page < cfg->pages_per_block) &&
+ (offset < cfg->page_data_size + cfg->page_spare_size));
+
+ /* convert address to NAND specific */
+ memset(addr, 0, addr_len);
+ row = (block * cfg->pages_per_block) + page;
+ for (i=0; i<cfg->colcycles; i++){
+ addr[i] = offset & 0xFF;
+ offset = offset >> 8;
+ }
+ for (; i<addr_len; i++){
+ addr[i] = row & 0xFF;
+ row = row >> 8;
+ }
+}
+
+/**
+ * @brief Translate block number to NAND internal address.
+ * @note This function designed for erasing purpose.
+ *
+ * @param[in] cfg pointer to the @p EMCNANDConfig from
+ * corresponding NAND driver
+ * @param[in] block block number
+ * @param[out] addr buffer to store calculated address
+ * @param[in] addr_len length of address buffer
+ *
+ * @notapi
+ */
+static void calc_blk_addr(const EMCNANDConfig *cfg,
+ uint32_t block, uint8_t *addr, size_t addr_len){
+ size_t i = 0;
+ uint32_t row = 0;
+
+ /* Incorrect buffer length.*/
+ osalDbgCheck(cfg->rowcycles == addr_len);
+ osalDbgCheck((block < cfg->blocks));
+
+ /* convert address to NAND specific */
+ memset(addr, 0, addr_len);
+ row = block * cfg->pages_per_block;
+ for (i=0; i<addr_len; i++){
+ addr[i] = row & 0xFF;
+ row = row >> 8;
+ }
+}
+
+#if EMCNAND_USE_BAD_MAP
+/**
+ * @brief Add new bad block to map.
+ *
+ * @param[in] emcnandp pointer to the @p EMCNANDDriver object
+ * @param[in] block block number
+ * @param[in] map pointer to bad block map
+ */
+static void bad_map_update(EMCNANDDriver *emcnandp, size_t block) {
+
+ uint32_t *map = emcnandp->config->bb_map;
+ const size_t BPMC = sizeof(uint32_t) * 8; /* bits per map claster */
+ size_t i;
+ size_t shift;
+
+ /* Nand device overflow.*/
+ osalDbgCheck(emcnandp->config->blocks > block);
+
+ i = block / BPMC;
+ shift = block % BPMC;
+ /* This block already mapped.*/
+ osalDbgCheck(((map[i] >> shift) & 1) != 1);
+ map[i] |= (uint32_t)1 << shift;
+}
+
+/**
+ * @brief Scan for bad blocks and fill map with their numbers.
+ *
+ * @param[in] emcnandp pointer to the @p EMCNANDDriver object
+ */
+static void scan_bad_blocks(EMCNANDDriver *emcnandp) {
+
+ const size_t blocks = emcnandp->config->blocks;
+ const size_t maplen = blocks / 32;
+
+ size_t b;
+ uint8_t m0;
+ uint8_t m1;
+
+ /* clear map just to be safe */
+ for (b=0; b<maplen; b++)
+ emcnandp->config->bb_map[b] = 0;
+
+ /* now write numbers of bad block to map */
+ for (b=0; b<blocks; b++){
+ m0 = emcnandReadBadMark(emcnandp, b, 0);
+ m1 = emcnandReadBadMark(emcnandp, b, 1);
+ if ((0xFF != m0) || (0xFF != m1)){
+ bad_map_update(emcnandp, b);
+ }
+ }
+}
+#endif /* EMCNAND_USE_BAD_MAP */
+
+/*===========================================================================*/
+/* Driver exported functions. */
+/*===========================================================================*/
+
+/**
+ * @brief EMCNAND Driver initialization.
+ * @note This function is implicitly invoked by @p halInit(), there is
+ * no need to explicitly initialize the driver.
+ *
+ * @init
+ */
+void emcnandInit(void) {
+
+ emcnand_lld_init();
+}
+
+/**
+ * @brief Initializes the standard part of a @p EMCNANDDriver structure.
+ *
+ * @param[out] emcnandp pointer to the @p EMCNANDDriver object
+ *
+ * @init
+ */
+void emcnandObjectInit(EMCNANDDriver *emcnandp) {
+
+#if EMCNAND_USE_MUTUAL_EXCLUSION
+#if CH_CFG_USE_MUTEXES
+ chMtxObjectInit(&emcnandp->mutex);
+#else
+ chSemObjectInit(&emcnandp->semaphore, 1);
+#endif /* CH_CFG_USE_MUTEXES */
+#endif /* EMCNAND_USE_MUTUAL_EXCLUSION */
+
+ emcnandp->state = EMCNAND_STOP;
+ emcnandp->config = NULL;
+}
+
+/**
+ * @brief Configures and activates the EMCNAND peripheral.
+ *
+ * @param[in] emcnandp pointer to the @p EMCNANDDriver object
+ * @param[in] config pointer to the @p EMCNANDConfig object
+ *
+ * @api
+ */
+void emcnandStart(EMCNANDDriver *emcnandp, const EMCNANDConfig *config) {
+
+ osalDbgCheck((emcnandp != NULL) && (config != NULL));
+ osalDbgAssert(config->emcp->state == EMC_READY,
+ "lower level driver not ready");
+ osalDbgAssert((emcnandp->state == EMCNAND_STOP) ||
+ (emcnandp->state == EMCNAND_READY),
+ "invalid state");
+
+ emcnandp->config = config;
+ pagesize_check(emcnandp->config->page_data_size);
+ emcnand_lld_start(emcnandp);
+ emcnandp->state = EMCNAND_READY;
+
+#if EMCNAND_USE_BAD_MAP
+ scan_bad_blocks(emcnandp);
+#endif /* EMCNAND_USE_BAD_MAP */
+}
+
+/**
+ * @brief Deactivates the EMCNAND peripheral.
+ *
+ * @param[in] emcnandp pointer to the @p EMCNANDDriver object
+ *
+ * @api
+ */
+void emcnandStop(EMCNANDDriver *emcnandp) {
+
+ osalDbgCheck(emcnandp != NULL);
+ osalDbgAssert((emcnandp->state == EMCNAND_STOP) ||
+ (emcnandp->state == EMCNAND_READY),
+ "invalid state");
+ emcnand_lld_stop(emcnandp);
+ emcnandp->state = EMCNAND_STOP;
+}
+
+/**
+ * @brief Read whole page.
+ *
+ * @param[in] emcnandp pointer to the @p EMCNANDDriver object
+ * @param[in] block block number
+ * @param[in] page page number related to begin of block
+ * @param[out] data buffer to store data
+ * @param[in] datalen length of data buffer
+ *
+ * @api
+ */
+void emcnandReadPageWhole(EMCNANDDriver *emcnandp, uint32_t block,
+ uint32_t page, uint8_t *data, size_t datalen) {
+
+ const EMCNANDConfig *cfg = emcnandp->config;
+ uint8_t addrbuf[8];
+ size_t addrlen = cfg->rowcycles + cfg->colcycles;
+
+ osalDbgCheck((emcnandp != NULL) && (data != NULL));
+ osalDbgCheck((datalen <= (cfg->page_data_size + cfg->page_spare_size)));
+ osalDbgAssert(emcnandp->state == EMCNAND_READY, "invalid state");
+
+ calc_addr(cfg, block, page, 0, addrbuf, addrlen);
+ emcnand_lld_read_data(emcnandp, data, datalen, addrbuf, addrlen, NULL);
+}
+
+/**
+ * @brief Write whole page.
+ *
+ * @param[in] emcnandp pointer to the @p EMCNANDDriver object
+ * @param[in] block block number
+ * @param[in] page page number related to begin of block
+ * @param[in] data buffer with data to be written
+ * @param[in] datalen length of data buffer
+ *
+ * @return The operation status reported by NAND IC (0x70 command).
+ *
+ * @api
+ */
+uint8_t emcnandWritePageWhole(EMCNANDDriver *emcnandp, uint32_t block,
+ uint32_t page, const uint8_t *data, size_t datalen) {
+
+ uint8_t retval;
+ const EMCNANDConfig *cfg = emcnandp->config;
+ uint8_t addr[8];
+ size_t addrlen = cfg->rowcycles + cfg->colcycles;
+
+ osalDbgCheck((emcnandp != NULL) && (data != NULL));
+ osalDbgCheck((datalen <= (cfg->page_data_size + cfg->page_spare_size)));
+ osalDbgAssert(emcnandp->state == EMCNAND_READY, "invalid state");
+
+ calc_addr(cfg, block, page, 0, addr, addrlen);
+ retval = emcnand_lld_write_data(emcnandp, data, datalen, addr, addrlen, NULL);
+ return retval;
+}
+
+/**
+ * @brief Read page data without spare area.
+ *
+ * @param[in] emcnandp pointer to the @p EMCNANDDriver object
+ * @param[in] block block number
+ * @param[in] page page number related to begin of block
+ * @param[out] data buffer to store data
+ * @param[in] datalen length of data buffer
+ * @param[out] ecc pointer to calculated ECC. Ignored when NULL.
+ *
+ * @api
+ */
+void emcnandReadPageData(EMCNANDDriver *emcnandp, uint32_t block, uint32_t page,
+ uint8_t *data, size_t datalen, uint32_t *ecc) {
+
+ const EMCNANDConfig *cfg = emcnandp->config;
+ uint8_t addrbuf[8];
+ size_t addrlen = cfg->rowcycles + cfg->colcycles;
+
+ osalDbgCheck((emcnandp != NULL) && (data != NULL));
+ osalDbgCheck((datalen <= cfg->page_data_size));
+ osalDbgAssert(emcnandp->state == EMCNAND_READY, "invalid state");
+
+ calc_addr(cfg, block, page, 0, addrbuf, addrlen);
+ emcnand_lld_read_data(emcnandp, data, datalen, addrbuf, addrlen, ecc);
+}
+
+/**
+ * @brief Write page data without spare area.
+ *
+ * @param[in] emcnandp pointer to the @p EMCNANDDriver object
+ * @param[in] block block number
+ * @param[in] page page number related to begin of block
+ * @param[in] data buffer with data to be written
+ * @param[in] datalen length of data buffer
+ * @param[out] ecc pointer to calculated ECC. Ignored when NULL.
+ *
+ * @return The operation status reported by NAND IC (0x70 command).
+ *
+ * @api
+ */
+uint8_t emcnandWritePageData(EMCNANDDriver *emcnandp, uint32_t block,
+ uint32_t page, const uint8_t *data, size_t datalen, uint32_t *ecc) {
+
+ uint8_t retval;
+ const EMCNANDConfig *cfg = emcnandp->config;
+ uint8_t addr[8];
+ size_t addrlen = cfg->rowcycles + cfg->colcycles;
+
+ osalDbgCheck((emcnandp != NULL) && (data != NULL));
+ osalDbgCheck((datalen <= cfg->page_data_size));
+ osalDbgAssert(emcnandp->state == EMCNAND_READY, "invalid state");
+
+ calc_addr(cfg, block, page, 0, addr, addrlen);
+ retval = emcnand_lld_write_data(emcnandp, data, datalen, addr, addrlen, ecc);
+ return retval;
+}
+
+/**
+ * @brief Read page spare area.
+ *
+ * @param[in] emcnandp pointer to the @p EMCNANDDriver object
+ * @param[in] block block number
+ * @param[in] page page number related to begin of block
+ * @param[out] spare buffer to store data
+ * @param[in] sparelen length of data buffer
+ *
+ * @api
+ */
+void emcnandReadPageSpare(EMCNANDDriver *emcnandp, uint32_t block,
+ uint32_t page, uint8_t *spare, size_t sparelen) {
+
+ const EMCNANDConfig *cfg = emcnandp->config;
+ uint8_t addr[8];
+ size_t addrlen = cfg->rowcycles + cfg->colcycles;
+
+ osalDbgCheck((NULL != spare) && (emcnandp != NULL));
+ osalDbgCheck(sparelen <= cfg->page_spare_size);
+ osalDbgAssert(emcnandp->state == EMCNAND_READY, "invalid state");
+
+ calc_addr(cfg, block, page, cfg->page_data_size, addr, addrlen);
+ emcnand_lld_read_data(emcnandp, spare, sparelen, addr, addrlen, NULL);
+}
+
+/**
+ * @brief Write page spare area.
+ *
+ * @param[in] emcnandp pointer to the @p EMCNANDDriver object
+ * @param[in] block block number
+ * @param[in] page page number related to begin of block
+ * @param[in] spare buffer with spare data to be written
+ * @param[in] sparelen length of data buffer
+ *
+ * @return The operation status reported by NAND IC (0x70 command).
+ *
+ * @api
+ */
+uint8_t emcnandWritePageSpare(EMCNANDDriver *emcnandp, uint32_t block,
+ uint32_t page, const uint8_t *spare, size_t sparelen) {
+
+ uint8_t retVal;
+ const EMCNANDConfig *cfg = emcnandp->config;
+ uint8_t addr[8];
+ size_t addrlen = cfg->rowcycles + cfg->colcycles;
+
+ osalDbgCheck((NULL != spare) && (emcnandp != NULL));
+ osalDbgCheck(sparelen <= cfg->page_spare_size);
+ osalDbgAssert(emcnandp->state == EMCNAND_READY, "invalid state");
+
+ calc_addr(cfg, block, page, cfg->page_data_size, addr, addrlen);
+ retVal = emcnand_lld_write_data(emcnandp, spare, sparelen, addr, addrlen, NULL);
+ return retVal;
+}
+
+/**
+ * @brief Mark block as bad.
+ *
+ * @param[in] emcnandp pointer to the @p EMCNANDDriver object
+ * @param[in] block block number
+ *
+ * @api
+ */
+void emcnandMarkBad(EMCNANDDriver *emcnandp, uint32_t block) {
+
+ uint8_t bb_mark[2] = {0, 0};
+ uint8_t op_status;
+ op_status = emcnandWritePageSpare(emcnandp, block, 0, bb_mark, sizeof(bb_mark));
+ osalDbgCheck(0 == (op_status & 1)); /* operation failed*/
+ op_status = emcnandWritePageSpare(emcnandp, block, 1, bb_mark, sizeof(bb_mark));
+ osalDbgCheck(0 == (op_status & 1)); /* operation failed*/
+
+#if EMCNAND_USE_BAD_MAP
+ bad_map_update(emcnandp, block);
+#endif
+}
+
+/**
+ * @brief Read bad mark out.
+ *
+ * @param[in] emcnandp pointer to the @p EMCNANDDriver object
+ * @param[in] block block number
+ * @param[in] page page number related to begin of block
+ *
+ * @return Bad mark.
+ *
+ * @api
+ */
+uint8_t emcnandReadBadMark(EMCNANDDriver *emcnandp,
+ uint32_t block, uint32_t page) {
+ uint8_t bb_mark[1];
+ emcnandReadPageSpare(emcnandp, block, page, bb_mark, sizeof(bb_mark));
+ return bb_mark[0];
+}
+
+/**
+ * @brief Erase block.
+ *
+ * @param[in] emcnandp pointer to the @p EMCNANDDriver object
+ * @param[in] block block number
+ *
+ * @return The operation status reported by NAND IC (0x70 command).
+ *
+ * @api
+ */
+uint8_t emcnandErase(EMCNANDDriver *emcnandp, uint32_t block){
+
+ uint8_t retVal;
+ const EMCNANDConfig *cfg = emcnandp->config;
+ uint8_t addr[4];
+ size_t addrlen = cfg->rowcycles;
+
+ osalDbgCheck(emcnandp != NULL);
+ osalDbgAssert(emcnandp->state == EMCNAND_READY, "invalid state");
+
+ calc_blk_addr(cfg, block, addr, addrlen);
+ retVal = emcnand_lld_erase(emcnandp, addr, addrlen);
+ return retVal;
+}
+
+/**
+ * @brief Report block badness.
+ *
+ * @param[in] emcnandp pointer to the @p EMCNANDDriver object
+ * @param[in] block block number
+ *
+ * @return block condition
+ * @retval true if the block is bad.
+ * @retval false if the block is good.
+ *
+ * @api
+ */
+bool emcnandIsBad(EMCNANDDriver *emcnandp, uint32_t block){
+
+ osalDbgCheck(emcnandp != NULL);
+ osalDbgAssert(emcnandp->state == EMCNAND_READY, "invalid state");
+
+#if EMCNAND_USE_BAD_MAP
+ uint32_t *map = emcnandp->config->bb_map;
+ const size_t BPMC = sizeof(uint32_t) * 8; /* bits per map claster */
+ size_t i;
+ size_t shift;
+
+ i = block / BPMC;
+ shift = block % BPMC;
+ if (((map[i] >> shift) & 1) == 1)
+ return true;
+ else
+ return false;
+#else
+ uint8_t m0, m1;
+ m0 = emcnandReadBadMark(emcnandp, block, 0);
+ m1 = emcnandReadBadMark(emcnandp, block, 1);
+ if ((0xFF != m0) || (0xFF != m1))
+ return true;
+ else
+ return false;
+#endif /* EMCNAND_USE_BAD_MAP */
+}
+
+#if EMCNAND_USE_MUTUAL_EXCLUSION || defined(__DOXYGEN__)
+/**
+ * @brief Gains exclusive access to the EMCNAND bus.
+ * @details This function tries to gain ownership to the EMCNAND bus, if the bus
+ * is already being used then the invoking thread is queued.
+ * @pre In order to use this function the option
+ * @p EMCNAND_USE_MUTUAL_EXCLUSION must be enabled.
+ *
+ * @param[in] emcnandp pointer to the @p EMCNANDDriver object
+ *
+ * @api
+ */
+void emcnandAcquireBus(EMCNANDDriver *emcnandp) {
+
+ osalDbgCheck(emcnandp != NULL);
+
+#if CH_CFG_USE_MUTEXES
+ chMtxLock(&emcnandp->mutex);
+#elif CH_CFG_USE_SEMAPHORES
+ chSemWait(&emcnandp->semaphore);
+#endif
+}
+
+/**
+ * @brief Releases exclusive access to the EMCNAND bus.
+ * @pre In order to use this function the option
+ * @p EMCNAND_USE_MUTUAL_EXCLUSION must be enabled.
+ *
+ * @param[in] emcnandp pointer to the @p EMCNANDDriver object
+ *
+ * @api
+ */
+void emcnandReleaseBus(EMCNANDDriver *emcnandp) {
+
+ osalDbgCheck(emcnandp != NULL);
+
+#if CH_CFG_USE_MUTEXES
+ chMtxUnlock(&emcnandp->mutex);
+#elif CH_CFG_USE_SEMAPHORES
+ chSemSignal(&emcnandp->semaphore);
+#endif
+}
+#endif /* EMCNAND_USE_MUTUAL_EXCLUSION */
+
+#endif /* HAL_USE_EMCNAND */
+
+/** @} */
+
+
+
+
diff --git a/os/hal/src/hal.c b/os/hal/src/hal.c
index f9717dd65..698e1b29b 100644
--- a/os/hal/src/hal.c
+++ b/os/hal/src/hal.c
@@ -119,7 +119,12 @@ void halInit(void) {
#if HAL_USE_RTC || defined(__DOXYGEN__)
rtcInit();
#endif
-
+#if HAL_USE_EMC || defined(__DOXYGEN__)
+ emcInit();
+#endif
+#if HAL_USE_EMCNAND || defined(__DOXYGEN__)
+ emcnandInit();
+#endif
/* Board specific initialization.*/
boardInit();