1 use core::fmt::Debug; 2 3 use alloc::{ 4 string::{String, ToString}, 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 exception::{irqdata::IrqLineStatus, irqdesc::irq_desc_manager, manage::irq_manager}, 14 libs::{ 15 rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard}, 16 spinlock::SpinLock, 17 }, 18 }; 19 20 use super::{ 21 dummychip::no_irq_chip, 22 irqchip::{IrqChip, IrqChipData, IrqChipGeneric, IrqGcFlags}, 23 irqdata::{IrqData, IrqHandlerData}, 24 irqdesc::{IrqDesc, IrqFlowHandler}, 25 HardwareIrqNumber, IrqNumber, 26 }; 27 28 static mut IRQ_DOMAIN_MANAGER: Option<Arc<IrqDomainManager>> = None; 29 30 /// 获取中断域管理器的引用 31 #[inline(always)] 32 pub fn irq_domain_manager() -> &'static Arc<IrqDomainManager> { 33 unsafe { IRQ_DOMAIN_MANAGER.as_ref().unwrap() } 34 } 35 36 pub(super) fn irq_domain_manager_init() { 37 unsafe { 38 IRQ_DOMAIN_MANAGER = Some(Arc::new(IrqDomainManager::new())); 39 } 40 } 41 /// 中断域管理器 42 pub struct IrqDomainManager { 43 domains: SpinLock<Vec<Arc<IrqDomain>>>, 44 inner: RwLock<InnerIrqDomainManager>, 45 } 46 47 impl IrqDomainManager { 48 pub fn new() -> IrqDomainManager { 49 IrqDomainManager { 50 domains: SpinLock::new(Vec::new()), 51 inner: RwLock::new(InnerIrqDomainManager { 52 default_domain: None, 53 }), 54 } 55 } 56 57 /// 创建一个新的线性映射的irqdomain, 并将其添加到irqdomain管理器中 58 /// 59 /// 创建的irqdomain,中断号是线性的,即从0开始,依次递增 60 /// 61 /// ## 参数 62 /// 63 /// - `name` - 中断域的名字 64 /// - `ops` - 中断域的操作 65 /// - `irq_size` - 中断号的数量 66 #[allow(dead_code)] 67 pub fn create_and_add_linear( 68 &self, 69 name: String, 70 ops: &'static dyn IrqDomainOps, 71 irq_size: u32, 72 ) -> Option<Arc<IrqDomain>> { 73 self.create_and_add( 74 name, 75 ops, 76 IrqNumber::new(0), 77 HardwareIrqNumber::new(0), 78 irq_size, 79 ) 80 } 81 82 /// 创建一个新的irqdomain, 并将其添加到irqdomain管理器中 83 /// 84 /// ## 参数 85 /// 86 /// - `name` - 中断域的名字 87 /// - `ops` - 中断域的操作 88 /// - `first_irq` - 起始软件中断号 89 /// - `first_hwirq` - 起始硬件中断号 90 /// - `irq_size` - 中断号的数量 91 /// 92 /// 参考 https://code.dragonos.org.cn/xref/linux-6.1.9/kernel/irq/irqdomain.c?fi=__irq_domain_add#139 93 pub fn create_and_add( 94 &self, 95 name: String, 96 ops: &'static dyn IrqDomainOps, 97 first_irq: IrqNumber, 98 first_hwirq: HardwareIrqNumber, 99 irq_size: u32, 100 ) -> Option<Arc<IrqDomain>> { 101 let domain = IrqDomain::new( 102 None, 103 Some(name), 104 ops, 105 IrqDomainFlags::NAME_ALLOCATED, 106 IrqDomainBusToken::Any, 107 first_irq + irq_size, 108 first_hwirq + irq_size, 109 )?; 110 111 self.add_domain(domain.clone()); 112 113 self.domain_associate_many(&domain, first_irq, first_hwirq, irq_size); 114 115 return Some(domain); 116 } 117 118 fn add_domain(&self, domain: Arc<IrqDomain>) { 119 self.domains.lock_irqsave().push(domain); 120 } 121 122 #[allow(dead_code)] 123 pub fn remove_domain(&self, domain: &Arc<IrqDomain>) { 124 let mut domains = self.domains.lock_irqsave(); 125 let index = domains 126 .iter() 127 .position(|x| Arc::ptr_eq(x, domain)) 128 .expect("domain not found"); 129 domains.remove(index); 130 } 131 132 /// 获取默认的中断域 133 #[allow(dead_code)] 134 pub fn default_domain(&self) -> Option<Arc<IrqDomain>> { 135 self.inner.read().default_domain.clone() 136 } 137 138 /// 设置默认的中断域 139 /// 140 /// 在创建IRQ映射的时候,如果没有指定中断域,就会使用默认的中断域 141 pub fn set_default_domain(&self, domain: Arc<IrqDomain>) { 142 self.inner.write_irqsave().default_domain = Some(domain); 143 } 144 145 /// 将指定范围的硬件中断号与软件中断号一一对应的关联起来 146 /// 147 /// ## 参数 148 /// 149 /// - `domain` - 中断域 150 /// - `first_irq` - 起始软件中断号 151 /// - `first_hwirq` - 起始硬件中断号 152 /// - `count` - 数量 153 pub fn domain_associate_many( 154 &self, 155 domain: &Arc<IrqDomain>, 156 first_irq: IrqNumber, 157 first_hwirq: HardwareIrqNumber, 158 count: u32, 159 ) { 160 for i in 0..count { 161 if let Err(e) = self.domain_associate(domain, first_irq + i, first_hwirq + i) { 162 kwarn!("domain associate failed: {:?}, domain '{:?}' didn't like hwirq {} to virq {} mapping.", e, domain.name(), (first_hwirq + i).data(), (first_irq + i).data()); 163 } 164 } 165 } 166 167 /// 将一个硬件中断号与一个软件中断号关联起来 168 /// 169 /// 参考 https://code.dragonos.org.cn/xref/linux-6.1.9/kernel/irq/irqdomain.c#562 170 pub fn domain_associate( 171 &self, 172 domain: &Arc<IrqDomain>, 173 irq: IrqNumber, 174 hwirq: HardwareIrqNumber, 175 ) -> Result<(), SystemError> { 176 if hwirq >= domain.revmap.read_irqsave().hwirq_max { 177 kwarn!( 178 "hwirq {} is out of range for domain {:?}", 179 hwirq.data(), 180 domain.name() 181 ); 182 return Err(SystemError::EINVAL); 183 } 184 let irq_data = irq_desc_manager() 185 .lookup(irq) 186 .ok_or_else(|| { 187 kwarn!("irq_desc not found for irq {}", irq.data()); 188 SystemError::EINVAL 189 })? 190 .irq_data(); 191 if irq_data.domain().is_some() { 192 kwarn!( 193 "irq {} is already associated with domain {:?}", 194 irq.data(), 195 irq_data.domain().unwrap().name() 196 ); 197 return Err(SystemError::EINVAL); 198 } 199 200 let mut irq_data_guard = irq_data.inner(); 201 irq_data_guard.set_hwirq(hwirq); 202 irq_data_guard.set_domain(Some(domain.clone())); 203 drop(irq_data_guard); 204 let r = domain.ops.map(domain, hwirq, irq); 205 if let Err(e) = r { 206 if e != SystemError::ENOSYS { 207 if e != SystemError::EPERM { 208 kinfo!("domain associate failed: {:?}, domain '{:?}' didn't like hwirq {} to virq {} mapping.", e, domain.name(), hwirq.data(), irq.data()); 209 } 210 let mut irq_data_guard = irq_data.inner(); 211 irq_data_guard.set_domain(None); 212 irq_data_guard.set_hwirq(HardwareIrqNumber::new(0)); 213 return Err(e); 214 } 215 } 216 217 if domain.name().is_none() { 218 let chip = irq_data.chip_info_read_irqsave().chip(); 219 domain.set_name(chip.name().to_string()); 220 } 221 222 self.irq_domain_set_mapping(domain, hwirq, irq_data); 223 224 irq_manager().irq_clear_status_flags(irq, IrqLineStatus::IRQ_NOREQUEST)?; 225 226 return Ok(()); 227 } 228 229 fn irq_domain_set_mapping( 230 &self, 231 domain: &Arc<IrqDomain>, 232 hwirq: HardwareIrqNumber, 233 irq_data: Arc<IrqData>, 234 ) { 235 if domain.no_map() { 236 return; 237 } 238 239 domain.revmap.write_irqsave().insert(hwirq, irq_data); 240 } 241 /// 递归调用 domain_ops->activate 以激活中断 242 /// 243 /// ## 参数 244 /// 245 /// - irq_data: 与中断关联的最外层 irq_data 246 /// - reserve: 如果为true,则仅预留一个中断向量,而不是分配一个 247 /// 248 /// 这是调用 domain_ops->activate 以编程中断控制器的第二步,以便中断实际上可以被传递。 249 pub fn activate_irq(&self, irq_data: &Arc<IrqData>, reserve: bool) -> Result<(), SystemError> { 250 let mut r = Ok(()); 251 if !irq_data.common_data().status().is_activated() { 252 r = self.do_activate_irq(Some(irq_data.clone()), reserve); 253 } 254 255 if r.is_err() { 256 irq_data.common_data().status().set_activated(); 257 } 258 259 return r; 260 } 261 262 #[inline(never)] 263 fn do_activate_irq( 264 &self, 265 irq_data: Option<Arc<IrqData>>, 266 reserve: bool, 267 ) -> Result<(), SystemError> { 268 let mut r = Ok(()); 269 270 if let Some(irq_data) = irq_data { 271 if let Some(domain) = irq_data.domain() { 272 let parent_data = irq_data.parent_data().and_then(|x| x.upgrade()); 273 if let Some(parent_data) = parent_data.clone() { 274 r = self.do_activate_irq(Some(parent_data), reserve); 275 } 276 277 if r.is_err() { 278 let tmpr = domain.ops.activate(&domain, &irq_data, reserve); 279 if let Err(e) = tmpr { 280 if e != SystemError::ENOSYS && parent_data.is_some() { 281 self.do_deactivate_irq(parent_data); 282 } 283 } 284 } 285 } 286 } 287 288 return r; 289 } 290 #[allow(clippy::only_used_in_recursion)] 291 fn do_deactivate_irq(&self, irq_data: Option<Arc<IrqData>>) { 292 if let Some(irq_data) = irq_data { 293 if let Some(domain) = irq_data.domain() { 294 domain.ops.deactivate(&domain, &irq_data); 295 let pp = irq_data.parent_data().and_then(|x| x.upgrade()); 296 297 if pp.is_some() { 298 self.do_deactivate_irq(pp); 299 } 300 } 301 } 302 } 303 304 /// `irq_domain_set_info` - 在 @domain 中为 @virq 设置完整的数据 305 /// 306 /// ## 参数 307 /// 308 /// - `domain`: 要匹配的中断域 309 /// - `virq`: IRQ号 310 /// - `hwirq`: 硬件中断号 311 /// - `chip`: 相关的中断芯片 312 /// - `chip_data`: 相关的中断芯片数据 313 /// - `handler`: 中断流处理器 314 /// - `handler_data`: 中断流处理程序数据 315 /// - `handler_name`: 中断处理程序名称 316 #[allow(clippy::too_many_arguments)] 317 pub fn domain_set_info( 318 &self, 319 domain: &Arc<IrqDomain>, 320 virq: IrqNumber, 321 hwirq: HardwareIrqNumber, 322 chip: Arc<dyn IrqChip>, 323 chip_data: Option<Arc<dyn IrqChipData>>, 324 flow_handler: &'static dyn IrqFlowHandler, 325 handler_data: Option<Arc<dyn IrqHandlerData>>, 326 handler_name: Option<String>, 327 ) { 328 let r = self.domain_set_hwirq_and_chip(domain, virq, hwirq, Some(chip), chip_data); 329 if r.is_err() { 330 return; 331 } 332 irq_manager().__irq_set_handler(virq, flow_handler, false, handler_name); 333 irq_manager().irq_set_handler_data(virq, handler_data).ok(); 334 } 335 336 /// `domain_set_hwirq_and_chip` - 在 @domain 中为 @virq 设置 hwirq 和 irqchip 337 /// 338 /// ## 参数 339 /// 340 /// - `domain`: 要匹配的中断域 341 /// - `virq`: IRQ号 342 /// - `hwirq`: hwirq号 343 /// - `chip`: 相关的中断芯片 344 /// - `chip_data`: 相关的芯片数据 345 pub fn domain_set_hwirq_and_chip( 346 &self, 347 domain: &Arc<IrqDomain>, 348 virq: IrqNumber, 349 hwirq: HardwareIrqNumber, 350 chip: Option<Arc<dyn IrqChip>>, 351 chip_data: Option<Arc<dyn IrqChipData>>, 352 ) -> Result<(), SystemError> { 353 let irq_data: Arc<IrqData> = self 354 .domain_get_irq_data(domain, virq) 355 .ok_or(SystemError::ENOENT)?; 356 let mut inner = irq_data.inner(); 357 let mut chip_info = irq_data.chip_info_write_irqsave(); 358 359 inner.set_hwirq(hwirq); 360 if let Some(chip) = chip { 361 chip_info.set_chip(Some(chip)); 362 } else { 363 chip_info.set_chip(Some(no_irq_chip())); 364 }; 365 366 chip_info.set_chip_data(chip_data); 367 368 return Ok(()); 369 } 370 371 /// `irq_domain_get_irq_data` - 获取与 @virq 和 @domain 关联的 irq_data 372 /// 373 /// ## 参数 374 /// 375 /// - `domain`: 要匹配的域 376 /// - `virq`: 要获取 irq_data 的IRQ号 377 pub fn domain_get_irq_data( 378 &self, 379 domain: &Arc<IrqDomain>, 380 virq: IrqNumber, 381 ) -> Option<Arc<IrqData>> { 382 let desc = irq_desc_manager().lookup(virq)?; 383 let mut irq_data = Some(desc.irq_data()); 384 385 while irq_data.is_some() { 386 let dt = irq_data.unwrap(); 387 if dt.domain().is_some() && Arc::ptr_eq(dt.domain().as_ref().unwrap(), domain) { 388 return Some(dt); 389 } 390 irq_data = dt.parent_data().and_then(|x| x.upgrade()); 391 } 392 393 return None; 394 } 395 396 /// `resolve_irq_mapping` - 从硬件中断号找到中断号。 397 /// 398 /// ## 参数 399 /// 400 /// - `domain`: 拥有此硬件中断的域 401 /// - `hwirq`: 该域空间中的硬件中断号 402 /// - `irq`: 如果需要,可选的指针以返回Linux中断 403 /// 404 /// ## 返回 405 /// 406 /// 返回一个元组,包含中断描述符和中断号 407 pub fn resolve_irq_mapping( 408 &self, 409 mut domain: Option<Arc<IrqDomain>>, 410 hwirq: HardwareIrqNumber, 411 ) -> Result<(Arc<IrqDesc>, IrqNumber), SystemError> { 412 if domain.is_none() { 413 domain = Some(self.default_domain().ok_or(SystemError::EINVAL)?); 414 } 415 416 let domain = domain.unwrap(); 417 418 if domain.no_map() { 419 if hwirq < domain.revmap_read_irqsave().hwirq_max { 420 let irq_desc = irq_desc_manager() 421 .lookup(IrqNumber::new(hwirq.data())) 422 .ok_or(SystemError::EINVAL)?; 423 if irq_desc.irq_data().hardware_irq() == hwirq { 424 let irq = irq_desc.irq_data().irq(); 425 return Ok((irq_desc, irq)); 426 } 427 } 428 429 return Err(SystemError::EINVAL); 430 } 431 432 let revmap = domain.revmap_read_irqsave(); 433 let irq_data = revmap.lookup(hwirq).ok_or(SystemError::EINVAL)?; 434 let irq_desc = irq_data.irq_desc().unwrap(); 435 return Ok((irq_desc, irq_data.irq())); 436 } 437 } 438 439 struct InnerIrqDomainManager { 440 default_domain: Option<Arc<IrqDomain>>, 441 } 442 443 /// 中断域 444 /// 445 /// 用于把硬件中断号翻译为软件中断号的映射的对象 446 /// 447 /// 参考 https://code.dragonos.org.cn/xref/linux-6.1.9/include/linux/irqdomain.h#164 448 #[allow(dead_code)] 449 #[derive(Debug)] 450 pub struct IrqDomain { 451 /// 中断域的名字 (二选一) 452 name: Option<&'static str>, 453 allocated_name: SpinLock<Option<String>>, 454 /// 中断域的操作 455 ops: &'static dyn IrqDomainOps, 456 inner: SpinLock<InnerIrqDomain>, 457 /// 中断号反向映射 458 revmap: RwLock<IrqDomainRevMap>, 459 } 460 461 #[allow(dead_code)] 462 #[derive(Debug)] 463 struct InnerIrqDomain { 464 /// this field not touched by the core code 465 host_data: Option<Arc<dyn IrqChipData>>, 466 /// host per irq_domain flags 467 flags: IrqDomainFlags, 468 /// The number of mapped interrupts 469 mapcount: u32, 470 bus_token: IrqDomainBusToken, 471 /// 指向 generic chip 列表的指针。 472 /// 有一个辅助函数用于为中断控制器驱动程序设置一个或 473 /// 多个 generic chip,该函数使用此指针并依赖于 generic chip 库。 474 generic_chip: Option<Arc<IrqDomainChipGeneric>>, 475 /// Pointer to a device that the domain represent, and that will be 476 /// used for power management purposes. 477 device: Option<Arc<dyn Device>>, 478 /// Pointer to parent irq_domain to support hierarchy irq_domains 479 parent: Option<Weak<IrqDomain>>, 480 } 481 482 impl IrqDomain { 483 #[allow(dead_code)] 484 pub fn new( 485 name: Option<&'static str>, 486 allocated_name: Option<String>, 487 ops: &'static dyn IrqDomainOps, 488 flags: IrqDomainFlags, 489 bus_token: IrqDomainBusToken, 490 irq_max: IrqNumber, 491 hwirq_max: HardwareIrqNumber, 492 ) -> Option<Arc<Self>> { 493 if name.is_none() && allocated_name.is_none() { 494 return None; 495 } 496 497 let x = IrqDomain { 498 name, 499 allocated_name: SpinLock::new(allocated_name), 500 ops, 501 inner: SpinLock::new(InnerIrqDomain { 502 host_data: None, 503 flags, 504 mapcount: 0, 505 bus_token, 506 generic_chip: None, 507 device: None, 508 parent: None, 509 }), 510 revmap: RwLock::new(IrqDomainRevMap { 511 map: HashMap::new(), 512 hwirq_max, 513 irq_max, 514 }), 515 }; 516 517 return Some(Arc::new(x)); 518 } 519 520 /// 中断域是否不对中断号进行转换 521 pub fn no_map(&self) -> bool { 522 self.inner 523 .lock_irqsave() 524 .flags 525 .contains(IrqDomainFlags::NO_MAP) 526 } 527 528 #[allow(dead_code)] 529 fn revmap_read_irqsave(&self) -> RwLockReadGuard<IrqDomainRevMap> { 530 self.revmap.read_irqsave() 531 } 532 533 #[allow(dead_code)] 534 fn revmap_write_irqsave(&self) -> RwLockWriteGuard<IrqDomainRevMap> { 535 self.revmap.write_irqsave() 536 } 537 538 #[allow(dead_code)] 539 fn set_hwirq_max(&self, hwirq_max: HardwareIrqNumber) { 540 self.revmap_write_irqsave().hwirq_max = hwirq_max; 541 } 542 543 pub fn name(&self) -> Option<String> { 544 if let Some(name) = self.name { 545 return Some(name.to_string()); 546 } 547 return self.allocated_name.lock_irqsave().clone(); 548 } 549 550 pub fn set_name(&self, name: String) { 551 *self.allocated_name.lock_irqsave() = Some(name); 552 } 553 554 /// The number of mapped interrupts 555 pub fn map_count(&self) -> u32 { 556 self.revmap_read_irqsave().map.len() as u32 557 } 558 559 pub fn host_data(&self) -> Option<Arc<dyn IrqChipData>> { 560 self.inner.lock_irqsave().host_data.clone() 561 } 562 563 pub fn set_host_data(&self, host_data: Option<Arc<dyn IrqChipData>>) { 564 self.inner.lock_irqsave().host_data = host_data; 565 } 566 } 567 568 /// 参考 https://code.dragonos.org.cn/xref/linux-6.1.9/include/linux/irqdomain.h#190 569 #[allow(dead_code)] 570 #[derive(Debug)] 571 struct IrqDomainRevMap { 572 map: HashMap<HardwareIrqNumber, Arc<IrqData>>, 573 hwirq_max: HardwareIrqNumber, 574 irq_max: IrqNumber, 575 } 576 577 impl IrqDomainRevMap { 578 fn insert(&mut self, hwirq: HardwareIrqNumber, irq_data: Arc<IrqData>) { 579 self.map.insert(hwirq, irq_data); 580 } 581 582 #[allow(dead_code)] 583 fn remove(&mut self, hwirq: HardwareIrqNumber) { 584 self.map.remove(&hwirq); 585 } 586 587 #[allow(dead_code)] 588 fn lookup(&self, hwirq: HardwareIrqNumber) -> Option<Arc<IrqData>> { 589 self.map.get(&hwirq).cloned() 590 } 591 } 592 593 bitflags! { 594 pub struct IrqDomainFlags: u32 { 595 /// Irq domain is hierarchical 596 const HIERARCHY = (1 << 0); 597 /// Irq domain name was allocated dynamically 598 const NAME_ALLOCATED = (1 << 1); 599 /// Irq domain is an IPI domain with virq per cpu 600 const IPI_PER_CPU = (1 << 2); 601 /// Irq domain is an IPI domain with single virq 602 const IPI_SINGLE = (1 << 3); 603 /// Irq domain implements MSIs 604 const MSI = (1 << 4); 605 /// Irq domain implements MSI remapping 606 const MSI_REMAP = (1 << 5); 607 /// Quirk to handle MSI implementations which do not provide masking 608 const MSI_NOMASK_QUIRK = (1 << 6); 609 /// Irq domain doesn't translate anything 610 const NO_MAP = (1 << 7); 611 /// Flags starting from IRQ_DOMAIN_FLAG_NONCORE are reserved 612 /// for implementation specific purposes and ignored by the core code 613 const NONCORE = (1 << 16); 614 } 615 } 616 617 /// 如果多个域有相同的设备节点,但服务于不同的目的(例如,一个域用于PCI/MSI,另一个用于有线IRQs), 618 /// 它们可以使用特定于总线的token进行区分。预计大多数域只会携带`DomainBusAny`。 619 /// 620 /// 参考 https://code.dragonos.org.cn/xref/linux-6.1.9/include/linux/irqdomain.h#78 621 #[allow(dead_code)] 622 #[derive(Debug, Clone, Copy, PartialEq, Eq)] 623 pub enum IrqDomainBusToken { 624 Any = 0, 625 Wired, 626 GenericMsi, 627 PciMsi, 628 PlatformMsi, 629 Nexus, 630 Ipi, 631 FslMcMsi, 632 TiSciIntaMsi, 633 Wakeup, 634 VmdMsi, 635 } 636 637 /// IrqDomain的操作方法 638 /// 639 /// 参考 https://code.dragonos.org.cn/xref/linux-6.1.9/include/linux/irqdomain.h#107 640 pub trait IrqDomainOps: Debug + Send + Sync { 641 /// 匹配一个中断控制器设备节点到一个主机。 642 fn match_node( 643 &self, 644 _irq_domain: &Arc<IrqDomain>, 645 _device_node: &Arc<DeviceNode>, 646 _bus_token: IrqDomainBusToken, 647 ) -> bool { 648 false 649 } 650 651 /// 创建或更新一个虚拟中断号与一个硬件中断号之间的映射。 652 /// 对于给定的映射,这只会被调用一次。 653 /// 654 /// 如果没有实现这个方法,那么就会返回`ENOSYS` 655 fn map( 656 &self, 657 _irq_domain: &Arc<IrqDomain>, 658 _hwirq: HardwareIrqNumber, 659 _virq: IrqNumber, 660 ) -> Result<(), SystemError> { 661 Err(SystemError::ENOSYS) 662 } 663 664 /// 删除一个虚拟中断号与一个硬件中断号之间的映射。 665 fn unmap(&self, irq_domain: &Arc<IrqDomain>, virq: IrqNumber); 666 667 fn activate( 668 &self, 669 _domain: &Arc<IrqDomain>, 670 _irq_data: &Arc<IrqData>, 671 _reserve: bool, 672 ) -> Result<(), SystemError> { 673 Err(SystemError::ENOSYS) 674 } 675 676 fn deactivate(&self, _domain: &Arc<IrqDomain>, _irq_data: &Arc<IrqData>) {} 677 } 678 679 #[allow(dead_code)] 680 #[derive(Debug)] 681 pub struct IrqDomainChipGeneric { 682 inner: SpinLock<InnerIrqDomainChipGeneric>, 683 } 684 685 #[allow(dead_code)] 686 #[derive(Debug)] 687 struct InnerIrqDomainChipGeneric { 688 irqs_per_chip: u32, 689 flags_to_clear: IrqGcFlags, 690 flags_to_set: IrqGcFlags, 691 gc_flags: IrqGcFlags, 692 gc: Vec<Arc<IrqChipGeneric>>, 693 } 694