개발

jackryu 2009. 5. 18. 13:30

 

 

Understanding Memory Technology Devices in Embedded Linux

 

 

When you push the power switch on your handheld, it's more than likely that it boots from flash memory. When you click some buttons to save data on your cell phone, in all probability, your data starts life in flash memory. Today, Linux has penetrated the embedded space and is no longer confined to desktops and servers. Linux avatars manifest in PDAs, music players, set-top boxes, and even medical-grade devices. The Memory Technology Devices (MTD) subsystem of the kernel is responsible for interfacing your system with various flavors of flash memory found in these devices. This chapter uses the example of a Linux handheld to learn about MTD.

In This Chapter

  • What's Flash Memory?

504

  • Linux-MTD Subsystem

505

  • Map Drivers

506

  • NOR Chip Drivers

511

  • NAND Chip Drivers

513

  • User Modules

516

  • MTD-Utils

518

  • Configuring MTD

519

  • eXecute In Place

520

  • The Firmware Hub

520

  • Debugging

524

  • Looking at the Sources

524

What's Flash Memory?

Flash memory is rewritable storage that does not need power supply to hold information. Flash memory banks are usually organized into sectors. Unlike conventional storage, writes to flash addresses have to be preceded by an erase of the corresponding locations. Moreover, erases of portions of flash can be performed only at the granularity of individual sectors. Because of these constraints, flash memory is best used with device drivers and filesystems that are tailored to suit them. on Linux, such specially designed drivers and filesystems are provided by the MTD subsystem.

Flash memory chips generally come in two flavors: NOR and NAND. NOR is the variety used to store firmware images on embedded devices, whereas NAND is used for large, dense, cheap, but imperfect1 storage as required by solid-state mass storage media such as USB pen drives and Disk-On-Modules (DOMs). NOR flash chips are connected to the processor via address and data lines like normal RAM, but NAND flash chips are interfaced using I/O and control lines. So, code resident on NOR flash can be executed in place, but that stored on NAND flash has to be copied to RAM before execution.

 

 

Linux-MTD Subsystem

The kernel's MTD subsystem shown in Figure 17.1 provides support for flash and similar nonvolatile solid-state storage. It consists of the following:

  • The MTD core, which is an infrastructure consisting of library routines and data structures used by the rest of the MTD subsystem
  • Map drivers that decide what the processor ought to do when it receives requests for accessing the flash
  • NOR Chip drivers that know about commands required to talk to NOR flash chips
  • NAND Chip drivers that implement low-level support for NAND flash controllers
  • User Modules, the layer that interacts with user-space programs
  • Individual device drivers for some special flash chips
Figure 17.1

Figure 17.1 The Linux-MTD subsystem.

 

 

 

 

Map Drivers

To MTD-enable your device, your first task is to tell MTD how to access the flash device. For this, you have to map your flash memory range for CPU access and provide methods to operate on the flash. The next task is to inform MTD about the different storage partitions residing on your flash. Unlike hard disks on PC-compatible systems, flash-based storage does not contain a standard partition table on the media. Because of this, disk-partitioning tools such as fdisk and cfdisk2 cannot be used to partition flash devices. Instead, partitioning information has to be implemented as part of kernel code.3 These tasks are accomplished with the help of an MTD map driver.

To better understand the function of map drivers, let's look at an example.

Device Example: Handheld

Consider the Linux handheld shown in Figure 17.2. The flash has a size of 32MB and is mapped to 0xC0000000 in the processor's address space. It contains three partitions, one each for the bootloader, the kernel, and the root filesystem. The bootloader partition starts from the top of the flash, the kernel partition begins at offset MY_KERNEL_START, and the root filesystem starts at offset MY_FS_START.4 The bootloader and the kernel reside on read-only partitions to avoid unexpected damage, while the filesystem partition is flagged read-write.

Figure 17.2

Figure 17.2 Flash Memory on a sample Linux handheld.

Let's first create the flash map and then proceed with the driver initialization. The map driver has to translate the flash layout shown in the figure to an mtd_partition structure. Listing 17.1 contains the mtd_partition definition corresponding to Figure 17.2. Note that the mask_flags field holds the permissions to be masked, so MTD_WRITEABLE implies a read-only partition.

Listing 17.1. Creating an MTD Partition Map

#define FLASH_START          0x00000000
#define MY_KERNEL_START      0x00080000 /* 512K for bootloader */
#define MY_FS_START          0x00280000 /* 2MB for kernel */
#define FLASH_END            0x02000000 /* 32MB */
static struct mtd_partition pda_partitions[] = {
  {
    .name       = "pda_btldr",        /* This string is used by
                                         /proc/mtd to identify
                                         the bootloader partition */
    .size:      = (MY_KERNEL_START-FLASH_START),
    .offset     = FLASH_START,        /* Start from top of flash */
    .mask_flags = MTD_WRITEABLE       /* Read-only partition */
  },
  {
    .name       = "pda_krnl",         /* Kernel partition */
    .size:      = (MY_FS_START-MY_KERNEL_START),
    .offset     = MTDPART_OFS_APPEND, /* Start immediately after
                                         the bootloader partition */
    .mask_flags = MTD_WRITEABLE       /* Read-only partition */
  },
  {
    .name:      = "pda_fs",           /* Filesystem partition */
    .size:      = MTDPART_SIZ_FULL,   /* Use up the rest of the
                                         flash */
    .offset     = MTDPART_OFS_NEXTBLK,/* Align this partition with
                                         the erase size */
  }
};

Listing 17.1 uses MTDPART_OFS_APPEND to start a partition adjacent to the previous one. The start addresses of writeable partitions, however, need to be aligned with the erase/sector size of the flash chip. To achieve this, the filesystem partition uses MTD_OFS_NEXTBLK rather than MTD_OFS_APPEND.

Now that you have populated the mtd_partition structure, let's proceed and complete a basic map driver for the example handheld. Listing 17.2 registers the map driver with the MTD core. It's implemented as a platform driver, assuming that your architecture-specific code registers an associated platform device having the same name. Rewind to the section "Device Example: Cell Phone" in Chapter 6, "Serial Drivers," for a discussion on platform devices and platform drivers. The platform_device is defined by the associated architecture-specific code as follows:

struct resource pda_flash_resource = { /* Used by Listing 17.3 */
  .start = 0xC0000000,                 /* Physical start of the
                                          flash in Figure 17.2 */
  .end   = 0xC0000000+0x02000000-1,    /* Physical end of flash */
  .flags = IORESOURCE_MEM,             /* Memory resource */
};
struct platform_device pda_platform_device = {
  .name = "pda",                   /* Platform device name */
  .id   = 0,                       /* Instance number */
  /* ... */
  .resource = &pda_flash_resource, /* See above */
};
platform_device_register(&pda_platform_device);

Listing 17.2. Registering the Map Driver

static struct platform_driver pda_map_driver = {
  .driver = {
    .name     =  "pda",         /* ID */
   },
  .probe      =  pda_mtd_probe, /* Probe */
  .remove     =  NULL,          /* Release */
  .suspend    =  NULL,          /* Power management */
  .resume     =  NULL,          /* Power management */
};

/* Driver/module Initialization */
static int __init pda_mtd_init(void)
{
  return platform_driver_register(&pda_map_driver);
}

/* Module Exit */
static int __init pda_mtd_exit(void)
{
  return platform_driver_uregister(&pda_map_driver);
}

Because the kernel finds that the name of the platform driver registered in Listing 17.2 matches with that of an already-registered platform device, it invokes the probe method, pda_mtd_probe(), shown in Listing 17.3. This routine

  • Reserves the flash memory address range using request_mem_region(), and obtains CPU access to that memory using ioremap_nocache(). You learned how to do this in Chapter 10, "Peripheral Component Interconnect."
  • Populates a map_info structure (discussed next) with information such as the start address and size of flash memory. The information in this structure is used while performing the probing in the next step.
  • Probes the flash via a suitable MTD chip driver (discussed in the next section). only the chip driver knows how to query the chip and elicit the command-set required to access it. The chip layer tries different permutations of bus widths and interleaves while querying. In Figure 17.2, two 16-bit flash banks are connected in parallel to fill the 32-bit processor bus width, so you have a two-way interleave.
  • Registers the mtd_partition structure that you populated earlier, with the MTD core.

Before looking at Listing 17.3, let's meet the map_info structure. It contains the address, size, and width of the flash memory and routines to access it:

struct map_info {
  char * name;               /* Name */
  unsigned long size;        /* Flash size */
  int bankwidth;             /* In bytes */
  /* ... */
  /* You need to implement custom routines for the following methods
     only if you have special needs. Else populate them with built-
     in methods using simple_map_init() as done in Listing 17.3 */
  map_word (*read)(struct map_info *, unsigned long);
  void     (*write)(struct map_info *, const map_word,
                    unsigned long);
  /* ... */
};

While we are in the topic of accessing flash chips, let's briefly revisit memory barriers that we discussed in Chapter 4, "Laying the Groundwork." An instruction reordering that appears semantically unchanged to the compiler (or the processor) may not be so in reality, so the ordering of data operations on flash memory is best left alone. You don't want to, for example, end up erasing a flash sector after writing to it, instead of doing the reverse. Also, the same flash chips, and hence their device drivers, are used on diverse embedded processors having different instruction reordering algorithms. For these reasons, MTD drivers are notable users of hardware memory barriers. simple_map_write(), a generic routine available to map drivers for use as the write() method in the map_info structure previously listed, inserts a call to mb() before returning. This ensures that the processor does not reorder flash reads or writes across the barrier.

Listing 17.3. Map Driver Probe Method

#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/ioport.h>

static int
pda_mtd_probe(struct platform_device *pdev)
{
  struct map_info *pda_map;
  struct mtd_info *pda_mtd;
  struct resource *res = pdev->resource;

  /* Populate pda_map with information obtained
     from the associated platform device */
  pda_map->virt = ioremap_nocache(res->start,
                                  (res->end – res->start + 1));
  pda_map->name = pdev->dev.bus_id;
  pda_map->phys = res->start;
  pda_map->size = res->end – res->start + 1;
  pda_map->bankwidth = 2;     /* Two 16-bit banks sitting
                                 on a 32-bit bus */
  simple_map_init(&pda_map);  /* Fill in default access methods */

  /* Probe via the CFI chip driver */
  pda_mtd = do_map_probe("cfi_probe", &pda_map);
  /* Register the mtd_partition structure */
  add_mtd_partitions(pda_mtd, pda_partitions, 3); /* Three Partitions */

  /* ... */
}

Don't worry if the CFI probing done in Listing 17.3 seems esoteric. It's discussed in the next section when we look at NOR chip drivers.

MTD now knows how your flash device is organized and how to access it. When you boot the kernel with your map driver compiled in, user-space applications can respectively see your bootloader, kernel, and filesystem partitions as /dev/mtd/0, /dev/mtd/1, and /dev/mtd/2. So, to test drive a new kernel image on the handheld, you can do this:

bash> dd if=zImage.new of=/dev/mtd/1

 

 

 

 

Looking at the Sources

In the kernel tree, the drivers/mtd/ directory contains the sources for the MTD layer. Map, chip, and NAND drivers live in the drivers/mtd/maps/, drivers/mtd/chips/, and drivers/mtd/nand/ subdirectories, respectively. Most MTD data structures are defined in header files present in include/linux/mtd/.

To access an unsupported BIOS firmware hub from Linux, implement a driver using drivers/mtd/maps/ichxrom.c as your starting point.

For examples of operating on NAND OOB data from user space, look at nanddump.c and nandwrite.c in the MTD-utils package.

Table 17.1 contains the main data structures used in this chapter and their location in the source tree. Table 17.2 lists the main kernel programming interfaces that you used in this chapter along with the location of their definitions.

Table 17.1. Summary of Data Structures

Data Structure

Location

Description

mtd_partition

include/linux/mtd/partitions.h

Representation of a flash chip's partition layout.

map_info

include/linux/mtd/map.h

Low-level access routines implemented by the map driver are passed to the chip driver using this structure.

mtd_info

include/linux/mtd/mtd.h

General device-specific information.

erase_info, erase_info_user

include/linux/mtd/mtd.h, include/mtd/mtd-abi.h

Structures used for flash erase management.

cfi_private

include/linux/mtd/cfi.h

Device-specific information maintained by NOR chip drivers.

amd_flash_info

drivers/mtd/chips/jedec_probe.c

Device-specific information supplied to the JEDEC chip driver.

nand_ecclayout

include/mtd/mtd-abi.h

Layout of the OOB spare area of a NAND chip.

Table 17.2. Summary of Kernel Programming Interfaces

Kernel Interface

Location

Description

simple_map_init()

drivers/mtd/maps/map_funcs.c

Initializes a map_info structure with generic flash access methods

do_map_probe()

drivers/mtd/chips/chipreg.c

Probes the NOR flash via a chip driver

add_mtd_partitions()

drivers/mtd/mtdpart.c

Registers an mtd_partition structure with the MTD core

 

 

 

 

 

 

펌 - http://www.informit.com/articles/article.aspx?p=1187102