1#[cfg(feature = "net")]
2use alloc::string::String;
3
4#[cfg(any(
5 target_arch = "loongarch64",
6 target_arch = "riscv64",
7 target_arch = "x86_64",
8))]
9use ax_hal::irq::CPU_LOCAL_IRQ_DOMAIN;
10#[cfg(any(
11 target_arch = "aarch64",
12 target_arch = "loongarch64",
13 target_arch = "riscv64",
14 target_arch = "x86_64",
15))]
16use ax_hal::irq::HwIrq;
17#[cfg(feature = "net")]
18use ax_hal::irq::IrqHandle;
19use ax_hal::irq::{IrqError, IrqId, IrqSource};
20
21pub fn resolve_legacy_irq(irq: usize) -> Result<IrqId, IrqError> {
23 ax_hal::irq::try_legacy_irq(irq)
24}
25
26pub fn resolve_binding_irq(irq: ax_driver::BindingIrq) -> Result<IrqId, IrqError> {
28 if let Some(id) = irq.irq_id() {
29 return Ok(id);
30 }
31
32 match irq {
33 ax_driver::BindingIrq::Id(id) => Ok(id),
34 ax_driver::BindingIrq::Source(source) => resolve_binding_irq_source(source),
35 }
36}
37
38fn resolve_binding_irq_source(source: ax_driver::BindingIrqSource) -> Result<IrqId, IrqError> {
39 match source {
40 ax_driver::BindingIrqSource::AcpiGsi(gsi) => {
41 ax_hal::irq::resolve_irq_source(IrqSource::AcpiGsi(gsi))
42 }
43 ax_driver::BindingIrqSource::AcpiGsiRoute(route) => {
44 ax_hal::irq::resolve_irq_source(IrqSource::AcpiGsiRoute(route))
45 }
46 ax_driver::BindingIrqSource::FdtInterrupt(spec) => resolve_fdt_irq_spec(spec),
47 }
48}
49
50#[cfg(any(
51 target_arch = "aarch64",
52 target_arch = "loongarch64",
53 target_arch = "riscv64"
54))]
55fn resolve_fdt_irq_spec(spec: ax_driver::FdtIrqSpec) -> Result<IrqId, IrqError> {
56 let mut intc = rdrive::get::<rdif_intc::Intc>(spec.controller)
57 .map_err(|_| IrqError::Unsupported)?
58 .lock()
59 .map_err(|_| IrqError::Controller)?;
60 let translation = intc.translate_fdt(&spec.cells)?;
61 intc.configure(&translation)?;
62 Ok(translation.id)
63}
64
65#[cfg(not(any(
66 target_arch = "aarch64",
67 target_arch = "loongarch64",
68 target_arch = "riscv64"
69)))]
70fn resolve_fdt_irq_spec(_spec: ax_driver::FdtIrqSpec) -> Result<IrqId, IrqError> {
71 Err(IrqError::Unsupported)
72}
73
74#[cfg(target_arch = "aarch64")]
76pub fn resolve_percpu_irq(irq: usize) -> IrqId {
77 let hwirq = HwIrq(u32::try_from(irq).expect("AArch64 per-CPU IRQ exceeds GIC INTID width"));
78 ax_hal::irq::resolve_percpu_irq(hwirq).expect("AArch64 per-CPU IRQ domain is not registered")
79}
80
81#[cfg(any(target_arch = "loongarch64", target_arch = "x86_64"))]
83pub fn resolve_percpu_irq(irq: usize) -> IrqId {
84 IrqId::new(CPU_LOCAL_IRQ_DOMAIN, HwIrq(irq as u32))
85}
86
87#[cfg(not(any(
89 target_arch = "aarch64",
90 target_arch = "loongarch64",
91 target_arch = "x86_64"
92)))]
93pub fn resolve_percpu_irq(irq: usize) -> IrqId {
94 #[cfg(target_arch = "riscv64")]
95 {
96 const RISCV_INTERRUPT_BIT: usize = 1usize << (usize::BITS as usize - 1);
97
98 if irq & RISCV_INTERRUPT_BIT != 0 {
99 return IrqId::new(
100 CPU_LOCAL_IRQ_DOMAIN,
101 HwIrq((irq & !RISCV_INTERRUPT_BIT) as u32),
102 );
103 }
104 }
105
106 resolve_legacy_irq(irq).expect("legacy per-CPU IRQ exceeds platform IRQ id width")
107}
108
109#[cfg(feature = "net")]
110pub(crate) struct RuntimeNetIrqRegistrar;
111
112#[cfg(feature = "net")]
113pub(crate) static NET_IRQ_REGISTRAR: RuntimeNetIrqRegistrar = RuntimeNetIrqRegistrar;
114
115#[cfg(feature = "net")]
116struct RuntimeNetIrqRegistration {
117 name: String,
118 handle: IrqHandle,
119 owner_cpu: usize,
120}
121
122#[cfg(feature = "net")]
123impl ax_net::PinnedNetIrqRegistration for RuntimeNetIrqRegistration {
124 fn owner_cpu(&self) -> usize {
125 self.owner_cpu
126 }
127
128 fn enable(&self) -> Result<(), ax_net::PinnedNetIrqError> {
129 ax_hal::irq::enable_irq(self.handle).map_err(map_net_irq_error)
130 }
131
132 fn disable_and_synchronize(&self) -> Result<(), ax_net::PinnedNetIrqError> {
133 match ax_hal::irq::disable_irq(self.handle) {
134 Ok(()) | Err(IrqError::NotFound) => {}
135 Err(error) => return Err(map_net_irq_error(error)),
136 }
137 match ax_hal::irq::synchronize_irq(self.handle) {
138 Ok(()) | Err(IrqError::NotFound) => Ok(()),
139 Err(error) => Err(map_net_irq_error(error)),
140 }
141 }
142}
143
144#[cfg(feature = "net")]
145impl Drop for RuntimeNetIrqRegistration {
146 fn drop(&mut self) {
147 if let Err(error) = ax_hal::irq::free_irq(self.handle) {
148 warn!(
149 "failed to free network IRQ registration {}: {error:?}",
150 self.name
151 );
152 }
153 }
154}
155
156#[cfg(feature = "net")]
157fn map_net_irq_error(err: IrqError) -> ax_net::PinnedNetIrqError {
158 match err {
159 IrqError::InvalidIrq | IrqError::InvalidCpu => ax_net::PinnedNetIrqError::Invalid,
160 IrqError::Busy => ax_net::PinnedNetIrqError::AffinityConflict,
161 IrqError::Unsupported | IrqError::CpuOffline => ax_net::PinnedNetIrqError::Unsupported,
162 IrqError::NoMemory
163 | IrqError::NotFound
164 | IrqError::Timeout
165 | IrqError::Controller
166 | IrqError::InIrqContext => ax_net::PinnedNetIrqError::Other,
167 }
168}
169
170#[cfg(feature = "net")]
171impl ax_net::PinnedNetIrqRegistrar for RuntimeNetIrqRegistrar {
172 fn register(
173 &self,
174 name: String,
175 irq: IrqId,
176 owner_cpu: usize,
177 action: ax_net::PinnedNetIrqAction,
178 ) -> Result<alloc::boxed::Box<dyn ax_net::PinnedNetIrqRegistration>, ax_net::PinnedNetIrqError>
179 {
180 let mut action = action;
181 let request = ax_hal::irq::IrqRequest::new(move |_context| match action.run() {
182 ax_net::PinnedNetIrqOutcome::Unhandled => ax_hal::irq::IrqReturn::Unhandled,
183 ax_net::PinnedNetIrqOutcome::Handled => ax_hal::irq::IrqReturn::Handled,
184 ax_net::PinnedNetIrqOutcome::Wake => ax_hal::irq::IrqReturn::Wake,
185 })
186 .execution(ax_hal::irq::IrqExecution::NonReentrant)
187 .share_mode(ax_hal::irq::ShareMode::Shared)
188 .auto_enable(ax_hal::irq::AutoEnable::No)
189 .affinity(ax_hal::irq::IrqAffinity::Fixed(ax_hal::irq::CpuId(
190 owner_cpu,
191 )));
192 let handle = ax_hal::irq::request_irq(irq, request).map_err(map_net_irq_error)?;
193 info!("registered {name} IRQ {irq:?} on fixed CPU {owner_cpu}");
194 Ok(alloc::boxed::Box::new(RuntimeNetIrqRegistration {
195 name,
196 handle,
197 owner_cpu,
198 }))
199 }
200}
201
202pub fn notify_cpu(cpu_id: usize) -> Result<(), ax_hal::irq::IrqError> {
209 #[cfg(any(feature = "ipi", feature = "wake-ipi"))]
210 {
211 if cpu_id >= ax_hal::cpu_num() {
212 return Err(ax_hal::irq::IrqError::InvalidCpu);
213 }
214 ax_ipi::notify_cpu(ax_hal::irq::CpuId(cpu_id)).map(|_| ())
215 }
216 #[cfg(not(any(feature = "ipi", feature = "wake-ipi")))]
217 {
218 let _ = cpu_id;
219 Err(ax_hal::irq::IrqError::Unsupported)
220 }
221}
222
223mod worker;
224pub use worker::FixedIrqWorkerSignal;