spi设备驱动

驱动端

1 驱动端probe函数

可能是设备树匹配,也可能是注册的spi_board_info
匹配成功时候,从设备的配置信息在struct spi_device *spi这里面

//实现一个struct file_operations,供打开的设备节点使用
static struct file_operations xxx_fops = {
	.owner =           THIS_MODULE,   
	.open  =           xxx_open,
	.unlocked_ioctl =  xxx_ioctl,
};
struct miscdevice misc_dev;

static int xxx_spi_probe_spi(struct spi_device *spi)
{

	spi->mode = SPI_MODE_3;
	spi->max_speed_hz = 2000000;
	spi->bits_per_word = 8;
	err = spi_setup(spi);//需要从新设置设备信息的时候需要设置一下,不然不用调
	
    misc_dev.fops = &xxx_fops;
    misc_dev.name = "xxx";
   misc_dev.minor = MISC_DYNAMIC_MINOR;
    ret = misc_register(&misc_dev);//注册一个杂项设备
    
    spi_set_drvdata(spi, an41908_data);//赋值私有数据

}

注册spi驱动

//设备树匹配
static const struct of_device_id xxx_dt_ids[] = {
    { .compatible = "xxx" },
    {},
};
 
 //id table匹配
static const struct spi_device_id xxx_spi_id[] = {
    {"xxx"},
    {}
};
MODULE_DEVICE_TABLE(spi, xxx_spi_id);
 
static struct spi_driver xxx_spi_driver = {
    .driver = {
        .name   = "xxx",
        .owner = THIS_MODULE,
        .of_match_table = of_match_ptr(xxx_dt_ids),
    },
    .probe      = xxx_spi_probe,
    .remove     = xxx_spi_remove,
    .id_table   = xxx_spi_id,
};
 
module_spi_driver(xxx_spi_driver);//注册spi设备驱动

2 spi读写函数

一般在ioctl中会调用读写spi函数,这里只需要赋值rx_buf和tx_buf,具体读写判断在spi控制器驱动中会判断,由于全双工,不用像i2c一样判断是读还是写,只要时钟使能,buf不为空就自行处理收发

static int __read_reg(	struct spi_device *spi, int reg)
{
	int error = 0;
	u16 cmd = CMD_READ(reg), in;
	u8 status;
	struct spi_message m;
	struct spi_transfer t[3];

	spi_message_init(&m);
	memset(t, 0, sizeof(t));

	t[0].tx_buf = &cmd;
	t[0].len = 2;
	t[0].bits_per_word = 12;
	spi_message_add_tail(&t[0], &m);

	t[1].rx_buf = ∈
	t[1].len = 2;
	t[1].bits_per_word = 16;
	spi_message_add_tail(&t[1], &m);

	t[2].rx_buf = &status;
	t[2].len = 1;
	t[2].bits_per_word = 4;
	spi_message_add_tail(&t[2], &m);

	error = spi_sync(spi, &m);
	if (error < 0)
		return error;
}

3、spi_async,spi_sync区别说明

最终都是调用__spi_queued_transfer,创建一个worker的内核线程处理收发数据

static int __spi_queued_transfer(struct spi_device *spi,
				 struct spi_message *msg,
				 bool need_pump)
{
	struct spi_master *master = spi->master;
	unsigned long flags;

	spin_lock_irqsave(&master->queue_lock, flags);

	if (!master->running) {
		spin_unlock_irqrestore(&master->queue_lock, flags);
		return -ESHUTDOWN;
	}
	msg->actual_length = 0;
	msg->status = -EINPROGRESS;

	list_add_tail(&msg->queue, &master->queue);
	if (!master->busy && need_pump)
		kthread_queue_work(&master->kworker, &master->pump_messages);

	spin_unlock_irqrestore(&master->queue_lock, flags);
	return 0;
}

spi_async 异步:使用spi_async()函数,它也可以用于原子上下文
spi_sync 同步:使用 spi_sync函数,它可能处于睡眠状态,不用在中断上下文中,借助完成量机制,完成spi的同步通信操作(主要借助完成量的complete、wait_for_completion这两个接口,休眠不能用在中断上下文,中断或原子上下文可以用try_wait_for_completion()和completion_done()都可以在IRQ或原子上下文中安全调用)

设备端

4、设备端注册

可以定义在设备树种,也可以通过spi_register_board_info注册一个struct spi_board_info 结构体
1 如下设备树定义定义通信频率,片选,相位时钟极性相关信息

&spi4 {
	status = "okay";
	#address-cells = <1>;
	#size-cells = <0>;
    xxx{
        compatible = "xxx";
        reg = <0>;
        spi-max-frequency = <2000000>;
        spi-cpha;
        spi-cpol;
        status = "ok";
        };
};

2、spi_register_board_info
在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值