Accessing flash memory from Linux

Raw NOR and NAND flash memory is handled by the memory technology device sub-system, or MTD, which provides basic interfaces to read, erase, and write blocks of flash memory. In the case of NAND flash, there are functions to handle the OOB area and to identify bad blocks.

For managed flash, you need drivers to handle the particular hardware interface. MMC/SD cards and eMMC use the mmcblk driver; CompactFlash and hard drives use the SCSI Disk driver, sd. USB flash drives use the usb_storage driver together with the sd driver.

The memory technology devices (MTD), sub-system was started by David Woodhouse in 1999 and has been extensively developed over the intervening years. In this section, I will concentrate on the way it handles the two main technologies, NOR and NAND flash.

MTD consists of three layers: a core set of functions, a set of drivers for various types of chips, and user-level drivers that present the flash memory as a character device or a block device, as shown in the following diagram:

Memory technology devices

The chip drivers are at the lowest level and interface with flash chips. Only a small number of drivers are needed for NOR flash chips, enough to cover the CFI standard and variations plus a few non-compliant chips which are now mostly obsolete. For NAND flash, you will need a driver for the NAND flash controller you are using; this is usually supplied as part of the board support package. There are drivers for about 40 of them in the current mainline kernel in the directory drivers/mtd/nand.

In most cases, you will want to partition the flash memory into a number of areas, for example, to provide space for a bootloader, a kernel image, or a root filesystem. In MTD, there are several ways to specify the size and location of partitions, the main ones being:

In the case of the first option, the kernel command line option to use is mtdparts, which is defined as follows in the Linux source code in drivers/mtd/cmdlinepart.c:

Perhaps an example will help. Imagine that you have one flash chip of 128 MB that is to be divided into five partitions. A typical command line would be:

The first element, before the colon, :, is mtd-id, which identifies the flash chip, either by number or by the name assigned by the board support package. If there is only one chip, as here, it can be left empty. If there is more than one chip, the information for each is separated by a semicolon, ;. Then, for each chip, there is a comma-separated list of partitions, each with a size in bytes, kilobytes, k, or megabytes, m, and a name in brackets. The ro suffix makes the partition read-only to MTD and is often used to prevent accidental overwriting of the bootloader. The size of the last partition for the chip may be replaced by a dash, -, indicating that it should take up all the remaining space.

You can see a summary of the configuration at runtime by reading /proc/mtd:

There is more detailed information for each partition in /sys/class/mtd, including the erase block size and the page size, and it is nicely summarized using mtdinfo:

The equivalent partition information can be written as part of the device tree like so:

A third alternative is to code the partition information as platform data in an mtd_partition structure, as shown in this example taken from arch/arm/mach-omap2/board-omap3beagle.c (NAND_BLOCK_SIZE is defined elsewhere to be 128K):

The character devices are the most important: they allow you to access the underlying flash memory as an array of bytes so that you can read and write (program) the flash. It also implements a number of ioctl functions that allow you to erase blocks and to manage the OOB area on NAND chips. The following list is in include/uapi/mtd/mtd-abi.h:

IOCTL

Description

MEMGETINFO

Gets basic MTD characteristic information

MEMERASE

Erases blocks in the MTD partition

MEMWRITEOOB

Writes out-of-band data for the page

MEMREADOOB

Reads out-of-band data for the page

MEMLOCK

Locks the chip (if supported)

MEMUNLOCK

Unlocks the chip (if supported)

MEMGETREGIONCOUNT

Gets the number of erase regions: non-zero if there are erase blocks of differing sizes in the partition, which is common for NOR flash, rare on NAND

MEMGETREGIONINFO

If MEMGETREGIONCOUNT is non-zero, this can be used to get the offset, size, and block count of each region

MEMGETOOBSEL

Deprecated

MEMGETBADBLOCK

Gets the bad block flag

MEMSETBADBLOCK

Sets the bad block flag

OTPSELECT

Sets OTP (one-time programmable) mode, if the chip supports it

OTPGETREGIONCOUNT

Gets the number of OTP regions

OTPGETREGIONINFO

Gets information about an OTP region

ECCGETLAYOUT

Deprecated

There is a set of utility programs known as mtd-utils for manipulating flash memory that makes use of these ioctl functions. The source is available from http://git.infradead.org/mtd-utils.git and is available as a package in the Yocto Project and Buildroot. The essential tools are shown in the following list. The package also contains utilities for the JFFS2 and UBI/UBIFS filesystems which I will cover later. For each of these tools, the MTD character device is one of the parameters:

To program NOR flash, you simply copy bytes to the MTD device node using the cp command or similar.

Unfortunately, this doesn't work with NAND memory as the copy will fail at the first bad block. Instead, use nandwrite, which skips over any bad blocks. To read back NAND memory, you should use nanddump which also skips bad blocks.

MMC/SD cards and eMMC chips are accessed using the mmcblk block driver. You need a host controller to match the MMC adapter you are using, which is part of the board support package. The drivers are located in the Linux source code in drivers/mmc/host.

MMC storage is partitioned using a partition table in exactly the same way you would for hard disks, using fdisk or a similar utility.