Linux下使用MTD驱动操作M25P80与W25闪存芯片 (linux mtd m25p80 w25)
随着智能设备的普及,嵌入式系统的需求越来越高。在嵌入式系统中,闪存芯片是最常用的存储设备之一。闪存芯片不仅可以存储数据,还可以用于引导程序和存储操作系统镜像等功能。在Linux系统中,MTD(Memory Technology Devices)子系统提供了一组操作和管理闪存芯片的API和工具。本文将介绍如何使用MTD驱动操作M25P80和W25闪存芯片。
一、M25P80与W25闪存芯片介绍
M25P80和W25是两种型号的SPI(Serial Peripheral Interface)闪存芯片,具有以下特点:
1. 容量大:M25P80和W25的容量分别为8MB和4MB,可以满足大多数嵌入式系统的存储需求。
2. 接口简单:SPI接口是一种四线串行接口,只需要四根线即可实现读写操作。
3. 速度快:SPI接口的时钟频率可以高达几十MHz,可以提高读写速度。
二、MTD驱动介绍
MTD(Memory Technology Devices)是Linux内核提供的一组操作和管理闪存芯片的API和工具。它通过驱动程序将闪存芯片映射到内存空间,提供了一个类似于文件系统的接口,可以方便地对闪存芯片进行读写操作。
MTD驱动由三部分组成:核心驱动(mtdcore)、设备驱动和机制驱动。核心驱动负责管理和分配闪存芯片的设备号和分区,设备驱动提供对特定型号闪存芯片的访问接口,机制驱动提供特定的垃圾回收和解决坏块的算法。MTD驱动的整体架构如下图所示:
![MTD架构图](https://s3.amazonaws.com/eda-tools/MTD.png)
三、M25P80和W25芯片操作
1. M25P80操作
首先需要编写一个设备驱动程序,用于实现对M25P80芯片的访问。以下是一个简单的设备驱动程序示例:
“`
#include
#include
#include
static struct mtd_info *m25p80_mtd;
static int __init m25p80_init(void)
{
void __iomem *addr;
int err;
addr = ioremap(0x80000000, 0x1000000);
m25p80_mtd = mtd_create_device(NULL, “m25p80”, 0x1000000, NULL);
if (IS_ERR(m25p80_mtd)) {
err = PTR_ERR(m25p80_mtd);
goto out_iounmap;
}
m25p80_mtd->priv = addr;
m25p80_mtd->read = m25p80_read;
m25p80_mtd->write = m25p80_write;
err = add_mtd_device(m25p80_mtd);
if (err)
goto out_mtd_destroy;
return 0;
out_mtd_destroy:
mtd_destroy_device(m25p80_mtd);
out_iounmap:
iounmap(addr);
return err;
}
static void __exit m25p80_exit(void)
{
del_mtd_device(m25p80_mtd);
mtd_destroy_device(m25p80_mtd);
}
static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf)
{
void __iomem *addr = mtd->priv;
memcpy_fromio(buf, addr + from, len);
*retlen = len;
return 0;
}
static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf)
{
void __iomem *addr = mtd->priv;
memcpy_toio(addr + to, buf, len);
*retlen = len;
return 0;
}
module_init(m25p80_init);
module_exit(m25p80_exit);
MODULE_LICENSE(“GPL”);
“`
以上代码实现了对M25P80芯片的读写操作。在初始化函数中,首先调用`ioremap`函数将闪存芯片的物理地址映射到内核地址空间上,然后调用`mtd_create_device`函数创建一个名为”m25p80″的MTD设备,并且注册了MTD设备的读写函数。这样就可以把闪存芯片映射到内存空间中,以文件系统的方式进行访问。
2. W25操作
以下是一个W25芯片的设备驱动程序示例:
“`
#include
#include
#include
static struct spi_nor *w25_nor;
static int __init w25_init(void)
{
struct spi_master *master;
struct spi_device *spi;
struct device *dev;
int err;
master = spi_busnum_to_master(0);
if (!master)
return -ENODEV;
spi = spi_alloc_device(master);
if (!spi)
return -ENOMEM;
spi->bits_per_word = 8;
spi->max_speed_hz = 5000000;
spi->mode = SPI_MODE_0;
err = spi_setup(spi);
if (err) {
spi_free_device(spi);
return err;
}
w25_nor = spi_nor_get_flash_info(spi, NULL);
if (IS_ERR(w25_nor)) {
err = PTR_ERR(w25_nor);
spi_free_device(spi);
return err;
}
w25_nor->controller.priv = spi;
dev = &spi->dev;
dev_set_drvdata(dev, w25_nor);
err = mtd_device_register(&w25_nor->mtd, NULL, 0);
if (err) {
spi_nor_put(w25_nor);
spi_free_device(spi);
}
return err;
}
static void __exit w25_exit(void)
{
mtd_device_unregister(&w25_nor->mtd);
spi_nor_put(w25_nor);
}
module_init(w25_init);
module_exit(w25_exit);
MODULE_LICENSE(“GPL”);
“`
与M25P80驱动程序不同的是,W25驱动程序使用了SPI-NOR闪存控制器。在初始化函数中,首先通过`spi_busnum_to_master`函数获取到SPI总线,然后调用`spi_alloc_device`函数分配一个SPI设备,并设置其相关参数。接着调用`spi_setup`函数配置SPI设备,并通过`spi_nor_get_flash_info`函数获取到W25芯片的信息。`spi_nor_get_flash_info`函数调用了闪存控制器的`read_id`函数,从闪存芯片中读取信息,并填充到`spi_nor`结构体中。最后调用`mtd_device_register`函数注册MTD设备。在注册时,使用了`spi_nor`结构体中mtd成员,mtd成员包含了MTD设备的大小、擦除和读写函数等信息。
四、