PCI中断——转贴
最近老和总线纠缠不清,这不PCI又来捣乱,要说俺对于PCI总线的认识,那可是“相~当~”的无知。不过无知者无畏,本着“实践是检验真理的唯一标准”的原则,花了几天时
间在TIANO/Linux下一通折腾,小有心得,记载如下:
大部分是根据调试Linux/TIANO的现象自己的猜测,应该基本正确。
先从硬件说起:
对于PCI总线,一共有8个中断线(INTA~INTH),采用电平触发可共享。
对于PCI设备,DEVICE和SLOT含义等价。每个SLOT有4个中断引脚(PINA~PIND),
设计好的PCI板卡可以使用4个中断引脚之中的任意N个。显然只有板卡设计者知
道设备用了SLOT上的哪些中断引脚,告诉BIOS的方法就是在每个FUNCTION的PCI
配置空间IRQ Pin(0x3D)处显示用了哪个PIN脚,为0表示不产生中断。
对于每个PCI SLOT上的4个中断引脚如何对应系统PCI总线上的8条中断线,这个
是固定的平台相关的或者说只有BIOS知道。
例如,Bridgeport平台上,BUS4 DEVICE0的eth0(FUNC0)和eth1(FUNC1)分别占
用PINA和PIND中断引脚,在这个SLOT上PINA对应PCI中断线INTC,PINB对应INTD。
然后需要把INTA~INTH映射到8259A上,这个是通过南桥ICHx上的LPC(D31:F0)实
现的,0x60~0x63和0x68~0x6B的8个寄存器就记录了8个PIRQ到8259A的16个中断
号之间的映射关系。可以这样想象,ICH有8条PCI中断线和系统中所有PCI SLOTs
相连,一旦某条中断线发生了中断,ICH就根据相应的PIRQ映射寄存器的值,向CPU
报告。对于CPU来说,只知道经典的IRQx被触发了。
映射关系建好后,BIOS再根据映射后的IRQ号写入每个FUNCTION的IRQ Line(0x3C),
以次告诉设备驱动或者OS,设备用了哪个IRQ号。
再说软件:
首先,无论是传统BIOS还是TIANO,都需要建立一张表来告诉OS,PCI SLOT上的4个中
断引脚和系统PCI总线上的8条中断线之间关系。
PLATFORM\IntelEpg\Bridgeport\Dxe\LegacyBiosPlatform\LegacyBiosPlatform.c
变量mPirqTableHead就是这张表,由于之前少了条E1000的映射信息,才导致了前不久
网卡没有中断无法工作的情况。因此需要在表内加入
0x04,0x00,0x62,0xDEF8,0x63,0xDEF8,0x60,0xDEF8,0x61,0xDEF8,0x00,0x00,
其中4个0x6x数字表示相应的PINx所对应的PIRQ,例如PINA的0x62表示其对应INTC中断线
,
4个0xDEF8是掩码,表示16个8259A中断中那些不能被PIRQ拿来映射。
对于TIANO来说完成PIRQ到8259A的映射,实现在
CSM\LegacyBios\Dxe\LegacyPci.c的PciProgramAllInterruptLineRegisters()中
大概的思路是
先PLATFORM\IntelEpg\Bridgeport\Dxe\LegacyBiosPlatform\LegacyBiosPlatform.c
LegacyBiosPlatform->TranslatePirq()计算各个PIRQ的映射关系,也就是先读ICH对
应的PIRQ映射寄存器值,为0就为其分配8259A的一个空闲中断号,没空闲的就和别人复
用一个IRQ号。总之只要保证所有相等的PIRQ都对应一个IRQ号。
然后CHIPSET\IntelIch\LegacyInterrupt\Dxe\LegacyInterrupt.c
LegacyInterrupt->WritePirq()写入ICH对应的PIRQ映射寄存器中。
最后把各个IRQ号写入设备的IRQ Line(0x3C)中,把8259A的相应的中断线配成电平触发
模式。
假如,mPirqTableHead表少了E1000映射关系,也可以在Linux做手脚,
/arch/i386/pci/irq.c的pirq_get_info()硬编码一条映射信息返回。
Linux关于PIRQ主要集中在/arch/i386/pci/irq.c的pcibios_lookup_irq()中
也就是PIRQ TABLE和LPC(pirq_router_dev)以及dev->irq这3个信息间绕来绕去。
值得注意的是dev->irq
先是来源于/drivers/pci/probe.c的pci_setup_device(),后者通过调用
pci_read_irq()也就是读取设备的IRQ Line(0x3C)。
如果为0,在pcibios_lookup_irq()结尾处的代码,会把它变为其他具有相同PIRQ号的
设备的IRQ号,当然如果不是0,而且又不和其他具有相同PIRQ号的设备的IRQ号一致,
那就出错。另外,如果BIOS没有进行PIRQ到8259A的映射,pcibios_lookup_irq()中间
的代码会进行类似的算法进行分配。
另外,如果由于种种原因,导致设备的IRQ Line(0x3C)的值和实际ICH对应的PIRQ映射
寄存器值不一致,例如,TIANO的PIRQ表中没加入E1000的PIRQ信息,但是,引导Linux
时候调用了TIANO的PciProgramAllInterruptLineRegisters(),此时可以在
arch/i386/pci/fixup.c中加入
static void __devinit pci_fixup_e1000(struct pci_dev *d)
{
unsigned intirq;
if (PCI_FUNC(d->devfn) == 0)
irq = 0x0;//0x7;
else
irq = 0x0;//0x9;
pci_write_config_byte(d, PCI_INTERRUPT_LINE, irq);
}
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x1096, pci_fixup_e1000);
--
一个人只拥有此生此世是不够的,他还应该拥有诗意的世界。
——王小波
[m
页:
[1]