SPI总线子系统
来源:互联网 发布:org.apache.ant jar 编辑:程序博客网 时间:2024/06/18 16:05
一 主要数据结构
- struct spi_device {
- struct device dev;
- struct spi_master *master;
- u32 max_speed_hz;
- u8 chip_select;
- u8 mode;
- #define SPI_CPHA 0x01 /* clock phase */
- #define SPI_CPOL 0x02 /* clock polarity */
- #define SPI_MODE_0 (0|0) /* (original MicroWire) */
- #define SPI_MODE_1 (0|SPI_CPHA)
- #define SPI_MODE_2 (SPI_CPOL|0)
- #define SPI_MODE_3 (SPI_CPOL|SPI_CPHA)
- #define SPI_CS_HIGH 0x04 /* chipselect active high? */
- #define SPI_LSB_FIRST 0x08 /* per-word bits-on-wire */
- #define SPI_3WIRE 0x10 /* SI/SO signals shared */
- #define SPI_LOOP 0x20 /* loopback mode */
- #define SPI_NO_CS 0x40 /* 1 dev/bus, no chipselect */
- #define SPI_READY 0x80 /* slave pulls low to pause */
- u8 bits_per_word;
- int irq;
- void *controller_state;
- void *controller_data;
- char modalias[SPI_NAME_SIZE];
- int cs_gpio; /* chip select gpio */
- /*
- * likely need more hooks for more protocol options affecting how
- * the controller talks to each chip, like:
- * - memory packing (12 bit samples into low bits, others zeroed)
- * - priority
- * - drop chipselect after each word
- * - chipselect delays
- * - ...
- */
- };
spi从设备,相当于i2c_client。它需要依附一个spi_master。
- struct spi_master {
- struct device dev;
- struct list_head list;
- /* other than negative (== assign one dynamically), bus_num is fully
- * board-specific. usually that simplifies to being SOC-specific.
- * example: one SOC has three SPI controllers, numbered 0..2,
- * and one board's schematics might show it using SPI-2. software
- * would normally use bus_num=2 for that controller.
- */
- s16 bus_num;
- /* chipselects will be integral to many controllers; some others
- * might use board-specific GPIOs.
- */
- u16 num_chipselect;
- /* some SPI controllers pose alignment requirements on DMAable
- * buffers; let protocol drivers know about these requirements.
- */
- u16 dma_alignment;
- /* spi_device.mode flags understood by this controller driver */
- u16 mode_bits;
- /* other constraints relevant to this driver */
- u16 flags;
- #define SPI_MASTER_HALF_DUPLEX BIT(0) /* can't do full duplex */
- #define SPI_MASTER_NO_RX BIT(1) /* can't do buffer read */
- #define SPI_MASTER_NO_TX BIT(2) /* can't do buffer write */
- /* lock and mutex for SPI bus locking */
- spinlock_t bus_lock_spinlock;
- struct mutex bus_lock_mutex;
- /* flag indicating that the SPI bus is locked for exclusive use */
- bool bus_lock_flag;
- /* Setup mode and clock, etc (spi driver may call many times).
- *
- * IMPORTANT: this may be called when transfers to another
- * device are active. DO NOT UPDATE SHARED REGISTERS in ways
- * which could break those transfers.
- */
- int (*setup)(struct spi_device *spi);
- /* bidirectional bulk transfers
- *
- * + The transfer() method may not sleep; its main role is
- * just to add the message to the queue.
- * + For now there's no remove-from-queue operation, or
- * any other request management
- * + To a given spi_device, message queueing is pure fifo
- *
- * + The master's main job is to process its message queue,
- * selecting a chip then transferring data
- * + If there are multiple spi_device children, the i/o queue
- * arbitration algorithm is unspecified (round robin, fifo,
- * priority, reservations, preemption, etc)
- *
- * + Chipselect stays active during the entire message
- * (unless modified by spi_transfer.cs_change != 0).
- * + The message transfers use clock and SPI mode parameters
- * previously established by setup() for this device
- */
- int (*transfer)(struct spi_device *spi,
- struct spi_message *mesg);
- /* called on release() to free memory provided by spi_master */
- void (*cleanup)(struct spi_device *spi);
- /*
- * These hooks are for drivers that want to use the generic
- * master transfer queueing mechanism. If these are used, the
- * transfer() function above must NOT be specified by the driver.
- * Over time we expect SPI drivers to be phased over to this API.
- */
- bool queued;
- struct kthread_worker kworker;
- struct task_struct *kworker_task;
- struct kthread_work pump_messages;
- spinlock_t queue_lock;
- struct list_head queue;
- struct spi_message *cur_msg;
- bool busy;
- bool running;
- bool rt;
- int (*prepare_transfer_hardware)(struct spi_master *master);
- int (*transfer_one_message)(struct spi_master *master,
- struct spi_message *mesg);
- int (*unprepare_transfer_hardware)(struct spi_master *master);
- /* gpio chip select */
- int *cs_gpios;
- };
- struct spi_driver {
- const struct spi_device_id *id_table;
- int (*probe)(struct spi_device *spi);
- int (*remove)(struct spi_device *spi);
- void (*shutdown)(struct spi_device *spi);
- int (*suspend)(struct spi_device *spi, pm_message_t mesg);
- int (*resume)(struct spi_device *spi);
- struct device_driver driver;
- };
- struct spi_transfer {
- /* it's ok if tx_buf == rx_buf (right?)
- * for MicroWire, one buffer must be null
- * buffers must work with dma_*map_single() calls, unless
- * spi_message.is_dma_mapped reports a pre-existing mapping
- */
- const void *tx_buf;
- void *rx_buf;
- unsigned len;
- dma_addr_t tx_dma;
- dma_addr_t rx_dma;
- unsigned cs_change:1;
- u8 bits_per_word;
- u16 delay_usecs;
- u32 speed_hz;
- struct list_head transfer_list;
- };
- struct spi_message {
- struct list_head transfers;
- struct spi_device *spi;
- unsigned is_dma_mapped:1;
- /* REVISIT: we might want a flag affecting the behavior of the
- * last transfer ... allowing things like "read 16 bit length L"
- * immediately followed by "read L bytes". Basically imposing
- * a specific message scheduling algorithm.
- *
- * Some controller drivers (message-at-a-time queue processing)
- * could provide that as their default scheduling algorithm. But
- * others (with multi-message pipelines) could need a flag to
- * tell them about such special cases.
- */
- /* completion is reported through a callback */
- void (*complete)(void *context);
- void *context;
- unsigned actual_length;
- int status;
- /* for optional use by whatever driver currently owns the
- * spi_message ... between calls to spi_async and then later
- * complete(), that's the spi_master controller driver.
- */
- struct list_head queue;
- void *state;
- };
spi_transfer定义了一对读写buffer,还有一个transfer_list;利用这个list把自己挂在spi_message的transfers上,也就是这两个结构合起来相当于i2c_msg。spi的传输单位就是一个spi_message。
- struct spi_board_info {
- /* the device name and module name are coupled, like platform_bus;
- * "modalias" is normally the driver name.
- *
- * platform_data goes to spi_device.dev.platform_data,
- * controller_data goes to spi_device.controller_data,
- * irq is copied too
- */
- char modalias[SPI_NAME_SIZE];
- const void *platform_data;
- void *controller_data;
- int irq;
- /* slower signaling on noisy or low voltage boards */
- u32 max_speed_hz;
- /* bus_num is board specific and matches the bus_num of some
- * spi_master that will probably be registered later.
- *
- * chip_select reflects how this chip is wired to that master;
- * it's less than num_chipselect.
- */
- u16 bus_num;
- u16 chip_select;
- /* mode becomes spi_device.mode, and is essential for chips
- * where the default of SPI_CS_HIGH = 0 is wrong.
- */
- u8 mode;
- /* ... may need additional spi_device chip config data here.
- * avoid stuff protocol drivers can set; but include stuff
- * needed to behave without being bound to a driver:
- * - quirks like clock rate mattering when not selected
- */
- };
二 主要函数接口
static int spi_match_device(struct device *dev, struct device_driver *drv)
相当于i2c_device_match(),i2c_device_match()->i2c_match_id()->strcmp(client->name, id->name)匹配成功返回1,否则0,结束。spi_match_device()->spi_match_id()->strcmp(sdev->modalias, id->name))仍然是匹配成功返回1,否则返回0。不同的是,对应i2c_device_match(),如果i2c_drvier中没有定义id_table,那直接就返回0了。而spi不是,它还会继续strcmp(spi->modalias, drv->name)根据这个确定返回值。所以我们看到i2c_driver中都会定义id_table,而spi_driver有时不定义,只保证pi->modalias和drv->name一致就好了。
struct spi_device *spi_new_device(struct spi_master *master, struct spi_board_info *chip)
调用spi_alloc_device(master)分配一个spi_device;调用spi_add_device(proxy)把分配的spi_device添加到系统中,spi_device是一种device,添加device必然会调用 device_add(&spi->dev)。
spi_add_device()->spi_setup(spi)->( spi->master->setup(spi)这是用于设置spi的mode和clock等;spi有四种模式。
int spi_register_board_info(struct spi_board_info const *info, unsigned n)
和i2c差不多,新出现的boardinfo是对spi_board_info的一个封装;register会把自己挂在一个全局的board_list上。与i2c不同的是,此时spi就会遍历spi_master_list,根据bus_num进行master和device的匹配,匹配成功就new device。如果主设备已经register,对于spi来说只要调用register_board_info,就可以自动new spi_device了;而i2c需要手动的调用i2c_new_device。int spi_register_master(struct spi_master *master)
spi_master是个device,所以还会用device_add();而且会把自己挂在spi_master_list全局的list上,这样register board info的时候才能找到这个master;当然,此时也会遍历board_list,找到匹配的info,创建spi device。这个函数中还有一段:if (master->transfer)
dev_info(dev, "master is unqueued, this is deprecated\n");
else {
status = spi_master_initialize_queue(master);
if (status) {
device_unregister(&master->dev);
goto done;
}
}
master->transfer已经实现的就略过,否则需要用内核提供的一套机制。
- static int spi_master_initialize_queue(struct spi_master *master)
- {
- int ret;
- master->queued = true;
- master->transfer = spi_queued_transfer;
- /* Initialize and start queue */
- ret = spi_init_queue(master);
- if (ret) {
- dev_err(&master->dev, "problem initializing queue\n");
- goto err_init_queue;
- }
- ret = spi_start_queue(master);
- if (ret) {
- dev_err(&master->dev, "problem starting queue\n");
- goto err_start_queue;
- }
- return 0;
- err_start_queue:
- err_init_queue:
- spi_destroy_queue(master);
- return ret;
- }
- static int spi_init_queue(struct spi_master *master)
- {
- struct sched_param param = { .sched_priority = MAX_RT_PRIO - 1 };
- INIT_LIST_HEAD(&master->queue);
- spin_lock_init(&master->queue_lock);
- master->running = false;
- master->busy = false;
- init_kthread_worker(&master->kworker);
- master->kworker_task = kthread_run(kthread_worker_fn,
- &master->kworker,
- dev_name(&master->dev));
- if (IS_ERR(master->kworker_task)) {
- dev_err(&master->dev, "failed to create message pump task\n");
- return -ENOMEM;
- }
- init_kthread_work(&master->pump_messages, spi_pump_messages);
- /*
- * Master config will indicate if this controller should run the
- * message pump with high (realtime) priority to reduce the transfer
- * latency on the bus by minimising the delay between a transfer
- * request and the scheduling of the message pump thread. Without this
- * setting the message pump thread will remain at default priority.
- */
- if (master->rt) {
- dev_info(&master->dev,
- "will run message pump with realtime priority\n");
- sched_setscheduler(master->kworker_task, SCHED_FIFO, ¶m);
- }
- return 0;
- }
init_kthread_worker(&master->kworker);初始化一个线程工作者,其结构中会包含当前线程工作项;主要初始化worker->lock、worker->work_list和worker->task。
master->kworker_task创建了一个线程;线程函数是kthread_worker_fn,该函数的参数是&master->kworker;线程name是dev_name(&master->dev)。
init_kthread_work(&master->pump_messages, spi_pump_messages);这个就是线程工作项了,其结构会依附一个线程工作者;这里初始化了&(work)->node,这个一个list,可能是要把自己挂在线程工作者的work_list上。
(work)->func = (fn);spi_pump_messages就是线程工作项的工作函数了。
master->rt是realtime标志,若设置表示高优先级的信息处理,有必要减少传输等待时间,把传输请求和信息pump线程之间的延时缩短最小;所以需要调用sched_setscheduler()改变thread的调度策略为实现级别。未设置保持默认优先级。
- static int spi_start_queue(struct spi_master *master)
- {
- unsigned long flags;
- spin_lock_irqsave(&master->queue_lock, flags);
- if (master->running || master->busy) {
- spin_unlock_irqrestore(&master->queue_lock, flags);
- return -EBUSY;
- }
- master->running = true;
- master->cur_msg = NULL;
- spin_unlock_irqrestore(&master->queue_lock, flags);
- queue_kthread_work(&master->kworker, &master->pump_messages);
- return 0;
- }
insert_kthread_work(worker, work, &worker->work_list);
- static void insert_kthread_work(struct kthread_worker *worker,
- struct kthread_work *work,
- struct list_head *pos)
- {
- lockdep_assert_held(&worker->lock);
- list_add_tail(&work->node, pos);
- work->worker = worker;
- if (likely(worker->task))
- wake_up_process(worker->task);
- }
该初始化的kwoker、work、task都初始好了;现在内核里有一个线程运行起来了。
- int kthread_worker_fn(void *worker_ptr)
- {
- struct kthread_worker *worker = worker_ptr;
- struct kthread_work *work;
- WARN_ON(worker->task);
- worker->task = current;
- repeat:
- set_current_state(TASK_INTERRUPTIBLE); /* mb paired w/ kthread_stop */
- if (kthread_should_stop()) {
- __set_current_state(TASK_RUNNING);
- spin_lock_irq(&worker->lock);
- worker->task = NULL;
- spin_unlock_irq(&worker->lock);
- return 0;
- }
- work = NULL;
- spin_lock_irq(&worker->lock);
- if (!list_empty(&worker->work_list)) {
- work = list_first_entry(&worker->work_list,
- struct kthread_work, node);
- list_del_init(&work->node);
- }
- worker->current_work = work;
- spin_unlock_irq(&worker->lock);
- if (work) {
- __set_current_state(TASK_RUNNING);
- work->func(work);
- } else if (!freezing(current))
- schedule();
- try_to_freeze();
- goto repeat;
- }
- static int spi_queued_transfer(struct spi_device *spi, struct spi_message *msg)
- {
- 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->running && !master->busy)
- queue_kthread_work(&master->kworker, &master->pump_messages);
- spin_unlock_irqrestore(&master->queue_lock, flags);
- return 0;
- }
1 把自己挂到&master->queue。
2 master->running为true确保master已经启动,master->busy为false确保mster不忙,queue_kthread_work()->insert_kthread_work()->(&work->node, pos),这样kthread_worker_fn线程函数里才能找到这个work,然后执行spi_pump_messages(),提交message;这个动作和spi_start_queue()差不多。
3 如果此时master->running为false,master未启动直接return了;如果此时已启动但是master->busy是true的,就只把msg挂到了&master->queue上,那什么时候queue_kthread_work呢?如果&master->queue一下挂了很多msg怎么办呢?按照排队的方式,就是每调用一次master->transfer,处理一个msg,是不阻塞的;同步机制需要另外实现。
- static void spi_pump_messages(struct kthread_work *work)
- {
- struct spi_master *master =
- container_of(work, struct spi_master, pump_messages);
- unsigned long flags;
- bool was_busy = false;
- int ret;
- /* Lock queue and check for queue work */
- spin_lock_irqsave(&master->queue_lock, flags);
- if (list_empty(&master->queue) || !master->running) {
- if (master->busy && master->unprepare_transfer_hardware) {
- ret = master->unprepare_transfer_hardware(master);
- if (ret) {
- spin_unlock_irqrestore(&master->queue_lock, flags);
- dev_err(&master->dev,
- "failed to unprepare transfer hardware\n");
- return;
- }
- }
- master->busy = false;
- spin_unlock_irqrestore(&master->queue_lock, flags);
- return;
- }
- /* Make sure we are not already running a message */
- if (master->cur_msg) {
- spin_unlock_irqrestore(&master->queue_lock, flags);
- return;
- }
- /* Extract head of queue */
- master->cur_msg =
- list_entry(master->queue.next, 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);
- 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");
- return;
- }
- }
- ret = master->transfer_one_message(master, master->cur_msg);
- if (ret) {
- dev_err(&master->dev,
- "failed to transfer one message from queue\n");
- return;
- }
- }
1 &master->queue为空说明没有message;master->running为false说明还未开始spi_start_queue(),这个master还未启动了;无论是没有message,还是未启动master->busy = false都是成立的,直接return。
2 如果master->cur_msg不为空说明已经有message在运行了,直接return,所以在驱动中message传输完需要master->cur_msg = NULL;;否则找一个message,怎么找的呢?到master->queue上找,(master->transfer = spi_queued_transfer就是这里挂上的)。找到后从master->queue上删除,否则会重复发送这个message。
3 master->busy则was_busy就为true,否则要更改master->busy从false到true。只根据was_busy来判断master->prepare_transfer_hardware()执行与否,为什么master->transfer_one_message()不用判断?难道要根据transfer_one_message()中check到master的状态直接return。
4 ret = master->transfer_one_message(master, master->cur_msg),这种方式的msg提交需要驱动实现 master->transfer_one_message()函数,别忘了master->cur_msg = NULL,否则下一个msg永远都别想提交了。
spi_queued_transfer机制总结:
1 内核提供了通用的master->transfer = spi_queued_transfer,其调用方式与驱动中实现该函数是一样的,只是现在驱动中需要实现的是master->transfer_one_message()。2 spi_queued_transfer负责把massage信息挂到&master->queue这个list上,然后&master->pump_messages这个work挂在&master->kworker的work_list上。
3 spi_master_initialize_queue()->spi_init_queue() run了一个线程,kthread_worker_fn会遍历&master->kworker->work_list上的work,执行其工作函数。
4 上述的工作函数是spi_init_queue()->init_kthread_work()初始化的,就是spi_pump_messages。
5 spi_pump_messages()中会遍历&master->queue找到message,提交message。
6 只有kthread_worker_fn是一直在跑的,spi_pump_messages()依赖于调用master->transfer;只有执行过spi_queued_transfer,work才会挂到worker上,spi_pump_messages()才能运行;有了message,spi_pump_messages()才能成功提交。
int spi_sync(struct spi_device *spi, struct spi_message *message)
{return __spi_sync(spi, message, 0);
}
int spi_async(struct spi_device *spi, struct spi_message *message)
spi的同步和异步传输,同步和异步的区别在哪里?spi异步:提交完message就马上返回;不会睡眠,可以在中断上下文等不可休眠的场合使用。但是需要complete同步机制,在wait_for_completion期间,不能操作message中的信息。spi同步:就是使用异步使用的一个实例,提交message后不会立即返回,应用complete进行休眠,一直等到处理完成。 spi_sync比较常用,需要注意的是在master->transfer(spi, message)函数中要调用message->complete(message->context)来更新完成量的状态,否则wait_for_completion永远也等不到同步信号;会一直睡下去的。__spi_sync()->spi_async_locked()->__spi_async()->(master->transfer(spi, message))
int spi_write_then_read(struct spi_device *spi, const void *txbuf, unsigned n_tx, void *rxbuf, unsigned n_rx)
spi同步写然后读;这个spi_sync()的一个应用实例。1 确定一个local_buf,这个buf里要存储的是txbuf+rxbuf的数据,要求(n_tx + n_rx)>=SPI_BUFSIZ(32)。如果小于也会扩展为32。
(n_tx + n_rx) > SPI_BUFSIZ,local_buf = kmalloc(max((unsigned)SPI_BUFSIZ, n_tx + n_rx), GFP_KERNEL);
否则,local_buf = buf;这个buf的malloc在spi_init()->buf = kmalloc(SPI_BUFSIZ, GFP_KERNEL);
2 初始化message,把spi_transfer x[2]挂在message上。
3 填充x[0].tx_buf和x[1].rx_buf结构,就是local_buf的前段和后段;x[0]是用于发送的,所以不需要rx_buf,同理x[1]不需要tx_buf。
4 spi_sync()提交message,memcpy(rxbuf, x[1].rx_buf, n_rx);。
5 善后处理,unlock、free。
三 spi总线注册
spi总线设备,注册等级2级。
spi_init()中malloc了一个buf,当n_tx + n_rx<= SPI_BUFSIZ时;
local_buf = buf;//local_buf 也是个中转站。
x[0].tx_buf = local_buf;
x[1].rx_buf = local_buf + n_tx;
status = bus_register(&spi_bus_type);注册一个子系统。
四 spi驱动程序开发
2 实现master->transfer或者master->transfer_one_message其中之一。
- SPI总线子系统
- spi子系统
- SPI子系统
- SPI总线
- SPI总线
- SPI总线
- spi总线
- SPI总线
- SPI总线
- SPI总线
- SPI总线
- SPI总线
- SPI总线
- spi总线
- SPI总线
- SPI总线
- spi总线
- spi总线
- poj1456(贪心)
- 2017程序员发展未来怎么样,听听大佬怎么讲
- NYOJ
- Java enum的用法详解
- Android 透明度数值
- SPI总线子系统
- STM32技巧: Keil错误提示“ File Not Found”
- Oracle之dual表
- InputStream Readers OutputStream Writers 不同
- Hololens开发中遇到的诡异问题
- 使用jetty在本地运行调试maven项目
- iOS 动态库(Dynamic框架)的创建以及引用添加(Embed Binary方式嵌入)
- LeetCode刷题(C++)——Longest Palindromic Substring(Medium)
- 浅谈对机器学习的理解