cnhnln 发表于 2006-4-11 21:07:41

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);


--
一个人只拥有此生此世是不够的,他还应该拥有诗意的世界。

                                              ——王小波


页: [1]
查看完整版本: PCI中断——转贴