Linux 驱动 SPI Flash(W25Q80DV)

  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

“读写擦”测试程序

猜你喜欢

转载自blog.csdn.net/luckydarcy/article/details/80683998