Embedded Linux Primer: A Practical Real-World Approach

10.3. MTD Partitions

Most Flash devices on a given hardware platform are divided into several sections, called partitions, similar to the partitions found on a typical desktop workstation hard drive. The MTD subsystem provides support for such Flash partitions. The MTD subsystem must be configured for MTD partitioning support. Figure 10-2 illustrates the configuration options for MTD partitioning support.

Figure 10-2. Kernel configuration for MTD partitioning support

Several methods exist for communicating the partition data to the Linux kernel. The following methods are currently supported. You can see the configuration options for each in Figure 10-2 under MTD Partitioning Support.

  • Redboot partition table parsing

  • Kernel command-line partition table definition

  • Board-specific mapping drivers

MTD also allows configurations without partition data. In this case, MTD simply treats the entire Flash memory as a single device.

10.3.1. Redboot Partition Table Partitioning

One of the more common methods of defining and detecting MTD partitions stems from one of the original implementations: Redboot partitions. Redboot is a bootloader found on many embedded boards, especially ARM XScale boards such as the ADI Engineering Coyote Reference Platform.

The MTD subsystem defines a method for storing partition information on the Flash device itself, similar in concept to a partition table on a hard disk. In the case of the Redboot partitions, the developer reserves and specifies a Flash erase block that holds the partition definitions. A mapping driver is selected that calls the partition parsing functions during boot to detect the partitions on the Flash device. Figure 10-2 shows the mapping driver for our example board; it is the final highlighted entry defining CONFIG_MTD_IXP4xx.

As usual, taking a detailed look at an example helps to illustrate these concepts. We start by looking at the information provided by the Redboot bootloader for the Coyote platform. Listing 10-4 captures some of the output of the Redboot bootloader upon power-up.

Listing 10-4. Redboot Messages on Power-Up

Platform: ADI Coyote (XScale) IDE/Parallel Port CPLD Version: 1.0 Copyright (C) 2000, 2001, 2002, Red Hat, Inc. RAM: 0x00000000-0x04000000, 0x0001f960-0x03fd1000 available FLASH: 0x50000000 - 0x51000000, 128 blocks of 0x00020000 bytes each. ...

This tells us that RAM on this board is physically mapped starting at address 0x00000000 and that Flash is mapped at physical address 0x50000000 through 0x51000000. We can also see that the Flash has 128 blocks of 0x00020000 (128KB) each.

Redboot contains a command to create and display partition information on the Flash. Listing 10-5 contains the output of the fis list command, part of the Flash Image System family of commands available in the Redboot bootloader.

Listing 10-5. Redboot Flash Partition List

RedBoot> fis list Name FLASH addr Mem addr Length Entry point RedBoot 0x50000000 0x50000000 0x00060000 0x00000000 RedBoot config 0x50FC0000 0x50FC0000 0x00001000 0x00000000 FIS directory 0x50FE0000 0x50FE0000 0x00020000 0x00000000 RedBoot>

From Listing 10-5, we see that the Coyote board has three partitions defined on the Flash. The partition named RedBoot contains the executable Redboot bootloader image. The partition named RedBoot config contains the configuration parameters maintained by the bootloader. The final partition named FIS directory holds information about the partition table itself.

When properly configured, the Linux kernel can detect and parse this partition table and create MTD partitions representing the physical partitions on Flash. Listing 10-6 reproduces a portion of the boot messages that are output from the aforementioned ADI Engineering Coyote board, booting a Linux kernel configured with support for detecting Redboot partitions.

Listing 10-6. Detecting Redboot Partitions on Linux Boot

... IXP4XX-Flash0: Found 1 x16 devices at 0x0 in 16-bit bank Intel/Sharp Extended Query Table at 0x0031 Using buffer write method cfi_cmdset_0001: Erase suspend on write enabled Searching for RedBoot partition table in IXP4XX-Flash0 at offset 0xfe0000 3 RedBoot partitions found on MTD device IXP4XX-Flash0 Creating 3 MTD partitions on "IXP4XX-Flash0": 0x00000000-0x00060000: "RedBoot" 0x00fc0000-0x00fc1000: "RedBoot config" 0x00fe0000-0x01000000: "FIS directory" ...

The first message in Listing 10-6 is printed when the Flash chip is detected, via the Common Flash Interface (CFI) driver, enabled via CONFIG_MTD_CFI. CFI is an industry-standard method for determining the Flash chip's characteristics, such as manufacturer, device type, total size, and erase block size. See Section 10.5.1, "Suggestions for Additional Reading," at the end of this chapter for a pointer to the CFI specification.

CFI is enabled via the kernel-configuration utility under the Memory Technology Devices (MTD) top-level menu. Select Detect flash chips by Common Flash Interface (CFI) probe under RAM/ROM/Flash chip drivers, as illustrated in Figure 10-3.

Figure 10-3. Kernel configuration for MTD CFI support

As shown in Listing 10-6, the Flash chip is detected via the CFI interface. Because we also enabled CONFIG_MTD_REDBOOT_PARTS (see Figure 10-2), MTD scans for the Redboot partition table on the Flash chip. Notice also that the chip has been enumerated with the device name IXP4XX-Flash0. You can see from Listing 10-6 that the Linux kernel has detected three partitions on the Flash chip, as enumerated previously using the fis list command in Redboot.

When the infrastructure is in place as described here, the Linux kernel automatically detects and creates kernel data structures representing the three Flash partitions. Evidence of these can be found in the /proc file system when the kernel has completed initialization, as shown in Listing 10-7.

Listing 10-7. Kernel MTD Flash Partitions

root@coyote:~# cat /proc/mtd dev: size erasesize name mtd0: 00060000 00020000 "RedBoot" mtd1: 00001000 00020000 "RedBoot config" mtd2: 00020000 00020000 "FIS directory" #

We can easily create a new Redboot partition. We use the Redboot FIS commands for this example, but we do not detail the Redboot commands in this book. However, the interested reader can consult the Redboot user documentation listed in Section 10.5.1 at the end of this chapter. Listing 10-8 shows the details of creating a new Redboot partition.

Listing 10-8. Creating a New Redboot Partition

RedBoot> load -r -v -b 0x01008000 coyote-40-zImage Using default protocol (TFTP) Raw file loaded 0x01008000-0x0114dccb, assumed entry at 0x01008000 RedBoot> fis create -b 0x01008000 -l 0x145cd0 -f 0x50100000 MyKernel ... Erase from 0x50100000-0x50260000: ........... ... Program from 0x01008000-0x0114dcd0 at 0x50100000: ........... ... Unlock from 0x50fe0000-0x51000000: . ... Erase from 0x50fe0000-0x51000000: . ... Program from 0x03fdf000-0x03fff000 at 0x50fe0000: . ... Lock from 0x50fe0000-0x51000000: .

First, we load the image we will use to create the new partition. We will use our kernel image for the example. We load it to memory address 0x01008000. Then we create the new partition using the Redboot fis create command. We have instructed Redboot to create the new partition in an area of Flash starting at 0x50100000. You can see the action as Redboot first erases this area of Flash and then programs the kernel image. In the final sequence, Redboot unlocks its directory area and updates the FIS Directory with the new partition information. Listing 10-9 shows the output of fis list with the new partition. Compare this with the output in Listing 10-5.

Listing 10-9. New Redboot Partition List

RedBoot> fis list Name FLASH addr Mem addr Length Entry point RedBoot 0x50000000 0x50000000 0x00060000 0x00000000 RedBoot config 0x50FC0000 0x50FC0000 0x00001000 0x00000000 FIS directory 0x50FE0000 0x50FE0000 0x00020000 0x00000000 MyKernel 0x50100000 0x50100000 0x00160000 0x01008000

Of course, when we boot the Linux kernel, it discovers the new partition and we can operate on it as we see fit. The astute reader might have realized the other benefit of this new partition: We can now boot the kernel from Flash instead of having to load it via tftp every time. The command is illustrated next. Simply pass the Redboot exec command the Flash starting address of the partition and the length of the image to transfer into RAM.

RedBoot> exec -b 0x50100000 -l 0x145cd0 Uncompressing Linux........... done, booting the kernel. ...

10.3.2. Kernel Command Line Partitioning

As detailed in Section 10.3, "MTD Partitions," the raw Flash partition information can be communicated to the kernel using other methods. Indeed, possibly the most straightforward, though perhaps not the simplest method is to manually pass the partition information directly on the kernel command line. Of course, as we have already learned, some bootloaders make that easy (for example U-Boot), whereas others do not have a facility to pass a kernel command line to the kernel upon boot. In these cases, the kernel command line must be configured at compile time and, therefore, is more difficult to change, requiring a recompile of the kernel itself each time the partitions are modified.

To enable command-line partitioning in the MTD subsystem, your kernel must be configured for this support. You can see this configuration option in Figure 10-2 under MTD partitioning support. Select the option for command-line partition table parsing, which defines the CONFIG_MTD_CMDLINE_PARTS option.

Listing 10-10 shows the format for defining a partition on the kernel command line (taken from .../drivers/mtd/cmdlinepart.c).

Listing 10-10. Kernel Command-Line MTD Partition Format

mtdparts=<mtddef>[;<mtddef] *<mtddef> := <mtd-id>:<partdef>[,<partdef>] *<partdef> := <size>[@offset][<name>][ro] *<mtd-id> := unique name used in mapping driver/device (mtd->name) *<size> := std linux memsize OR "-" to denote all remaining space *<name> := '(' NAME ')'

Each mtddef parameter passed on the kernel command line defines a separate partition. As shown is Listing 10-10, each mtddef definition contains multiple parts. You can specify a unique ID, partition size, and offset from the start of the Flash. You can also pass the partition a name and, optionally, the read-only attribute. Referring back to our Redboot partition definitions in Listing 10-5, we could statically define these on the kernel command line as follows:

mtdparts=MainFlash:384K(Redboot),4K(config),128K(FIS),-(unused)

With this definition, the kernel would instantiate four MTD partitions, with an MTD ID of MainFlash, containing the sizes and layout matching that found in Listing 10-5.

10.3.3. Mapping Driver

The final method for defining your board-specific Flash layout is to use a dedicated board-specific mapping driver. The Linux kernel source tree contains many examples of mapping drivers, located in .../drivers/mtd/maps. Any one of these will provide good examples for how to create your own. The implementation details vary by architecture.

The mapping driver is a proper kernel module, complete with module_init() and module_exit() calls, as described in Chapter 8, "Device Driver Basics." A typical mapping driver is small and easy to navigate, often containing fewer than a couple dozen lines of C.

Listing 10-11 reproduces a section of .../drivers/mtd/maps/pq2fads. This mapping driver defines the Flash device on a Freescale PQ2FADS evaluation board that supports the MPC8272 and other processors.

Listing 10-11. PQ2FADs Flash Mapping Driver

... static struct mtd_partition pq2fads_partitions[] = { { #ifdef CONFIG_ADS8272 .name = "HRCW", .size = 0x40000, .offset = 0, .mask_flags= MTD_WRITEABLE, /* force read-only */ }, { .name = "User FS", .size = 0x5c0000, .offset = 0x40000, #else .name = "User FS", .size = 0x600000, .offset = 0, #endif }, { .name = "uImage", .size = 0x100000, .offset = 0x600000, .mask_flags = MTD_WRITEABLE, /* force read-only */ }, { .name = "bootloader", .size = 0x40000, .offset = 0x700000, .mask_flags = MTD_WRITEABLE, /* force read-only */ }, { .name = "bootloader env", .size = 0x40000, .offset = 0x740000, .mask_flags = MTD_WRITEABLE, /* force read-only */ } }; /* pointer to MPC885ADS board info data */ extern unsigned char __res[]; static int __init init_pq2fads_mtd(void) { bd_t *bd = (bd_t *)__res; physmap_configure(bd->bi_flashstart, bd->bi_flashsize, PQ2FADS_BANK_WIDTH, NULL); physmap_set_partitions(pq2fads_partitions, sizeof (pq2fads_partitions) / sizeof (pq2fads_partitions[0])); return 0; } static void __exit cleanup_pq2fads_mtd(void) { } module_init(init_pq2fads_mtd); module_exit(cleanup_pq2fads_mtd); ...

This simple but complete Linux device driver communicates the PQ2FADS Flash mapping to the MTD subsystem. Recall from Chapter 8 that when a function in a device driver is declared with the module_init() macro, it is automatically invoked during Linux kernel boot at the appropriate time. In this PQ2FADS mapping driver, the module initialization function init_pq2fads_mtd() performs just two simple calls:

  • physmap_configure() passes to the MTD subsystem the Flash chip's physical address, size, and bank width, along with any special setup function required to access the Flash.

  • physmap_set_partitions() passes the board's unique partition information to the MTD subsystem from the partition table defined in the pq2fads_partitions[] array found at the start of this mapping driver.

Following this simple example, you can derive a mapping driver for your own board.

10.3.4. Flash Chip Drivers

MTD has support for a wide variety of Flash chips and devices. Chances are very good that your chosen chip has also been supported. The most common Flash chips support the Common Flash Interface (CFI) mentioned earlier. Older Flash chips might have JEDEC support, which is an older Flash compatibility standard. Figure 10-4 shows the kernel configuration from a recent Linux kernel snapshot. This version supports many Flash types.

Figure 10-4. Flash device support

If your Flash chip is not supported, you must provide a device file yourself. Using one of the many examples in .../drivers/mtd/chips as a starting point, customize or create your own Flash device driver. Better yet, unless the chip was just introduced with some newfangled interface, chances are good that someone has already produced a driver.

10.3.5. Board-Specific Initialization

Along with a mapping driver, your board-specific (platform) setup must provide the underlying definitions for proper MTD Flash system operation. Listing 10-12 reproduces the relevant portions of .../arch/arm/mach-ixp4xx/coyote-setup.c.

Listing 10-12. Coyote-Specific Board Setup

static struct flash_platform_data coyote_flash_data = { .map_name = "cfi_probe", .width = 2, }; static struct resource coyote_flash_resource = { .start = COYOTE_FLASH_BASE, .end = COYOTE_FLASH_BASE + COYOTE_FLASH_SIZE - 1, .flags = IORESOURCE_MEM, }; static struct platform_device coyote_flash = { .name = "IXP4XX-Flash", .id = 0, .dev = { .platform_data = &coyote_flash_data, }, .num_resources = 1, .resource = &coyote_flash_resource, }; ... static struct platform_device *coyote_devices[] __initdata = { &coyote_flash, &coyote_uart }; static void __init coyote_init(void) { ... platform_add_devices(coyote_devices, ARRAY_SIZE(coyote_devices)); } ...

In Listing 10-12, only the relevant portions of the coyote-setup.c platform initialization file are reproduced. Starting from the bottom, the coyote_init() function calls platform_add_devices(), specifying the Coyote-specific devices defined earlier in this file. You'll notice that two devices are defined just above the coyote_init() routine. The one we're interested in for this discussion is coyote_flash. This structure of type struct platform_device contains all the important details needed by the Linux kernel and MTD subsystem.

The .name member of the coyote_flash structure binds our platform-specific Flash resource to a mapping driver with the same name. You can see this in the mapping driver file .../drivers/mtd/maps/ixp4xx.c. The .resource member communicates the base address of the Flash on the board. The .dev member, which contains a .platform_data member, ties our Flash setup to a chip driver. In this case, we have specified that our board will use the CFI probe method, specified in the kernel configuration as CONFIG_MTD_CFI. You can see this configuration selection in Figure 10-4.

Depending on your own architecture and board, you can use a method similar to this to define the Flash support for your own board.

Категории