Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1297431
  • 博文数量: 254
  • 博客积分: 1586
  • 博客等级: 上尉
  • 技术积分: 2295
  • 用 户 组: 普通用户
  • 注册时间: 2009-01-15 16:38
个人简介

linux学习中

文章分类

全部博文(254)

文章存档

2016年(6)

2015年(2)

2014年(74)

2013年(93)

2012年(12)

2011年(2)

2010年(51)

2009年(14)

分类: LINUX

2010-07-12 18:06:26

第一部分: 中断的初始化

在  init/main.c 文件中的 start_kernel() 函数中调用      init_IRQ() 函数。
在   arch/mips/kernel/irq.c 中。

void __init init_IRQ(void)
{
    arch_init_irq();

#ifdef CONFIG_KGDB
    if (kgdb_flag) {
        printk("Wait for gdb client connection ...\n");
        set_debug_traps();
        breakpoint();
    }
#endif
}



只调用了       arch_init_irq() 函数,这个是与体系结构相关的,移植相关的。

在  arch/mips/cq8401/common/irq.c


void __init arch_init_irq(void)
{
    unsigned long cp0_status;

    /* Mask all IRQs, except HW0, which is attached by INTC. Other irq
     * is not implemented in Phoenix core. */
    cp0_status = read_c0_status();
    cp0_status &= ~0x4;             /* clear ERL */
    cp0_status |= 0x00000400;   /* set IP2 */
    set_c0_status(cp0_status & 0xffff04ff);

    /* Init the choosen SOC's irq_desc. defined in irq_xxx.c */
    soc_init_irq();
/*
#ifdef CONFIG_KGDB
    printk("Setting debug traps - please connect the remote debugger.\n");

    set_debug_traps();

    // you may move this line to whereever you want
    breakpoint();
#endif
*/
}


其实最主要的函数是        soc_init_irq()  函数。


void __init soc_init_irq(void)
{
    int i;
    for (i=0;i<32;i++) {
        disable_intc_irq(i);
        irq_desc[i].chip= &intc_irq_type;
    }
    for (i=0;i         irq_desc[IRQ_DMA_0+i].chip = &dma_irq_type;
    enable_intc_irq(IRQ_DMAC);
    for (i=0;i         irq_desc[IRQ_PCIC_0+i].chip = &pcic_irq_type;
    enable_intc_irq(IRQ_PCIC);
    for (i=0;i         irq_desc[IRQ_GPIO_0+i].status = IRQ_DISABLED;
        irq_desc[IRQ_GPIO_0+i].action = NULL;
        irq_desc[IRQ_GPIO_0+i].depth = 1;
        irq_desc[IRQ_GPIO_0+i].chip = &gpio_irq_type;
    }
    enable_intc_irq(IRQ_GPIO0);
    enable_intc_irq(IRQ_GPIO1);
    enable_intc_irq(IRQ_GPIO2);
}

现在  NUM_PCID == 4,有错误,不应该只有  4 个中断,而是 


1        W
2        X
3        Y
4        Z

任意两个互联决定。





*********************************************************************************************************

第二部分:  PCI 中断号的分配

在  arch/mips/pci/pci.c 文件中的  pcibios_init()  函数。

static int __init pcibios_init(void)
{
    struct pci_controller *hose;
    struct pci_bus *bus;
    int next_busno;
    int need_domain_info = 0;
    /* Scan all of the recorded PCI controllers.  */
    for (next_busno = 0, hose = hose_head; hose; hose = hose->next) {

        if (!hose->iommu)
            PCI_DMA_BUS_IS_PHYS = 1;

        if (hose->get_busno && pci_probe_only)
            next_busno = (*hose->get_busno)();

        bus = pci_scan_bus(next_busno, hose->pci_ops, hose);
        hose->bus = bus;
        hose->need_domain_info = need_domain_info;
        if (bus) {
            next_busno = bus->subordinate + 1;
            /* Don't allow 8-bit bus number overflow inside the hose -
               reserve some space for bridges. */
            if (next_busno > 224) {
                next_busno = 0;
                need_domain_info = 1;
                                }
                      }
          }

    if (!pci_probe_only)
        pci_assign_unassigned_resources();
    pci_fixup_irqs(common_swizzle, pcibios_map_irq);

    return 0;
}


这个函数有   subsys_initcall(pcibios_init)  进行了声明,
使   pcibios_init() 函数  位于 代码段   的 initcall4 段中 。

宏  subsys_initcall(fn)的定义
 #define subsys_initcall(fn)     __define_initcall("4",fn,4)

在   start_kernel() 函数中调用了

start_kernel() 
----------->rest_init()
-------------> kernel_thread()
---------------------->kernel_init()
--------------------------->do_basic_setup()
---------------------------------> do_initcalls()

在这个      do_initcalls()  函数中调用了 在   initcall(n)(n从0 到7) 段中 的函数。


通过这样调用了   pcibios_init(void) 函数,给  PCI 设备分配了中断号。


***************************************************************************************************************************


在  pcibios_init(void) 函数中的最后调用   pci_fixup_irqs() 函数,确定了  PCI 设备的中断号。

  pci_fixup_irqs() 函数在  drivers/pci/setup-irq.c 文件中。

void __init
pci_fixup_irqs(u8 (*swizzle)(struct pci_dev *, u8 *),
           int (*map_irq)(struct pci_dev *, u8, u8))
{
    struct pci_dev *dev = NULL;
    while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) {
        pdev_fixup_irq(dev, swizzle, map_irq);
    }
}


pdev_fixup_irq() 函数同样位于    drivers/pci/setup-irq.c 文件中。

static void __init
pdev_fixup_irq(struct pci_dev *dev,
           u8 (*swizzle)(struct pci_dev *, u8 *),
           int (*map_irq)(struct pci_dev *, u8, u8))
{
    u8 pin, slot;
    int irq = 0;

    /* If this device is not on the primary bus, we need to figure out
       which interrupt pin it will come in on.   We know which slot it
       will come in on 'cos that slot is where the bridge is.   Each
       time the interrupt line passes through a PCI-PCI bridge we must
       apply the swizzle function.  */

    pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin);
    /* Cope with illegal. */
    if (pin > 4)
        pin = 1;

    if (pin != 0) {
        /* Follow the chain of bridges, swizzling as we go.  */
        slot = (*swizzle)(dev, &pin);

        irq = (*map_irq)(dev, slot, pin);
        if (irq == -1)
            irq = 0;
         }
    dev->irq = irq;

    pr_debug("PCI: fixup irq: (%s) got %d\n",
        dev->dev.kobj.name, dev->irq);

    /* Always tell the device, so the driver knows what is
       the real IRQ to use; the device does not use it. */
    pcibios_update_irq(dev, irq);
}


调用    pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin) 函数,
确定了   PCI 设备的 功能上的 配置空间中  intrupt pin的值,
这个值表明了这个功能设备使用那个  INT pin 和  PCI 控制器连接。

在  pdev_fixup_irq() 函数中调用   (*swizzle)(dev, &pin)   函数指针指向的函数,确定了
PCI 设备的  插槽号, 这个插槽号 和  IDSEL 引脚有关系。
表明了 IDSEL 引脚和  AD(n) 相连接。
最后在把   这个  slot 号,  mod 4 ,取余数,得到了  CPU 使用那个   INT pin 和  PCI 设备连接。

调用  (*map_irq)(dev, slot, pin)   函数指针指向的函数,确定  具体的  中断号。



***************************************************************************************************************************

由    pcibios_init(void) 函数调用的
pci_fixup_irqs(common_swizzle, pcibios_map_irq)  函数知道,

确定  slot 号的具体函数为  common_swizzle()函数
确定 中断号  的值 的函数是    pcibios_map_irq()  函数。



 common_swizzle()函数 位于   arch/mips/pci/pci.c 文件中。


static u8 __init common_swizzle(struct pci_dev *dev, u8 *pinp)
{
    u8 pin = *pinp;

    while (dev->bus->parent) {
        pin = bridge_swizzle(pin, PCI_SLOT(dev->devfn));
        /* Move up the chain of bridges. */
        dev = dev->bus->self; 
        }
    *pinp = pin;

    /* The slot is the slot of the last bridge. */
    return PCI_SLOT(dev->devfn);
}


 pcibios_map_irq()  函数 是移植相关的,有具体的硬件体系结构决定。

位于   arch/mips/cq8401/common/pci.c 文件中。

int __init pcibios_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
{
    dev->irq = 0xff;
    switch (slot) {
    case 12:
    case 13:
    default:
        dev->irq = IRQ_PCIC_0 + (80 - slot) % 4 + pin - 1;
        break;
        }

    pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq);
    return dev->irq;
}

计算出   中断号 之后,把中断号写入   配置空间的   interrupt line 寄存器中。

*********************************************************************************************************

第三部分:  中断相应

在   arch/mips/cq8401/common/irq.c  文件中。


asmlinkage void plat_irq_dispatch(void)
{
    int irq = 0;
    static unsigned long ipl = 0;

    ipl |= REG_INTC_IPR;

    if (ipl == 0)
        return;

    /* find out the real irq defined in irq_xxx.c */
    irq = soc_real_irq(&ipl);

    if (irq < 0)
        return;

    //REG_INTC_IPR = ipl;
    do_IRQ(irq);
}

调用      soc_real_irq(&ipl)  函数,查看具体发生中断的中断号。

int soc_real_irq(unsigned long *ipending)
{
    int irq = -1, i;

    for (i=31;i>=0;i--)
        if (*ipending & (1 << i)) {
            irq = i;
            break;
        }

    *ipending &= ~(1 << irq);
    switch (irq) {
    case IRQ_GPIO0:
        irq = __gpio_group_irq(0) + IRQ_GPIO_0;
        break;
    case IRQ_GPIO1:
        irq = __gpio_group_irq(1) + IRQ_GPIO_0 + 32;
        break;
    case IRQ_GPIO2:
        irq = __gpio_group_irq(2) + IRQ_GPIO_0 + 64;
        break;
    case IRQ_DMAC:
        irq = __dmac_get_irq() + IRQ_DMA_0;
        break;
    case IRQ_PCIC:
        irq = __pcic_get_irq() + IRQ_PCIC_0;
        break;
         }

    return irq;
}




*********************************************************************************************************


第四部分

1)PCI-to-Chip Mailbox Transmit Register
这些寄存器用来  PCI主设备发送信息到 CPU。
它们是被  PCI 主设备写入的,每个字节分别写入。
这些寄存器每一位的更新,都会自动的更新  mailbox status register 的对应位。

2)PCI-to-Chip Mailbox Receive Register
这些寄存器用来  CPU 接收  PCI主设备发送的信息。
它们可以由  CPU 每一个字节的分别读出。
当读出寄存器中的当前值时,自动更新  mailbox status register 的对应位。


3)Chip-to-PCI Mailbox Transmit Register
这些寄存器用来  CPU 发送信息到  PCI主设备。
它们可以由  CPU 每一个字节的分别写入。
这些寄存器每一位的更新,都会自动的更新  mailbox status register 的对应位。


4)Chip-to-PCI Mailbox Receive Register
这些寄存器用来  PCI主设备接收  CPU 发送的信息。
它们是被  PCI 主设备读出的,可以每个字节分别的读出。
当读出寄存器中的当前值时,自动更新  mailbox status register 的对应位。


5) PBC Mailbox Status Register
这个寄存器的每一位表明对应的   mailbox register 每个字节是否包含相应的未读数据。



6)PCI Mailbox Status Register
这个寄存器的每一位表明对应的   mailbox register 每个字节是否包含相应的未读数据。


7)PCI-to-Chip Doorbell Register
这个寄存器被 PCI 主设备和  CPU 以 host 和  satellite 模式联系使用。
如果  mailbox 的中断是没有使能的,任何的  P2C_DB (这个寄存器)位被设置,
都会产生一个中断。
PCI 主设备写  1  ,来设置每一位。 CPU 写  1  ,来清除每一位。
当  PCI 主设备和  CPU 读取这个寄存器时,会返回相应的值。


8)Chip-to-PCI Doorbell Register
这个寄存器用来  CPU 和  PCI 主设备联系使用。
如果  mailbox 的中断是使能的,这个寄存器的每一位置“1”,都会递送一个  pci_inta_n 中断
信号到  PCI 总线上,这个功能只用于  staellite 模式。
CPU 写  1  ,来设置每一位。PCI 主设备写  1  ,来清除每一位。
当  PCI 主设备和  CPU 读取这个寄存器时,会返回相应的值。



9)PBC Configuration Address Register
[31]配置使能位,0-关闭配置,1-使能配置
[23:16]PCI 总线号,为 8 位的,总共 256 个总线。
[15:11]为  5  位,PCI 设备号,可以为  32 个设备。
           在  type 0 配置中,它用来表明  32 个可能  PCI 设备的一个。
          用来在给定的 总线上选择  32 个设备中的一个 。
          这  type 1 配置中,它的内容会被拷贝到  PCI 的   AD 总线上。
[10:8]PCI 功能号。
         被用来指明目标板上的  8 个功能之一。
        每一个功能有单独的配置空间。
[7:2]PCI 配置寄存器的地址。
        被用来表明目标配置空间中的  64 个  32 位寄存器中的一个偏移地址。



10)PBC Configuration Data Register
这个寄存器包含配置空间寄存器的  读/写  的数据。


11)PBC Status Register
这个寄存器表明了  PBC  的状态。

[15:12]  PCI 总线中断的状态。共有  A、B、C、D 四个中断。
            0-表明没有中断,1-表明对应的中断产生。
[11]地址奇偶校验错误。
            0-没有地址校验错误。1-有地址校验错误。
[10]数据奇偶校验错误。
            0-没有校验错误。1-有校验错误。
[9]主设备失败
            0-没有 。1-有
[8]Target Abort
            0-没有。1-有。
[7:4]Mailbox 发送寄存器状态。
            0-Mailbox 不是空的,里面有有效的数据。
            1-Mailbox 是空的,里面没有有效的数据。
[3:0]Mailbox 接收寄存器状态。
            0-Mailbox 不是满的,有 1  到  3 个字节没有有效数据。
            1-Mailbox 是满的。




12) PCI Status Register
这个寄存器包含的位表明了  PCI 总线的状态。




13)PBC Interrupt Enable Register
这个寄存器控制 PBC 的中断。
[30]控制仲裁模式。
        0-内部仲裁模式。1-外部仲裁模式。
[6:3]控制 INTD、INTC、INTB、INTA 引脚的中断。
[2] Mailbox 的中断控制。
[1]总得中断使能位。
[0]PBC 模式位。
    0 - satellite 模式。
    1 - bost 模式。



13)PCI Interrupt Enable Register
这个寄存器控制  PCI 设备的中断。
[1]  mailbox 中断使能位。
    0-关闭  mailbox 中断。
    1- 使能  mailbox 中断。
[0]中断使能位。
    0-关闭中断。
    1-使能中断。



14)PCI-to-Chip Memory Address Translation Register
这个寄存器决定了  PCI 主设备访问 本地 MEM 的位置和大小。

[31:18]  MEM 的位置。
    定义了????????????????
[3:0]  MEM 的大小。
    这些位定义了  PCI Memory Base Register 0 定义的 MEM 的大小。
    




15)Chip-to-PCI Memory Translation Register
当产生内存读写命令时,这个寄存器的内容  映射为  AHB 总线地址的高   5  位,和 其余的  27 位
地址来形成  32 位 的PCI 地址。

C2P_MTR0 is used when accessing 0xA8000000-0xAFFFFFFF physical address space.
C2P_MTR1 is used when accessing 0xB0000000-0xB7FFFFFF physical address space.
C2P_MTR2 is used when accessing 0xB8000000-0xBFFFFFFF physical address space.


16)Chip-to-PCI I/O Translation Register
当产生内存读写命令时,这个寄存器的内容  映射为  AHB 总线地址的高   5  位,和 其余的  27 位
地址来形成  32 位 的PCI 地址。

C2P_IOTR is used when access 0xA0000000-0xA7FFFFFF physical address space.




17)PBC Interrupt Acknowledge Register
在  host 模式下,  CPU 在  PCI 总线上通过读   PCI_INTACK  寄存器来产生一个中断响应。
这个寄存器包含了个这个中断 向量号。????????
??????????????????????
阅读(3581) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~