copilot-swe-agent[bot] b10da2d554 [documentation][device_driver_model] Add comprehensive PIC documentation (EN) - critical module 1/6 1 Minggu lalu
..
README.md b10da2d554 [documentation][device_driver_model] Add comprehensive PIC documentation (EN) - critical module 1/6 1 Minggu lalu

README.md

Platform Interrupt Controller (PIC) Framework

Introduction

The Platform Interrupt Controller (PIC) framework provides a unified abstraction layer for managing hardware interrupts in RT-Thread's Device Driver Model. It supports various interrupt controllers including ARM GIC (Generic Interrupt Controller) v1/v2/v3, and provides comprehensive interrupt management capabilities including cascading, MSI support, and inter-processor interrupts (IPI).

Key Features

  • Unified Interrupt Management: Abstract interface for all interrupt controllers
  • Interrupt Cascading: Support for hierarchical interrupt routing
  • CPU Affinity: Configure which CPUs can handle specific interrupts
  • Priority Control: Set interrupt priorities for different IRQ sources
  • Trigger Modes: Support edge/level triggered interrupts
  • IPI Support: Inter-processor interrupt mechanisms for multi-core systems
  • MSI/MSI-X: Message Signaled Interrupts for PCIe devices
  • Statistics: Optional ISR execution time tracking

Architecture

The PIC framework consists of:

  1. PIC Core (pic.c): Central interrupt management and routing
  2. PIC IRQ (struct rt_pic_irq): Individual interrupt descriptor
  3. PIC Operations (struct rt_pic_ops): Hardware-specific callbacks
  4. ISR Management: Interrupt Service Routine registration and execution

    ┌──────────────────────────────────────────┐
    │         Application/Drivers              │
    │  (Call rt_pic_attach_irq, enable, etc.) │
    └──────────────────┬───────────────────────┘
                   │
    ┌──────────────────▼───────────────────────┐
    │       PIC Framework (pic.c)              │
    │  - IRQ allocation & management           │
    │  - ISR dispatch                          │
    │  - Cascading support                     │
    │  - Affinity & priority management        │
    └──────────────────┬───────────────────────┘
                   │
    ┌──────────────────▼───────────────────────┐
    │     PIC Operations (rt_pic_ops)          │
    │  - irq_enable/disable                    │
    │  - irq_mask/unmask                       │
    │  - irq_set_priority                      │
    │  - irq_set_affinity                      │
    │  - irq_send_ipi (for multi-core)         │
    └──────────────────┬───────────────────────┘
                   │
    ┌──────────────────▼───────────────────────┐
    │   Hardware Interrupt Controller          │
    │   (GICv2, GICv3, etc.)                   │
    └──────────────────────────────────────────┘
    

Kconfig Configuration

Location in menuconfig

RT-Thread Components
    └── Device Drivers
        └── Using Device Driver Model with DeviceTree (RT_USING_DM)
            └── Using Programmable Interrupt Controller (PIC) (RT_USING_PIC)

Configuration Options

RT_USING_PIC

  • Type: bool
  • Default: n
  • Description: Enable the Platform Interrupt Controller framework
  • Dependencies: RT_USING_DM, RT_USING_ADT, RT_USING_ADT_BITMAP
  • Purpose: Main switch for PIC support

RT_USING_PIC_STATISTICS

  • Type: bool
  • Default: n
  • Description: Enable ISR execution time statistics
  • Dependencies: RT_USING_PIC, RT_USING_KTIME, RT_USING_INTERRUPT_INFO
  • Purpose: Track min/max/average IRQ handling times for profiling

MAX_HANDLERS

  • Type: int
  • Range: 1-4294967294
  • Default: 256
  • Description: Maximum number of interrupt handlers the system can register
  • Purpose: Defines the size of the global IRQ hash table

RT_PIC_ARM_GIC

  • Type: bool
  • Default: n
  • Description: Enable ARM Generic Interrupt Controller v1/v2 support
  • Dependencies: RT_USING_PIC, RT_USING_OFW
  • Purpose: Support for ARM GICv1/v2 hardware

RT_PIC_ARM_GIC_V2M

  • Type: bool
  • Default: n
  • Description: Enable ARM GIC V2M (MSI support for GICv2)
  • Dependencies: RT_PIC_ARM_GIC, RT_PCI_MSI, RT_USING_OFW
  • Purpose: Message Signaled Interrupts on GICv2

RT_PIC_ARM_GIC_V3

  • Type: bool
  • Default: n
  • Description: Enable ARM Generic Interrupt Controller v3 support
  • Dependencies: RT_USING_PIC, RT_USING_OFW
  • Purpose: Support for ARM GICv3 hardware

RT_PIC_ARM_GIC_V3_ITS

  • Type: bool
  • Default: n
  • Description: Enable ARM GICv3 ITS (Interrupt Translation Service)
  • Dependencies: RT_PIC_ARM_GIC_V3, RT_PCI_MSI, RT_USING_OFW, RT_USING_ADT_REF
  • Purpose: MSI/MSI-X support for GICv3 via ITS

RT_PIC_ARM_GIC_V3_ITS_IRQ_MAX

  • Type: int
  • Default: 127 (64-bit), 63 (32-bit)
  • Description: Maximum IRQ number for GICv3 ITS
  • Dependencies: RT_PIC_ARM_GIC_V3_ITS
  • Purpose: Limit ITS-managed interrupt range

Device Tree Bindings

GICv2 Example

gic: interrupt-controller@08000000 {
    compatible = "arm,cortex-a15-gic", "arm,cortex-a9-gic";
    #interrupt-cells = <3>;
    #address-cells = <1>;
    interrupt-controller;
    reg = <0x08000000 0x1000>,  /* Distributor */
          <0x08010000 0x1000>;  /* CPU interface */
};

/* Consumer example: UART using GIC */
uart0: serial@09000000 {
    compatible = "arm,pl011", "arm,primecell";
    reg = <0x09000000 0x1000>;
    interrupts = <0 5 4>;  /* SPI, IRQ 5, IRQ_TYPE_LEVEL_HIGH */
    interrupt-parent = <&gic>;
};

GICv3 Example

gic: interrupt-controller@08000000 {
    compatible = "arm,gic-v3";
    #interrupt-cells = <3>;
    #address-cells = <2>;
    #size-cells = <2>;
    interrupt-controller;
    reg = <0x0 0x08000000 0 0x10000>,  /* Distributor */
          <0x0 0x080A0000 0 0xF60000>; /* Redistributor */
    
    gic_its: msi-controller@08020000 {
        compatible = "arm,gic-v3-its";
        msi-controller;
        #msi-cells = <1>;
        reg = <0x0 0x08020000 0 0x20000>;
    };
};

/* Consumer with interrupts */
timer {
    compatible = "arm,armv8-timer";
    interrupts = <1 13 0xff08>,  /* Physical Secure PPI */
                 <1 14 0xff08>,  /* Physical Non-Secure PPI */
                 <1 11 0xff08>,  /* Virtual PPI */
                 <1 10 0xff08>;  /* Hypervisor PPI */
    interrupt-parent = <&gic>;
};

Interrupt Cells Explanation

For ARM GIC, interrupts property has 3 cells:

  1. Type: 0 = SPI (Shared Peripheral Interrupt), 1 = PPI (Private Peripheral Interrupt)
  2. Number: IRQ number within the type (SPI: 0-987, PPI: 0-15)
  3. Flags: Trigger type and priority
    • IRQ_TYPE_EDGE_RISING = 1
    • IRQ_TYPE_EDGE_FALLING = 2
    • IRQ_TYPE_LEVEL_HIGH = 4
    • IRQ_TYPE_LEVEL_LOW = 8

Application API

IRQ Attachment

rt_pic_attach_irq

rt_err_t rt_pic_attach_irq(int irq, rt_isr_handler_t handler, void *uid, 
                            const char *name, int flags);

Attach an interrupt service routine to an IRQ.

Parameters:

  • irq: IRQ number to attach to
  • handler: ISR function pointer void (*handler)(int irq, void *param)
  • uid: User-defined parameter passed to the handler
  • name: Descriptive name for the interrupt (for debugging)
  • flags: Interrupt flags (currently unused, pass 0)

Returns:

  • RT_EOK: Success
  • -RT_EINVAL: Invalid IRQ number
  • -RT_ENOMEM: Out of memory

Example:

void uart_isr(int irq, void *param)
{
    struct uart_device *uart = (struct uart_device *)param;
    /* Handle interrupt */
    rt_kprintf("UART interrupt occurred\n");
}

/* In driver probe function */
static rt_err_t uart_probe(struct rt_platform_device *pdev)
{
    struct uart_device *uart;
    int irq;
    
    /* Get IRQ from device tree */
    irq = rt_platform_get_irq(pdev, 0);
    if (irq < 0)
        return irq;
    
    /* Attach ISR */
    rt_err_t ret = rt_pic_attach_irq(irq, uart_isr, uart, "uart0", 0);
    if (ret != RT_EOK)
        return ret;
    
    /* Enable the interrupt */
    rt_pic_irq_enable(irq);
    
    return RT_EOK;
}

rt_pic_detach_irq

rt_err_t rt_pic_detach_irq(int irq, void *uid);

Detach an interrupt service routine from an IRQ.

Parameters:

  • irq: IRQ number
  • uid: Must match the uid passed to rt_pic_attach_irq

Returns:

  • RT_EOK: Success
  • -RT_EINVAL: Invalid IRQ or UID mismatch

IRQ Control

rt_pic_irq_enable / rt_pic_irq_disable

void rt_pic_irq_enable(int irq);
void rt_pic_irq_disable(int irq);

Enable or disable an interrupt at the PIC level.

Parameters:

  • irq: IRQ number

Example:

/* Enable interrupt before starting operation */
rt_pic_irq_enable(uart_irq);

/* Disable during critical sections */
rt_pic_irq_disable(uart_irq);
/* ... critical code ... */
rt_pic_irq_enable(uart_irq);

rt_pic_irq_mask / rt_pic_irq_unmask

void rt_pic_irq_mask(int irq);
void rt_pic_irq_unmask(int irq);

Mask or unmask an interrupt (similar to enable/disable but may have different semantics on some hardware).

Parameters:

  • irq: IRQ number

rt_pic_irq_ack

void rt_pic_irq_ack(int irq);

Acknowledge an interrupt (required for some edge-triggered interrupts).

Parameters:

  • irq: IRQ number

rt_pic_irq_eoi

void rt_pic_irq_eoi(int irq);

Signal End-Of-Interrupt to the PIC.

Parameters:

  • irq: IRQ number

Priority Management

rt_pic_irq_set_priority

rt_err_t rt_pic_irq_set_priority(int irq, rt_uint32_t priority);

Set the priority of an interrupt.

Parameters:

  • irq: IRQ number
  • priority: Priority value (lower = higher priority typically)

Returns:

  • RT_EOK: Success
  • -RT_ENOSYS: Not supported by hardware

Example:

/* Set high priority for critical timer interrupt */
rt_pic_irq_set_priority(timer_irq, 0);

/* Set lower priority for UART */
rt_pic_irq_set_priority(uart_irq, 128);

rt_pic_irq_get_priority

rt_uint32_t rt_pic_irq_get_priority(int irq);

Get the current priority of an interrupt.

Parameters:

  • irq: IRQ number

Returns: Current priority value

CPU Affinity (Multi-core)

rt_pic_irq_set_affinity

rt_err_t rt_pic_irq_set_affinity(int irq, rt_bitmap_t *affinity);

Set which CPUs can handle a specific interrupt.

Parameters:

  • irq: IRQ number
  • affinity: Bitmap of allowed CPUs

Returns:

  • RT_EOK: Success
  • -RT_ENOSYS: Not supported

Example:

RT_IRQ_AFFINITY_DECLARE(cpumask);

/* Route interrupt to CPU 0 and CPU 2 */
rt_bitmap_clear(cpumask, RT_BITMAP_LEN(RT_CPUS_NR));
RT_IRQ_AFFINITY_SET(cpumask, 0);
RT_IRQ_AFFINITY_SET(cpumask, 2);
rt_pic_irq_set_affinity(eth_irq, cpumask);

rt_pic_irq_get_affinity

rt_err_t rt_pic_irq_get_affinity(int irq, rt_bitmap_t *out_affinity);

Get the current CPU affinity for an interrupt.

Parameters:

  • irq: IRQ number
  • out_affinity: Output bitmap

Returns:

  • RT_EOK: Success
  • -RT_EINVAL: Invalid IRQ

Trigger Mode

rt_pic_irq_set_triger_mode

rt_err_t rt_pic_irq_set_triger_mode(int irq, rt_uint32_t mode);

Set the trigger mode for an interrupt.

Parameters:

  • irq: IRQ number
  • mode: Trigger mode
    • RT_IRQ_MODE_EDGE_RISING: Rising edge
    • RT_IRQ_MODE_EDGE_FALLING: Falling edge
    • RT_IRQ_MODE_EDGE_BOTH: Both edges
    • RT_IRQ_MODE_LEVEL_HIGH: High level
    • RT_IRQ_MODE_LEVEL_LOW: Low level

Returns:

  • RT_EOK: Success
  • -RT_ENOSYS: Not supported

rt_pic_irq_get_triger_mode

rt_uint32_t rt_pic_irq_get_triger_mode(int irq);

Get the current trigger mode.

Parameters:

  • irq: IRQ number

Returns: Current trigger mode

Inter-Processor Interrupts (IPI)

rt_pic_irq_send_ipi

void rt_pic_irq_send_ipi(int irq, rt_bitmap_t *cpumask);

Send an inter-processor interrupt to specified CPUs.

Parameters:

  • irq: IPI number (typically in range 0-15)
  • cpumask: Bitmap of target CPUs

Example:

RT_IRQ_AFFINITY_DECLARE(targets);

/* Send IPI to CPU 1 */
rt_bitmap_clear(targets, RT_BITMAP_LEN(RT_CPUS_NR));
RT_IRQ_AFFINITY_SET(targets, 1);
rt_pic_irq_send_ipi(0, targets);  /* IPI 0 */

State Management

rt_pic_irq_set_state / rt_pic_irq_get_state

rt_err_t rt_pic_irq_set_state(int irq, int type, rt_bool_t state);
rt_err_t rt_pic_irq_get_state(int irq, int type, rt_bool_t *out_state);

Set or get interrupt state (pending, active, masked).

Parameters:

  • irq: IRQ number
  • type: State type
    • RT_IRQ_STATE_PENDING: Interrupt is pending
    • RT_IRQ_STATE_ACTIVE: Interrupt is being serviced
    • RT_IRQ_STATE_MASKED: Interrupt is masked
  • state / out_state: State value

Returns:

  • RT_EOK: Success
  • -RT_ENOSYS: Not supported

Complete Driver Example: GPIO Interrupt Handler

#include <rtthread.h>
#include <rtdevice.h>
#include <drivers/pic.h>
#include <drivers/platform.h>

struct gpio_int_device
{
    struct rt_device parent;
    void *base;
    int irq;
    rt_uint32_t pin_mask;
};

/* GPIO interrupt service routine */
static void gpio_isr(int irq, void *param)
{
    struct gpio_int_device *gpio = (struct gpio_int_device *)param;
    rt_uint32_t status;
    
    /* Read interrupt status register */
    status = readl(gpio->base + GPIO_INT_STATUS);
    
    /* Clear interrupt */
    writel(status, gpio->base + GPIO_INT_CLEAR);
    
    /* Handle each pin */
    for (int i = 0; i < 32; i++)
    {
        if (status & (1 << i))
        {
            rt_kprintf("GPIO pin %d triggered\n", i);
            /* Notify application or trigger event */
        }
    }
    
    /* Send EOI to PIC */
    rt_pic_irq_eoi(irq);
}

static rt_err_t gpio_int_probe(struct rt_platform_device *pdev)
{
    struct gpio_int_device *gpio;
    struct rt_device *dev = &pdev->parent;
    int irq;
    rt_err_t ret;
    
    gpio = rt_calloc(1, sizeof(*gpio));
    if (!gpio)
        return -RT_ENOMEM;
    
    /* Map registers */
    gpio->base = rt_dm_dev_iomap(dev, 0);
    if (!gpio->base)
    {
        ret = -RT_ERROR;
        goto err_free;
    }
    
    /* Get IRQ from device tree */
    irq = rt_platform_get_irq(pdev, 0);
    if (irq < 0)
    {
        ret = irq;
        goto err_iounmap;
    }
    gpio->irq = irq;
    
    /* Set interrupt trigger mode - rising edge */
    ret = rt_pic_irq_set_triger_mode(irq, RT_IRQ_MODE_EDGE_RISING);
    if (ret != RT_EOK && ret != -RT_ENOSYS)
    {
        rt_kprintf("Failed to set trigger mode: %d\n", ret);
        goto err_iounmap;
    }
    
    /* Set interrupt priority (high priority) */
    ret = rt_pic_irq_set_priority(irq, 32);
    if (ret != RT_EOK && ret != -RT_ENOSYS)
    {
        rt_kprintf("Failed to set priority: %d\n", ret);
    }
    
    /* Attach interrupt handler */
    ret = rt_pic_attach_irq(irq, gpio_isr, gpio, "gpio-int", 0);
    if (ret != RT_EOK)
    {
        rt_kprintf("Failed to attach IRQ: %d\n", ret);
        goto err_iounmap;
    }
    
    /* Configure GPIO pins for interrupt */
    writel(0xFFFFFFFF, gpio->base + GPIO_INT_ENABLE);  /* Enable all pins */
    writel(0x00000000, gpio->base + GPIO_INT_MASK);    /* Unmask all */
    
    /* Enable interrupt at PIC level */
    rt_pic_irq_enable(irq);
    
    /* Store device data */
    rt_platform_set_drvdata(pdev, gpio);
    
    rt_kprintf("GPIO interrupt controller probed successfully (IRQ %d)\n", irq);
    return RT_EOK;
    
err_iounmap:
    rt_dm_dev_iounmap(dev, gpio->base);
err_free:
    rt_free(gpio);
    return ret;
}

static rt_err_t gpio_int_remove(struct rt_platform_device *pdev)
{
    struct gpio_int_device *gpio = rt_platform_get_drvdata(pdev);
    
    /* Disable interrupt */
    rt_pic_irq_disable(gpio->irq);
    
    /* Detach handler */
    rt_pic_detach_irq(gpio->irq, gpio);
    
    /* Disable GPIO interrupts */
    writel(0x00000000, gpio->base + GPIO_INT_ENABLE);
    
    /* Cleanup */
    rt_dm_dev_iounmap(&pdev->parent, gpio->base);
    rt_free(gpio);
    
    return RT_EOK;
}

static const struct rt_ofw_node_id gpio_int_ofw_ids[] =
{
    { .compatible = "myvendor,gpio-interrupt-controller" },
    { /* sentinel */ }
};

static struct rt_platform_driver gpio_int_driver =
{
    .name = "gpio-int",
    .ids = gpio_int_ofw_ids,
    .probe = gpio_int_probe,
    .remove = gpio_int_remove,
};

static int gpio_int_drv_register(void)
{
    rt_platform_driver_register(&gpio_int_driver);
    return 0;
}
INIT_DEVICE_EXPORT(gpio_int_drv_register);

PIC Provider Implementation

For implementing a custom interrupt controller driver:

#include <rtthread.h>
#include <drivers/pic.h>

struct my_pic_data
{
    void *base;
    struct rt_pic pic;
};

static void my_pic_irq_enable(struct rt_pic_irq *pirq)
{
    struct rt_pic *pic = pirq->pic;
    struct my_pic_data *priv = pic->priv_data;
    
    /* Enable interrupt in hardware */
    writel(1 << pirq->hwirq, priv->base + MY_PIC_ENABLE_REG);
}

static void my_pic_irq_disable(struct rt_pic_irq *pirq)
{
    struct rt_pic *pic = pirq->pic;
    struct my_pic_data *priv = pic->priv_data;
    
    /* Disable interrupt in hardware */
    writel(1 << pirq->hwirq, priv->base + MY_PIC_DISABLE_REG);
}

static void my_pic_irq_ack(struct rt_pic_irq *pirq)
{
    struct rt_pic *pic = pirq->pic;
    struct my_pic_data *priv = pic->priv_data;
    
    /* Acknowledge interrupt */
    writel(1 << pirq->hwirq, priv->base + MY_PIC_ACK_REG);
}

static void my_pic_irq_eoi(struct rt_pic_irq *pirq)
{
    struct rt_pic *pic = pirq->pic;
    struct my_pic_data *priv = pic->priv_data;
    
    /* Send EOI signal */
    writel(1 << pirq->hwirq, priv->base + MY_PIC_EOI_REG);
}

static rt_err_t my_pic_irq_set_priority(struct rt_pic_irq *pirq, rt_uint32_t priority)
{
    struct rt_pic *pic = pirq->pic;
    struct my_pic_data *priv = pic->priv_data;
    
    /* Set priority in hardware register */
    writel(priority, priv->base + MY_PIC_PRIORITY_REG(pirq->hwirq));
    
    return RT_EOK;
}

static int my_pic_irq_map(struct rt_pic *pic, int hwirq, rt_uint32_t mode)
{
    int irq;
    
    /* Allocate software IRQ number */
    irq = pic->irq_start + hwirq;
    
    /* Configure trigger mode in hardware */
    /* ... */
    
    return irq;
}

static rt_err_t my_pic_irq_parse(struct rt_pic *pic, struct rt_ofw_cell_args *args,
                                  struct rt_pic_irq *out_pirq)
{
    if (args->args_count != 2)
        return -RT_EINVAL;
    
    /* Parse: <hwirq trigger_type> */
    out_pirq->hwirq = args->args[0];
    out_pirq->mode = args->args[1];
    
    return RT_EOK;
}

static const struct rt_pic_ops my_pic_ops =
{
    .name = "my-pic",
    .irq_enable = my_pic_irq_enable,
    .irq_disable = my_pic_irq_disable,
    .irq_ack = my_pic_irq_ack,
    .irq_eoi = my_pic_irq_eoi,
    .irq_set_priority = my_pic_irq_set_priority,
    .irq_map = my_pic_irq_map,
    .irq_parse = my_pic_irq_parse,
};

static rt_err_t my_pic_probe(struct rt_platform_device *pdev)
{
    struct my_pic_data *priv;
    rt_err_t ret;
    
    priv = rt_calloc(1, sizeof(*priv));
    if (!priv)
        return -RT_ENOMEM;
    
    /* Map registers */
    priv->base = rt_dm_dev_iomap(&pdev->parent, 0);
    if (!priv->base)
    {
        ret = -RT_ERROR;
        goto err_free;
    }
    
    /* Initialize PIC structure */
    priv->pic.ops = &my_pic_ops;
    priv->pic.priv_data = priv;
    
    /* Allocate IRQ range (32 interrupts) */
    ret = rt_pic_linear_irq(&priv->pic, 32);
    if (ret != RT_EOK)
    {
        rt_kprintf("Failed to allocate IRQs\n");
        goto err_iounmap;
    }
    
    /* Configure each IRQ */
    for (int i = 0; i < 32; i++)
    {
        rt_pic_config_irq(&priv->pic, i, i);  /* Map 1:1 */
    }
    
    /* Call user extensions if needed */
    rt_pic_user_extends(&priv->pic);
    
    /* Initialize hardware */
    writel(0xFFFFFFFF, priv->base + MY_PIC_DISABLE_REG);  /* Disable all */
    writel(0xFFFFFFFF, priv->base + MY_PIC_CLEAR_REG);    /* Clear pending */
    
    rt_platform_set_drvdata(pdev, priv);
    rt_kprintf("My PIC initialized with %d IRQs\n", 32);
    
    return RT_EOK;
    
err_iounmap:
    rt_dm_dev_iounmap(&pdev->parent, priv->base);
err_free:
    rt_free(priv);
    return ret;
}

static const struct rt_ofw_node_id my_pic_ofw_ids[] =
{
    { .compatible = "myvendor,my-pic" },
    { /* sentinel */ }
};

static struct rt_platform_driver my_pic_driver =
{
    .name = "my-pic",
    .ids = my_pic_ofw_ids,
    .probe = my_pic_probe,
};

RT_PIC_OFW_DECLARE(my_pic, my_pic_ofw_ids, my_pic_probe);

Best Practices

For Interrupt Consumers

  1. Always disable interrupts during cleanup:

    rt_pic_irq_disable(irq);
    rt_pic_detach_irq(irq, uid);
    
  2. Handle spurious interrupts:

    static void my_isr(int irq, void *param)
    {
       if (!check_interrupt_source())
           return;  /* Spurious interrupt */
           
       /* Handle interrupt */
    }
    
  3. Use EOI for level-triggered interrupts:

    static void my_isr(int irq, void *param)
    {
       handle_interrupt();
       clear_interrupt_source();  /* Clear in device first */
       rt_pic_irq_eoi(irq);       /* Then send EOI to PIC */
    }
    
  4. Set appropriate priorities:

    • Critical real-time interrupts: 0-31 (highest)
    • Normal device interrupts: 32-127
    • Low-priority interrupts: 128-255
  5. Consider CPU affinity on multi-core systems:

    /* Pin network interrupt to CPU 0 for better cache locality */
    RT_IRQ_AFFINITY_DECLARE(mask);
    rt_bitmap_clear(mask, RT_BITMAP_LEN(RT_CPUS_NR));
    RT_IRQ_AFFINITY_SET(mask, 0);
    rt_pic_irq_set_affinity(net_irq, mask);
    

For PIC Providers

  1. Implement minimum required operations:

    • irq_enable, irq_disable
    • irq_ack or irq_eoi
    • irq_map, irq_parse
  2. Use rt_pic_linear_irq for simple IRQ allocation:

    ret = rt_pic_linear_irq(pic, num_irqs);
    
  3. Support cascading for hierarchical interrupt controllers:

    struct rt_pic_irq *parent_pirq = rt_pic_find_pirq(parent_pic, parent_irq);
    rt_pic_cascade(child_pirq, parent_irq);
    
  4. Implement statistics support (optional):

    #ifdef RT_USING_PIC_STATISTICS
    /* Framework tracks this automatically */
    #endif
    

Troubleshooting

Interrupt Not Firing

  1. Check interrupt is enabled:

    rt_pic_irq_enable(irq);
    
  2. Verify device interrupt is not masked:

    • Check device-specific interrupt enable registers
    • Ensure interrupt source is configured correctly
  3. Confirm device tree configuration:

    • Verify interrupts property
    • Check interrupt-parent is correct
  4. Check trigger mode:

    • Edge vs. level triggered
    • Active high vs. active low

Interrupt Storms

  1. Always acknowledge/clear interrupts:

    /* Clear source BEFORE EOI */
    clear_device_interrupt();
    rt_pic_irq_eoi(irq);
    
  2. For level-triggered, clear condition:

    • Level-triggered interrupts re-assert if condition persists
    • Must clear the underlying condition in the device

ISR Not Called

  1. Verify IRQ number:

    rt_kprintf("Attached to IRQ %d\n", irq);
    
  2. Check MAX_HANDLERS:

    • Ensure IRQ < MAX_HANDLERS
  3. Confirm attachment succeeded:

    ret = rt_pic_attach_irq(irq, isr, param, "name", 0);
    if (ret != RT_EOK)
       rt_kprintf("Attach failed: %d\n", ret);
    

Priority/Affinity Not Working

  • Some PICs don't support priority or affinity
  • Check return value for -RT_ENOSYS
  • Verify hardware capabilities

Performance Considerations

  1. Keep ISRs short: Defer heavy processing to threads
  2. Minimize register access: Cache PIC state when possible
  3. Use shared interrupts carefully: Check device status first
  4. Consider IRQ coalescing: For high-rate interrupt sources
  5. Profile with statistics:

    /* Enable RT_USING_PIC_STATISTICS */
    /* Check /proc or debug output for timing data */
    

Related Modules

  • OFW (Device Tree): IRQ parsing and interrupt-parent resolution
  • Platform Device: rt_platform_get_irq for IRQ retrieval
  • PCI: MSI/MSI-X interrupt support via PIC
  • Pinctrl: GPIO interrupt configuration

References

  • ARM GIC Architecture Specification
  • Device Tree Interrupt Mapping Specification
  • RT-Thread Interrupt Management Documentation
  • components/drivers/pic/pic.c - Core implementation
  • components/drivers/include/drivers/pic.h - API header