W25Q80DV 是 Winbond 的一款 SPI Flash,容量大小为 8M bit。如果还没看 W25Q80DV 的数据手册,赶紧去看!
https://blog.csdn.net/lu_embedded/article/details/80682374
本文描述的是在 i.MX6q 硬件平台上添加 W25Q80DV 芯片(SPI 设备),Linux 内核版本为 kernel-3.10.17,采用 DeviceTree 描述硬件连接信息。
硬件连接
i.MX6q 是基于 NXP 四核 ARM Cortex-A9 架构的高性能处理器,它上面有 5 个 SPI 控制器,分别是 ECSPI1~5。在我们这里的测试平台上的硬件连接的情况是这样的:
管脚描述:
驱动模型
MTD
MTD设备分为四层(从设备节点直到底层硬件驱动),这四层从上到下依次是:
- 设备节点
- MTD设备层
- MTD原始设备层
- 硬件驱动层
MTD 子系统实现了 SPI flash 芯片驱动程序,其例子为 drivers/mtd/devices/mtd_dataflash.c
驱动文件
对于 SPI Flash 设备,重点关注的驱动文件是:
drivers/mtd/devices/m25p80.c
/*
* Erase an address range on the flash chip. The address range may extend
* one or more erase sectors. Return an error is there is a problem erasing.
*/
static int m25p80_erase(struct mtd_info *mtd, struct erase_info *instr)
{
// ...
}
/*
* Read an address range from the flash chip. The address range
* may be any size provided it is within the physical boundaries.
*/
static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, u_char *buf)
{
// ...
}
/*
* Write an address range to the flash chip. Data must be written in
* FLASH_PAGESIZE chunks. The address range may be any size provided
* it is within the physical boundaries.
*/
static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len,
size_t *retlen, const u_char *buf)
{
// ...
}
static int m25p_probe(struct spi_device *spi)
{
const struct spi_device_id *id = spi_get_device_id(spi);
struct flash_platform_data *data;
struct m25p *flash;
struct flash_info *info;
unsigned i;
struct mtd_part_parser_data ppdata;
struct device_node __maybe_unused *np = spi->dev.of_node;
// ...
if (info->jedec_id) {
const struct spi_device_id *jid;
jid = jedec_probe(spi);
if (IS_ERR(jid)) {
return PTR_ERR(jid);
} else if (jid != id) {
/*
* JEDEC knows better, so overwrite platform ID. We
* can't trust partitions any longer, but we'll let
* mtd apply them anyway, since some partitions may be
* marked read-only, and we don't want to lose that
* information, even if it's not 100% accurate.
*/
dev_warn(&spi->dev, "found %s, expected %s\n",
jid->name, id->name);
id = jid;
info = (void *)jid->driver_data;
}
}
// ...
if (data && data->name)
flash->mtd.name = data->name;
else
flash->mtd.name = dev_name(&spi->dev);
flash->mtd.type = MTD_NORFLASH;
flash->mtd.writesize = 1;
flash->mtd.flags = MTD_CAP_NORFLASH;
flash->mtd.size = info->sector_size * info->n_sectors;
flash->mtd._erase = m25p80_erase;
flash->mtd._read = m25p80_read;
/* flash protection support for STmicro chips */
if (JEDEC_MFR(info->jedec_id) == CFI_MFR_ST) {
flash->mtd._lock = m25p80_lock;
flash->mtd._unlock = m25p80_unlock;
}
/* sst flash chips use AAI word program */
if (info->flags & SST_WRITE)
flash->mtd._write = sst_write;
else
flash->mtd._write = m25p80_write;
/* prefer "small sector" erase if possible */
if (info->flags & SECT_4K) {
flash->erase_opcode = OPCODE_BE_4K;
flash->mtd.erasesize = 4096;
} else {
flash->erase_opcode = OPCODE_SE;
flash->mtd.erasesize = info->sector_size;
}
// ...
}
static int m25p_remove(struct spi_device *spi)
{
struct m25p *flash = dev_get_drvdata(&spi->dev);
int status;
/* Clean up MTD stuff. */
status = mtd_device_unregister(&flash->mtd);
if (status == 0) {
kfree(flash->command);
kfree(flash);
}
return 0;
}
static struct spi_driver m25p80_driver = {
.driver = {
.name = "m25p80",
.owner = THIS_MODULE,
},
.id_table = m25p_ids,
.probe = m25p_probe,
.remove = m25p_remove,
/* REVISIT: many of these chips have deep power-down modes, which
* should clearly be entered on suspend() to minimize power use.
* And also when they're otherwise idle...
*/
};
module_spi_driver(m25p80_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Mike Lavender");
MODULE_DESCRIPTION("MTD SPI driver for ST M25Pxx flash chips");
额,open 函数呢?
drivers/mtd/mtdchar.c
static int mtdchar_open(struct inode *inode, struct file *file)
{
int minor = iminor(inode);
int devnum = minor >> 1;
int ret = 0;
struct mtd_info *mtd;
struct mtd_file_info *mfi;
struct inode *mtd_ino;
// ...
}
static int mtdchar_close(struct inode *inode, struct file *file)
{
// ...
}
static ssize_t mtdchar_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
// ...
}
static ssize_t mtdchar_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
// ...
}
设备树节点
参考 Documentation/devicetree/bindings/mtd/m25p80.txt
&ecspi4 {
fsl,spi-num-chipselects = <1>;
cs-gpios = <&gpio3 20 0>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_ecspi4_1 &pinctrl_ecspi4_cs_0>;
status = "okay";
flash: m25p80@0 {
#address-cells = <1>;
#size-cells = <1>;
compatible = "winbond,w25q80bl";
spi-max-frequency = <20000000>;
reg = <0>;
};
};
&iomuxc {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_hog_1 &pinctrl_hog_2>;
spi4 {
pinctrl_ecspi4_cs_0: ecspi4_cs_grp-0 {
fsl,pins = <
MX6QDL_PAD_EIM_D20__GPIO3_IO20 0x80000000 /* ECSPI4_CS0 */
>;
};
pinctrl_ecspi4_cs_1: ecspi4_cs_grp-1 {
fsl,pins = <
MX6QDL_PAD_EIM_A25__GPIO5_IO02 0x80000000 /* ECSPI4_CS1 */
>;
};
pinctrl_ecspi4_1: ecspi4grp-1 {
fsl,pins = <
MX6QDL_PAD_EIM_D22__ECSPI4_MISO 0x170f1
MX6QDL_PAD_EIM_D28__ECSPI4_MOSI 0x1B008
MX6QDL_PAD_EIM_D21__ECSPI4_SCLK 0x170f1
>;
};
};
};
编译&测试
输入命令 dmesg | grep spi
,看到如下内容则说明已经探测到 w25q80bl 设备了
[ 1.931184] m25p80 spi32765.0: w25q80bl (1024 Kbytes)
[ 1.935767] spi_imx 2014000.ecspi: probed
挂载 MTD 设备挂载
通过 MTD 子系统,我们可以对 Flash 进行格式化,然后挂载,通过文件系统操作:
# mkfs.vfat /dev/mtdblock1
# mount -t vfat /dev/mtdblock1 /home/root/w25q80