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 = {
		.name	= "spi_flash",
		.owner	= THIS_MODULE,
	},
	.probe		= spi_flash_probe,
	.remove		= __devexit_p(spi_flash_remove),
};

static int spi_flash_init(void)
{
    return spi_register_driver(&spi_flash_drv);
}

当内核中注册了同名的设备,会调用该驱动的 probe 程序

/* 分配 mtd_info 结构体 */
static struct mtd_info spi_flash_dev;

static int __devinit spi_flash_probe(struct spi_device *spi)
{
    int mid, did;
    
    spi_flash = spi;

    s3c2410_gpio_cfgpin(spi->chip_select, S3C2410_GPIO_OUTPUT);
    SPIFlashInit();
    SPIFlashReadID(&mid, &did);
    printk("SPI Flash ID: %02x %02x
", mid, did);
    memset(&spi_flash_dev, 0, sizeof(spi_flash_dev));
        
    /* 构造并注册这个 mtd_info
     * mtd_device_register(master, parts, nr_parts)
     */

	/* Setup the MTD structure */
	spi_flash_dev.name = "spi_flash";
	spi_flash_dev.type = MTD_NORFLASH;
	spi_flash_dev.flags = MTD_CAP_NORFLASH;
	spi_flash_dev.size = 0x200000;  /* 2M */
	spi_flash_dev.writesize = 1;
	spi_flash_dev.writebufsize = 4096; /* 没有用到 */
	spi_flash_dev.erasesize = 4096;  /* 擦除的最小单位 */

	spi_flash_dev.owner = THIS_MODULE;
	spi_flash_dev._erase = spi_flash_erase;
	spi_flash_dev._read  = spi_flash_read;
	spi_flash_dev._write = spi_flash_write;

    mtd_device_register(&spi_flash_dev, NULL, 0);
     
    return 0;
}

spi_flash_dev._erase = spi_flash_erase;
spi_flash_dev._read  = spi_flash_read;
spi_flash_dev._write = spi_flash_write;

这三个函数与前面一篇文章所调用的函数基本相同,只是 SPI 的发送我们需要调用内核中的函数来完成,程序如下(linux/spi.h):

static inline int
spi_write(struct spi_device *spi, const void *buf, size_t len)
{
	struct spi_transfer	t = {
			.tx_buf		= buf,
			.len		= len,
		};
	struct spi_message	m;

	spi_message_init(&m);
	spi_message_add_tail(&t, &m);
	return spi_sync(spi, &m);
}

忙等待函数我们也要加以修改,避免浪费 CPU 资源,程序如下:

static void SPIFlashWaitWhenBusy(void)
{
    while (SPIFlashReadStatusReg1() & 1)
    {
        /* 休眠一段时间 */
		set_current_state(TASK_INTERRUPTIBLE);
        schedule_timeout(HZ/100);  /* 休眠10MS后再次判断 */
    }
}

将进程设置为可中断的等待状态 TASK_INTERRUPTIBLE 。

状态解释:进程被挂起(睡眠),直到某个条件变为真。产生一个硬件中断,释放进程正在等待的系统资源,或传递一个信号都是可以唤醒进程的条件 (把进程的状态放回到 TASK_RUNNING)。

使用 schedule_timeout 函数,该方法会让需要延迟的任务睡眠到指定的延时时间后在重新运行。

原文地址:https://www.cnblogs.com/GyForever1004/p/8542810.html