代码编织梦想

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。在我们这里的测试平台上的硬件连接的情况是这样的:

![这里写图片描述](https://img-blog.csdn.net/20180613185133995?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2x1Y2t5ZGFyY3k=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)

管脚描述:

![这里写图片描述](https://img-blog.csdn.net/20180613190334653?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2x1Y2t5ZGFyY3k=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)

##驱动模型

![这里写图片描述](https://img-blog.csdn.net/20180620165807370?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2x1Y2t5ZGFyY3k=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)

##MTD

MTD设备分为四层(从设备节点直到底层硬件驱动),这四层从上到下依次是:

  • 设备节点
  • MTD设备层
  • MTD原始设备层
  • 硬件驱动层

MTD 子系统实现了 SPI flash 芯片驱动程序,其驱动 Demo 为:
  drivers/mtd/devices/mtd_dataflash.c
  drivers/mtd/devices/m25p80.c

##驱动文件

对于我们这里的 W25Q80DV 设备,重点关注的驱动文件是:
  drivers/mtd/devices/m25p80.c
  其主要代码框架如下:


static int m25p80_erase(struct mtd_info *mtd, struct erase_info *instr)
{
    /* 省略 */
}

static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf)
{
    /* 省略 */
}

static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf)
{
    /* 省略 */
}

static const struct spi_device_id m25p_ids[] = {
        #ifdef CONFIG_ARCH_ADVANTECH
        /* Micron N25Q */
        { "n25q", INFO(0x20ba16, 0, 64 * 1024,  64, SECT_4K) },
        { "n25q", INFO(0x20bb16, 0, 64 * 1024,  64, SECT_4K) },
        #endif
        /* Atmel -- some are (confusingly) marketed as "DataFlash" */
        { "at25fs010",  INFO(0x1f6601, 0, 32 * 1024,   4, SECT_4K) },
        { "at25fs040",  INFO(0x1f6604, 0, 64 * 1024,   8, SECT_4K) },

        /* ST Microelectronics -- newer production may have feature updates */
        { "m25p40",  INFO(0x202013,  0,  64 * 1024,   8, 0) },
        { "m25p80",  INFO(0x202014,  0,  64 * 1024,  16, 0) },

        /* Winbond -- w25x "blocks" are 64K, "sectors" are 4KiB */
        { "w25q64", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) },
        { "w25q80", INFO(0xef5014, 0, 64 * 1024,  16, SECT_4K) },
        { "w25q80bl", INFO(0xef4014, 0, 64 * 1024,  16, SECT_4K) },
        { "w25q128", INFO(0xef4018, 0, 64 * 1024, 256, SECT_4K) },
        { "w25q256", INFO(0xef4019, 0, 64 * 1024, 512, SECT_4K) },
        { },
};
MODULE_DEVICE_TABLE(spi, m25p_ids);

static const struct spi_device_id *jedec_probe(struct spi_device *spi)
{
    /* 省略 */
}

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");

通读 m25p80.c 驱动代码,我们可以找出大概的脉络。首先是通过 module_spi_driver 函数注册 m25p80_driver 驱动,其中实现了 probe 和 remove 函数,分别是 m25p_probem25p_remove。并且填写了一张名为 m25p_ids 的兼容设备表,通过查看 W25Q80DV 的数据手册,JEDEC 设备 ID 为 0xef4014,也就是对应 m25p_ids 中的 w25q80bl。

//INFO ( _jedec_id, _ext_id, _sector_size, _n_sectors, _flags )
{ "w25q80bl", INFO(0xef4014, 0, 64 * 1024,  16, SECT_4K) }

其中 INFO 是一个宏定义,其作用是将设备参数填写到内部的 flash_info 结构体实例中,在设备匹配成功后使用。
  在 m25p_probe 函数中指定了 m25p80_readm25p80_writem25p80_erase 等文件操作函数,当应用程序使用 read、write、ioctl 等接口操作时最终会调用到这里。

额,那 open 和 close 函数呢?
  还记得我们把 W25Q80DV 注册成 MTD 设备了嘛,所以另外一些操作函数在 drivers/mtd/mtdchar.c 中定义。实际上,它不仅有 mtdchar_openmtdchar_close 等函数,还有 mtdchar_readmtdchar_write 函数,而它们会调用 m25p80.c 中的 m25p80_readm25p80_write 函数。

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)
{
    // ...
}

##设备树节点

接下来要根据实际的硬件连接情况修改设备树文件,不用担心不会写,因为芯片厂商一定会提供参考信息的,比如这里的 SPI Flash,参考以下文件:
  Documentation/devicetree/bindings/mtd/m25p80.txt
  我这里的 W25Q80DV 连接到 i.MX6Q 的 ECSPI4,具体如下:

&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
			>;
		};
	};
};

##编译&验证

重新编译 image 和 dtb:

$ source /opt/poky/1.5.3/environment-setup-cortexa9hf-vfp-neon-poky-linux-gnueabi
$ make uImage LOADADDR=0x10008000
$ make imx6q-rom5420-b1.dtb

更新系统后重新启动,进入 shell。输入命令 dmesg | grep spi,看到如下内容则说明内核已经探测到 w25q80bl 设备,把设备和驱动程序匹配上了。

[    1.931184] m25p80 spi32765.0: w25q80bl (1024 Kbytes)
[    1.935767] spi_imx 2014000.ecspi: probed

查看设备文件:

# ls /dev/mtd*
/dev/mtd0       /dev/mtd1       /dev/mtdblock0
/dev/mtd0ro     /dev/mtd1ro     /dev/mtdblock1

可以看到多了 /dev/mtd1 之类的设备节点,其中 /dev/mtd1 是字符设备,/dev/mtdblock1 是块设备,/dev/mtd1ro 是只读字符设备。

##挂载 MTD 设备挂载

因为我们把 SPI Flash 注册成 MTD 设备了,因此,我们可以通过 MTD 子系统和文件系统对其进行操作。
  首先对 Flash 进行格式化,然后挂载,接着就可以通过文件系统操作:

# mkfs.vfat /dev/mtdblock1
# mount -t vfat /dev/mtdblock1 /home/root/w25q80
# cd /home/root/w25q80
# echo "Hello W25Q80" > file.txt
# sync

然后断电重启,看看文件及其内容是否还在,并且与断电前一致。

##“读写擦”测试程序

除了通过文件系统操作 W25Q80DV 设备外,也可以直接打开 /dev/mtd1 设备节点对其进行操作。
  测试程序有点长,我放在 GitHub 上了,大家可以参考下:
https://github.com/luhuadong/Linux-programming/blob/master/driver/mtd/test/mtd_go.c

未经授权许可,禁止以任何形式转载!
本文链接:https://blog.csdn.net/lu_embedded/article/details/80683998

linux spi/qspi nor flash相关驱动代码_sinbingzoo的博客-爱代码爱编程

对于 spi 接口,本身是可以接很多种类的外设的,比如用于接带有SPI通信协议的芯片,通常是将其作为字符设备类型注册。 对于接SPI/QSPI Flash时,又通常作为mtd设备(memory technology device内存技术设备)。MTD的主要目的是为了使新的memory设备的驱动更加简单,为此它在硬件和上层之间提供了一个抽象的接口,主要是读写

linux下spi flash-w25q64驱动调试-爱代码爱编程

   1 简介     W25Q64是华邦公司推出的大容量SPI FLASH产品,其容量为8M。该25Q系列的器件在灵活性和性能方面远远超过普通的串行闪存器件。W25Q64将8M字节的容量分为128个块(block),每

全志V3S/F1C100笔记之烧录sunxi-tools-爱代码爱编程

1.clong sunxi-tools git clone https://github.com/Icenowy/sunxi-tools.git -b f1c100s-spiflash 2.install libusb sudo apt install libz libusb-1.0-0-dev 2.1 sudo git branch -a 2.2

W25Qxx Flash存储-爱代码爱编程

1. W25Qxx Flash芯片介绍 Page:256 Bytes Sector:16 Pages(4KB) Block:16 Sector(64KB) 1. 存储大小 W25Q80DV 容量为 8M-bit,16 Block,256 Sector,4096 Page W25Q16 容量为 16M-Bit,32

flash挂载到系统 spi_LINUX SPI总线模块分析说明(spi模块分析之二)-爱代码爱编程

上一篇文章我们简要介绍了SPI驱动模块,本章我们详细说明一下spi总线、设备、驱动模块的注册、注销以及这几个模块之间的关联。 SPI总线的注册 spi模块也是基于LINUX设备-总线-驱动模型进行开发的,因此其spi总线,也是需要注册到 LINUX系统的总线模块的,调用的接口为是bus_register,我们看下spi总线都定义了哪些内容 如下

linux下spi flash驱动程序,关于spi flash芯片m25p80驱动以及其简单的mtd驱动分析-爱代码爱编程

项目中用到了spi flash芯片MX25L25635E,之前在uboot下简单分析了驱动代码,调试该flash擦除的bug,一直没有时间分 析内核中关于该芯片的驱动,以下是对该芯片驱动的一个简单分析: 1、先粘贴一些flash的理论部分: MTD层为NOR FLASH和NAND FLASH设备提供统一接口。MTD将文件系统与底层FLASH存

linux下spi flash驱动程序,SPI Flash(W25Q16DV) 驱动-爱代码爱编程

大体上可分为以下几个部分: 1.注册设备驱动 spi_register_driver 2.分配 mtd_info 结构体 3.配置 mtd_info 结构体 4.注册 mtd_info 结构体 构建 spi_driver 并注册 static struct spi_driver spi_flash_drv = { .driver = {

linux设备probe,你了解Embeded linux中的probe-爱代码爱编程

一、基于linux-3.18.20、mac驱动 二、启动时机: 所谓的"probe”,是指在Linux内核中,如果存在相同名称的device和device_driver,内核就会执行device_driver中的probe回调函数,而该函数就是所有driver的入口,可以执行诸如硬件设备初始化、字符设备注册、设备文件操作ops注册等动作("remo

嵌入式linux SPI驱动开发,Linux下spi驱动开发之m25p10驱动测试-爱代码爱编程

目标:在华清远见的FS_S5PC100平台上编写一个简单的spi驱动模块,在probe阶段实现对m25p10的ID号探测、flash擦除、flash状态读取、flash写入、flash读取等操作。代码已经经过测试,运行于2.6.35内核。理解下面代码需要参照m25p10的芯片手册。其实下面的代码和处理器没有太大关系,这也是spi子系统的分层特点。 #

linux中probe函数的pm管理,linux中 probe函数的何时调用的-爱代码爱编程

所以的驱动教程上都说:只有设备和驱动的名字匹配,BUS就会调用驱动的probe函数,但是有时我们要看看probe函数里面到底做了什么,还有传递给probe函数的参数我们就不知道在哪定义(反正不是我们在驱动里定义的),如果不知道传递进的参数,去看probe函数总是感觉不求甚解的样子(你对系统不求甚解,系统也会对你的要求不求甚解的),心里对自己写出的程序没

linux+4.1.15+flash空间,spi flash support in embedded linux box-爱代码爱编程

Serial Peripheral Interface (SPI) flash memory is often used in an embedded Linux box to hold bootloader. During very initial board bringup, SPI flash memory needs to be program

Linux内核4.14版本——SPI NOR子系统(2)——spi-nor.c分析-爱代码爱编程

1. 简介 2. spi_nor_scan 2.1 检查结构体struct spi_nor是否合格,匹配支持的nor flash ID得到info 2.1.1 spi_nor_check 2.1.2 spi_nor_read_id 2.1.3 struct flash_info 2.2 初始化结构体struct spi_nor_flash_pa