mirror of
https://github.com/RT-Thread/rt-thread.git
synced 2026-02-04 04:51:53 +00:00
[documentation][device_driver_model] Add comprehensive PIC documentation (EN) - critical module 1/6
Co-authored-by: BernardXiong <1241087+BernardXiong@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,966 @@
|
||||
# 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
|
||||
|
||||
```dts
|
||||
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
|
||||
|
||||
```dts
|
||||
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
|
||||
|
||||
```c
|
||||
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:**
|
||||
```c
|
||||
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
|
||||
|
||||
```c
|
||||
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
|
||||
|
||||
```c
|
||||
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:**
|
||||
```c
|
||||
/* 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
|
||||
|
||||
```c
|
||||
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
|
||||
|
||||
```c
|
||||
void rt_pic_irq_ack(int irq);
|
||||
```
|
||||
|
||||
Acknowledge an interrupt (required for some edge-triggered interrupts).
|
||||
|
||||
**Parameters:**
|
||||
- `irq`: IRQ number
|
||||
|
||||
#### rt_pic_irq_eoi
|
||||
|
||||
```c
|
||||
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
|
||||
|
||||
```c
|
||||
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:**
|
||||
```c
|
||||
/* 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
|
||||
|
||||
```c
|
||||
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
|
||||
|
||||
```c
|
||||
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:**
|
||||
```c
|
||||
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
|
||||
|
||||
```c
|
||||
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
|
||||
|
||||
```c
|
||||
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
|
||||
|
||||
```c
|
||||
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
|
||||
|
||||
```c
|
||||
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:**
|
||||
```c
|
||||
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
|
||||
|
||||
```c
|
||||
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
|
||||
|
||||
```c
|
||||
#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:
|
||||
|
||||
```c
|
||||
#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**:
|
||||
```c
|
||||
rt_pic_irq_disable(irq);
|
||||
rt_pic_detach_irq(irq, uid);
|
||||
```
|
||||
|
||||
2. **Handle spurious interrupts**:
|
||||
```c
|
||||
static void my_isr(int irq, void *param)
|
||||
{
|
||||
if (!check_interrupt_source())
|
||||
return; /* Spurious interrupt */
|
||||
|
||||
/* Handle interrupt */
|
||||
}
|
||||
```
|
||||
|
||||
3. **Use EOI for level-triggered interrupts**:
|
||||
```c
|
||||
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**:
|
||||
```c
|
||||
/* 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**:
|
||||
```c
|
||||
ret = rt_pic_linear_irq(pic, num_irqs);
|
||||
```
|
||||
|
||||
3. **Support cascading for hierarchical interrupt controllers**:
|
||||
```c
|
||||
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):
|
||||
```c
|
||||
#ifdef RT_USING_PIC_STATISTICS
|
||||
/* Framework tracks this automatically */
|
||||
#endif
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Interrupt Not Firing
|
||||
|
||||
1. **Check interrupt is enabled**:
|
||||
```c
|
||||
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**:
|
||||
```c
|
||||
/* 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**:
|
||||
```c
|
||||
rt_kprintf("Attached to IRQ %d\n", irq);
|
||||
```
|
||||
|
||||
2. **Check MAX_HANDLERS**:
|
||||
- Ensure IRQ < MAX_HANDLERS
|
||||
|
||||
3. **Confirm attachment succeeded**:
|
||||
```c
|
||||
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**:
|
||||
```c
|
||||
/* 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
|
||||
Reference in New Issue
Block a user