SPI子系统源码剖析--(2)Spi_Master驱动框架
1. spi_masterspi_master对应spi控制器是对引脚的管理同时可以通过cs引脚选择从设备发送消息2. SPI传输概述1.1 数据组织方式使用SPI传输时最小的传输单位是spi_transfer对于一个设备可以发起多个spi_transfer这些spi_transfer会放入一个spi_message里 内部通过struct list_head 结构体构成queue可以通过of_container()函数找到spi_transfer结构体的首地址进而调用spi_transfer指定tx_buf、rx_buf、len同一个SPI设备的spi_transfer使用spi_message来管理同一个SPI Master下的spi_message放在一个队列里所以反过来SPI传输的流程是这样的从spi_master的队列里取出每一个spi_message从spi_message的队列里取出一个spi_transfer处理spi_transfer1.2 SPI控制器数据结构有两套传输方法3. SPI传输两种方法的源码剖析第一种对于这种老方法的传输我们要去提供master-transfer()函数的具体实现。其次对于_spi_sync()执行完spi_async_locked()后就开始休眠等待消息发送完成内核线程会发送完一个一个的xfer结束之后就会产生中断 将其唤醒3.1 第一种方法的代码实现如下#include linux/module.h #include linux/kernel.h #include linux/sched.h #include linux/errno.h #include linux/timer.h #include linux/delay.h #include linux/list.h #include linux/workqueue.h #include linux/interrupt.h #include linux/platform_device.h #include linux/io.h #include linux/spi/spi.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); /* 假装硬件传输已经完成 */ mesg-status 0; if (mesg-complete) mesg-complete(mesg-context); } } static int spi_virtual_transfer(struct spi_device *spi, struct spi_message *mesg) { #if 0 /* 方法1: 直接实现spi传输 */ /* 假装传输完成, 直接唤醒 status 要指定为已经结束传输否则会出错*/ mesg-status 0; 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) { struct spi_master *master; int ret; /* 分配/设置/注册spi_master */ g_virtual_master master spi_alloc_master(pdev-dev, 0); if (master NULL) { dev_err(pdev-dev, spi_alloc_master error.\n); return -ENOMEM; } // 设置传输函数在_sync_transfer中会调用 master-transfer spi_virtual_transfer; INIT_WORK(g_virtual_ws, spi_virtual_work); // 指定of_node节点否则master下的从设备无法构造出device // 把“设备树信息”从 platform_device 传递给 spi_master master-dev.of_node pdev-dev.of_node; ret spi_register_master(master); if (ret 0) { printk(KERN_ERR spi_register_master error.\n); spi_master_put(master); return ret; } return 0; } static int spi_virtual_remove(struct platform_device *pdev) { /* 反注册spi_master */ spi_unregister_master(g_virtual_master); return 0; } static struct platform_driver spi_virtual_driver { .probe spi_virtual_probe, .remove spi_virtual_remove, .driver { .name virtual_spi, .of_match_table spi_virtual_dt_ids, }, }; static int virtual_master_init(void) { return platform_driver_register(spi_virtual_driver); } static void virtual_master_exit(void) { platform_driver_unregister(spi_virtual_driver); } module_init(virtual_master_init); module_exit(virtual_master_exit); MODULE_DESCRIPTION(Virtual SPI bus driver); MODULE_LICENSE(GPL); MODULE_AUTHOR(www.100ask.net);注意 要给master指定of_node节点否则master下的从设备无法构造出device其次对于传输状态位status的操作。第二种方法总体调用关系在进入_syn_spi_register_master()之后调用关系如下下面是对上面的函数调用的仔细分析static int __spi_sync(struct spi_device *spi, struct spi_message *message) { DECLARE_COMPLETION_ONSTACK(done); // 设定完成量实现同步等待 int status; struct spi_master *master spi-master; unsigned long flags; status __spi_validate(spi, message); // 校验 SPI 消息的合法性 if (status ! 0) return status; message-complete spi_complete; //绑定回调函数 message-context done; //消息传输完成触发 spi_complete唤醒 done 完成量解除阻塞 message-spi spi; //内核 / 用户可通过统计信息监控 SPI 传输性能 SPI_STATISTICS_INCREMENT_FIELD(master-statistics, spi_sync); SPI_STATISTICS_INCREMENT_FIELD(spi-statistics, spi_sync); // 会在spi_register_master函数中进行判断初始化 if (master-transfer spi_queued_transfer) { // 队列式传输 bus_lock_spinlock是SPI 主控制器的总线锁避免多线程并发提交消息 spin_lock_irqsave(master-bus_lock_spinlock, flags); trace_spi_message_submit(message); // 将消息加入主控制器的传输队列不立即执行 status __spi_queued_transfer(spi, message, false); // 解锁恢复中断 spin_unlock_irqrestore(master-bus_lock_spinlock, flags); } else { status spi_async_locked(spi, message); } if (status 0) { /* Push out the messages in the calling context if we * can. */ if (master-transfer spi_queued_transfer) { SPI_STATISTICS_INCREMENT_FIELD(master-statistics, spi_sync_immediate); SPI_STATISTICS_INCREMENT_FIELD(spi-statistics, spi_sync_immediate); __spi_pump_messages(master, false); // “泵出”队列中的消息触发硬件传输 } /* 调用者会停在这里直到 * 硬件完成所有 SPI 消息的传输 * 触发 spi_complete 回调 * complete(done) 唤醒完成量 */ wait_for_completion(done); status message-status; } message-context NULL; return status; }_spi_pump_message()函数static void __spi_pump_messages(struct spi_master *master, bool in_kthread) { unsigned long flags; bool was_busy false; int ret; /* Lock queue */ spin_lock_irqsave(master-queue_lock, flags); /* Make sure we are not already running a message 指向当前正在处理的 SPI 消息*/ if (master-cur_msg) { spin_unlock_irqrestore(master-queue_lock, flags); return; } /* If another context is idling the device then defer */ if (master-idling) { kthread_queue_work(master-kworker, master-pump_messages); spin_unlock_irqrestore(master-queue_lock, flags); return; } /* Check if the queue is idle */ if (list_empty(master-queue) || !master-running) { if (!master-busy) { // 控制器完全空闲 → 直接退出 spin_unlock_irqrestore(master-queue_lock, flags); return; } /* Only do teardown in the thread 非线程上下文 → 提交任务到专属 kworker 线程*/ //收尾操作释放内存、硬件反初始化是耗时且可能休眠的 if (!in_kthread) { kthread_queue_work(master-kworker, master-pump_messages); spin_unlock_irqrestore(master-queue_lock, flags); return; } // 执行控制器收尾清理 master-busy false; master-idling true; spin_unlock_irqrestore(master-queue_lock, flags); kfree(master-dummy_rx); master-dummy_rx NULL; kfree(master-dummy_tx); master-dummy_tx NULL; // 反初始化 SPI 硬件 比如关闭 SPI 时钟、释放片选引脚、重置硬件寄存器 if (master-unprepare_transfer_hardware master-unprepare_transfer_hardware(master)) dev_err(master-dev, failed to unprepare transfer hardware\n); // 标记 SPI 控制器最后一次忙碌的时间然后调触发“自动挂起”仅保留必要的寄存器供电 if (master-auto_runtime_pm) { pm_runtime_mark_last_busy(master-dev.parent); pm_runtime_put_autosuspend(master-dev.parent); } trace_spi_master_idle(master); spin_lock_irqsave(master-queue_lock, flags); master-idling false; // 标记“空闲化处理”完成 spin_unlock_irqrestore(master-queue_lock, flags); return; } /* Extract head of queue */ master-cur_msg list_first_entry(master-queue, struct spi_message, queue); list_del_init(master-cur_msg-queue); if (master-busy)//记录并更新忙状态 was_busy true; else master-busy true; spin_unlock_irqrestore(master-queue_lock, flags); mutex_lock(master-io_mutex); if (!was_busy master-auto_runtime_pm) { ret pm_runtime_get_sync(master-dev.parent); //唤醒SPI控制器并上电 if (ret 0) { dev_err(master-dev, Failed to power device: %d\n, ret); mutex_unlock(master-io_mutex); return; } } //如果控制器从空闲转忙, 首次传输前设置硬件即可 if (!was_busy) trace_spi_master_busy(master); if (!was_busy master-prepare_transfer_hardware) { ret master-prepare_transfer_hardware(master); if (ret) { dev_err(master-dev, failed to prepare transfer hardware\n); if (master-auto_runtime_pm) pm_runtime_put(master-dev.parent); mutex_unlock(master-io_mutex); return; } } trace_spi_message_start(master-cur_msg); // SPI 控制器的单消息个性化配置函数调整传输速率、数据位宽8 位 / 16 位 if (master-prepare_message) { ret master-prepare_message(master, master-cur_msg); if (ret) { dev_err(master-dev, failed to prepare message: %d\n, ret); master-cur_msg-status ret; spi_finalize_current_message(master); goto out; } master-cur_msg_prepared true; } // 内核核心函数将 SPI 消息的缓冲区映射为硬件可访问的地址如 DMA 物理地址 // 解决 “内核虚拟地址硬件无法直接访问” 的问题 ret spi_map_msg(master, master-cur_msg); if (ret) { master-cur_msg-status ret; spi_finalize_current_message(master); goto out; } // SPI 控制器驱动必须实现的硬件传输核心函数 // 连接 “软件逻辑” 到 “硬件操作” ret master-transfer_one_message(master, master-cur_msg); if (ret) { dev_err(master-dev, failed to transfer one message from queue\n); goto out; } out: mutex_unlock(master-io_mutex); /* Prod the scheduler in case transfer_one() was busy waiting “如果 transfer_one_message 是‘忙等’实现就触发调度器让步” 传输成功 让出 CPU让其他线程 / 进程运行” */ if (!ret) cond_resched(); }transfer_one_message() 会在这个spi_master_initialize_queue中对master-transfer_one_message spi_transfer_one_message;赋值进而调用下面的函数spi_transfer_one_message()/* * spi_transfer_one_message - Default implementation of transfer_one_message() * * This is a standard implementation of transfer_one_message() for * drivers which implement a transfer_one() operation. It provides * standard handling of delays and chip select management. */ static int spi_transfer_one_message(struct spi_master *master, struct spi_message *msg) { struct spi_transfer *xfer; bool keep_cs false; int ret 0; unsigned long long ms 1; struct spi_statistics *statm master-statistics; // 主控制器的统计信息总传输量 struct spi_statistics *stats msg-spi-statistics; // 当前SPI设备的统计信息 // 1. 选中目标SPI设备 spi_set_cs(msg-spi, true); SPI_STATISTICS_INCREMENT_FIELD(statm, messages); // 主控制器的“总消息数”1 SPI_STATISTICS_INCREMENT_FIELD(stats, messages); // 遍历当前 spi_message 中的所有 spi_transfer 节点 // 注意: 使用struct list_head 链接整个链表通过container_of反推结构体地址 list_for_each_entry(xfer, msg-transfers, transfer_list) { trace_spi_transfer_start(msg, xfer); spi_statistics_add_transfer_stats(statm, xfer, master); spi_statistics_add_transfer_stats(stats, xfer, master); if (xfer-tx_buf || xfer-rx_buf) { //重置SPI 控制器的 “完成量” 状态 前提是要先初始化 reinit_completion(master-xfer_completion); // 调用 SPI 控制器驱动的 transfer_one 函数为 Bitbang 模式 // 若为硬件 SPI 模式这里会把 tx_buf 的物理地址写入DMA寄存器 ret master-transfer_one(master, msg-spi, xfer); if (ret 0) { SPI_STATISTICS_INCREMENT_FIELD(statm, errors); SPI_STATISTICS_INCREMENT_FIELD(stats, errors); dev_err(msg-spi-dev, SPI transfer failed: %d\n, ret); goto out; } //ret 0异步传输 if (ret 0) { ret 0; // 计算“理论传输时间”毫秒 // 公式(8位/字节 * 1000毫秒/秒 * 传输字节数) / SPI速率Hz ms 8LL * 1000LL * xfer-len; do_div(ms, xfer-speed_hz); ms ms 200; /* some tolerance */ // 加 2 倍理论时间 200ms 容错是为了兼容硬件延迟 if (ms UINT_MAX) ms UINT_MAX; // 异步等待 ms wait_for_completion_timeout(master-xfer_completion, msecs_to_jiffies(ms)); } if (ms 0) { SPI_STATISTICS_INCREMENT_FIELD(statm, timedout); SPI_STATISTICS_INCREMENT_FIELD(stats, timedout); dev_err(msg-spi-dev, SPI transfer timed out\n); msg-status -ETIMEDOUT; } } else { if (xfer-len) dev_err(msg-spi-dev, Bufferless transfer has length %u\n, xfer-len); } trace_spi_transfer_stop(msg, xfer); if (msg-status ! -EINPROGRESS) goto out; // 微秒级延迟 if (xfer-delay_usecs) udelay(xfer-delay_usecs); if (xfer-cs_change) { if (list_is_last(xfer-transfer_list, msg-transfers)) { keep_cs true; } else { // 在两个 transfer 之间产生“帧边界” 让设备知道这是两次独立通信 spi_set_cs(msg-spi, false); udelay(10); spi_set_cs(msg-spi, true); } } msg-actual_length xfer-len; } out: if (ret ! 0 || !keep_cs) spi_set_cs(msg-spi, false); // 正常传输时 msg-status 初始是 -EINPROGRESS 正在进行中 if (msg-status -EINPROGRESS) msg-status ret; if (msg-status master-handle_err) master-handle_err(master, msg); spi_res_release(master, msg); // 清空 master-cur_msg 重置完成量 保证下一次传输时状态干净。 spi_finalize_current_message(master); return ret; }按照上面的函数调用transfer_one()函数会在 spi-bitbang.c 中的spi_bitbang_start()函数中进行与spi_bitbang_transfer_one()函数绑定static int spi_bitbang_transfer_one(struct spi_master *master, struct spi_device *spi, struct spi_transfer *transfer) { struct spi_bitbang *bitbang spi_master_get_devdata(master); int status 0; //根据这个 spi_device 和 transfer 的参数 配置本次传输环境 if (bitbang-setup_transfer) { status bitbang-setup_transfer(spi, transfer); if (status 0) goto out; } if (transfer-len) status bitbang-txrx_bufs(spi, transfer); if (status transfer-len) status 0; else if (status 0) status -EREMOTEIO; out: spi_finalize_current_transfer(master); //调用complete(master-xfer_completion); return status; }3.2 第二种方法的代码实现如下#include linux/module.h #include linux/kernel.h #include linux/sched.h #include linux/errno.h #include linux/timer.h #include linux/delay.h #include linux/list.h #include linux/workqueue.h #include linux/interrupt.h #include linux/platform_device.h #include linux/io.h #include linux/spi/spi.h #include linux/spi/spi_bitbang.h static struct spi_master *g_virtual_master; static struct spi_bitbang *g_virtual_bitbang; static struct completion g_xfer_done; static const struct of_device_id spi_virtual_dt_ids[] { { .compatible 100ask,virtual_spi_master, }, { /* sentinel */ } }; /* xxx_isr() { complete(g_xfer_done) } */ static int spi_virtual_transfer(struct spi_device *spi, struct spi_transfer *transfer) { int timeout; #if 1 /* 1. init complete */ reinit_completion(g_xfer_done); /* 2. 启动硬件传输 */ complete(g_xfer_done); /* 3. wait for complete */ timeout wait_for_completion_timeout(g_xfer_done, 100); if (!timeout) { dev_err(spi-dev, I/O Error in PIO\n); return -ETIMEDOUT; } #endif return transfer-len; } static void spi_virtual_chipselect(struct spi_device *spi, int is_on) { } static int spi_virtual_probe(struct platform_device *pdev) { struct spi_master *master; int ret; /* 分配/设置/注册spi_master */ g_virtual_master master spi_alloc_master(pdev-dev, sizeof(struct spi_bitbang)); if (master NULL) { dev_err(pdev-dev, spi_alloc_master error.\n); return -ENOMEM; } // 从 master里取出这个 bitbang控制器对应的私有结构体 g_virtual_bitbang spi_master_get_devdata(master); init_completion(g_xfer_done); /* 怎么设置spi_master? * 1. spi_master使用默认的函数 * 2. 分配/设置 spi_bitbang结构体: 主要是实现里面的txrx_bufs函数 * 3. spi_master要能找到spi_bitbang */ g_virtual_bitbang-master master; g_virtual_bitbang-txrx_bufs spi_virtual_transfer; g_virtual_bitbang-chipselect spi_virtual_chipselect; master-dev.of_node pdev-dev.of_node; #if 0 ret spi_register_master(master); if (ret 0) { printk(KERN_ERR spi_register_master error.\n); spi_master_put(master); return ret; } #else // 包含了注册以及bitbang结构体中函数的初始化操作 ret spi_bitbang_start(g_virtual_bitbang); if (ret) { printk(bitbang start failed with %d\n, ret); return ret; } #endif return 0; } static int spi_virtual_remove(struct platform_device *pdev) { #if 0 /* 反注册spi_master */ spi_unregister_master(g_virtual_master); #endif spi_bitbang_stop(g_virtual_bitbang); spi_master_put(g_virtual_master); return 0; } static struct platform_driver spi_virtual_driver { .probe spi_virtual_probe, .remove spi_virtual_remove, .driver { .name virtual_spi, .of_match_table spi_virtual_dt_ids, }, }; static int virtual_master_init(void) { return platform_driver_register(spi_virtual_driver); } static void virtual_master_exit(void) { platform_driver_unregister(spi_virtual_driver); } module_init(virtual_master_init); module_exit(virtual_master_exit); MODULE_DESCRIPTION(Virtual SPI bus driver); MODULE_LICENSE(GPL); MODULE_AUTHOR(www.100ask.net);1. 在probe函数中分配 设置和注册spi_master, 需要注意的点在第一个方法中提过2. 要实现txrx_bufs() chipselect() 。两个必须的函数实现。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2425349.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!