diff options
author | barthess <barthess@35acf78f-673a-0410-8e92-d51de3d6d3f4> | 2014-08-05 08:00:56 +0000 |
---|---|---|
committer | barthess <barthess@35acf78f-673a-0410-8e92-d51de3d6d3f4> | 2014-08-05 08:00:56 +0000 |
commit | 88a09ec74105c34e3eb66eb5ba84f0e448ce7a2a (patch) | |
tree | fe8ade2bad038927e12d26ee9e39222dc974fa27 /os/hal/src | |
parent | adb00b4f8f7d34b0fa026bc8fc083e3464d6c32a (diff) | |
download | ChibiOS-88a09ec74105c34e3eb66eb5ba84f0e448ce7a2a.tar.gz ChibiOS-88a09ec74105c34e3eb66eb5ba84f0e448ce7a2a.tar.bz2 ChibiOS-88a09ec74105c34e3eb66eb5ba84f0e448ce7a2a.zip |
Added FSMC NAND driver
git-svn-id: svn://svn.code.sf.net/p/chibios/svn/trunk@7123 35acf78f-673a-0410-8e92-d51de3d6d3f4
Diffstat (limited to 'os/hal/src')
-rw-r--r-- | os/hal/src/emc.c | 128 | ||||
-rw-r--r-- | os/hal/src/emcnand.c | 603 | ||||
-rw-r--r-- | os/hal/src/hal.c | 7 |
3 files changed, 737 insertions, 1 deletions
diff --git a/os/hal/src/emc.c b/os/hal/src/emc.c new file mode 100644 index 000000000..61948b5d2 --- /dev/null +++ b/os/hal/src/emc.c @@ -0,0 +1,128 @@ +/* + ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010, + 2011,2012,2013 Giovanni Di Sirio. + + This file is part of ChibiOS/RT. + + ChibiOS/RT is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS/RT is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +/* + Concepts and parts of this file have been contributed by Uladzimir Pylinsky + aka barthess. + */ + +/** + * @file emc.c + * @brief EMC Driver code. + * + * @addtogroup EMC + * @{ + */ + +#include "hal.h" + +#if HAL_USE_EMC || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local types. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local variables. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief EMC Driver initialization. + * @note This function is implicitly invoked by @p halInit(), there is + * no need to explicitly initialize the driver. + * + * @init + */ +void emcInit(void) { + + emc_lld_init(); +} + +/** + * @brief Initializes the standard part of a @p EMCDriver structure. + * + * @param[out] emcp pointer to the @p EMCDriver object + * + * @init + */ +void emcObjectInit(EMCDriver *emcp) { + + emcp->state = EMC_STOP; + emcp->config = NULL; +} + +/** + * @brief Configures and activates the EMC peripheral. + * + * @param[in] emcp pointer to the @p EMCDriver object + * @param[in] config pointer to the @p EMCConfig object + * + * @api + */ +void emcStart(EMCDriver *emcp, const EMCConfig *config) { + + osalDbgCheck((emcp != NULL) && (config != NULL)); + + osalSysLock(); + osalDbgAssert((emcp->state == EMC_STOP) || (emcp->state == EMC_READY), + "invalid state"); + emcp->config = config; + emc_lld_start(emcp); + emcp->state = EMC_READY; + osalSysUnlock(); +} + +/** + * @brief Deactivates the EMC peripheral. + * + * @param[in] emcp pointer to the @p EMCDriver object + * + * @api + */ +void emcStop(EMCDriver *emcp) { + + osalDbgCheck(emcp != NULL); + + osalSysLock(); + osalDbgAssert((emcp->state == EMC_STOP) || (emcp->state == EMC_READY), + "invalid state"); + emc_lld_stop(emcp); + emcp->state = EMC_STOP; + osalSysUnlock(); +} + +#endif /* HAL_USE_EMC */ + +/** @} */ diff --git a/os/hal/src/emcnand.c b/os/hal/src/emcnand.c new file mode 100644 index 000000000..f68a4b6e7 --- /dev/null +++ b/os/hal/src/emcnand.c @@ -0,0 +1,603 @@ +/* + ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010, + 2011,2012,2013 Giovanni Di Sirio. + + This file is part of ChibiOS/RT. + + ChibiOS/RT is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS/RT is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +/* + Concepts and parts of this file have been contributed by Uladzimir Pylinsky + aka barthess. + */ + +/** + * @file emcnand.c + * @brief EMCNAND Driver code. + * + * @addtogroup EMCNAND + * @{ + */ + +#include "hal.h" + +#if HAL_USE_EMCNAND || defined(__DOXYGEN__) + +#include "string.h" /* for memset */ + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local types. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local variables. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +/** + * @brief Check page size. + * + * @param[in] page_data_size size of page data area + * + * @notapi + */ +static void pagesize_check(size_t page_data_size){ + + /* Page size out of bounds.*/ + osalDbgCheck((page_data_size >= EMCNAND_MIN_PAGE_SIZE) && + (page_data_size <= EMCNAND_MAX_PAGE_SIZE)); + + /* Page size must be power of 2.*/ + osalDbgCheck(((page_data_size - 1) & page_data_size) == 0); +} + +/** + * @brief Translate block-page-offset scheme to NAND internal address. + * + * @param[in] cfg pointer to the @p EMCNANDConfig from + * corresponding NAND driver + * @param[in] block block number + * @param[in] page page number related to begin of block + * @param[in] offset data offset related to begin of page + * @param[out] addr buffer to store calculated address + * @param[in] addr_len length of address buffer + * + * @notapi + */ +static void calc_addr(const EMCNANDConfig *cfg, + uint32_t block, uint32_t page, uint32_t offset, + uint8_t *addr, size_t addr_len){ + size_t i = 0; + uint32_t row = 0; + + /* Incorrect buffer length.*/ + osalDbgCheck(cfg->rowcycles + cfg->colcycles == addr_len); + osalDbgCheck((block < cfg->blocks) && (page < cfg->pages_per_block) && + (offset < cfg->page_data_size + cfg->page_spare_size)); + + /* convert address to NAND specific */ + memset(addr, 0, addr_len); + row = (block * cfg->pages_per_block) + page; + for (i=0; i<cfg->colcycles; i++){ + addr[i] = offset & 0xFF; + offset = offset >> 8; + } + for (; i<addr_len; i++){ + addr[i] = row & 0xFF; + row = row >> 8; + } +} + +/** + * @brief Translate block number to NAND internal address. + * @note This function designed for erasing purpose. + * + * @param[in] cfg pointer to the @p EMCNANDConfig from + * corresponding NAND driver + * @param[in] block block number + * @param[out] addr buffer to store calculated address + * @param[in] addr_len length of address buffer + * + * @notapi + */ +static void calc_blk_addr(const EMCNANDConfig *cfg, + uint32_t block, uint8_t *addr, size_t addr_len){ + size_t i = 0; + uint32_t row = 0; + + /* Incorrect buffer length.*/ + osalDbgCheck(cfg->rowcycles == addr_len); + osalDbgCheck((block < cfg->blocks)); + + /* convert address to NAND specific */ + memset(addr, 0, addr_len); + row = block * cfg->pages_per_block; + for (i=0; i<addr_len; i++){ + addr[i] = row & 0xFF; + row = row >> 8; + } +} + +#if EMCNAND_USE_BAD_MAP +/** + * @brief Add new bad block to map. + * + * @param[in] emcnandp pointer to the @p EMCNANDDriver object + * @param[in] block block number + * @param[in] map pointer to bad block map + */ +static void bad_map_update(EMCNANDDriver *emcnandp, size_t block) { + + uint32_t *map = emcnandp->config->bb_map; + const size_t BPMC = sizeof(uint32_t) * 8; /* bits per map claster */ + size_t i; + size_t shift; + + /* Nand device overflow.*/ + osalDbgCheck(emcnandp->config->blocks > block); + + i = block / BPMC; + shift = block % BPMC; + /* This block already mapped.*/ + osalDbgCheck(((map[i] >> shift) & 1) != 1); + map[i] |= (uint32_t)1 << shift; +} + +/** + * @brief Scan for bad blocks and fill map with their numbers. + * + * @param[in] emcnandp pointer to the @p EMCNANDDriver object + */ +static void scan_bad_blocks(EMCNANDDriver *emcnandp) { + + const size_t blocks = emcnandp->config->blocks; + const size_t maplen = blocks / 32; + + size_t b; + uint8_t m0; + uint8_t m1; + + /* clear map just to be safe */ + for (b=0; b<maplen; b++) + emcnandp->config->bb_map[b] = 0; + + /* now write numbers of bad block to map */ + for (b=0; b<blocks; b++){ + m0 = emcnandReadBadMark(emcnandp, b, 0); + m1 = emcnandReadBadMark(emcnandp, b, 1); + if ((0xFF != m0) || (0xFF != m1)){ + bad_map_update(emcnandp, b); + } + } +} +#endif /* EMCNAND_USE_BAD_MAP */ + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief EMCNAND Driver initialization. + * @note This function is implicitly invoked by @p halInit(), there is + * no need to explicitly initialize the driver. + * + * @init + */ +void emcnandInit(void) { + + emcnand_lld_init(); +} + +/** + * @brief Initializes the standard part of a @p EMCNANDDriver structure. + * + * @param[out] emcnandp pointer to the @p EMCNANDDriver object + * + * @init + */ +void emcnandObjectInit(EMCNANDDriver *emcnandp) { + +#if EMCNAND_USE_MUTUAL_EXCLUSION +#if CH_CFG_USE_MUTEXES + chMtxObjectInit(&emcnandp->mutex); +#else + chSemObjectInit(&emcnandp->semaphore, 1); +#endif /* CH_CFG_USE_MUTEXES */ +#endif /* EMCNAND_USE_MUTUAL_EXCLUSION */ + + emcnandp->state = EMCNAND_STOP; + emcnandp->config = NULL; +} + +/** + * @brief Configures and activates the EMCNAND peripheral. + * + * @param[in] emcnandp pointer to the @p EMCNANDDriver object + * @param[in] config pointer to the @p EMCNANDConfig object + * + * @api + */ +void emcnandStart(EMCNANDDriver *emcnandp, const EMCNANDConfig *config) { + + osalDbgCheck((emcnandp != NULL) && (config != NULL)); + osalDbgAssert(config->emcp->state == EMC_READY, + "lower level driver not ready"); + osalDbgAssert((emcnandp->state == EMCNAND_STOP) || + (emcnandp->state == EMCNAND_READY), + "invalid state"); + + emcnandp->config = config; + pagesize_check(emcnandp->config->page_data_size); + emcnand_lld_start(emcnandp); + emcnandp->state = EMCNAND_READY; + +#if EMCNAND_USE_BAD_MAP + scan_bad_blocks(emcnandp); +#endif /* EMCNAND_USE_BAD_MAP */ +} + +/** + * @brief Deactivates the EMCNAND peripheral. + * + * @param[in] emcnandp pointer to the @p EMCNANDDriver object + * + * @api + */ +void emcnandStop(EMCNANDDriver *emcnandp) { + + osalDbgCheck(emcnandp != NULL); + osalDbgAssert((emcnandp->state == EMCNAND_STOP) || + (emcnandp->state == EMCNAND_READY), + "invalid state"); + emcnand_lld_stop(emcnandp); + emcnandp->state = EMCNAND_STOP; +} + +/** + * @brief Read whole page. + * + * @param[in] emcnandp pointer to the @p EMCNANDDriver object + * @param[in] block block number + * @param[in] page page number related to begin of block + * @param[out] data buffer to store data + * @param[in] datalen length of data buffer + * + * @api + */ +void emcnandReadPageWhole(EMCNANDDriver *emcnandp, uint32_t block, + uint32_t page, uint8_t *data, size_t datalen) { + + const EMCNANDConfig *cfg = emcnandp->config; + uint8_t addrbuf[8]; + size_t addrlen = cfg->rowcycles + cfg->colcycles; + + osalDbgCheck((emcnandp != NULL) && (data != NULL)); + osalDbgCheck((datalen <= (cfg->page_data_size + cfg->page_spare_size))); + osalDbgAssert(emcnandp->state == EMCNAND_READY, "invalid state"); + + calc_addr(cfg, block, page, 0, addrbuf, addrlen); + emcnand_lld_read_data(emcnandp, data, datalen, addrbuf, addrlen, NULL); +} + +/** + * @brief Write whole page. + * + * @param[in] emcnandp pointer to the @p EMCNANDDriver object + * @param[in] block block number + * @param[in] page page number related to begin of block + * @param[in] data buffer with data to be written + * @param[in] datalen length of data buffer + * + * @return The operation status reported by NAND IC (0x70 command). + * + * @api + */ +uint8_t emcnandWritePageWhole(EMCNANDDriver *emcnandp, uint32_t block, + uint32_t page, const uint8_t *data, size_t datalen) { + + uint8_t retval; + const EMCNANDConfig *cfg = emcnandp->config; + uint8_t addr[8]; + size_t addrlen = cfg->rowcycles + cfg->colcycles; + + osalDbgCheck((emcnandp != NULL) && (data != NULL)); + osalDbgCheck((datalen <= (cfg->page_data_size + cfg->page_spare_size))); + osalDbgAssert(emcnandp->state == EMCNAND_READY, "invalid state"); + + calc_addr(cfg, block, page, 0, addr, addrlen); + retval = emcnand_lld_write_data(emcnandp, data, datalen, addr, addrlen, NULL); + return retval; +} + +/** + * @brief Read page data without spare area. + * + * @param[in] emcnandp pointer to the @p EMCNANDDriver object + * @param[in] block block number + * @param[in] page page number related to begin of block + * @param[out] data buffer to store data + * @param[in] datalen length of data buffer + * @param[out] ecc pointer to calculated ECC. Ignored when NULL. + * + * @api + */ +void emcnandReadPageData(EMCNANDDriver *emcnandp, uint32_t block, uint32_t page, + uint8_t *data, size_t datalen, uint32_t *ecc) { + + const EMCNANDConfig *cfg = emcnandp->config; + uint8_t addrbuf[8]; + size_t addrlen = cfg->rowcycles + cfg->colcycles; + + osalDbgCheck((emcnandp != NULL) && (data != NULL)); + osalDbgCheck((datalen <= cfg->page_data_size)); + osalDbgAssert(emcnandp->state == EMCNAND_READY, "invalid state"); + + calc_addr(cfg, block, page, 0, addrbuf, addrlen); + emcnand_lld_read_data(emcnandp, data, datalen, addrbuf, addrlen, ecc); +} + +/** + * @brief Write page data without spare area. + * + * @param[in] emcnandp pointer to the @p EMCNANDDriver object + * @param[in] block block number + * @param[in] page page number related to begin of block + * @param[in] data buffer with data to be written + * @param[in] datalen length of data buffer + * @param[out] ecc pointer to calculated ECC. Ignored when NULL. + * + * @return The operation status reported by NAND IC (0x70 command). + * + * @api + */ +uint8_t emcnandWritePageData(EMCNANDDriver *emcnandp, uint32_t block, + uint32_t page, const uint8_t *data, size_t datalen, uint32_t *ecc) { + + uint8_t retval; + const EMCNANDConfig *cfg = emcnandp->config; + uint8_t addr[8]; + size_t addrlen = cfg->rowcycles + cfg->colcycles; + + osalDbgCheck((emcnandp != NULL) && (data != NULL)); + osalDbgCheck((datalen <= cfg->page_data_size)); + osalDbgAssert(emcnandp->state == EMCNAND_READY, "invalid state"); + + calc_addr(cfg, block, page, 0, addr, addrlen); + retval = emcnand_lld_write_data(emcnandp, data, datalen, addr, addrlen, ecc); + return retval; +} + +/** + * @brief Read page spare area. + * + * @param[in] emcnandp pointer to the @p EMCNANDDriver object + * @param[in] block block number + * @param[in] page page number related to begin of block + * @param[out] spare buffer to store data + * @param[in] sparelen length of data buffer + * + * @api + */ +void emcnandReadPageSpare(EMCNANDDriver *emcnandp, uint32_t block, + uint32_t page, uint8_t *spare, size_t sparelen) { + + const EMCNANDConfig *cfg = emcnandp->config; + uint8_t addr[8]; + size_t addrlen = cfg->rowcycles + cfg->colcycles; + + osalDbgCheck((NULL != spare) && (emcnandp != NULL)); + osalDbgCheck(sparelen <= cfg->page_spare_size); + osalDbgAssert(emcnandp->state == EMCNAND_READY, "invalid state"); + + calc_addr(cfg, block, page, cfg->page_data_size, addr, addrlen); + emcnand_lld_read_data(emcnandp, spare, sparelen, addr, addrlen, NULL); +} + +/** + * @brief Write page spare area. + * + * @param[in] emcnandp pointer to the @p EMCNANDDriver object + * @param[in] block block number + * @param[in] page page number related to begin of block + * @param[in] spare buffer with spare data to be written + * @param[in] sparelen length of data buffer + * + * @return The operation status reported by NAND IC (0x70 command). + * + * @api + */ +uint8_t emcnandWritePageSpare(EMCNANDDriver *emcnandp, uint32_t block, + uint32_t page, const uint8_t *spare, size_t sparelen) { + + uint8_t retVal; + const EMCNANDConfig *cfg = emcnandp->config; + uint8_t addr[8]; + size_t addrlen = cfg->rowcycles + cfg->colcycles; + + osalDbgCheck((NULL != spare) && (emcnandp != NULL)); + osalDbgCheck(sparelen <= cfg->page_spare_size); + osalDbgAssert(emcnandp->state == EMCNAND_READY, "invalid state"); + + calc_addr(cfg, block, page, cfg->page_data_size, addr, addrlen); + retVal = emcnand_lld_write_data(emcnandp, spare, sparelen, addr, addrlen, NULL); + return retVal; +} + +/** + * @brief Mark block as bad. + * + * @param[in] emcnandp pointer to the @p EMCNANDDriver object + * @param[in] block block number + * + * @api + */ +void emcnandMarkBad(EMCNANDDriver *emcnandp, uint32_t block) { + + uint8_t bb_mark[2] = {0, 0}; + uint8_t op_status; + op_status = emcnandWritePageSpare(emcnandp, block, 0, bb_mark, sizeof(bb_mark)); + osalDbgCheck(0 == (op_status & 1)); /* operation failed*/ + op_status = emcnandWritePageSpare(emcnandp, block, 1, bb_mark, sizeof(bb_mark)); + osalDbgCheck(0 == (op_status & 1)); /* operation failed*/ + +#if EMCNAND_USE_BAD_MAP + bad_map_update(emcnandp, block); +#endif +} + +/** + * @brief Read bad mark out. + * + * @param[in] emcnandp pointer to the @p EMCNANDDriver object + * @param[in] block block number + * @param[in] page page number related to begin of block + * + * @return Bad mark. + * + * @api + */ +uint8_t emcnandReadBadMark(EMCNANDDriver *emcnandp, + uint32_t block, uint32_t page) { + uint8_t bb_mark[1]; + emcnandReadPageSpare(emcnandp, block, page, bb_mark, sizeof(bb_mark)); + return bb_mark[0]; +} + +/** + * @brief Erase block. + * + * @param[in] emcnandp pointer to the @p EMCNANDDriver object + * @param[in] block block number + * + * @return The operation status reported by NAND IC (0x70 command). + * + * @api + */ +uint8_t emcnandErase(EMCNANDDriver *emcnandp, uint32_t block){ + + uint8_t retVal; + const EMCNANDConfig *cfg = emcnandp->config; + uint8_t addr[4]; + size_t addrlen = cfg->rowcycles; + + osalDbgCheck(emcnandp != NULL); + osalDbgAssert(emcnandp->state == EMCNAND_READY, "invalid state"); + + calc_blk_addr(cfg, block, addr, addrlen); + retVal = emcnand_lld_erase(emcnandp, addr, addrlen); + return retVal; +} + +/** + * @brief Report block badness. + * + * @param[in] emcnandp pointer to the @p EMCNANDDriver object + * @param[in] block block number + * + * @return block condition + * @retval true if the block is bad. + * @retval false if the block is good. + * + * @api + */ +bool emcnandIsBad(EMCNANDDriver *emcnandp, uint32_t block){ + + osalDbgCheck(emcnandp != NULL); + osalDbgAssert(emcnandp->state == EMCNAND_READY, "invalid state"); + +#if EMCNAND_USE_BAD_MAP + uint32_t *map = emcnandp->config->bb_map; + const size_t BPMC = sizeof(uint32_t) * 8; /* bits per map claster */ + size_t i; + size_t shift; + + i = block / BPMC; + shift = block % BPMC; + if (((map[i] >> shift) & 1) == 1) + return true; + else + return false; +#else + uint8_t m0, m1; + m0 = emcnandReadBadMark(emcnandp, block, 0); + m1 = emcnandReadBadMark(emcnandp, block, 1); + if ((0xFF != m0) || (0xFF != m1)) + return true; + else + return false; +#endif /* EMCNAND_USE_BAD_MAP */ +} + +#if EMCNAND_USE_MUTUAL_EXCLUSION || defined(__DOXYGEN__) +/** + * @brief Gains exclusive access to the EMCNAND bus. + * @details This function tries to gain ownership to the EMCNAND bus, if the bus + * is already being used then the invoking thread is queued. + * @pre In order to use this function the option + * @p EMCNAND_USE_MUTUAL_EXCLUSION must be enabled. + * + * @param[in] emcnandp pointer to the @p EMCNANDDriver object + * + * @api + */ +void emcnandAcquireBus(EMCNANDDriver *emcnandp) { + + osalDbgCheck(emcnandp != NULL); + +#if CH_CFG_USE_MUTEXES + chMtxLock(&emcnandp->mutex); +#elif CH_CFG_USE_SEMAPHORES + chSemWait(&emcnandp->semaphore); +#endif +} + +/** + * @brief Releases exclusive access to the EMCNAND bus. + * @pre In order to use this function the option + * @p EMCNAND_USE_MUTUAL_EXCLUSION must be enabled. + * + * @param[in] emcnandp pointer to the @p EMCNANDDriver object + * + * @api + */ +void emcnandReleaseBus(EMCNANDDriver *emcnandp) { + + osalDbgCheck(emcnandp != NULL); + +#if CH_CFG_USE_MUTEXES + chMtxUnlock(&emcnandp->mutex); +#elif CH_CFG_USE_SEMAPHORES + chSemSignal(&emcnandp->semaphore); +#endif +} +#endif /* EMCNAND_USE_MUTUAL_EXCLUSION */ + +#endif /* HAL_USE_EMCNAND */ + +/** @} */ + + + + diff --git a/os/hal/src/hal.c b/os/hal/src/hal.c index f9717dd65..698e1b29b 100644 --- a/os/hal/src/hal.c +++ b/os/hal/src/hal.c @@ -119,7 +119,12 @@ void halInit(void) { #if HAL_USE_RTC || defined(__DOXYGEN__)
rtcInit();
#endif
-
+#if HAL_USE_EMC || defined(__DOXYGEN__)
+ emcInit();
+#endif
+#if HAL_USE_EMCNAND || defined(__DOXYGEN__)
+ emcnandInit();
+#endif
/* Board specific initialization.*/
boardInit();
|