The main task of this paper is to realize the NOR FLASH driver of MX29LV800BBTC on board of JZ2440 development board.
A Brief Introduction to MX29LV800BBTC
This is a NOR FLASH memory chip with a capacity of 2MB and a bit width of 16 bits. It can be accessed directly like memory, but it can not be written directly like memory. It supports XIP. Its schematic diagram is as follows:
Its pins are defined as follows:
LDATA0~LDATA15: Data pins
LADDR1~LADDR20: Address pins
LnOE: Read-enabled pins
LnWE: Write enabled pins
nGCS0: Chip Selection Pin
II. Driver Programming
The driver is based on the Linux-3.4.10 kernel.
1. General implementation steps
A. Assign a map_info structural variable
b. Set this structure variable
c. Use: do_map_probe()
d. Adding partitions
2. Driver Programming
2.1 In the preliminary preparatory work, in order to facilitate the writing of the whole driver program, the global structure is defined, and a global variable of the structure is defined, which is implemented as follows:
2.2 Assign a pointer variable to a map_info structure/* Construct a structure for writing driver easily */ struct yl_nor_mtd{ struct map_info *map_info; // Define pointer variables for map_info structure struct mtd_info *mtd_info; // Define pointer variables for mtd_info structure }; /* Define a global variable of yl_nor_mtd structure */ static struct yl_nor_mtd nor_mtd;
/* 1,Assign a map_info structure variable */ nor_mtd.map_info = kzalloc(sizeof(struct map_info), GFP_KERNEL);
2.3 Set this structure variable
2.4 Using this structural variable, an example of mtd_info structural variable is obtained./* 2,Set up */ nor_mtd.map_info->name = "yl_nor_flash"; // Name nor_mtd.map_info->phys = 0; // Physical Base Address nor_mtd.map_info->size = 0x200000; // Size, >= Real Size nor_mtd.map_info->bankwidth = 2; // Bit width /* Setting the base address of the virtual address */ nor_mtd.map_info->virt = ioremap(nor_mtd.map_info->phys, nor_mtd.map_info->size); if (nor_mtd.map_info->virt == NULL) { printk("Failed to ioremap flash region\n"); ret = -EIO; goto out2; } /* Universal initialization of other nor flash */ simple_map_init(nor_mtd.map_info);
2.5 Adding Partitions/* 3,Use */ nor_mtd.mtd_info = do_map_probe("cfi_probe", nor_mtd.map_info); if(nor_mtd.mtd_info == NULL) { printk("do_map_probe for cfi_probe failure!\n"); ret = -ENXIO; goto out3; }
Adding partitions requires a partition table as the partition basis. The partition table is implemented as follows:/* 4,Adding partitions */ ret = mtd_device_parse_register(nor_mtd.mtd_info, NULL, NULL, yl_s3c_nor_partitions, ARRAY_SIZE(yl_s3c_nor_partitions));
/* Define Norflash partition table */ static struct mtd_partition yl_s3c_nor_partitions[] = { [0] = { .name = "bootloader_nor", .size = SZ_256K, .offset = 0, }, [1] = { .name = "params_nor", .offset = MTDPART_OFS_APPEND, .size = SZ_128K, }, [2] = { .name = "rootfs_nor", .offset = MTDPART_OFS_APPEND, .size = MTDPART_SIZ_FULL, } };
#include <linux/module.h> #include <linux/types.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/slab.h> #include <linux/device.h> #include <linux/platform_device.h> #include <linux/mtd/mtd.h> #include <linux/mtd/map.h> #include <linux/mtd/partitions.h> #include <linux/mtd/concat.h> #include <linux/io.h> /* Construct a structure for writing driver easily */ struct yl_nor_mtd{ struct map_info *map_info; // Define pointer variables for map_info structure struct mtd_info *mtd_info; // Define pointer variables for mtd_info structure }; /* Define a global variable of yl_nor_mtd structure */ static struct yl_nor_mtd nor_mtd; /* Define Norflash partition table */ static struct mtd_partition yl_s3c_nor_partitions[] = { [0] = { .name = "bootloader_nor", .size = SZ_256K, .offset = 0, }, [1] = { .name = "params_nor", .offset = MTDPART_OFS_APPEND, .size = SZ_128K, }, [2] = { .name = "rootfs_nor", .offset = MTDPART_OFS_APPEND, .size = MTDPART_SIZ_FULL, } }; /* Module entry function */ static int __init yl_s3c_nor_init(void) { int ret = 0; /* 1,Assign a map_info structure variable */ nor_mtd.map_info = kzalloc(sizeof(struct map_info), GFP_KERNEL); if (!nor_mtd.map_info) { printk("kzalloc for map_info error!\n"); ret = -ENOMEM; goto out1; } /* 2,Set up */ nor_mtd.map_info->name = "yl_nor_flash"; // Name nor_mtd.map_info->phys = 0; // Physical Base Address nor_mtd.map_info->size = 0x200000; // Size, >= Real Size nor_mtd.map_info->bankwidth = 2; // Bit width /* Setting the base address of the virtual address */ nor_mtd.map_info->virt = ioremap(nor_mtd.map_info->phys, nor_mtd.map_info->size); if (nor_mtd.map_info->virt == NULL) { printk("Failed to ioremap flash region\n"); ret = -EIO; goto out2; } /* Universal initialization of other nor flash */ simple_map_init(nor_mtd.map_info); /* 3,Use */ nor_mtd.mtd_info = do_map_probe("cfi_probe", nor_mtd.map_info); if(nor_mtd.mtd_info == NULL) { printk("do_map_probe for cfi_probe failure!\n"); ret = -ENXIO; goto out3; } /* 4,Adding partitions */ ret = mtd_device_parse_register(nor_mtd.mtd_info, NULL, NULL, yl_s3c_nor_partitions, ARRAY_SIZE(yl_s3c_nor_partitions)); if(ret) // fail { printk("mtd_device_parse_register error!\n"); ret = -ENODEV; goto out3; } else // Success { return 0; } out3: iounmap(nor_mtd.map_info->virt); out2: kfree(nor_mtd.map_info); out1: return ret; } /* Exit function of module */ static void __exit yl_s3c_nor_exit(void) { /* Some operations for export, release resources, unbind and cancel registration */ mtd_device_unregister(nor_mtd.mtd_info); iounmap(nor_mtd.map_info->virt); kfree(nor_mtd.map_info); } module_init(yl_s3c_nor_init); module_exit(yl_s3c_nor_exit); MODULE_LICENSE("GPL");
3. Compile the driver and load the module into the kernel. The results are as follows:

