经过前面第四篇文章(移植 u-boot-2020.07 到 iTOP-4412(四)支持中断),主要目的还是为了能在 u-boot 打开 UART 中断,改善 console 的手感。现在 UART 中断虽然已经正确打开使用,但是手感仍然极差,现象是接受到 RX 中断后,立即 TX 一个字母,可以观察到 RX 中断接收正常,但 TX 却没有按预期工作,可能会正确接收几个中断后才发送一次 TX。
该现象目前没找到 BUG 在哪,准备换一个串口转 usb 的线,如果是这个原因的话那真是太坑了(CH340 一生黑),之后更新结果。
后记:
果然 CH340 就是比较 LJ,换了 40 块钱的绿联 serial 转 usb 就好了。
所以把波特率改到了 230400 ,特别好用。
Exynos-4412 共支持 160 个中断源,当然我们这里只对 irq 进行支持。
对中断进行统一的管理,先注册中断 callback,进入中断后找到对应的 callback。
pt_regs 参数为 SVC/USR 模式的寄存器信息。
typedef int (*irq_cb_t) (int irq, void * data);
struct irq_entry
{
irq_cb_t callback;
void * data;
};
static struct irq_entry irq_list[MAX_IRQ_NUMBER];
int register_irq(int irq, irq_cb_t cb, void * data)
{
if(irq < 0 || irq >= MAX_IRQ_NUMBER)
return -EFAULT;
if(cb == NULL)
return -EFAULT;
if(irq_list[irq].callback != NULL)
return -EFAULT;
irq_list[irq].callback = cb;
irq_list[irq].data = data;
return 0;
}
int xhr4412_do_irq(struct pt_regs *pt_regs)
{
static ulongx irq_count = 0;
static ulongx irq_null_cb_count = 0;
struct irq_entry * entry;
int cpu = getl(ICCIAR_CPU0);
int irq = cpu & 0x3FF;
int ret;
cpu &= ~0x3FF;
irq_count++;
if(irq < MAX_IRQ_NUMBER) {
entry = &irq_list[irq];
if(entry->callback != NULL) {
ret = entry->callback(irq, entry->data);
} else {
printf("\nnull irq_cb, irq_count = (%lu/%lu) cpu = %d irq = %d\n",
++irq_null_cb_count, irq_count, cpu, irq);
ret = -ENOTTY;
}
} else {
printf("\nirq num is invalid, cpu = %d irq = %d\n", cpu, irq);
ret = -EFAULT;
}
setl(ICCEOIR_CPU0, cpu | irq);
return ret;
}
注册 UART 中断处理函数。初始化 UART ,打开 UART 中断使能,CPU 中断使能等。
这里要主要一下 u-boot 对 serial 的初始化时机,这个问题卡了我一些时间。可以多读一下 u-boot 的官方文档和源码。
同时涉及了设备树、u-boot DM 模型(类似 linux)、重定位等方面。
简单来说 serial 会在 common/board_f.c
初始化一次,然后在 common/board_r.c
再初始化一次,第一次初始化时,代码还没有重定位完毕,所以不能使用全局变量,这时不应该使用软件 buff。
这里我处理得并不好,只是简单的通过 global_data 来实现了区分两次初始化,更一般的做法是应该遵循 u-boot 的 DM 模型。
因为 buff 是通过队列的形式保存数据,所以中断和非中断都会对它操作,所以需要对共享的部分进行互斥。我自己实现了简单的 lock,不过 u-boot 也有提供 linux 类似的 spin_lock。
不知道如何测试是否正确,不过目前看起来还没有问题,如果发现问题也请联系我,谢谢。
static inline void spin_lock_xhr(void)
{
ulongx cpsr = 0;
ulongx mode;
__asm__ __volatile__ (
"mrs %0, cpsr\n\t"
"and %1, %0, #0x1F\n\t"
: "+r" (cpsr), "=r" (mode)
:);
if(mode == ARM_MOD_SVC || mode == ARM_MOD_USR)
{
__asm__ __volatile__ (
"orr %0, %0, #0x80\n\t"
"msr cpsr, %0\n\t" : : "r" (cpsr));
}
}
static inline void spin_unlock_xhr(void)
{
ulongx cpsr = 0;
ulongx mode;
__asm__ __volatile__ (
"mrs %0, cpsr\n\t"
"and %1, %0, #0x1F\n\t"
: "+r" (cpsr), "=r" (mode)
:);
if(mode == ARM_MOD_SVC || mode == ARM_MOD_USR)
{
__asm__ __volatile__ (
"bic %0, %0, #0x80\n\t"
"msr cpsr, %0\n\t" : : "r" (cpsr));
}
}
目前还是效果不佳,虽然是中断,但是感觉是硬件有问题?
效果不佳,先加一个配置选项,默认不选该功能。
果然,几块钱的 CH340 不太行,换了 40 块钱的绿联后就好了。。也不会感觉卡顿了。。