TOC PREV NEXT INDEX

Writing Device Drivers for LynxOS


Writing Flash Memory Technology Drivers-MTDs

Access to a linear flash device in LynxOS is implemented through a two layer model. The upper layer, flash_mgr(4), implements the user interface and encompasses all common algorithms required to access any linear flash device. This includes argument checking, memory mapping and adjustments, synchronization, and so on. The lower layer is a Memory Technology Driver (MTD), which is a device driver responsible for implementing hardware specific details of programming a particular flash device.

The MTD does not interface directly with user applications. It interacts with the flash_mgr module, through the interface defined by a set of entry points and data structures, described in the following sections. This two layer model provides a unified interface to any flash device and removes the need for redundant algorithms within MTD modules.

Interface Overview

An MTD is implemented as a character device driver. From the point of view of the device driver structure, an MTD is very simple-there are only two entry points required by the LynxOS character device interface that are used in an MTD. These are install and uninstall.

In the install routine, an MTD registers a callback routine, along with some related data, with the flash_mgr module. The callback routine is responsible for implementing a fixed set of flash memory operations for the particular flash device. flash_mgr invokes the callback routine any time there is the need to access the flash device at the physical level.

In the uninstall routine, the MTD deregisters the callback, thus notifying the flash_mgr that the MTD is no longer available for device accesses.

While there is the single flash_mgr component in the kernel, there may be multiple active (registered) MTDs, each implementing access to its own flash device. flash_mgr supports multiple opens to different flash devices and concurrent I/O operations for them. The Flash ID, passed by an MTD to flash_mgr at registration time, is used as a key for mapping application requests to a particular MTD.

The following sections provide a detailed description of the interface between the flash_mgr module and an MTD. All entry points and data structures are defined in the header file $ENV_PREFIX/sys/dheaders/flash_mtd.h.

Registering with flash_mgr

An MTD registers with flash_mgr by invoking the flash_mtd_register entry point. A pointer to the MTD registration data is passed as the only parameter to the routine.

MTD Registration Data

The MTD registration data, as passed by an MTD to flash_mgr, is described by the following data structures:

typedef struct flash_mtd_area_s
{
u_int offset; /* Offset from Flash Base*/
u_int size; /* Size in Bytes */
}
flash_mtd_area_t;

typedef struct flash_mtd_register_s
{
int flash_id; /* Flash ID */
u_int mtd_attr; /* MTD Attributes */
u_int flash_addr; /* Flash Base */
u_int sector_size;/* Sector Size (bytes) */
u_int flash_size; /* Flash Size (bytes) */
char * info_str; /* Device Info String */
flash_mtd_area_t /* Device Control */
/* Registers */
spec_locs[FLASH_MTD_SPEC_LOC_NUM];
flash_mtd_area_t /* Partitions Info */
parts[FLASH_MTD_PART_NUM];
void * user_param; /* MTD Specific Data */
flash_mtd_callback_t /* Pointer to Callback */
callback;
}
flash_mtd_register_t;

To register with flash_mgr, an MTD should fill in a structure of type flash_mtd_register_t with the registration data. The various fields in the structure should be initialized to appropriate values. Unused fields should be set to zero. Once the structure has been initialized, the MTD makes itself known to flash_mgr by calling flash_mtd_register and passing the address of the structure as the parameter. If registration is successful, flash_mtd_register returns zero. For example:

char * flash_example_install(
flash_example_info_t * info )
{
flash_mtd_register_t reg_data;
...
/* Fill in the registration data.
*/
reg_data.flash_id = ...;
...
/* Register with the flash manager.
*/
if ( flash_mtd_register( & reg_data ) != 0 )
{
return (char *) SYSERR;
}
...
}

Flash ID

The Flash ID is one of the most important elements of the registration data. It is an integer key used by flash_mgr to identify a particular MTD among all the MTD modules installed in the kernel. To access a flash device, the application opens a corresponding device special file. The Flash ID is encoded into a 4-bit field within the minor device number. flash_mgr extracts it and uses it as a key to map application requests to a particular MTD module.

A Flash ID number must be unique for each MTD configured into the kernel. The 4-bit Flash ID field in the minor number allows you to install up to 16 MTDs simultaneously, with Flash IDs ranging from 0 to 15. For an easier configuration, it is recommended that the Flash ID be copied from the device information block, as shown in the example below:

reg_data.flash_id = info->flash_id;

Note that once you modify the Flash ID of an MTD, you have to change the minor numbers for all corresponding device nodes accordingly. By default, special nodes for the flash devices are installed in the configuration file $ENV_PREFIX/sys/cfg/flash.cfg.

Device Info String

The info_str field is a pointer to the device information string. This is an arbitrary character string describing the flash device. flash_mgr returns this string as a part of the flash information block whenever an application issues a FLASH_GET_INFO ioctl command. The string must be contained in a nonautomatic variable. For example:

reg_data.info_str = "rpxl8xx on-board FLASH";

Flash Size

The size of the flash memory is passed to flash_mgr through the flash_size field. Size is specified in bytes:

reg_data.flash_size = 4 * MByte;

Sector Size

The size of the flash device sector is passed to flash_mgr through the sector_size field. Size is specified in bytes:

reg_data.flash_size = 256 * KByte;

Registration Attributes

The registration attributes are passed to flash_mgr through the mtd_attr field as a bitwise combination of the following logical flags:

FLASH_MTD_ATTR_READ

MTD supports read accesses to the flash device. If this flag is not specified, any application request that requires invocation of the callback routine with the operation code of FLASH_MTD_REQ_READ is rejected in flash_mgr.
FLASH_MTD_ATTR_WRITE

MTD supports write accesses to the flash device. If this flag is not specified, any application request that requires invocation of the callback routine with the operation code of FLASH_MTD_REQ_WRITE is rejected in flash_mgr.
FLASH_MTD_ATTR_ERASE_ALL

MTD supports erase of the entire flash device. If this flag is not specified, any application request that requires invocation of the callback routine with the operation code of FLASH_MTD_REQ_ERASE_ALL is rejected in flash_mgr.
FLASH_MTD_ATTR_ERASE

MTD supports erase of a particular sector of the flash device. If this flag is not specified, any application request that requires invocation of the callback routine with the operation code of FLASH_MTD_REQ_ERASE is rejected in flash_mgr.
FLASH_MTD_ATTR_SPECIFIC

MTD supports device specific operations. If this flag is not specified, any application request that requires invocation of the callback routine with the operation code of FLASH_MTD_REQ_SPECIFIC is rejected in flash_mgr.
FLASH_MTD_ATTR_FF_ERASED

For this device, an erased byte returns 0xFF on a read access. Specification of this flag for a device granting this condition is likely to improve the overall driver throughput.
FLASH_MTD_ATTR_AUTO_READ

MTD relies on flash_mgr to execute read accesses. The MTD callback routine is not invoked upon a read request from user application. Instead, flash_mgr reads flash memory as conventional memory. If this flag is specified, FLASH_MTD_ATTR_READ is ignored.
FLASH_MTD_ATTR_NO_MAP

Entire flash memory is already mapped into a contiguous region of the kernel virtual space. No mapping is needed in flash_mgr. If this flag is specified, flash_mgr interprets the flash base address as a virtual address in the kernel space.
FLASH_MTD_ATTR_MAP_SPECIFIC

MTD notifies flash_mgr that whenever a FLASH_MTD_REQ_SPECIFIC operation is invoked on the MTD, the entire flash memory must be mapped into a contiguous region of the kernel virtual space. If flash_mgr cannot satisfy this condition, any request to execute such an operation is rejected in flash_mgr.
FLASH_MTD_ATTR_CACHE

MTD requests that the special flash cache be enabled by flash_mgr. The flash cache resides on top of the in-memory disk cache and is useful only if flash device is accessed through the block interface of flash_mgr. Synchronization of a flash device with enabled flash cache requires execution of the flash_sync(1) utility after a write out of the disk cache. Flash cache is designed to both improve the overall flash file system access rate and prevent the flash memory exhaustion. It is especially efficient for devices with a large sector size.

The following example shows how the mtd_attr field can be initialized for the registration:

reg.mtd_attr = FLASH_MTD_ATTR_NO_MAP |
/* Already mapped */
FLASH_MTD_ATTR_WRITE |
/* Write Op. */
FLASH_MTD_ATTR_AUTO_READ |
/* No Read Op. */
FLASH_MTD_ATTR_ERASE |
/* Erase Op. */
FLASH_MTD_ATTR_ERASE_ALL |
/* Erase All Op. */
FLASH_MTD_ATTR_FF_ERASED |
/* 0xFF if erased */
FLASH_MTD_ATTR_CACHE;
/* Flash cached */

Flash Base

The flash_base field is used to pass the base address of the flash memory to flash_mgr. Interpretation of this field depends on whether the FLASH_MTD_ATTR_NO_MAP flag is set in the mtd_attr field.

If the flag is set, flash_mgr interprets the base address as a virtual address in the kernel memory map and relies on the entire flash memory to be already mapped into the kernel space. flash_base in this case contains a starting address of the flash memory in the virtual space. In the following example MTD uses the permap(9) kernel service to map the entire flash into the kernel space:

/* Notify flash_mgr that flash is mapped by the MTD.
*/
reg.mtd_attr = FLASH_MTD_ATTR_NO_MAP | ... ;
/* Map the flash; set flash_base to the
* virtual address.
*/
reg.flash_base = (u_int) permap( FLASH_PHYS_BASE,
FLASH_SIZE );

If the flag is reset, flash_mgr interprets the base address as a physical address of the flash memory in the system memory map. In this case, flash_mgr takes over the burden of mapping the flash memory and guarantees that whenever the MTD callback routine is invoked, part of flash memory on which the operation is executed is mapped into the kernel virtual space. The following example illustrates this approach:

/* Notify flash_mgr that flash is not mapped.
*/
reg.mtd_attr = /* no FLASH_MTD_ATTR_NO_MAP */ | ... ;
/* Set flash_base to the physical address.
*/
reg.flash_base = FLASH_PHYS_BASE;

Device Control Registers

An MTD is allowed to register up to three control register windows. A control register window is an address range within the flash memory that is used to access hardware control and status registers. Usually, an MTD needs to be able to access the control and status registers in order to program certain operations of the flash device (for example, flash erase).

Note that registration of control registers windows is needed only if an MTD does not use the FLASH_MTD_ATTR_NO_MAP attribute to register with the flash_mgr module. In this case flash_mgr takes over the responsibility of mapping the hardware registers and guarantees that any time the MTD callback routine is invoked, all registered control registers windows are mapped into the kernel virtual space.

Because the hardware control registers are located within the flash memory address range, there is no need to define control registers windows for an MTD that uses the FLASH_MTD_ATTR_NO_MAP attribute for the registration. As soon as the MTD creates the mapping for the entire flash memory, the control registers windows become mapped automatically.

The control registers windows are created by initializing the spec_locs array. If a window is unused, the corresponding size field should be set to zero.

The following example shows registration of two control registers windows:

/* Notify flash_mgr that flash is not mapped.
*/
reg.mtd_attr = /* no FLASH_MTD_ATTR_NO_MAP */ | ... ;
/* Define the hardware registers windows.
*/
for ( i = 0; i < FLASH_MTD_SPEC_LOC_NUM; i ++ )
{
reg.spec_locs[i].size = 0;
}
reg.spec_locs[0].offset = 0x5555 * 4;
/* Command Reg1 */
reg.spec_locs[0].size = 4;/* 4 bytes */
reg.spec_locs[1].offset = 0x2AAA * 4;
/* Command Reg2 */
reg.spec_locs[1].size = 4;/* 4 bytes */

Partitions Information

A flash memory device can be divided in up to seven possible overlapping partitions. Unlike a conventional disk, there is no partition table or a similar partitions descriptor maintained in the flash memory. Instead, the partitions data are maintained in the software tables of flash_mgr on a per-device basis. Device partitions information is passed by an MTD to the flash_mtd module at registration time and is effective until the MTD is deregistered.

Device partitions information is passed to the flash_mgr through the parts field, which is a 7-entry table of partition descriptors. Each entry of the table contains information about one partition of the flash device. The entry with index 0 defines partition number 1, the entry with index 1 partition number 2, and so on. If an entry has the size field set to zero, the corresponding partition is undefined and cannot be accessed from user applications.

To get access to a flash partition, an application opens a corresponding device special node. The partition number is encoded as a 3-bit field into the device minor number. A value of 0 corresponds to the entire flash device; any other value (1 to 7) defines the partition number. Other than passing the partitions information to flash_mgr at the registration time, an MTD is insensitive to device partitions. Flash addresses and sector numbers passed to the MTD callback routine by flash_mgr are relative to the flash memory base.

To allow for easy partitioning of a flash device, it is recommended that the partitions information be copied from the device information block, as shown in the example below:

for ( i = 0; i < FLASH_MTD_PART_NUM; i ++ )
{
reg.parts[i] = info->parts[i];
}

The user can change the device partitions by modifying the device information block and rebuilding the kernel. Shown below is a sample device block for the above example:

flash_example_info_t flash_example_info =
{
...
/* Device Partitions :
* there are 7 partitions;
* there must be an entry for each.
* If a partition is unused, size is set to 0.
*/
0 * MByte, 1 * MByte, /* Partition 1: 0 - 1 MB */
1 * MByte, 3 * MByte, /* Partition 2: 1 MB - 4 MB */
0, 0, /* Partition 3: unused */
0, 0, /* Partition 4: unused */
0, 0, /* Partition 5: unused */
0, 0, /* Partition 6: unused */
0, 0, /* Partition 7: unused */
};

Callback Routine

A pointer to the callback routine is passed to flash_mgr through the callback field. Pointer to MTD specific data is passed through the user_param field. This pointer is passed back to the callback routine any time the callback is invoked by flash_mgr.

The following example shows registration of a callback routine. A pointer to the MTD statics structure is registered as the MTD specific data:

flash_statics_t * s;
...
reg.user_param = s;
reg.callback = flash_example_callback;

Deregistering from flash_mgr

An MTD deregisters from flash_mgr by invoking the flash_mtd_deregister entry point. The Flash ID is passed as the only parameter to the routine. For example:

void flash_example_uninstall(
flash_example_statics_t * s )
{
...
/* Deregister the MTD.
*/
flash_mtd_deregister( s->flash_id );

Writing Callback Routines

The MTD callback routine is invoked by the flash_mgr module with the following syntax:

int flash_mtd_rpxl_callback (
int op, /* Operation Code */
u_int flash_base,
/* Flash in Virtual Space */
flash_mtd_param_t * op_param,
/* Operation Parameter */
void * user_param );
/* MTD Specific Data */

Operation Code

The first parameter contains an operation code, which can be any of the following:

FLASH_MTD_REQ_READ
Request to read a specified area of flash memory into a memory buffer.
FLASH_MTD_REQ_WRITE
Request to write a memory buffer into a specified area of flash memory.
FLASH_MTD_REQ_ERASE
Request to erase specified sectors of flash device.
FLASH_MTD_REQ_ERASE_ALL
Request to erase the entire flash device.
FLASH_MTD_REQ_SPECIFIC
Request to execute a device specific operation. Operation code and a single operation parameter are passed through the additional parameter.

Flash Virtual Base

The second parameter contains an address of the flash memory base in the kernel virtual space. The MTD should use it to convert relative flash addresses to absolute addresses in the kernel space. For example:

/* Execute a flash command.
*/
* (u_char *) ( flash_base + 0x5555 * 4 + chip ) = 0xAA;
* (u_char *) ( flash_base + 0x2AAA * 4 + chip ) = 0x55;
* (u_char *) ( flash_base + 0x5555 * 4 + chip ) = cmd;

Operation Parameter

The third parameter is supplementary to the operation code parameter and contains a description of the requested operation. The following type is used:

typedef union flash_mtd_param_u
{
struct {
void * buf; /* Data Buffer */
flash_mtd_area_t area; /* Flash Area */
} rw;
struct {
u_int start_sector; /* First Sector */
u_int sectors_num; /* Number of Sectors */
}erase;
struct {
int req_code; /* Request Code */
void * req_param; /* Request Parameters */
}specific;
}
flash_mtd_param_t;

The rw structure is used when the operation code is equal to either FLASH_MTD_REQ_READ or FLASH_MTD_REQ_WRITE. The buf pointer specifies a data buffer in memory. The area structure specifies an area in flash. Offset is relative to the flash base.

The erase structure is used when the operation code is equal to FLASH_MTD_REQ_ERASE. The start_sector field specifies the first sector to erase. The sectors_num field specifies number of sectors to erase.

The specific structure is used when the operation code is equal to FLASH_MTD_REQ_SPECIFIC. The req_code field contains an MTD specific operation code. The req_param field is a pointer to an operation specific parameters area.

MTD Specific Data

The fourth parameter is a pointer to the MTD specific data. flash_mgr sets this parameter to the value passed by the MTD through the user_param field of the MTD registration data.

Return Code

If an operation completes successfully, the callback routine returns zero. Any other value indicates a failure.

Synchronization

The flash_mgr layer resolves all synchronization issues prior to invoking an MTD callback routine. An MTD is guaranteed that:



LynuxWorks, Inc.
855 Branham Lane East
San Jose, CA 95138
http://www.lynuxworks.com
1.800.255.5969
TOC PREV NEXT INDEX