Skip to content

MT29F4G08 NAND Flash Driver#100683

Closed
LinderPi wants to merge 3 commits intozephyrproject-rtos:mainfrom
endresshauser-lp:mt29f4g08_driver
Closed

MT29F4G08 NAND Flash Driver#100683
LinderPi wants to merge 3 commits intozephyrproject-rtos:mainfrom
endresshauser-lp:mt29f4g08_driver

Conversation

@LinderPi
Copy link
Copy Markdown
Contributor

@LinderPi LinderPi commented Dec 8, 2025

Introduces a NAND flash driver for Micron's MT29F4G08 memory device that is configurable via devicetree. A generic flash controller driver communicates to the flexible memory controller of STM32 MCUs. Any other NAND flash driver can be implemented to work via FMC in the future. The chip-specific driver provides the flash API for upper layers and enables the on-die ECC feature.

The flash controller is strongly inspired by the STM32 HAL NAND driver following the ONFI specification. However, it optionally uses DMA to transfer a read page. In future, an additional function can be implemented for entire page reads. At the moment this is not required by upper layers and the direct memory transfer is somehow slower to a yet unknown problem.

As it is the first NAND flash driver in Zephyr, some API extensions are foreseen for bad block management by a flash translation layer (FTL). As proposed in #99908, we are currently working on a NFTL disk driver module based on Dhara. A first version is implemented and available here for testing.

As an optional enhancement, a bad block map could be kept in memory such that the driver does not need to ask the memory device for bad blocks on every request.

Copy link
Copy Markdown
Contributor

@etienne-lms etienne-lms left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor comments only.

Comment thread drivers/flash/Kconfig.stm32_fmc
Comment thread drivers/flash/flash_stm32_fmc_mt29f4g08.c Outdated
Comment thread drivers/flash/flash_stm32_fmc_mt29f4g08.c
Comment thread drivers/flash/flash_stm32_fmc_mt29f4g08.c Outdated
Comment thread drivers/flash/flash_stm32_fmc_mt29f4g08.c Outdated
Comment thread drivers/flash/flash_stm32_fmc_nand.c Outdated
Comment thread drivers/flash/flash_stm32_fmc_nand.c Outdated
Comment thread drivers/flash/flash_stm32_fmc_nand.h Outdated
Comment thread include/zephyr/drivers/flash/nand_flash_api_ex.h Outdated
Comment thread drivers/flash/flash_stm32_fmc_nand.c Outdated
@LinderPi LinderPi force-pushed the mt29f4g08_driver branch 2 times, most recently from a5ce021 to 7514a70 Compare December 10, 2025 16:11
@LinderPi LinderPi requested a review from etienne-lms December 11, 2025 07:32
Comment on lines +557 to +560
(void)FMC_NAND_CommonSpace_Timing_Init(dev_data->instance, &timing,
dev_data->init.NandBank);
(void)FMC_NAND_AttributeSpace_Timing_Init(dev_data->instance, &timing,
dev_data->init.NandBank);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In theory, we're not supposed to call directly these apis, which are not meant to be public, but API internal to the HAL driver. As such there is no guarantee on these APIs (portability, stability, ..).
Can you detail why you chose not to call HAL_NAND_Init() directly ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The STM32 HAL NAND driver does not provide the entire functionality we need (DMA support, status read, etc.). Additionally, there is no intend to further develop it. This is the reasoning I got from ST's official support: "The NAND is not so popular since the xSPI memories we see the FMC usage less and less."

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The STM32 HAL NAND driver does not provide the entire functionality we need (DMA support, status read, etc.).

Ok, that's clear. However, I need to check internally if FMC_NAND_() usage is safe.

Comment thread drivers/flash/flash_stm32_fmc_nand.c Outdated
Comment thread drivers/flash/flash_stm32_fmc_nand.c Outdated
Comment thread drivers/flash/Kconfig.stm32_fmc
Comment thread drivers/flash/flash_stm32_fmc_mt29f4g08.c Outdated
*(int *)out =
((ret == 0) && (spare_area[0] != 0xFF)) ? NAND_BLOCK_BAD : NAND_BLOCK_GOOD;
break;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think these braces (in both case sequences) are useful. There indentation could be confusing. Could you remove them?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The advantage is to define operation specific variables in the block. Also, the indentation remains the same.

Comment thread drivers/flash/flash_stm32_fmc_mt29f4g08.c Outdated
struct nand_flash_address address;
uint8_t spare_area[config->spare_area_size];

if (block * config->block_size >= config->flash_size) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (block * config->block_size >= config->flash_size) {
off_t offset;
if (u32_mul_overflow(block, config->block_size, &offset) || (offset >= config->flash_size)) {

and below:

-		address = flash_mt29f4g08_calculate_address(config, block * config->block_size);
+		address = flash_mt29f4g08_calculate_address(config, offset);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice point. Thanks :)

/* Get page data into buffer */
for (size_t index = 0; index < dev_data->page_size; index++) {
config->page_buffer[index] = sys_read8(NAND_DEVICE);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using config->page_buffer allocated in cache memory is useful when using DMAs, but here, maybe worth the straight load data read into the likely cached data buffer.
If using this scheme, you could avoid flash_stm32_fmc_nand_page_buffer_XXX[] allocations.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What could work is something like:

/* Read chunk of page data directly into output buffer */
for (size_t index = 0; index < dev_data->page_size; index++) {
	if (index >= page_offset && index < (page_offset + chunk)) {
		*data = sys_read8(NAND_DEVICE);
		data++;
	} else {
		(void)sys_read8(NAND_DEVICE);
	}
}

In our case, this speeds up reading a page from 3.6 MiBps to 4.5 MiBps. Is this what you have imagined?

@LinderPi
Copy link
Copy Markdown
Contributor Author

I also implemented the suggestions from #100858 (comment) moving the extended operations to the generic definition. The filename did no more make sense why it is now called nand_flash.h for NAND flash specific data structures and maybe functions in future.

Some API extensions are foreseen for bad block management by a flash
translation layer (FTL).

Signed-off-by: Pascal Linder <pascal.linder@zuehlke.com>
Introduces a NAND flash driver for Micron's MT29F4G08 memory device that
is configurable via devicetree. A generic flash controller driver
communicates to the flexible memory controller of STM32 MCUs. Any other
NAND flash driver can be implemented to work via FMC in the future. The
chip-specific driver provides the flash API for upper layers and enables
the on-die ECC feature.

As it is the first NAND flash driver in Zephyr, some API extensions are
foreseen for bad block management by a flash translation layer (FTL).

Signed-off-by: Pascal Linder <pascal.linder@zuehlke.com>
Signed-off-by: Tim Pambor <tim.pambor@codewrights.de>
Renames overlays to be board-specific and adds a build test for the newly
introduced NAND flash driver for MT29F4G08 via STM32 FMC.

Signed-off-by: Pascal Linder <pascal.linder@zuehlke.com>
@zephyrbot zephyrbot added the area: Devicetree Binding PR modifies or adds a Device Tree binding label Jan 14, 2026
@zephyrbot zephyrbot requested a review from JarmouniA January 14, 2026 17:41
Comment thread dts/bindings/memory-controllers/st,stm32-fmc-nand.yaml
# SPDX-License-Identifier: Apache-2.0

description: |
NAND flash driver for Micron MT29F4G08 flash memory.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
NAND flash driver for Micron MT29F4G08 flash memory.
Micron MT29F4G08 NAND flash memory.


compatible: "micron,mt29f4g08"

properties:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a description to all the properties.

@sonarqubecloud
Copy link
Copy Markdown

flash_mt29f4g08_calculate_address(config, offset);

ret = flash_stm32_fmc_nand_read_page_chunk(controller, &address, page_offset, chunk,
(uint8_t *)data);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry if this was asked before but should not this driver be controller-independent instead? It seems every vendor will have to maintain their own variant of flash_vendor_mt29f4g08.c.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First, the split between flash_stm32_fmc_nand.c and flash_stm32_fmc_mt29f4g08.c is due to the MT29F4G08 driver making use of on‑die ECC, meaning error detection and correction are handled inside the NAND device itself. This is a vendor‑specific feature. As a result, supporting on‑die ECC reliably requires a NAND flash vendor‑specific flash driver rather than a fully generic one.

It would indeed be possible to add a controller‑specific but flash chip vendor‑agnostic driver such as a hypothetical flash_stm32_fmc_onfi.c. Such a driver could work with ONFI‑compliant NAND devices across different vendors. However, to stay generic, it would need to rely on host ECC (calculated in the MCU) or software ECC, and therefore could not take advantage of on‑die ECC features provided by some flashes.

The reason these drivers are NAND controller‑dependent is timing. Parallel NAND flashes have fairly strict and sometimes subtle timing requirements (setup/hold times, access times, etc.), and each controller exposes its own mechanism and parameters for generating those timings. In practice, it is very difficult to define a standardized, controller‑independent interface that maps cleanly onto the timing configuration model of every NAND controller. For the same reason, we also have vendor‑specific bindings for e.g. SDRAM, where the timing parameters are inherently tied to the memory controller’s register layout and capabilities. See, e.g.:

st,sdram-timing:
type: array
required: true
description: |
SDRAM timing configuration. Expected fields, in order, are,
- TMRD: Delay between a Load Mode Register command and an Active or
Refresh command in number of memory clock cycles.
- TXSR: Delay from releasing the Self-refresh command to issuing the
Activate command in number of memory clock cycles. If two SDRAM
devices are used, the FMC_SDTR1 and FMC_SDTR2 must be programmed with
the same TXSR timing corresponding to the slowest SDRAM device
- TRAS: Minimum Self-refresh period in number of memory clock cycles.
- TRC: Delay between the Refresh command and the Activate command, as
well as the delay between two consecutive Refresh commands. It is
expressed in number of memory clock cycles. If two SDRAM devices are
used, the TRC must be programmed with the timings of the slowest
device in both banks.
- TWP: Delay between a Write and a Precharge command in number of memory
clock cycles
- TRP: Delay between a Precharge command and another command in number
of memory clock cycles. If two SDRAM devices are used, the TRP must be
programmed with the timing of the slowest device in both banks.
- TRCD: Delay between the Activate command and a Read/Write command in
number of memory clock cycles.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First, the split between flash_stm32_fmc_nand.c and flash_stm32_fmc_mt29f4g08.c is due to the MT29F4G08 driver making use of on‑die ECC, meaning error detection and correction are handled inside the NAND device itself. This is a vendor‑specific feature. As a result, supporting on‑die ECC reliably requires a NAND flash vendor‑specific flash driver rather than a fully generic one.

It would indeed be possible to add a controller‑specific but flash chip vendor‑agnostic driver such as a hypothetical flash_stm32_fmc_onfi.c. Such a driver could work with ONFI‑compliant NAND devices across different vendors. However, to stay generic, it would need to rely on host ECC (calculated in the MCU) or software ECC, and therefore could not take advantage of on‑die ECC features provided by some flashes.

The reason these drivers are NAND controller‑dependent is timing. Parallel NAND flashes have fairly strict and sometimes subtle timing requirements (setup/hold times, access times, etc.), and each controller exposes its own mechanism and parameters for generating those timings. In practice, it is very difficult to define a standardized, controller‑independent interface that maps cleanly onto the timing configuration model of every NAND controller. For the same reason, we also have vendor‑specific bindings for e.g. SDRAM, where the timing parameters are inherently tied to the memory controller’s register layout and capabilities. See, e.g.:

st,sdram-timing:
type: array
required: true
description: |
SDRAM timing configuration. Expected fields, in order, are,
- TMRD: Delay between a Load Mode Register command and an Active or
Refresh command in number of memory clock cycles.
- TXSR: Delay from releasing the Self-refresh command to issuing the
Activate command in number of memory clock cycles. If two SDRAM
devices are used, the FMC_SDTR1 and FMC_SDTR2 must be programmed with
the same TXSR timing corresponding to the slowest SDRAM device
- TRAS: Minimum Self-refresh period in number of memory clock cycles.
- TRC: Delay between the Refresh command and the Activate command, as
well as the delay between two consecutive Refresh commands. It is
expressed in number of memory clock cycles. If two SDRAM devices are
used, the TRC must be programmed with the timings of the slowest
device in both banks.
- TWP: Delay between a Write and a Precharge command in number of memory
clock cycles
- TRP: Delay between a Precharge command and another command in number
of memory clock cycles. If two SDRAM devices are used, the TRP must be
programmed with the timing of the slowest device in both banks.
- TRCD: Delay between the Activate command and a Read/Write command in
number of memory clock cycles.

I did not actually mean that there should be single driver that covers all NAND chips. I was asking if it is possible to move the calls to STM32 FMC inside flash_stm32_fmc_mt29f4g08.c behind some common API so that it becomes vendor-agnostic, such as flash_mt29f4g08.c. Otherwise, we are going to end up with flash_vendorA_mt29f4g08.c, flash_vendorB_mt29f4g08.c whenever someone decides to add support for their controller.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did not actually mean that there should be single driver that covers all NAND chips. I was asking if it is possible to move the calls to STM32 FMC inside flash_stm32_fmc_mt29f4g08.c behind some common API so that it becomes vendor-agnostic, such as flash_mt29f4g08.c. Otherwise, we are going to end up with flash_vendorA_mt29f4g08.c, flash_vendorB_mt29f4g08.c whenever someone decides to add support for their controller.

Yes, I wanted to make clear both decisions, why it is flash-chip dependent and why it is flash-controller dependent as others might have the same question. The second part was about the flash-controller dependency.

The reason these drivers are NAND controller‑dependent is timing. Parallel NAND flashes have fairly strict and sometimes subtle timing requirements (setup/hold times, access times, etc.), and each controller exposes its own mechanism and parameters for generating those timings. In practice, it is very difficult to define a standardized, controller‑independent interface that maps cleanly onto the timing configuration model of every NAND controller. For the same reason, we also have vendor‑specific bindings for e.g. SDRAM, where the timing parameters are inherently tied to the memory controller’s register layout and capabilities. See, e.g.:

st,sdram-timing:
type: array
required: true
description: |
SDRAM timing configuration. Expected fields, in order, are,
- TMRD: Delay between a Load Mode Register command and an Active or
Refresh command in number of memory clock cycles.
- TXSR: Delay from releasing the Self-refresh command to issuing the
Activate command in number of memory clock cycles. If two SDRAM
devices are used, the FMC_SDTR1 and FMC_SDTR2 must be programmed with
the same TXSR timing corresponding to the slowest SDRAM device
- TRAS: Minimum Self-refresh period in number of memory clock cycles.
- TRC: Delay between the Refresh command and the Activate command, as
well as the delay between two consecutive Refresh commands. It is
expressed in number of memory clock cycles. If two SDRAM devices are
used, the TRC must be programmed with the timings of the slowest
device in both banks.
- TWP: Delay between a Write and a Precharge command in number of memory
clock cycles
- TRP: Delay between a Precharge command and another command in number
of memory clock cycles. If two SDRAM devices are used, the TRP must be
programmed with the timing of the slowest device in both banks.
- TRCD: Delay between the Activate command and a Read/Write command in
number of memory clock cycles.


/* Feature data. */
uint8_t feature_data[4];
};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this header common for both serial/parallel nand? This feature structure looks for parallel nand with ONFI definition.

@hakehuang
Copy link
Copy Markdown
Contributor

hakehuang commented Feb 9, 2026

Thanks for this NTFL driver and do we have any real board testing available for this? and what testing scope shall we defined?

@tpambor tpambor mentioned this pull request Mar 11, 2026
const struct flash_mt29f4g08_config *config = dev->config;
const struct device *controller = config->controller;
int ret;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Chrck 'len==0' first, if call is not going to do any work do not fail it on other checks.

const struct flash_mt29f4g08_config *config = dev->config;
const struct device *controller = config->controller;
int ret;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check size against 0 first, it is pointless to fail call when monwork is going to happen.

Comment on lines +14 to +52
reg:
type: int
required: true

page-size:
type: int
required: true

spare-area-size:
type: int
required: true

block-size:
type: int
required: true

plane-size:
type: int
required: true

flash-size:
type: int
required: true

setup-time:
type: int
default: 0

wait-setup-time:
type: int
default: 0

hold-setup-time:
type: int
default: 0

hiz-setup-time:
type: int
default: 0
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing descriptions, units are not explained.

Comment on lines +700 to +711

/**
* Checks whether a block is marked as bad. As input it takes the address of the block
* (off_t *). As output it returns @ref flash_block_status (enum flash_block_status *).
*/
FLASH_IS_BAD_BLOCK,

/**
* Marks a block as bad. As input it takes the address of the block (off_t *). There is no
* output.
*/
FLASH_MARK_BAD_BLOCK,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please explicitly number these. It is easier to debug when i do not have to count enums, and future depreciation, if happens, would be easier.

const struct flash_mt29f4g08_config *config = dev->config;
const struct device *controller = config->controller;
int ret;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check len first, and return 0 if no work is going to happen.

@tpambor tpambor added this to the v4.5.0 milestone Mar 23, 2026
@tpambor
Copy link
Copy Markdown
Contributor

tpambor commented Apr 20, 2026

Currently, no capacity to work on this.

@tpambor tpambor closed this Apr 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: Boards/SoCs area: Devicetree Binding PR modifies or adds a Device Tree binding area: Devicetree Bindings area: Flash area: MEMC area: Tests Issues related to a particular existing or missing test platform: STM32 ST Micro STM32

Projects

None yet

Development

Successfully merging this pull request may close these issues.

10 participants