From 03479db4547e7f0b8ac40839862410dc637c3579 Mon Sep 17 00:00:00 2001 From: thewon Date: Sat, 3 Jul 2021 14:58:55 +0800 Subject: [PATCH] add nand flash mtd device interface --- .../CubeMX_Config/Inc/stm32f4xx_hal_conf.h | 2 +- .../stm32f429-atk-apollo/board/ports/Kconfig | 310 +++++++++ .../board/ports/SConscript | 53 ++ .../board/ports/nand_mt29f4g08.c | 603 ++++++++++++++++++ .../board/ports/nand_mt29f4g08.h | 101 +++ components/drivers/include/drivers/mtd_nand.h | 124 +++- components/drivers/mtd/mtd_nand.c | 494 +++++++++++++- 7 files changed, 1659 insertions(+), 28 deletions(-) create mode 100644 bsp/stm32/stm32f429-atk-apollo/board/ports/Kconfig create mode 100644 bsp/stm32/stm32f429-atk-apollo/board/ports/SConscript create mode 100644 bsp/stm32/stm32f429-atk-apollo/board/ports/nand_mt29f4g08.c create mode 100644 bsp/stm32/stm32f429-atk-apollo/board/ports/nand_mt29f4g08.h diff --git a/bsp/stm32/stm32f429-atk-apollo/board/CubeMX_Config/Inc/stm32f4xx_hal_conf.h b/bsp/stm32/stm32f429-atk-apollo/board/CubeMX_Config/Inc/stm32f4xx_hal_conf.h index 953a90cf2f..b64208da49 100644 --- a/bsp/stm32/stm32f429-atk-apollo/board/CubeMX_Config/Inc/stm32f4xx_hal_conf.h +++ b/bsp/stm32/stm32f429-atk-apollo/board/CubeMX_Config/Inc/stm32f4xx_hal_conf.h @@ -45,7 +45,7 @@ /* #define HAL_DCMI_MODULE_ENABLED */ /* #define HAL_DMA2D_MODULE_ENABLED */ #define HAL_ETH_MODULE_ENABLED -/* #define HAL_NAND_MODULE_ENABLED */ +#define HAL_NAND_MODULE_ENABLED /* #define HAL_NOR_MODULE_ENABLED */ /* #define HAL_PCCARD_MODULE_ENABLED */ /* #define HAL_SRAM_MODULE_ENABLED */ diff --git a/bsp/stm32/stm32f429-atk-apollo/board/ports/Kconfig b/bsp/stm32/stm32f429-atk-apollo/board/ports/Kconfig new file mode 100644 index 0000000000..1a63064fee --- /dev/null +++ b/bsp/stm32/stm32f429-atk-apollo/board/ports/Kconfig @@ -0,0 +1,310 @@ +menu "Hardware Drivers Config" + +config SOC_STM32F429IG + bool + select SOC_SERIES_STM32F4 + select RT_USING_COMPONENTS_INIT + select RT_USING_USER_MAIN + default y + +menu "Onboard Peripheral Drivers" + + config BSP_USING_USB_TO_USART + bool "Enable USB TO USART (uart1)" + select BSP_USING_UART + select BSP_USING_UART1 + default y + + config BSP_USING_COM2 + bool "Enable COM2 (uart2 pin conflict with Ethernet and PWM)" + select BSP_USING_UART + select BSP_USING_UART2 + default n + + config BSP_USING_COM3 + bool "Enable COM3 (uart3 pin conflict with Ethernet)" + select BSP_USING_UART3 + default n + + config BSP_USING_SDRAM + bool "Enable SDRAM" + select BSP_USING_FMC + default n + + config BSP_USING_SPI_FLASH + bool "Enable SPI FLASH (W25Q256 spi5)" + select BSP_USING_SPI + select BSP_USING_SPI5 + select RT_USING_SFUD + select RT_SFUD_USING_SFDP + default n + + config BSP_USING_NAND_FLASH + bool "Enable NAND FLASH (MT29F4G08)" + select BSP_USING_FMC + select RT_USING_MTD_NAND + default n + + config BSP_USING_MPU9250 + bool "Enable MPU 9250 (i2c1)" + select BSP_USING_I2C1 + select PKG_USING_MPU6XXX + default n + + config PHY_USING_LAN8720A + bool + + config BSP_USING_ETH + bool "Enable Ethernet" + select BSP_USING_I2C1 + select PKG_USING_PCF8574 + select RT_USING_LWIP + select PHY_USING_LAN8720A + default n + + config BSP_USING_SDCARD + bool "Enable SDCARD (sdio)" + select BSP_USING_SDIO + select RT_USING_DFS + select RT_USING_DFS_ELMFAT + default n + + config BSP_USING_AUDIO + bool "Enable AUDIO (WM8978)" + select BSP_USING_I2C1 + select RT_USING_AUDIO + default n + + if BSP_USING_AUDIO + config BSP_USING_AUDIO_PLAY + bool "Enable Audio Play" + default y + + config BSP_USING_AUDIO_RECORD + bool "Enable Audio Record" + select BSP_USING_AUDIO_PLAY + default n + endif + +endmenu + +menu "On-chip Peripheral Drivers" + + config BSP_USING_GPIO + bool "Enable GPIO" + select RT_USING_PIN + default y + + menuconfig BSP_USING_UART + bool "Enable UART" + default y + select RT_USING_SERIAL + if BSP_USING_UART + config BSP_USING_UART1 + bool "Enable UART1" + default y + + config BSP_UART1_RX_USING_DMA + bool "Enable UART1 RX DMA" + depends on BSP_USING_UART1 && RT_SERIAL_USING_DMA + default n + + config BSP_USING_UART2 + bool "Enable UART2" + default n + + config BSP_UART2_RX_USING_DMA + bool "Enable UART2 RX DMA" + depends on BSP_USING_UART2 && RT_SERIAL_USING_DMA + default n + + config BSP_USING_UART3 + bool "Enable UART3" + default n + + config BSP_UART3_RX_USING_DMA + bool "Enable UART3 RX DMA" + depends on BSP_USING_UART3 && RT_SERIAL_USING_DMA + default n + endif + + config BSP_USING_ON_CHIP_FLASH + bool "Enable on-chip FLASH" + default n + menuconfig BSP_USING_CAN + bool "Enable CAN" + default n + select RT_USING_CAN + if BSP_USING_CAN + config BSP_USING_CAN1 + bool "Enable CAN1" + default n + endif + + menuconfig BSP_USING_SPI + bool "Enable SPI BUS" + default n + select RT_USING_SPI + if BSP_USING_SPI + config BSP_USING_SPI1 + bool "Enable SPI1 BUS" + default n + + config BSP_SPI1_TX_USING_DMA + bool "Enable SPI1 TX DMA" + depends on BSP_USING_SPI1 + default n + + config BSP_SPI1_RX_USING_DMA + bool "Enable SPI1 RX DMA" + depends on BSP_USING_SPI1 + select BSP_SPI1_TX_USING_DMA + default n + + config BSP_USING_SPI2 + bool "Enable SPI2 BUS" + default n + + config BSP_SPI2_TX_USING_DMA + bool "Enable SPI2 TX DMA" + depends on BSP_USING_SPI2 + default n + + config BSP_SPI2_RX_USING_DMA + bool "Enable SPI2 RX DMA" + depends on BSP_USING_SPI2 + select BSP_SPI2_TX_USING_DMA + default n + + config BSP_USING_SPI5 + bool "Enable SPI5 BUS" + default n + + config BSP_SPI5_TX_USING_DMA + bool "Enable SPI5 TX DMA" + depends on BSP_USING_SPI5 + default n + + config BSP_SPI5_RX_USING_DMA + bool "Enable SPI5 RX DMA" + depends on BSP_USING_SPI5 + select BSP_SPI5_TX_USING_DMA + default n + endif + + menuconfig BSP_USING_I2C1 + bool "Enable I2C1 BUS (software simulation)" + default n + select RT_USING_I2C + select RT_USING_I2C_BITOPS + select RT_USING_PIN + if BSP_USING_I2C1 + comment "Notice: PH4 --> 116; PH5 --> 117" + config BSP_I2C1_SCL_PIN + int "I2C1 scl pin number" + range 1 176 + default 116 + config BSP_I2C1_SDA_PIN + int "I2C1 sda pin number" + range 1 176 + default 117 + endif + + menuconfig BSP_USING_TIM + bool "Enable timer" + default n + select RT_USING_HWTIMER + if BSP_USING_TIM + config BSP_USING_TIM11 + bool "Enable TIM11" + default n + + config BSP_USING_TIM13 + bool "Enable TIM13" + default n + + config BSP_USING_TIM14 + bool "Enable TIM14" + default n + endif + + menuconfig BSP_USING_PWM + bool "Enable pwm" + default n + select RT_USING_PWM + if BSP_USING_PWM + menuconfig BSP_USING_PWM2 + bool "Enable timer2 output pwm" + default n + if BSP_USING_PWM2 + config BSP_USING_PWM2_CH4 + bool "Enable PWM2 channel4" + default n + endif + endif + + menuconfig BSP_USING_ADC + bool "Enable ADC" + default n + select RT_USING_ADC + if BSP_USING_ADC + config BSP_USING_ADC1 + bool "Enable ADC1" + default n + endif + + menuconfig BSP_USING_ONCHIP_RTC + bool "Enable RTC" + select RT_USING_RTC + default n + if BSP_USING_ONCHIP_RTC + choice + prompt "Select clock source" + default BSP_RTC_USING_LSE + + config BSP_RTC_USING_LSE + bool "RTC USING LSE" + + config BSP_RTC_USING_LSI + bool "RTC USING LSI" + endchoice + endif + + config BSP_USING_WDT + bool "Enable Watchdog Timer" + select RT_USING_WDT + default n + + menuconfig BSP_USING_USBH + bool "Enable USB Host" + select RT_USING_USB_HOST + default n + if BSP_USING_USBH + menuconfig RT_USBH_MSTORAGE + bool "Enable Udisk Drivers" + default n + if RT_USBH_MSTORAGE + config UDISK_MOUNTPOINT + string "Udisk mount dir" + default "/" + endif + endif + + config BSP_USING_SDIO + bool "Enable SDIO" + select RT_USING_SDIO + select RT_USING_DFS + default n + + config BSP_USING_FMC + bool + default n + source "../libraries/HAL_Drivers/Kconfig" + +endmenu + +menu "Board extended module Drivers" + +endmenu + +endmenu diff --git a/bsp/stm32/stm32f429-atk-apollo/board/ports/SConscript b/bsp/stm32/stm32f429-atk-apollo/board/ports/SConscript new file mode 100644 index 0000000000..2fa57a9f7a --- /dev/null +++ b/bsp/stm32/stm32f429-atk-apollo/board/ports/SConscript @@ -0,0 +1,53 @@ +import os +import rtconfig +from building import * + +Import('SDK_LIB') + +cwd = GetCurrentDir() + +# add general drivers +src = Split(''' +board.c +CubeMX_Config/Src/stm32f4xx_hal_msp.c +''') + +if GetDepend(['BSP_USING_ETH']): + src += Glob('ports/phy_reset.c') + +if GetDepend(['BSP_USING_SPI_FLASH']): + src += Glob('ports/spi_flash_init.c') + +if GetDepend(['BSP_USING_NAND_FLASH']): + src += Glob('ports/nand_mt29f4g08.c') + +if GetDepend(['BSP_USING_SDCARD']): + src += Glob('ports/sdcard_port.c') + +if GetDepend(['BSP_USING_AUDIO']): + src += Glob('ports/audio/drv_wm8978.c') + src += Glob('ports/audio/drv_sound.c') + +if GetDepend(['BSP_USING_AUDIO_RECORD']): + src += Glob('ports/audio/drv_mic.c') + +path = [cwd] +path += [cwd + '/CubeMX_Config/Inc'] +path += [cwd + '/ports'] + +if GetDepend(['BSP_USING_AUDIO']): + path += [cwd + '/ports/audio'] + +startup_path_prefix = SDK_LIB + +if rtconfig.CROSS_TOOL == 'gcc': + src += [startup_path_prefix + '/STM32F4xx_HAL/CMSIS/Device/ST/STM32F4xx/Source/Templates/gcc/startup_stm32f429xx.s'] +elif rtconfig.CROSS_TOOL == 'keil': + src += [startup_path_prefix + '/STM32F4xx_HAL/CMSIS/Device/ST/STM32F4xx/Source/Templates/arm/startup_stm32f429xx.s'] +elif rtconfig.CROSS_TOOL == 'iar': + src += [startup_path_prefix + '/STM32F4xx_HAL/CMSIS/Device/ST/STM32F4xx/Source/Templates/iar/startup_stm32f429xx.s'] + +CPPDEFINES = ['STM32F429xx'] +group = DefineGroup('Drivers', src, depend = [''], CPPPATH = path, CPPDEFINES = CPPDEFINES) + +Return('group') diff --git a/bsp/stm32/stm32f429-atk-apollo/board/ports/nand_mt29f4g08.c b/bsp/stm32/stm32f429-atk-apollo/board/ports/nand_mt29f4g08.c new file mode 100644 index 0000000000..7ea2b58ac8 --- /dev/null +++ b/bsp/stm32/stm32f429-atk-apollo/board/ports/nand_mt29f4g08.c @@ -0,0 +1,603 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2020-06-30 thread-liu first version + * 2021-06-21 THEWON add MT29F4G08 + */ + +#include +#include + +#ifdef RT_USING_MTD_NAND + +#define DRV_DEBUG +#define LOG_TAG "drv.nand" +#include +#include "nand_mt29f4g08.h" +#include +#include +#include "yaffsfs.h" + +struct rthw_fmc +{ + rt_uint32_t id; +}; +static struct rthw_fmc _device = {0}; +static rt_nand_driver_t nand_drv; +static NAND_HandleTypeDef NAND_Handler; + +/* nand wait RB */ +static rt_err_t rt_hw_nand_wait_rb() +{ + rt_uint32_t time = 0; + int pin = 0; + + while (1) + { + pin = rt_pin_read(NAND_WAITRB_PIN); + if (pin == 1) + { + break; + } + time++; + if (time >= 0X1FFFFFFF) + { + return -RT_ETIMEOUT; + } + } + return RT_EOK; +} + +/* nand delay */ +static void rt_hw_nand_delay(volatile rt_uint32_t i) +{ + while (i > 0) + { + i--; + } +} + +/* read nand flash status */ +static rt_uint8_t rt_hw_nand_read_status(void) +{ + rt_uint8_t result = RT_EOK; + + NAND_CMD = NAND_STATUS; + + rt_hw_nand_delay(NAND_TWHR_DELAY); + + result = NAND_DATA; + + return result; +} + +/* wait nand flash read */ +static rt_err_t rt_hw_nand_wait_ready(void) +{ + rt_err_t result = RT_EOK; + rt_uint32_t time = 0; + + while (1) + { + result = rt_hw_nand_read_status(); + + if (result & NAND_READY) + { + break; + } + time++; + if (time >= 0X1FFFFFFF) + { + return -RT_ETIMEOUT; + } + } + + return RT_EOK; +} + +/* set nand mode */ +static rt_err_t rt_hw_nand_set_mode(rt_uint8_t mode) +{ + NAND_CMD = NAND_FEATURE; + NAND_ADDR = 0x01; + NAND_DATA = mode; + NAND_DATA = 0; + NAND_DATA = 0; + NAND_DATA = 0; + + if (rt_hw_nand_wait_ready() == RT_EOK) + { + return RT_EOK; + } + else + { + return -RT_ERROR; + } +} + +/* reset nand flash */ +static rt_err_t rt_hw_nand_reset(void) +{ + NAND_CMD = NAND_RESET; + + if (rt_hw_nand_wait_ready() == RT_EOK) + { + return RT_EOK; /* success */ + } + else + { + return -RT_ERROR; + } +} + +/* read nand flash id */ +static rt_uint32_t _read_id() +{ + rt_uint32_t id; + rt_uint8_t deviceid[5]; + + NAND_CMD = NAND_READID; /* read id command */ + NAND_ADDR = 0x00; + + deviceid[0] = NAND_DATA; /* Byte 0 */ + deviceid[1] = NAND_DATA; /* Byte 1 */ + deviceid[2] = NAND_DATA; /* Byte 2 */ + deviceid[3] = NAND_DATA; /* Byte 3 */ + deviceid[4] = NAND_DATA; /* Byte 4 */ + + id = ((uint32_t)deviceid[4]) << 24 | ((uint32_t)deviceid[3]) << 16 | ((uint32_t)deviceid[2]) << 8 | deviceid[1]; + + LOG_D("nand id: 0x%08x", id); + + return id; +} + +static rt_err_t mt29f4g08_erase_block(rt_uint32_t block) +{ + block <<= NAND_PPB_BW; + + NAND_CMD = NAND_ERASE_MODE; + NAND_ADDR = (rt_uint8_t)block; + NAND_ADDR = (rt_uint8_t)(block >> 8); + NAND_ADDR = (rt_uint8_t)(block >> 16); + NAND_CMD = NAND_ERASE_BLOCK; + + rt_hw_nand_wait_rb(); + + return rt_hw_nand_read_status(); +} + +static rt_err_t mt29f4g08_erase_chip(rt_nand_driver_t nand) +{ + int i; + for (i = 0; i < nand->parent.block_total; i++) + { + mt29f4g08_erase_block(i); + } + return RT_EOK; +} + +static rt_err_t mt29f4g08_cmdfunc(rt_nand_driver_t nand, int cmd, int page, int offset) +{ + int ret = 0; + + RT_ASSERT(nand != RT_NULL); + + switch (cmd) + { + case RT_NAND_CMD_PAGE_RD: + NAND_CMD = NAND_READ_MODE;// ncmd(K9F1G_PGAEREAD1); + + NAND_ADDR = offset; + NAND_ADDR = (offset >> 8); + NAND_ADDR = (page); + NAND_ADDR = (page >> 8); + NAND_ADDR = (page >> 16); + + NAND_CMD = NAND_READ_PAGE;// (K9F1G_PAGEREAD2); + rt_hw_nand_wait_rb();// nwait(); + NAND_CMD = NAND_READ_MODE;// ncmd(K9F1G_PGAEREAD1); + break; + case RT_NAND_CMD_PAGE_WR_BGN: + NAND_CMD = NAND_PROG_MODE;// ncmd(K9F1G_PAGEPROGRAM1); + + NAND_ADDR = offset;// naddr(offset); + NAND_ADDR = (offset >> 8);// naddr(offset >> 8); + NAND_ADDR = (page);// naddr(page); + NAND_ADDR = (page >> 8);// naddr(page >> 8); + NAND_ADDR = (page >> 16); + break; + case RT_NAND_CMD_PAGE_WR_END: + NAND_CMD = NAND_PROG_PAGE;// ncmd(K9F1G_PAGEPROGRAM2); + rt_hw_nand_wait_rb();// nwait(); + ret = rt_hw_nand_read_status() & NAND_ERROR; + break; + case RT_NAND_CMD_BLK_ERASE: + ret = mt29f4g08_erase_block(page); + break; + case RT_NAND_CMD_ECC_EN: + HAL_NAND_ECC_Enable(&NAND_Handler);// FSMC_NANDECCCmd(FSMC_Bank2_NAND, ENABLE); + break; + case RT_NAND_CMD_ECC_DIS: + HAL_NAND_ECC_Disable(&NAND_Handler);// FSMC_NANDECCCmd(FSMC_Bank2_NAND,DISABLE); + break; + default: + break; + } + + return ret; +} + +static rt_err_t mt29f4g08_read_buf(rt_nand_driver_t nand, rt_uint8_t *buf, int len) +{ + RT_ASSERT(nand != RT_NULL); + RT_ASSERT(buf != RT_NULL); + + while (len) + { + *buf = NAND_DATA; + buf++; + len--; + } + + return 0; +} + +static rt_err_t mt29f4g08_write_buf(rt_nand_driver_t nand, const rt_uint8_t *buf, int len) +{ + RT_ASSERT(nand != RT_NULL); + RT_ASSERT(buf != RT_NULL); + + while (len) + { + NAND_DATA = *buf; + buf++; + len--; + } + return 0; +} + +static void stm32f4_calc_ecc(rt_nand_driver_t nand, const rt_uint8_t *data, rt_uint8_t *ecc_code) +{ + rt_uint32_t e; + RT_ASSERT(nand != RT_NULL); + RT_ASSERT(data != RT_NULL); + RT_ASSERT(ecc != RT_NULL); + + HAL_NAND_GetECC(&NAND_Handler, &e, 5); + + ecc_code[0] = e; + ecc_code[1] = e >> 8; + ecc_code[2] = e >> 16; +} + +static int stm32f4_correct(rt_nand_driver_t nand, rt_uint8_t *data, rt_uint8_t *ecc_read, rt_uint8_t *ecc_calc) +{ + rt_uint32_t diff0, diff1, diff2; + rt_uint32_t bit, byte; + + RT_ASSERT(nand != RT_NULL); + RT_ASSERT(data != RT_NULL); + RT_ASSERT(ecc_read != RT_NULL); + RT_ASSERT(ecc_calc != RT_NULL); + + diff0 = ecc_read[0] ^ ecc_calc[0]; + diff1 = ecc_read[1] ^ ecc_calc[1]; + diff2 = ecc_read[2] ^ ecc_calc[2]; + + if ((diff0 | diff1 | diff2) == 0) + { + return 0; /* ECC is ok */ + } + + /* sometimes people do not think about using the ECC, so check + * to see if we have an 0xff,0xff,0xff read ECC and then ignore + * the error, on the assumption that this is an un-eccd page. + */ + if ((ecc_read[0] & ecc_read[1] & ecc_read[2]) == 0xff) + { + return 0; + } + + /* Can we correct this ECC (ie, one row and column change). + * Note, this is similar to the 256 error code on smartmedia */ + + if (((diff0 ^ (diff0 >> 1)) & 0x55) == 0x55 && + ((diff1 ^ (diff1 >> 1)) & 0x55) == 0x55 && + ((diff2 ^ (diff2 >> 1)) & 0x55) == 0x55) { + /* calculate the bit position of the error */ + + bit = ((diff2 >> 3) & 1) | + ((diff2 >> 4) & 2) | + ((diff2 >> 5) & 4); + + /* calculate the byte position of the error */ + + byte = ((diff2 << 7) & 0x100) | + ((diff1 << 0) & 0x80) | + ((diff1 << 1) & 0x40) | + ((diff1 << 2) & 0x20) | + ((diff1 << 3) & 0x10) | + ((diff0 >> 4) & 0x08) | + ((diff0 >> 3) & 0x04) | + ((diff0 >> 2) & 0x02) | + ((diff0 >> 1) & 0x01); + + data[byte] ^= (1 << bit); + return 0; + } + + /* if there is only one bit difference in the ECC, then + * one of only a row or column parity has changed, which + * means the error is most probably in the ECC itself */ + + diff0 |= (diff1 << 8); + diff0 |= (diff2 << 16); + + /* equal to "(diff0 & ~(1 << __ffs(diff0)))" */ + if ((diff0 & (diff0 - 1)) == 0) + { + return 1; + } + + return -1; +} + +static const struct rt_mtd_oob_region _layout[2] = +{ + {2, 38}, //free + {40, 24} //ecc +}; + +static const struct rt_nand_driver_ops _chip_ops = +{ + _read_id, + mt29f4g08_cmdfunc, + mt29f4g08_read_buf, + mt29f4g08_write_buf, + 0, + 0 +}; + +#ifdef MT29F4G08ABADA +static rt_err_t stm32_nand_init() +{ + FMC_NAND_PCC_TimingTypeDef ComSpaceTiming, AttSpaceTiming; + + NAND_Handler.Instance = FMC_NAND_DEVICE; + NAND_Handler.Init.NandBank = FMC_NAND_BANK3; + NAND_Handler.Init.Waitfeature = FMC_NAND_PCC_WAIT_FEATURE_DISABLE; + NAND_Handler.Init.MemoryDataWidth = FMC_NAND_PCC_MEM_BUS_WIDTH_8; + NAND_Handler.Init.EccComputation = FMC_NAND_ECC_DISABLE; + NAND_Handler.Init.ECCPageSize = FMC_NAND_ECC_PAGE_SIZE_2048BYTE; + NAND_Handler.Init.TCLRSetupTime = 0; + NAND_Handler.Init.TARSetupTime = 1; + + ComSpaceTiming.SetupTime = 2; + ComSpaceTiming.WaitSetupTime = 3; + ComSpaceTiming.HoldSetupTime = 2; + ComSpaceTiming.HiZSetupTime = 1; + + AttSpaceTiming.SetupTime = 2; + AttSpaceTiming.WaitSetupTime = 3; + AttSpaceTiming.HoldSetupTime = 2; + AttSpaceTiming.HiZSetupTime = 1; + + HAL_NAND_Init(&NAND_Handler, &ComSpaceTiming, &AttSpaceTiming); + + rt_pin_mode(NAND_WAITRB_PIN, PIN_MODE_INPUT_PULLUP); + + rt_hw_nand_reset(); /* reset nand flash*/ + rt_thread_delay(100); + + /* read id */ + _device.id = _read_id(); + + if (_device.id != NAND_DEV_ID) + { + LOG_E("nand id 0x%08x not support", _device.id); + return -RT_ERROR; /* can't find nand flash */ + } + + rt_hw_nand_set_mode(4); /* set mode 4, high speed mode*/ + + return RT_EOK; +} +#elif defined(MT29F8G08ABACA) +static void rt_hw_nand_gpio_init(void) +{ + GPIO_InitTypeDef GPIO_InitStruct = {0}; + RCC_PeriphCLKInitTypeDef PeriphClkInit = {0}; + + if (IS_ENGINEERING_BOOT_MODE()) + { + PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_FMC; + PeriphClkInit.AdcClockSelection = RCC_FMCCLKSOURCE_ACLK; + if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK) + { + Error_Handler(); + } + } + __HAL_RCC_FMC_CLK_ENABLE(); + __HAL_RCC_GPIOD_CLK_ENABLE(); + __HAL_RCC_GPIOE_CLK_ENABLE(); + __HAL_RCC_GPIOG_CLK_ENABLE(); + + /* PD6 R/B */ + GPIO_InitStruct.Pin = GPIO_PIN_6; + GPIO_InitStruct.Mode = GPIO_MODE_INPUT; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; + HAL_GPIO_Init(GPIOD, &GPIO_InitStruct); + + /* PG9 NCE */ + GPIO_InitStruct.Pin = GPIO_PIN_9; + GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; + GPIO_InitStruct.Alternate = GPIO_AF12_FMC; + HAL_GPIO_Init(GPIOG, &GPIO_InitStruct); + + /* PD0,1,4,5,11,12,14,15 */ + GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5 | + GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_14 | GPIO_PIN_15; + GPIO_InitStruct.Pull = GPIO_NOPULL; + HAL_GPIO_Init(GPIOD, &GPIO_InitStruct); + + /* PE7,8,9,10 */ + GPIO_InitStruct.Pin = GPIO_PIN_7 | GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10; + HAL_GPIO_Init(GPIOE, &GPIO_InitStruct); +} +static rt_err_t stm32_nand_init() +{ + uint32_t tempreg = 0; + + rt_hw_nand_gpio_init(); + + tempreg |= 0 << 1; /* disable Wait feature enable bit */ + tempreg |= 0 << 4; /* Data bus width 8*/ + tempreg |= 0 << 6; /* disable ECC */ + tempreg |= 1 << 17; /* ECC page 512 BYTE */ + tempreg |= 5 << 9; /* set TCLR */ + tempreg |= 5 << 13; /* set TAR */ + FMC_Bank3_R->PCR = tempreg; /* set nand control register */ + + tempreg &= 0; + tempreg |= 3 << 0; /* set MEMSET */ + tempreg |= 5 << 8; /* set MEMWAIT */ + tempreg |= 2 << 16; /* set MEMHOLD */ + tempreg |= 3 << 24; /* set MEMHIZ */ + FMC_Bank3_R->PMEM = tempreg; + FMC_Bank3_R->PATT = 0; /* Attribute memory space timing registers */ + FMC_Bank3_R->PCR |= 1 << 2; /* NAND Flash memory bank enable bit */ + FMC_Bank1_R->BTCR[0] |= (uint32_t)1 << 31; /* enable fmc */ + + rt_hw_nand_reset(); /* reset nand flash*/ + rt_thread_delay(100); + + /* read id */ + _device.id = _read_id(); + + if (_device.id != NAND_DEV_ID) + { + LOG_E("nand id 0x%08x not support", _device.id); + return RT_ERROR; /* can't find nand flash */ + } + + rt_hw_nand_set_mode(4); /* set mode 4, high speed mode*/ + + return RT_EOK; +} +#endif +int rt_hw_nand_init(void) +{ + rt_err_t result = RT_EOK; + struct rt_mtd_nand_device nand_dev; + + result = stm32_nand_init(); + if (result != RT_EOK) + { + LOG_D("nand flash init error!"); + return -RT_ERROR; + } + +#ifdef MT29F4G08ABADA + nand_dev.page_size = NAND_MAX_PAGE_SIZE; + nand_dev.pages_per_block = 64; + nand_dev.plane_num = 2; + nand_dev.oob_size = 64; + nand_dev.oob_free = 64 - ((2048) * 3 / 256); + nand_dev.block_start = 0; + nand_dev.block_end = 4095; +#elif defined(MT29F8G08ABACA) + nand_dev.total_size = 1024 * 1024 * 1024; + nand_dev.page_size = NAND_MAX_PAGE_SIZE; + nand_dev.pages_per_block = 64; + nand_dev.plane_num = 4; + nand_dev.oob_size = 64; + nand_dev.oob_free = 64 - ((2048) * 3 / 256); + nand_dev.block_start = 0; + nand_dev.block_end = 8191; +#endif + nand_dev.block_total = nand_dev.block_end - nand_dev.block_start + 1; + nand_dev.total_size = nand_dev.block_total + * nand_dev.pages_per_block + * nand_dev.page_size; + + nand_drv = rt_malloc(sizeof(struct rt_nand_driver)); + nand_drv->parent = nand_dev; + nand_drv->ops = &_chip_ops; + nand_drv->ecc.ecc_bytes = 3; + nand_drv->ecc.ecc_stepsize = 256; + nand_drv->ecc.ecc_step = 8; + nand_drv->ecc.layout = &_layout[1]; + nand_drv->ecc.calculate = stm32f4_calc_ecc; + nand_drv->ecc.correct = stm32f4_correct; + nand_drv->free_layout = &_layout[0]; + +// rt_mtd_nand_driver_init(nand_drv); + + result = rt_mtd_nand_register_device("root", &nand_drv->parent); + if (result != RT_EOK) + { + return -RT_ERROR; + } + + LOG_D("nand flash init success, id: 0x%08x\n", _device.id); + + return RT_EOK; +} +INIT_DEVICE_EXPORT(rt_hw_nand_init); + +int rt_mount_nand_yaffs2(void) +{ + rt_err_t result = RT_EOK; + rt_mtd_nand_t mtd_dev = RT_NULL; + + mtd_dev = (rt_mtd_nand_t)rt_device_find("root"); + if (mtd_dev == RT_NULL) + { + LOG_D("no mtd device found\n"); + return -RT_ENOSYS; + } + + yaffs_start_up(); + + result = dfs_mount(mtd_dev->parent.parent.name, "/", "yaffs", 0, 0); + if (result == RT_EOK) + { + LOG_D("Mount YAFFS2 on NAND successfully\n"); + } + else + { + result = dfs_mkfs("yaffs", mtd_dev->parent.parent.name); + if (result == RT_EOK) + { + result = dfs_mount(mtd_dev->parent.parent.name, "/", "yaffs", 0, 0); + } + if (result != RT_EOK) + { + LOG_D("Mount YAFFS2 on NAND failed\n"); + return -RT_ERROR; + } + LOG_D("Mount YAFFS2 on NAND successfully\n"); + } + if (result == RT_EOK) + { + struct stat l_stat; + result = stat("/udisk", &l_stat); + if ((result == RT_EOK) && (S_ISDIR(l_stat.st_mode))) { + } else { + result = mkdir("/udisk", O_CREAT); + } + } + + return RT_EOK; +} +INIT_ENV_EXPORT(rt_mount_nand_yaffs2); + +#endif diff --git a/bsp/stm32/stm32f429-atk-apollo/board/ports/nand_mt29f4g08.h b/bsp/stm32/stm32f429-atk-apollo/board/ports/nand_mt29f4g08.h new file mode 100644 index 0000000000..03ecdd1030 --- /dev/null +++ b/bsp/stm32/stm32f429-atk-apollo/board/ports/nand_mt29f4g08.h @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2020-00-30 thread-liu first version + * 2021-06-21 THEWON add MT29F4G08 + */ + +#ifndef __DRV_NAND_H__ +#define __DRV_NAND_H__ + +#include "board.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +#ifndef RT_DEBUG_NAND +#define RT_DEBUG_NAND 0 +#endif + +/* define your nand flash here */ +#define MT29F4G08ABADA +//#define MT29F8G08ABACA +//#define MT29F16G08ABABA + +#ifdef MT29F4G08ABADA +#define NAND_DEV_ID 0x569590DC /* Byte 4 3 2 1 */ +#define NAND_MAX_PAGE_SIZE 2048 +#define NAND_PPB_BW 6 // pow(2, NAND_PPB_BW) == NAND_MAX_PAGE_SIZE +#elif defined(MT29F8G08ABACA) +#define NAND_DEV_ID 0x64A690D3 +#define NAND_MAX_PAGE_SIZE 2048 +#define NAND_PPB_BW 6 +#elif defined(MT29F16G08ABABA) +#define NAND_DEV_ID 0x89260048 +#define NAND_MAX_PAGE_SIZE 2048 +#define NAND_PPB_BW 6 +#else +#error "Must define one NAND Flash device" +#endif + +#define NAND_WAITRB_PIN GET_PIN(D, 6) + +#define NAND_ECC_SECTOR_SIZE 512 + +#define NAND_TWHR_DELAY 25 +#define NAND_TBERS_DELAY 4 + +#ifdef STM32F429xx +#define NAND_REG_PCR FMC_Bank2_3->PCR3 +#define NAND_REG_SR FMC_Bank2_3->SR3 +#define NAND_REG_ECCR FMC_Bank2_3->ECCR3 +#define NAND_REG_PMEM FMC_Bank2_3->PMEM3 +#define NAND_REG_PATT FMC_Bank2_3->PATT3 +#define NAND_REG_BTCR FMC_Bank1->BTCR +#elif defined(STMA32MP1xx) +#define NAND_REG_PCR FMC_Bank3_R->PCR +#define NAND_REG_SR FMC_Bank3_R->SR +#define NAND_REG_ECCR FMC_Bank3_R->HECCR +#define NAND_REG_PMEM FMC_Bank3_R->PMEM +#define NAND_REG_PATT FMC_Bank3_R->PATT +#define NAND_REG_BTCR FMC_Bank1_R->BTCR +#endif + +#define NAND_BASE_ADDR ((rt_uint32_t)0x80000000) /* nand base address */ +#define NAND_DATA (*(__IO rt_uint8_t *)NAND_BASE_ADDR) +#define NAND_CMD (*(__IO rt_uint8_t *)(NAND_BASE_ADDR | 1 << 16)) /* command */ +#define NAND_ADDR (*(__IO rt_uint8_t *)(NAND_BASE_ADDR | 1 << 17)) /* data */ + +/* nand flash command */ +#define NAND_READID 0x90 +#define NAND_FEATURE 0xEF +#define NAND_RESET 0xFF +#define NAND_STATUS 0x70 + +#define NAND_READ_MODE 0x00 +#define NAND_READ_PAGE 0x30 +#define NAND_PROG_MODE 0x80 +#define NAND_PROG_PAGE 0x10 +#define NAND_ERASE_MODE 0x60 +#define NAND_ERASE_BLOCK 0xD0 + +#define NAND_MOVEOUT_MODE 0x00 +#define NAND_MOVEOUT_DATA 0x35 +#define NAND_MOVEIN_MODE 0x85 +#define NAND_MOVEIN_DATA 0x10 + +/* nand flash status */ +#define NAND_ECC1BITERR 0x03 /* ECC 1bit err */ +#define NAND_ECC2BITERR 0x04 /* ECC 2bit or more err */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/components/drivers/include/drivers/mtd_nand.h b/components/drivers/include/drivers/mtd_nand.h index a452da61dd..8057c1d337 100644 --- a/components/drivers/include/drivers/mtd_nand.h +++ b/components/drivers/include/drivers/mtd_nand.h @@ -7,6 +7,7 @@ * Date Author Notes * 2011-12-05 Bernard the first version * 2011-04-02 prife add mark_badblock and check_block + * 2021-06-25 THEWON add nand flash general purpose interface */ /* @@ -17,9 +18,7 @@ #define __MTD_NAND_H__ #include - -struct rt_mtd_nand_driver_ops; -#define RT_MTD_NAND_DEVICE(device) ((struct rt_mtd_nand_device*)(device)) +#include "board.h" #define RT_MTD_EOK 0 /* NO error */ #define RT_MTD_EECC 101 /* ECC error */ @@ -29,31 +28,63 @@ struct rt_mtd_nand_driver_ops; #define RT_MTD_ESRC 105 /* source issue */ #define RT_MTD_EECC_CORRECT 106 /* ECC error but correct */ +/* MTD control cmd types */ +typedef enum +{ + RT_MTD_CTRL_GET_ID = 0, + RT_MTD_CTRL_ERASE, + RT_MTD_CTRL_CHECK, + RT_MTD_CTRL_MARK, + RT_MTD_CTRL_READ, + RT_MTD_CTRL_WRITE, +} rt_mtd_cmd_t; + +/* NAND Flash control cmd types */ +typedef enum +{ + RT_NAND_CMD_PAGE_RD, /* read data to chip's page buffer, do WaitBusy after this cmd in low driver */ + RT_NAND_CMD_PAGE_WR_BGN, /* write data to chip's page buffer */ + RT_NAND_CMD_PAGE_WR_END, /* do flash programe */ + RT_NAND_CMD_BLK_ERASE, /* erase block */ + RT_NAND_CMD_ECC_EN, /* enable gen HWECC */ + RT_NAND_CMD_ECC_DIS /* disable gen HWECC */ +} rt_nand_cmd_t; + +typedef struct rt_nand_driver *rt_nand_driver_t; +struct rt_nand_driver_ops; + +#define RT_MTD_NAND_DEVICE(device) ((struct rt_mtd_nand_device*)(device)) + +struct rt_mtd_nand_driver_ops; + struct rt_mtd_nand_device { struct rt_device parent; + rt_size_t total_size; rt_uint16_t page_size; /* The Page size in the flash */ rt_uint16_t oob_size; /* Out of bank size */ rt_uint16_t oob_free; /* the free area in oob that flash driver not use */ rt_uint16_t plane_num; /* the number of plane in the NAND Flash */ - rt_uint32_t pages_per_block; /* The number of page a block */ + rt_uint16_t pages_per_block; /* The number of page a block */ rt_uint16_t block_total; /* Only be touched by driver */ - rt_uint32_t block_start; /* The start of available block*/ - rt_uint32_t block_end; /* The end of available block */ + rt_uint16_t block_start; /* The start of available block */ + rt_uint16_t block_end; /* The end of available block */ /* operations interface */ const struct rt_mtd_nand_driver_ops *ops; + + void *priv; }; +typedef struct rt_mtd_nand_device *rt_mtd_nand_t; struct rt_mtd_nand_driver_ops { - rt_err_t (*read_id)(struct rt_mtd_nand_device *device); - - rt_err_t (*read_page)(struct rt_mtd_nand_device *device, + rt_uint32_t (*read_id)(); + rt_err_t (*read_page)(rt_mtd_nand_t device, rt_off_t page, rt_uint8_t *data, rt_uint32_t data_len, rt_uint8_t *spare, rt_uint32_t spare_len); @@ -64,17 +95,83 @@ struct rt_mtd_nand_driver_ops const rt_uint8_t *spare, rt_uint32_t spare_len); rt_err_t (*move_page)(struct rt_mtd_nand_device *device, rt_off_t src_page, rt_off_t dst_page); - rt_err_t (*erase_block)(struct rt_mtd_nand_device *device, rt_uint32_t block); - rt_err_t (*check_block)(struct rt_mtd_nand_device *device, rt_uint32_t block); - rt_err_t (*mark_badblock)(struct rt_mtd_nand_device *device, rt_uint32_t block); + rt_err_t (*erase_block)(struct rt_mtd_nand_device *device, rt_int32_t block); + rt_err_t (*check_block)(struct rt_mtd_nand_device *device, rt_int32_t block); + rt_err_t (*mark_badblock)(struct rt_mtd_nand_device *device, rt_int32_t block); +}; + +struct rt_mtd_nand_rw_args +{ + rt_off_t page; + rt_uint8_t *data; + rt_uint32_t data_len; + rt_uint8_t *spare; + rt_uint32_t spare_len; +}; + +struct rt_mtd_oob_region +{ + rt_uint8_t offset; + rt_uint8_t length; +}; + +struct rt_nand_ecc +{ + rt_uint8_t ecc_bytes; /* gen ecc bytes per ecc step(usually 3) */ + rt_uint16_t ecc_stepsize:12; /* min 256 */ + rt_uint16_t ecc_step:4; /* ecc step */ + + /* driver must set the two interface if HWECC */ + void (*calculate)(rt_nand_driver_t chip, const rt_uint8_t *data, rt_uint8_t *ecc_code); + /* return max bit flips if can't correct,return -1 ECC is error(0 success) */ + int (*correct)(rt_nand_driver_t chip, rt_uint8_t *data, rt_uint8_t *ecc_read, rt_uint8_t *ecc_calc); + /* ignore if NONECC */ + const struct rt_mtd_oob_region *layout; +}; + +struct rt_nand_buffers +{ + rt_uint8_t *oob_poi; + rt_uint8_t *pagebuf; + rt_uint8_t *ecc_calc; + rt_uint8_t *ecc_code; +}; + +struct rt_nand_driver +{ + struct rt_mtd_nand_device parent; + + /* driver must init these */ + struct rt_nand_ecc ecc; + const struct rt_nand_driver_ops *ops; + const struct rt_mtd_oob_region *free_layout; + + /* driver do not touch */ + struct rt_nand_buffers buffers; +}; + +struct rt_nand_driver_ops +{ + rt_uint32_t (*read_id)(void); + + rt_err_t (*cmdfunc)(rt_nand_driver_t nand, int cmd, int page, int offset); /* send nand operation cmd, return Status bits(0 success), + if nand is busy please wait in low driver */ + rt_err_t (*read_buf)(rt_nand_driver_t nand, rt_uint8_t *buf, int len); /* read data from nand chip's page buffer */ + rt_err_t (*write_buf)(rt_nand_driver_t nand, const rt_uint8_t *buf, int len); /* write data to nand chip's page buffer */ + rt_err_t (*checkbad)(rt_nand_driver_t nand, rt_uint32_t blk); /* if NULL OOB[0] used as bad mark(not 0xff is bad) */ + rt_err_t (*markbad)(rt_nand_driver_t nand, rt_uint32_t blk); /* if NULL OOB[0] used as bad mark(set to 0x00) */ }; rt_err_t rt_mtd_nand_register_device(const char *name, struct rt_mtd_nand_device *device); +#if defined(RT_MTD_NAND_DEBUG) && defined(RT_USING_FINSH) rt_inline rt_uint32_t rt_mtd_nand_read_id(struct rt_mtd_nand_device *device) { + rt_uint32_t id; RT_ASSERT(device->ops->read_id); - return device->ops->read_id(device); + id = device->ops->read_id(); + rt_kprintf("nand id: 0x%08x", id); + return 0; } rt_inline rt_err_t rt_mtd_nand_read( @@ -133,5 +230,6 @@ rt_inline rt_err_t rt_mtd_nand_mark_badblock(struct rt_mtd_nand_device *device, return -RT_ENOSYS; } } +#endif /* defined(RT_MTD_NAND_DEBUG) && defined(RT_USING_FINSH) */ #endif /* MTD_NAND_H_ */ diff --git a/components/drivers/mtd/mtd_nand.c b/components/drivers/mtd/mtd_nand.c index 646625ade5..38ad8e1c59 100644 --- a/components/drivers/mtd/mtd_nand.c +++ b/components/drivers/mtd/mtd_nand.c @@ -6,6 +6,7 @@ * Change Logs: * Date Author Notes * 2011-12-05 Bernard the first version + * 2021-06-25 THEWON add nand flash general purpose interface */ /* @@ -16,45 +17,486 @@ #ifdef RT_USING_MTD_NAND +#include "rtdef.h" +#include "board.h" + +#define MTD_NAND_DRV(x) ((rt_nand_driver_t)(x)) +#ifndef min +#define min(a,b) (a>b? b:a) +#endif + +static rt_uint8_t *_fill_oob(rt_nand_driver_t chip, const rt_uint8_t *oob, rt_size_t len) +{ + rt_uint32_t boffs; + rt_size_t bytes; + const struct rt_mtd_oob_region *free = chip->free_layout; + + rt_memset(chip->buffers.oob_poi, 0xFF, chip->parent.oob_size); + + bytes = min(len, free->length); + boffs = free->offset; + + rt_memcpy(chip->buffers.oob_poi + boffs, oob, bytes); + + return chip->buffers.oob_poi; +} + +static rt_uint8_t *_transfer_oob(rt_nand_driver_t chip, rt_uint8_t *oob, rt_size_t len) +{ + rt_uint32_t boffs = 0; + rt_size_t bytes = 0; + const struct rt_mtd_oob_region *free = chip->free_layout; + + for (; free->length && len; free++, len -= bytes) + { + /* Read request not from offset 0? */ + bytes = min(len, free->length); + boffs = free->offset; + + rt_memcpy(oob, chip->buffers.oob_poi + boffs, bytes); + oob += bytes; + } + + return chip->buffers.oob_poi; +} + +static rt_err_t _read_page_hwecc(rt_nand_driver_t chip, rt_uint8_t *buf) +{ + rt_uint16_t i; + rt_uint16_t eccbytes, stepsize, eccsteps, eccpos; + rt_uint8_t *p = RT_NULL; + rt_uint8_t *ecc_calc = RT_NULL; + rt_uint8_t *ecc_code = RT_NULL; + rt_err_t ret = RT_MTD_EOK; + + eccbytes = chip->ecc.ecc_bytes; + stepsize = chip->ecc.ecc_stepsize; + eccsteps = chip->ecc.ecc_step; + eccpos = chip->ecc.layout->offset; + + ecc_calc = chip->buffers.ecc_calc; + ecc_code = chip->buffers.ecc_code; + + p = buf; + + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += stepsize) + { + chip->ops->cmdfunc(chip, RT_NAND_CMD_ECC_EN, 0, 0); + chip->ops->read_buf(chip, p, stepsize); + chip->ecc.calculate(chip, p, &ecc_calc[i]); + chip->ops->cmdfunc(chip, RT_NAND_CMD_ECC_DIS, 0, 0); + } + + chip->ops->read_buf(chip, chip->buffers.oob_poi, chip->parent.oob_size); + rt_memcpy(ecc_code, &chip->buffers.oob_poi[eccpos], chip->ecc.layout->length); + + eccsteps = chip->ecc.ecc_step; + p = buf; + + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += stepsize) + { + int stat; + + stat = chip->ecc.correct(chip, p, &ecc_code[i], &ecc_calc[i]); + if (stat == 1) + { + ret = -RT_MTD_EECC_CORRECT; + } + else if (stat == -1) + { + ret = -RT_MTD_EECC; + } + } + + return ret; +} + +static rt_err_t _write_page_hwecc(rt_nand_driver_t chip, const rt_uint8_t *buf) +{ + rt_uint16_t i; + rt_uint16_t eccbytes, stepsize, eccsteps, eccpos; + rt_uint8_t *ecc_calc = RT_NULL; + const rt_uint8_t *p = buf; + + eccbytes = chip->ecc.ecc_bytes; + stepsize = chip->ecc.ecc_stepsize; + eccsteps = chip->ecc.ecc_step; + eccpos = chip->ecc.layout->offset; + + ecc_calc = chip->buffers.ecc_calc; + + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += stepsize) + { + chip->ops->cmdfunc(chip, RT_NAND_CMD_ECC_EN, 0, 0); + chip->ops->write_buf(chip, p, stepsize); + chip->ecc.calculate(chip, p, &ecc_calc[i]); + chip->ops->cmdfunc(chip, RT_NAND_CMD_ECC_DIS, 0, 0); + } + + rt_memcpy(&chip->buffers.oob_poi[eccpos], ecc_calc, chip->ecc.layout->length); + + chip->ops->write_buf(chip, chip->buffers.oob_poi, chip->parent.oob_size); + + return RT_MTD_EOK; +} + +static rt_err_t _read_oob_std(rt_nand_driver_t chip, rt_off_t page) +{ + chip->ops->cmdfunc(chip, RT_NAND_CMD_PAGE_RD, page, chip->parent.page_size); + chip->ops->read_buf(chip, chip->buffers.oob_poi, chip->parent.oob_size); + + return RT_MTD_EOK; +} + +static rt_err_t _write_oob_std(rt_nand_driver_t chip, rt_off_t page) +{ + int status; + + chip->ops->cmdfunc(chip, RT_NAND_CMD_PAGE_WR_BGN, page, chip->parent.page_size); + chip->ops->write_buf(chip, chip->buffers.oob_poi, chip->parent.oob_size); + /* Send command to program the OOB data */ + status = chip->ops->cmdfunc(chip, RT_NAND_CMD_PAGE_WR_END, -1, -1); + + return status & NAND_ERROR ? -RT_MTD_EIO : RT_MTD_EOK; +} + +static rt_err_t _only_read_oob(rt_nand_driver_t chip, + rt_off_t page, + rt_uint8_t *spare, + rt_size_t spare_len) +{ + int len; + rt_uint8_t *oobbuf = spare; + rt_size_t ooblen = spare_len; + rt_err_t ret = RT_MTD_EOK; + + len = chip->free_layout->length; + + _read_oob_std(chip, page); + + len = min(len, ooblen); + oobbuf = _transfer_oob(chip, oobbuf, len); + + return ret; +} + +static rt_err_t _only_write_oob(rt_nand_driver_t chip, + rt_off_t page, + const rt_uint8_t *spare, + rt_size_t spare_len) +{ + const rt_uint8_t *oobbuf = spare; + rt_size_t ooblen = spare_len; + rt_err_t ret = RT_MTD_EOK; + + _fill_oob(chip, oobbuf, ooblen); + + ret = _write_oob_std(chip, page); + + return ret; +} + +static rt_err_t _do_read_desc(rt_nand_driver_t chip, + rt_off_t page, + rt_uint8_t *data, rt_uint32_t data_len, + rt_uint8_t *spare, rt_uint32_t spare_len) +{ + rt_uint8_t *oobbuf = spare; + rt_size_t ooblen = spare_len; + rt_err_t ret = RT_MTD_EOK; + + chip->ops->cmdfunc(chip, RT_NAND_CMD_PAGE_RD, page, 0x00); + ret = _read_page_hwecc(chip, data); + if (ret != RT_MTD_EOK) + { + return ret; + } + + if (oobbuf != RT_NULL) + { + ooblen = min(spare_len, chip->free_layout->length); + if (ooblen) + { + oobbuf = _transfer_oob(chip, oobbuf, ooblen); + } + } + + return ret; +} + +static rt_err_t _do_write_desc(rt_nand_driver_t chip, + rt_off_t page, + const rt_uint8_t *data, rt_uint32_t data_len, + const rt_uint8_t *spare, rt_uint32_t spare_len) +{ + const rt_uint8_t *oobbuf = spare; + rt_size_t ooblen = spare_len; + rt_err_t ret = RT_MTD_EOK; + + if (oobbuf != RT_NULL) + { + ooblen = min(spare_len, chip->free_layout->length); + if (ooblen) + { + oobbuf = _fill_oob(chip, oobbuf, ooblen); + } + } + else + { + /* We still need to erase leftover OOB data */ + rt_memset(chip->buffers.oob_poi, 0xFF, chip->parent.oob_size); + } + chip->ops->cmdfunc(chip, RT_NAND_CMD_PAGE_WR_BGN, page, 0x00); + + _write_page_hwecc(chip, data); + + ret = chip->ops->cmdfunc(chip, RT_NAND_CMD_PAGE_WR_END, -1, -1); + + return ret; +} + +static rt_err_t _read_page(rt_mtd_nand_t device, + rt_off_t page, + rt_uint8_t *data, rt_uint32_t data_len, + rt_uint8_t *spare, rt_uint32_t spare_len) +{ + rt_err_t ret = RT_MTD_EOK; + rt_nand_driver_t chip = MTD_NAND_DRV(device); + + if (data == RT_NULL || data_len == 0) + { + ret = _only_read_oob(chip, page, spare, spare_len); + } + else + { + ret = _do_read_desc(chip, page, data, data_len, spare, spare_len); + } + + return ret; +} + +static rt_err_t _write_page(rt_mtd_nand_t device, + rt_off_t page, + const rt_uint8_t *data, rt_uint32_t data_len, + const rt_uint8_t *spare, rt_uint32_t spare_len) +{ + rt_err_t ret = RT_MTD_EOK; + rt_nand_driver_t chip = MTD_NAND_DRV(device); + + if (data == RT_NULL || data_len == 0) + { + ret = _only_write_oob(chip, page, spare, spare_len); + } + else + { + ret = _do_write_desc(chip, page, data, data_len, spare, spare_len); + } + + return ret; +} + +static rt_err_t _move_page(rt_mtd_nand_t device, rt_off_t src_page, rt_off_t dst_page) +{ + return RT_MTD_EOK; +} + +static rt_err_t _erase_block(rt_mtd_nand_t device, rt_int32_t block) +{ + rt_err_t ret = RT_EOK; + rt_nand_driver_t chip = MTD_NAND_DRV(device); + + ret = chip->ops->cmdfunc(chip, RT_NAND_CMD_BLK_ERASE, block, 0); + + return ret; +} + +static rt_err_t _check_block(rt_mtd_nand_t device, rt_int32_t block) +{ + rt_err_t ret; + rt_nand_driver_t chip = MTD_NAND_DRV(device); + + if (chip->ops->checkbad) + { + ret = chip->ops->checkbad(chip, block); + } + else + { + int page = block * chip->parent.pages_per_block; + _read_oob_std(chip, page); + ret = chip->buffers.oob_poi[0] != 0xFF; + } + + return ret; +} + +static rt_err_t _mark_badblock(rt_mtd_nand_t device, rt_int32_t block) +{ + rt_err_t ret = RT_MTD_EOK; + rt_nand_driver_t chip = MTD_NAND_DRV(device); + + if (chip->ops->markbad) + { + ret = chip->ops->markbad(chip, block); + } + else + { + int page = block * chip->parent.pages_per_block; + rt_memset(chip->buffers.oob_poi, 0xFF, chip->parent.oob_size); + chip->buffers.oob_poi[0] = 0; + ret = _write_oob_std(chip, page); + } + + return ret; +} + +static rt_uint32_t _read_id(rt_mtd_nand_t device) +{ + rt_uint32_t id; + rt_nand_driver_t chip = MTD_NAND_DRV(device); + id = chip->ops->read_id(); + return id; +} + /** * RT-Thread Generic Device Interface */ static rt_err_t _mtd_init(rt_device_t dev) { - return RT_EOK; + return RT_MTD_EOK; } static rt_err_t _mtd_open(rt_device_t dev, rt_uint16_t oflag) { - return RT_EOK; + return RT_MTD_EOK; } static rt_err_t _mtd_close(rt_device_t dev) { - return RT_EOK; + return RT_MTD_EOK; } static rt_size_t _mtd_read(rt_device_t dev, - rt_off_t pos, - void *buffer, - rt_size_t size) + rt_off_t pos, + void *buffer, + rt_size_t size) { + /* return read size (count of block) */ return size; } static rt_size_t _mtd_write(rt_device_t dev, - rt_off_t pos, + rt_off_t pos, const void *buffer, - rt_size_t size) + rt_size_t size) { return size; } static rt_err_t _mtd_control(rt_device_t dev, int cmd, void *args) { - return RT_EOK; + rt_ubase_t block; + rt_err_t result = RT_MTD_EOK; + rt_mtd_nand_t nand = RT_NULL; + + RT_ASSERT(dev != RT_NULL); + RT_ASSERT(args != RT_NULL); + + nand = (rt_mtd_nand_t) dev; + + switch (cmd) + { + case RT_MTD_CTRL_GET_ID : + { + rt_uint32_t id; + id = nand->ops->read_id(nand); + *(rt_uint32_t*)args = id; + } + break; + case RT_MTD_CTRL_ERASE : + block = *(rt_uint32_t*)args; + if (block > nand->block_end - nand->block_start) + { + return -RT_MTD_ESRC; + } + nand->ops->erase_block(nand, block + nand->block_start); + break; + case RT_MTD_CTRL_CHECK : + if (nand->ops->check_block == RT_NULL) + { + return RT_MTD_EOK; + } + block = *(rt_uint32_t*)args; + if (block > nand->block_end) + { + return -RT_MTD_ESRC; + } + nand->ops->check_block(nand, block + nand->block_start); + break; + case RT_MTD_CTRL_MARK : + if (nand->ops->mark_badblock == RT_NULL) + { + return RT_MTD_EOK; + } + block = *(rt_uint32_t*)args; + if (block > nand->block_end) + { + return -RT_MTD_ESRC; + } + nand->ops->mark_badblock(nand, block + nand->block_start); + break; + case RT_MTD_CTRL_READ : + { + struct rt_mtd_nand_rw_args *rw; + rw = (struct rt_mtd_nand_rw_args*)args; + if (rw->page < 0 || rw->page > nand->block_total * nand->pages_per_block) + { + return -RT_MTD_ESRC; + } + if ((rw->data && rw->data_len > nand->page_size) || + (rw->spare && rw->spare_len > nand->oob_size)) + { + return -RT_MTD_ESRC; + } + result = nand->ops->read_page(nand, + rw->page + nand->block_start * nand->pages_per_block, + rw->data, rw->data_len, rw->spare, rw->spare_len); + } + break; + case RT_MTD_CTRL_WRITE : + { + struct rt_mtd_nand_rw_args *rw; + rw = (struct rt_mtd_nand_rw_args*)args; + if (rw->page < 0 || rw->page > nand->block_total * nand->pages_per_block) + { + return -RT_MTD_ESRC; + } + if ((rw->data && rw->data_len > nand->page_size) || + (rw->spare && rw->spare_len > nand->oob_size)) + { + return -RT_MTD_ESRC; + } + nand->ops->write_page(nand, + rw->page + nand->block_start * nand->pages_per_block, + rw->data, rw->data_len, rw->spare, rw->spare_len); + } + break; + default: + break; + } + return result; } +static const struct rt_mtd_nand_driver_ops _nand_drv_ops = +{ + _read_id, + _read_page, + _write_page, + _move_page, + _erase_block, + _check_block, + _mark_badblock, +}; + #ifdef RT_USING_DEVICE_OPS const static struct rt_device_ops mtd_nand_ops = { @@ -71,9 +513,27 @@ rt_err_t rt_mtd_nand_register_device(const char *name, struct rt_mtd_nand_device *device) { rt_device_t dev; + rt_uint8_t *buf = RT_NULL; + rt_nand_driver_t chip = MTD_NAND_DRV(device); + + RT_ASSERT(device != RT_NULL); + + device->ops = &_nand_drv_ops; + + buf = rt_malloc(device->oob_size * 3); + if (buf == RT_NULL) + { + return -RT_ENOMEM; + } + + chip->buffers.oob_poi = buf; + buf += chip->parent.oob_size; + chip->buffers.ecc_calc = buf; + buf += chip->parent.oob_size; + chip->buffers.ecc_code = buf; + chip->buffers.pagebuf = 0; /* alloc when unaligen access */ dev = RT_DEVICE(device); - RT_ASSERT(dev != RT_NULL); /* set device class and generic device interface */ dev->type = RT_Device_Class_MTD; @@ -108,13 +568,19 @@ static void mtd_dump_hex(const rt_uint8_t *ptr, rt_size_t buflen) rt_kprintf("%06x: ", i); for (j = 0; j < 16; j++) if (i + j < buflen) + { rt_kprintf("%02x ", buf[i + j]); + } else + { rt_kprintf(" "); + } rt_kprintf(" "); for (j = 0; j < 16; j++) if (i + j < buflen) + { rt_kprintf("%c", __is_print(buf[i + j]) ? buf[i + j] : '.'); + } rt_kprintf("\n"); } } @@ -129,7 +595,7 @@ int mtd_nandid(const char *name) return -RT_ERROR; } - return rt_mtd_nand_read_id(nand); + return rt_mtd_nand_read_id(); } int mtd_nand_read(const char *name, int block, int page) @@ -222,12 +688,12 @@ int mtd_nand_write(const char *name, int block, int page) oob_ptr = page_ptr + nand->page_size; /* prepare page data */ - for (index = 0; index < nand->page_size; index ++) + for (index = 0; index < nand->page_size; index++) { page_ptr[index] = index & 0xff; } /* prepare oob data */ - for (index = 0; index < nand->oob_size; index ++) + for (index = 0; index < nand->oob_size; index++) { oob_ptr[index] = index & 0xff; } @@ -270,7 +736,7 @@ int mtd_nand_erase_all(const char *name) return -RT_ERROR; } - for (index = 0; index < (nand->block_end - nand->block_start); index ++) + for (index = nand->block_start; index <= nand->block_end; index++) { rt_mtd_nand_erase_block(nand, index); } -- Gitee