在linux驱动程序中定期调用spi_write

我正在为LCD显示器编写驱动程序。 根据应用笔记,我需要定期向命令写一个伪SPI写,以最大化其对比度。 为此,我设置了一个定时器,并尝试从定时器处理程序中编写对比度最大化的2字节伪命令。

但是,出现问题是因为s​​pi_write函数导致完整的内核崩溃并出现以下错误:

BUG: scheduling while atomic: swapper/1/0/0x00000102 

基于以下post: 如何解决“BUG:调度而primefaces:swapper / 0x00000103 / 0,CPU#0”? 在TSC2007驱动程序?

“primefaces调度”表示你试图在某个你不应该睡觉的地方睡觉 – 就像在受自旋锁保护的关键部分或中断处理程序中一样。

也许对spi_write的调用会触发某种睡眠行为。 禁止在这里睡觉是有意义的,因为基于堆栈跟踪,我看到代码处于软IRQ状态:

 [] (schedule_timeout) from [] (wait_for_common+0x114/0x15c) [] (wait_for_common) from [] (spi_sync+0x70/0x88) [] (spi_sync) from [] (plt_lcd_send_toggle_comin_cmd+0x7c/0x84 [plt_lcd_spi]) [] (plt_lcd_send_toggle_comin_cmd [plt_lcd_spi]) from [] (plt_lcd_timer_handler+0xc/0x2c [plt_lcd_spi]) [] (plt_lcd_timer_handler [plt_lcd_spi]) from [] (call_timer_fn.isra.26+0x20/0x30) [] (call_timer_fn.isra.26) from [] (run_timer_softirq+0x1ec/0x21c) [] (run_timer_softirq) from [] (__do_softirq+0xe0/0x1c8) [] (__do_softirq) from [] (irq_exit+0x58/0xac) [] (irq_exit) from [] (__handle_domain_irq+0x80/0xa0) [] (__handle_domain_irq) from [] (gic_handle_irq+0x38/0x5c) [] (gic_handle_irq) from [] (__irq_svc+0x40/0x74) 

我的问题是:实现这种周期性行为的正确方法是什么,SPI事务需要定期发生?

以下是计时器处理程序的摘要(虽然有一些手动修改,使名称更通用 – 我可能在过程中插入了一些拼写错误)

 static void lcd_timer_handler(unsigned long data) { // priv is a private structure that contains private info for the // driver: timer structure, timer timeout, context for the dummy command lcd_priv * const priv = (memlcd_priv *) data; unsigned char dummy[2]; dummy[0] = get_dummy_command_code(priv); dummy[1] = 0; // command must be terminated by a 0. // This is the call that causes the failure. // priv->spi is a struct spi_device * spi_write(priv->spi, ((const void *) dummy), 2); // Re-arm the timer mod_timer(&priv->timer, jiffies + priv->timer_timeout); } 

谢谢!

编辑:这是我在实施以下答案中的建议后想出的。 很好地工作,但使用delayed_work涉及必须跳过一些箍。

 typedef struct lcd_priv { /* private stuff: */ /* ... */ /* workqueue stuff: */ struct workqueue_struct * wq; struct delayed_work periodic_work; } lcd_priv; void lcd_periodic_work(struct work_struct * work_struct_ptr) { /* * Old documentation refers to a "data" pointer, but the API * no longer supports it. The developer is invited to put the work_struct * inside what would have been pointed to by "data" and to use container_of() * to recover this master struct. * See http://lwn.net/Articles/211279/ for more info. */ struct delayed_work * delayed = container_of(work_struct_ptr, struct delayed_work, work); lcd_priv * priv = container_of(delayed, lcd_priv, periodic_work); /* (prepare spi buffer in priv->spi_buf) */ /* ... */ /* This could be any activity that goes to sleep: */ spi_write(priv->spi, ((const void *) &priv->spi_buf[0]), 2); queue_delayed_work(priv->wq, &priv->periodic_work, TOGGLE_FREQUENCY); } static void lcd_start_workqueue(lcd_priv * const priv) { priv->wq = create_singlethread_workqueue("lcd_periodic_st_wq"); INIT_DELAYED_WORK(&priv->periodic_work, lcd_periodic_work); queue_delayed_work(priv->wq, &priv->periodic_work, TOGGLE_FREQUENCY); } static void lcd_stop_workqueue(lcd_priv * const priv) { destroy_workqueue(priv->wq); } 

如果查看spi_write源代码,它会调用spi_sync ,如果查看第一行spi_sync – > mutex_lock ,那么spi_write不能在中断内运行,也不能通过.configsysfs修复。

我的问题是:实现这种周期性行为的正确方法是什么,其中> SPI事务需要定期发生?

答案取决于您的硬件,您希望通过SPI发送数据的频率,您接受的延迟等。

你可以在spi_write回调中使用spi_write ,参见https://www.safaribooksonline.com/library/view/understanding-the-linux/0596005652/ch04s08.html

专门为这类事物设计的workqueue(运行在中断环境中无法运行的东西),

您也可以使用spi_async来安排通过spi进行写入。 spy_async可以在中断处理程序中调用。

如果延迟无关紧要,你也可以将事物移动到用户空间,并通过spidev接口写入SPI。