需要帮助处理共享ARM9 GPIO中断(Linux)上多个共享I2C MAX3107芯片。

3
我们的团队正在使用嵌入式处理器(Phytec LPC3180,ARM9)。我们设计了一个板子,在LPC3180的I2C总线上包括四个MAX3107串口芯片。如果有影响的话,我们正在运行内核2.6.10,这是该处理器的最新版本(对该产品的支持并不好; 我们不得不开发或修复Phytec提供的许多驱动程序,并且Phytec似乎对升级此产品的Linux代码(尤其是内核版本)没有兴趣。这很遗憾,因为LPC3180是一款不错的设备,特别是在不需要以太网且实际上不想要以太网的低功耗嵌入式产品的情况下(由于以太网控制器芯片的相关功耗)。目前安装的处理程序(由其他人开发)基于顶部处理程序和底部工作队列方法。

当I2C总线上的四个设备(MAX3107 UART芯片)之一接收到字符时,它会生成中断。所有四个MAX3107芯片的中断线是共享的(开漏下拉),并且该线连接到3180的GPIO引脚,该引脚配置为电平中断。当3017之一生成中断时,将运行一个处理程序,该处理程序执行以下处理(粗略):

spin_lock_irqsave();
disable_irq_nosync(irqno);
irq_enabled = 0;
irq_received = 1;
spin_unlock_irqrestore()
set_queued_work();  // Queue up work for all four devices for every interrupt
                    // because at this point we don't know which of the four
                    // 3107's generated the interrupt
return IRQ_HANDLED;

请注意,我发现有些令人不安的是,在离开上述代码之前,并没有重新启用中断。相反,驱动程序是这样编写的,即通过底半部工作队列任务重新启用中断(使用“enable_irq(LPC_IRQ_LINE)”函数调用)。由于工作队列任务不在中断上下文中运行,我认为它们可能会休眠,这对于中断处理程序来说可能是一个坏主意。
上述方法的原理如下: 1. 如果四个MAX3107串口芯片中的一个接收到字符并生成中断(例如),则中断处理程序需要确定实际引起中断的四个I2C设备中的哪一个。但是,显然不能在上半部分中断处理程序的上下文中读取I2C设备,因为I2C读取可能会休眠,这被认为不适合作为中断处理程序的上半部分。 2. 采用的方法来解决上述问题(即哪个设备引起了中断)是禁用中断并退出上半部分处理程序,之后非中断上下文代码可以查询I2C总线上的每个设备以确定哪个接收到了字符(因此生成了中断)。 3. 一旦底半部分处理程序确定了哪个设备引发了中断,底半部分代码将禁用该芯片上的中断,以便它不会重新触发LPC3180的中断线。在这样做后,它读取串行数据并退出。
主要问题似乎是在中断处理程序的顶半部分没有一种方法可以查询四个MAX3107串行通信芯片。如果顶半部分仅在返回之前重新启用中断,这将导致同一芯片再次生成中断,我认为会出现这样的情况:顶半部分禁用了中断,安排底半部分工作队列并禁用中断,但等到底半部分代码到达引起中断的芯片时,又出现了另一个中断,如此往复。
任何有关处理此驱动程序的建议都将不胜感激。我真的不喜欢在驱动程序的顶半部分允许禁用中断,但在退出顶半部分驱动程序之前未重新启用它的想法。这不太安全。
谢谢,
吉姆
附注:在阅读中,我发现线程中断是应对上述需求的一种方式(至少这是我根据网站文章 http://lwn.net/Articles/302043/ 的解释得出的)。我不确定Phytec提供的2.6.10内核是否包含线程中断功能。我打算在接下来的几天里研究一下。

Jim,你在这方面有什么进展吗?我正在处理一个相同的项目,需要在Linux下通过I2C实现多个UART要求。 - ckglobalroaming
@ckglobalroaming 我们小组中的一个人确实成功地开发了可用的I2C串口驱动程序,但这很困难。此外,我们偶尔会遇到I2C总线锁死的情况,需要完全断电才能恢复。我再也不会尝试在I2C下实现多个UART了。相反,只要有,我会使用SPI总线实现。 - Jim Luby
2个回答

1

如果您的代码编写正确,那么在处理先前的中断完成之前设备发出中断就不会有影响。您是正确的,您不希望在顶部执行阻塞操作,但在底部执行阻塞操作是可以接受的,实际上这也是它们存在的原因之一!

在这种情况下,我建议采用一种方法,即顶部仅安排底部,然后底部循环遍历所有4个设备并处理任何挂起的请求。可能需要处理多个设备,也可能不需要处理。

更新: 确实,您可能会通过负载测试来过载系统,并且软件可能需要优化以处理重负载。此外,我没有3180和四个3107(或类似)来测试这个问题,所以我是在理论上说话,但我不清楚为什么您需要完全禁用中断。

一般来说,当硬件设备断言中断时,直到当前中断被清除,它才不会断言另一个中断。因此,您有4个设备共享一个int线:

  1. 你的上半部分触发并将某些内容添加到工作队列中(即触发下半部分)。
  2. 你的下半部分扫描该接口线上的所有设备(即所有四个3107)。
  3. 如果其中一个设备引起了中断,那么你将读取所有必要的数据以完全处理数据(可能将其放入队列以进行更高级别的处理?)。
  4. 你“清除”当前设备上的中断。

当你清除中断时,设备可以触发另一个中断,但在此之前不能触发。

关于这个特定设备的更多细节:

似乎这个设备(MAX3107)有一个128个字的缓冲区,默认情况下每个单词后都会被中断。但是通过设置FIFO级别寄存器,似乎你应该能够更好地利用缓冲区。然后,只有在接收到那些字数后才会被中断(或者如果你将tx FIFO填满超过阈值的话,你应该减慢传输速度(即在软件中缓冲更多))。

看起来这个想法基本上是定期从设备中获取数据(可能每100毫秒或10毫秒或任何对您有效的时间间隔),然后只有当中断作为警告提示您已经越过阈值时,才会执行周期性函数,或增加其调用速率。


嗨,Chris - 我认为我担心的情况甚至会发生在任何 UART 收到单个字符的情况下。考虑以下情况:1. 字符到达 UART。2. 发生中断/顶半处理程序禁用中断,底半工作排队。3. 顶半重新启用中断。4. 在底半有机会与芯片通信并禁用中断之前,中断再次发生(即由 3107 持有的共享 IRQ 保持低电平)。似乎这种情况会一遍又一遍地发生,使得底半部分工作的排队永远不会执行;相反,顶半处理程序将继续被调用,等等。 - 谢谢 - Jim - Jim Luby
谢谢你,Chris!你最近的跟进帖子非常有帮助。我没有想到在驱动程序的顶部不禁用中断。这似乎是一个非常好的主意。- 保重 - Jim - Jim Luby
嗨,克里斯 - 假设我遵循您建议的四步处理方法。当我服务于3107时,使得任何/所有四个3107上的中断被清除,这应该会导致共享的IRQ线路变高,从而允许后续的3107生成中断。内核需要“看到”顶部驱动程序中的“return IRQ_HANDLED”调用之外的任何内容吗?也就是说,内核是否认为中断已经在运行“return_IRQ_HANDLED”时被处理了?我们仍然需要“return_IRQ_HANDLED”函数调用吗?-谢谢!-吉姆 - Jim Luby
是的,假设这些是该int线上唯一的设备,您应该能够无条件地返回IRQ_HANDLED。请参阅此链接,快速了解int处理程序返回值的概述:http://www.makelinux.net/books/lkd2/ch06lev1sec4 - Chris Desjardins
嗨,克里斯 - 好的,我会输入“接受”来关闭这个交互。我有更多关于GPIO引脚中断的问题,但为了保持清洁,我会创建一个特定于它们的新帖子。 - 再次感谢 - 吉姆 - Jim Luby

1

中断被启用和禁用,因为我们使用基于级别的中断,而不是基于边缘。这样做的后果在驱动程序头文件中明确解释,Jim,你已经有了。

基于级别的中断是必需的,以避免由于一个字符在一个UART上到达之后立即到达另一个UART而丢失边沿中断:服务第一个有效地消除了第二个,因此第二个字符将丢失。事实上,在初始的边沿中断版本的驱动程序中,一旦> 1个UART被激活,这正是发生的。

当前方案是否存在观察到的故障?

问候, 驱动程序作者(其他人)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接