编写SPI_Master驱动程序

参考资料:

  • 内核头文件:include\linux\spi\spi.h
  • 内核文档:Documentation\devicetree\bindings\spi\spi-bus.txt
    • 内核源码:drivers\spi\spi.c drivers\spi\spi-sh.c

一、SPI驱动框架

1.1 总体框架

1.2 编写SPI_Master驱动

1.2.1 编写设备树

在设备树中,对于SPI Master,必须的属性如下;

  • address-cells:这个SPI Master下的SPI设备,需要多少个cell来表述它的片选引脚
  • size-cells:必须设置为0
  • compatible:根据它找到SPI Master驱动
    可选的属性如下;
  • cs-gpios:SPI Master可以使用多个GPIO当做片选,可以找在这个属性列出那些GPIO
  • num-cs:片选引脚总数
    其他属性都是驱动程序相关的,不同的SPI Master驱动程序要求的属性可能不一样。
    在SPI Master对应的设备树节点下,每一个子节点都对应一个SPI设备,这个SPI设备连接在该SPI Master下面。
    这些子节点中,必选的属性如下:
  • compatible:根据它找到SPI Devcie驱动
  • reg:用来表示它使用哪个片选引脚
  • spi-max-frequency:片选,该SPI设备支持的最大SPI时钟

可选的属性如下:

  • spi-cpol:这是一个空属性(没有值),表示CPOL为1,即平时SPI时钟为低电平
  • spi-cpha:这是一个空属性(没有值),表示CPHA为1,即在时钟的第2个边沿采样数据
  • spi_cs-high:这是一个空属性(没有值),表示片选移交高电平有效
  • spi-3wire:这是一个空属性(没有值),表示使用SPI三线模式
  • spi-lsb-first:这是一个空属性(没有值),表示使用SPI传输数据时先传输最低位(LSB)
  • spi-tx-bus-width:表示有几条MOSI引脚,没有这个属性默认只有1条MOSI引脚
  • spi-rx-bus-width:表示有几条MISO引脚,没有这个属性默认只有1条MISO引脚
  • spi-rx-delay-us:单位是毫秒,表示每次读传输后要延时多久
  • spi-tx-delay-us:单位是毫秒,表示每次写传输后要延时多久

1.2.2 编写驱动程序

  • 核心为:分配、设置、注册spi_master结构体
  • 对于老方法,spi_master结构体的核心是transfer函数

二、编写程序

2.1 数据传输流程

在这里插入图片描述

2.2 代码

&spi3 {
	compatible = "100ask,virtual_spi_master";
	status = "okay";
	cs-gpios = <&gpio0 27 GPIO_ACTIVE_LOW>;
	num-chipselects = <1>;
	#address-cells = <1>;
	#size-cells = <0>;

	virtual_spi_dev:virual_spi_dev@0 {
		compatible = "100ask,virtual_spi_dev";
		reg = <0>;
		spi-max-frequency: <100000>;
	};

};

#include <linux/init.h>
#include <linux/module.h>
#include <linux/spi/spi.h>
#include <asm/uaccess.h>
#include <linux/device.h>
#include <linux/of.h>

static struct spi_master *g_virtual_master;
static struct work_struct g_virtual_ws;

static const struct of_device_id spi_virtual_dt_ids[] = {
	{ .compatible = "100ask,virtual_spi_master" },
	{ /* sentinel */ }
};

static void spi_virtual_work(struct work_struct *work)
{
	struct spi_message *mesg;

	while(!list_empty(&g_virtual_master->queue)) {
		mesg = list_entry(g_virtual_master->queue.next, struct spi_message, queue);
		list_del_init(&mesg->queue);

		/* pretend the message is transfered */
		/* 假装硬件传输已经完成 */
		mesg->status = 0;
		if(mesg->complete) 
			mesg->complete(mesg->context);
	}
}

static int spi_virtual_transfer(struct spi_device *spi, struct spi_message *mesg)
{
#if 1
	/* 方法1: 直接实现spi传输 */
	/* 假装传输完成, 直接唤醒 */
	mesg->complete(mesg->context);
	return 0;

#else
	/* 方法2: 使用工作队列启动SPI传输、等待完成 */
	/* 把消息放入队列 */
	mesg->actual_length = 0;
	mesg->status = -EINPROGRESS;
	list_add_tail(&mesg->queue, &spi->master->queue);

	/* 启动工作队列 */
	schedule_work(&g_virtual_ws);
	/* 直接返回 */
	return 0;
#endif
}

static int spi_virtual_probe(struct platform_device *pdev)
{
	int ret;

	/* 分配/设置/注册spi_master */
	g_virtual_master = spi_alloc_master(&pdev->dev, 0);
	if(g_virtual_master == NULL) {
		dev_err(&pdev->dev, "spi_alloc_master error.\n");
		return -ENOMEM;
	}

	g_virtual_master->transfer = spi_virtual_transfer;
	INIT_WORK(&g_virtual_ws, spi_virtual_work);

	ret = spi_register_master(g_virtual_master);
	if(ret < 0) {
		printk(KERN_ERR "spi_register_master error.\n");
		spi_master_put(g_virtual_master);
		return ret;
	}

	return 0;
}

static int spi_virtual_remove(struct platform_device *pdev)
{
	spi_unregister_master(g_virtual_master);
	spi_master_put(g_virtual_master);
	return 0;
}

static struct platform_driver spidev_spi_driver = {
	.driver = {
		.name = "100ask,virtual_spi_master",
		.of_match_table = of_match_ptr(spidev_dt_ids),
	},
	.probe = spi_virtual_probe,
	.remove = spi_virtual_remove,
};

static int __init virtual_master_init(void)
{
	return platform_register(spidev_spi_driver);
}
module_init(virtual_master_init);

static void __exit virtual_master_exit(void)
{
	platform_driver_unregister(&spidev_spi_driver);
}
module_exit(virtual_master_exit);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Virtual SPI bus driver");
MODULE_AUTHOR("Tuo");

版权声明:本文为ch122633原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。