Added FSMC NAND driver

git-svn-id: svn://svn.code.sf.net/p/chibios/svn/trunk@7123 35acf78f-673a-0410-8e92-d51de3d6d3f4
master
barthess 2014-08-05 08:00:56 +00:00
parent adb00b4f8f
commit 88a09ec741
14 changed files with 2379 additions and 3 deletions

View File

@ -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

93
os/hal/include/emc.h Normal file
View File

@ -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_ */
/** @} */

152
os/hal/include/emcnand.h Normal file
View File

@ -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_ */
/** @} */

View File

@ -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"

View File

@ -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 */
/** @} */

View File

@ -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_ */
/** @} */

View File

@ -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 */
/** @} */

View File

@ -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_ */
/** @} */

View File

@ -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 \

View File

@ -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)
/** @} */
/*===========================================================================*/

View File

@ -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_ */

128
os/hal/src/emc.c Normal file
View File

@ -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 */
/** @} */

603
os/hal/src/emcnand.c Normal file
View File

@ -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 */
/** @} */

View File

@ -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();