From 88a09ec74105c34e3eb66eb5ba84f0e448ce7a2a Mon Sep 17 00:00:00 2001 From: barthess Date: Tue, 5 Aug 2014 08:00:56 +0000 Subject: [PATCH] Added FSMC NAND driver git-svn-id: svn://svn.code.sf.net/p/chibios/svn/trunk@7123 35acf78f-673a-0410-8e92-d51de3d6d3f4 --- os/hal/hal.mk | 4 +- os/hal/include/emc.h | 93 +++ os/hal/include/emcnand.h | 152 +++++ os/hal/include/hal.h | 2 + os/hal/ports/STM32/LLD/emc_lld.c | 185 ++++++ os/hal/ports/STM32/LLD/emc_lld.h | 247 +++++++ os/hal/ports/STM32/LLD/emcnand_lld.c | 555 ++++++++++++++++ os/hal/ports/STM32/LLD/emcnand_lld.h | 361 +++++++++++ os/hal/ports/STM32/STM32F4xx/platform.mk | 4 +- os/hal/ports/STM32/STM32F4xx/stm32_rcc.h | 29 + os/hal/ports/STM32/STM32F4xx/stm32_registry.h | 12 + os/hal/src/emc.c | 128 ++++ os/hal/src/emcnand.c | 603 ++++++++++++++++++ os/hal/src/hal.c | 7 +- 14 files changed, 2379 insertions(+), 3 deletions(-) create mode 100644 os/hal/include/emc.h create mode 100644 os/hal/include/emcnand.h create mode 100644 os/hal/ports/STM32/LLD/emc_lld.c create mode 100644 os/hal/ports/STM32/LLD/emc_lld.h create mode 100644 os/hal/ports/STM32/LLD/emcnand_lld.c create mode 100644 os/hal/ports/STM32/LLD/emcnand_lld.h create mode 100644 os/hal/src/emc.c create mode 100644 os/hal/src/emcnand.c 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 . +*/ +/* + 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 . +*/ +/* + 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 . +*/ +/* + 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 . +*/ +/* + 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 . +*/ +/* + 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; imap_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; imap_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 . +*/ +/* + 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 . +*/ +/* + 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 . +*/ +/* + 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; icolcycles; i++){ + addr[i] = offset & 0xFF; + offset = offset >> 8; + } + for (; i> 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> 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; bconfig->bb_map[b] = 0; + + /* now write numbers of bad block to map */ + for (b=0; bmutex); +#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();