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 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