xref: /DragonOS/kernel/src/exception/irqdomain.rs (revision 3bc96fa4a9c01d91cddeb152fe78d6408351c29f)
1 use core::fmt::Debug;
2 
3 use alloc::{
4     string::String,
5     sync::{Arc, Weak},
6     vec::Vec,
7 };
8 use hashbrown::HashMap;
9 use system_error::SystemError;
10 
11 use crate::{
12     driver::{base::device::Device, open_firmware::device_node::DeviceNode},
13     libs::{rwlock::RwLock, spinlock::SpinLock},
14 };
15 
16 use super::{
17     irqchip::{IrqChipGeneric, IrqGcFlags},
18     HardwareIrqNumber, IrqNumber,
19 };
20 
21 /// 中断域
22 ///
23 /// 用于把硬件中断号翻译为软件中断号的映射的对象
24 ///
25 /// 参考 https://code.dragonos.org.cn/xref/linux-6.1.9/include/linux/irqdomain.h#164
26 #[allow(dead_code)]
27 #[derive(Debug)]
28 pub struct IrqDomain {
29     /// 中断域的名字 (二选一)
30     name: Option<&'static str>,
31     allocated_name: Option<String>,
32     /// 中断域的操作
33     ops: &'static dyn IrqDomainOps,
34     inner: SpinLock<InnerIrqDomain>,
35     /// 中断号反向映射
36     revmap: RwLock<IrqDomainRevMap>,
37 }
38 
39 #[allow(dead_code)]
40 #[derive(Debug)]
41 struct InnerIrqDomain {
42     /// host per irq_domain flags
43     flags: IrqDomainFlags,
44     /// The number of mapped interrupts
45     mapcount: u32,
46     bus_token: IrqDomainBusToken,
47     /// 指向 generic chip 列表的指针。
48     /// 有一个辅助函数用于为中断控制器驱动程序设置一个或
49     /// 多个 generic chip,该函数使用此指针并依赖于 generic chip 库。
50     generic_chip: Option<Arc<IrqDomainChipGeneric>>,
51     /// Pointer to a device that the domain represent, and that will be
52     /// used for power management purposes.
53     device: Option<Arc<dyn Device>>,
54     /// Pointer to parent irq_domain to support hierarchy irq_domains
55     parent: Option<Weak<IrqDomain>>,
56 }
57 
58 impl IrqDomain {
59     #[allow(dead_code)]
60     pub fn new(
61         name: Option<&'static str>,
62         allocated_name: Option<String>,
63         ops: &'static dyn IrqDomainOps,
64         flags: IrqDomainFlags,
65         bus_token: IrqDomainBusToken,
66     ) -> Option<Arc<Self>> {
67         if name.is_none() && allocated_name.is_none() {
68             return None;
69         }
70 
71         let x = IrqDomain {
72             name,
73             allocated_name,
74             ops,
75             inner: SpinLock::new(InnerIrqDomain {
76                 flags,
77                 mapcount: 0,
78                 bus_token,
79                 generic_chip: None,
80                 device: None,
81                 parent: None,
82             }),
83             revmap: RwLock::new(IrqDomainRevMap {
84                 map: HashMap::new(),
85                 hwirq_max: HardwareIrqNumber::new(0),
86             }),
87         };
88 
89         return Some(Arc::new(x));
90     }
91 }
92 
93 /// 参考 https://code.dragonos.org.cn/xref/linux-6.1.9/include/linux/irqdomain.h#190
94 #[allow(dead_code)]
95 #[derive(Debug)]
96 struct IrqDomainRevMap {
97     map: HashMap<HardwareIrqNumber, IrqNumber>,
98     hwirq_max: HardwareIrqNumber,
99 }
100 
101 bitflags! {
102     pub struct IrqDomainFlags: u32 {
103         /// Irq domain is hierarchical
104         const HIERARCHY = (1 << 0);
105         /// Irq domain name was allocated dynamically
106         const NAME_ALLOCATED = (1 << 1);
107         /// Irq domain is an IPI domain with virq per cpu
108         const IPI_PER_CPU = (1 << 2);
109         /// Irq domain is an IPI domain with single virq
110         const IPI_SINGLE = (1 << 3);
111         /// Irq domain implements MSIs
112         const MSI = (1 << 4);
113         /// Irq domain implements MSI remapping
114         const MSI_REMAP = (1 << 5);
115         /// Quirk to handle MSI implementations which do not provide masking
116         const MSI_NOMASK_QUIRK = (1 << 6);
117         /// Irq domain doesn't translate anything
118         const NO_MAP = (1 << 7);
119         /// Flags starting from IRQ_DOMAIN_FLAG_NONCORE are reserved
120         /// for implementation specific purposes and ignored by the core code
121         const NONCORE = (1 << 16);
122     }
123 }
124 
125 /// 如果多个域有相同的设备节点,但服务于不同的目的(例如,一个域用于PCI/MSI,另一个用于有线IRQs),
126 /// 它们可以使用特定于总线的token进行区分。预计大多数域只会携带`DomainBusAny`。
127 ///
128 /// 参考 https://code.dragonos.org.cn/xref/linux-6.1.9/include/linux/irqdomain.h#78
129 #[allow(dead_code)]
130 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
131 pub enum IrqDomainBusToken {
132     Any = 0,
133     Wired,
134     GenericMsi,
135     PciMsi,
136     PlatformMsi,
137     Nexus,
138     Ipi,
139     FslMcMsi,
140     TiSciIntaMsi,
141     Wakeup,
142     VmdMsi,
143 }
144 
145 /// IrqDomain的操作方法
146 ///
147 /// 参考 https://code.dragonos.org.cn/xref/linux-6.1.9/include/linux/irqdomain.h#107
148 pub trait IrqDomainOps: Debug + Send + Sync {
149     /// 匹配一个中断控制器设备节点到一个主机。
150     fn match_node(
151         &self,
152         irq_domain: &Arc<IrqDomain>,
153         device_node: &Arc<DeviceNode>,
154         bus_token: IrqDomainBusToken,
155     ) -> bool;
156 
157     /// 创建或更新一个虚拟中断号与一个硬件中断号之间的映射。
158     /// 对于给定的映射,这只会被调用一次。
159     fn map(
160         &self,
161         irq_domain: &Arc<IrqDomain>,
162         hwirq: HardwareIrqNumber,
163         virq: IrqNumber,
164     ) -> Result<(), SystemError>;
165 
166     /// 删除一个虚拟中断号与一个硬件中断号之间的映射。
167     fn unmap(&self, irq_domain: &Arc<IrqDomain>, virq: IrqNumber);
168 }
169 
170 #[allow(dead_code)]
171 #[derive(Debug)]
172 pub struct IrqDomainChipGeneric {
173     inner: SpinLock<InnerIrqDomainChipGeneric>,
174 }
175 
176 #[allow(dead_code)]
177 #[derive(Debug)]
178 struct InnerIrqDomainChipGeneric {
179     irqs_per_chip: u32,
180     flags_to_clear: IrqGcFlags,
181     flags_to_set: IrqGcFlags,
182     gc_flags: IrqGcFlags,
183     gc: Vec<Arc<IrqChipGeneric>>,
184 }
185