Skip to main content

ax_driver/
binding_info.rs

1use alloc::vec::Vec;
2
3use axklib::irq::{legacy_irq_raw, try_legacy_irq};
4use irq_framework::{AcpiGsiRoute, IrqId, IrqSource};
5use rdrive::DeviceId;
6
7#[derive(Clone, Debug, Default, PartialEq, Eq)]
8pub struct BindingInfo {
9    irqs: Vec<BindingIrqBinding>,
10}
11
12#[derive(Clone, Debug, Eq, PartialEq)]
13pub struct BindingIrqBinding {
14    pub source_id: usize,
15    pub irq: BindingIrq,
16}
17
18#[derive(Clone, Debug, Eq, PartialEq)]
19pub enum BindingIrq {
20    Id(IrqId),
21    Source(BindingIrqSource),
22}
23
24#[derive(Clone, Debug, Eq, PartialEq)]
25pub enum BindingIrqSource {
26    AcpiGsi(u32),
27    AcpiGsiRoute(AcpiGsiRoute),
28    FdtInterrupt(FdtIrqSpec),
29}
30
31/// Fully described FDT interrupt specifier.
32#[derive(Clone, Debug, Eq, PartialEq)]
33pub struct FdtIrqSpec {
34    pub controller: DeviceId,
35    pub cells: Vec<u32>,
36}
37
38#[derive(Clone, Copy, Debug, Eq, PartialEq)]
39#[cfg(feature = "pci")]
40pub enum PciIrqRequirement {
41    Optional,
42    Required,
43}
44
45impl BindingInfo {
46    pub const fn empty() -> Self {
47        Self { irqs: Vec::new() }
48    }
49
50    pub fn with_irq(irq: Option<usize>) -> Result<Self, irq_framework::IrqError> {
51        Ok(Self::with_binding_irq(
52            irq.map(BindingIrq::try_legacy).transpose()?,
53        ))
54    }
55
56    pub fn with_irq_id(irq: Option<IrqId>) -> Self {
57        Self::with_binding_irq(irq.map(BindingIrq::id))
58    }
59
60    pub fn with_binding_irq(irq: Option<BindingIrq>) -> Self {
61        match irq {
62            Some(irq) => Self::with_irq_sources([(0, irq)]),
63            None => Self::empty(),
64        }
65    }
66
67    pub fn with_irq_sources(irqs: impl IntoIterator<Item = (usize, BindingIrq)>) -> Self {
68        Self {
69            irqs: irqs
70                .into_iter()
71                .map(|(source_id, irq)| BindingIrqBinding { source_id, irq })
72                .collect(),
73        }
74    }
75
76    pub fn irq(&self) -> Option<&BindingIrq> {
77        self.irq_for_source(0)
78            .or_else(|| self.irqs.first().map(|binding| &binding.irq))
79    }
80
81    pub fn irq_cloned(&self) -> Option<BindingIrq> {
82        self.irq().cloned()
83    }
84
85    pub fn irq_for_source(&self, source_id: usize) -> Option<&BindingIrq> {
86        self.irqs
87            .iter()
88            .find(|binding| binding.source_id == source_id)
89            .map(|binding| &binding.irq)
90    }
91
92    pub fn irq_for_source_cloned(&self, source_id: usize) -> Option<BindingIrq> {
93        self.irq_for_source(source_id).cloned()
94    }
95
96    pub fn irq_sources(&self) -> &[BindingIrqBinding] {
97        &self.irqs
98    }
99
100    pub fn irq_num(&self) -> Option<usize> {
101        self.irq().and_then(BindingIrq::legacy_num)
102    }
103
104    pub fn irq_num_for_source(&self, source_id: usize) -> Option<usize> {
105        self.irq_for_source(source_id)
106            .and_then(BindingIrq::legacy_num)
107    }
108}
109
110impl BindingIrq {
111    pub const fn id(id: IrqId) -> Self {
112        Self::Id(id)
113    }
114
115    pub fn try_legacy(raw: usize) -> Result<Self, irq_framework::IrqError> {
116        Ok(Self::Id(try_legacy_irq(raw)?))
117    }
118
119    pub const fn acpi_gsi(gsi: u32) -> Self {
120        Self::Source(BindingIrqSource::AcpiGsi(gsi))
121    }
122
123    pub const fn acpi_gsi_route(route: AcpiGsiRoute) -> Self {
124        Self::Source(BindingIrqSource::AcpiGsiRoute(route))
125    }
126
127    pub fn fdt_interrupt_with_controller(controller: DeviceId, cells: impl Into<Vec<u32>>) -> Self {
128        Self::Source(BindingIrqSource::fdt_interrupt_with_controller(
129            controller, cells,
130        ))
131    }
132
133    pub const fn irq_id(&self) -> Option<IrqId> {
134        match self {
135            Self::Id(id) => Some(*id),
136            Self::Source(_) => None,
137        }
138    }
139
140    pub fn legacy_num(&self) -> Option<usize> {
141        self.irq_id().and_then(legacy_irq_raw)
142    }
143
144    pub fn as_irq_source(&self) -> Option<IrqSource> {
145        match self {
146            Self::Id(_) => None,
147            Self::Source(source) => source.as_irq_source(),
148        }
149    }
150}
151
152impl BindingIrqSource {
153    pub const fn acpi_gsi(gsi: u32) -> Self {
154        Self::AcpiGsi(gsi)
155    }
156
157    pub const fn acpi_gsi_route(route: AcpiGsiRoute) -> Self {
158        Self::AcpiGsiRoute(route)
159    }
160
161    pub fn fdt_interrupt_with_controller(controller: DeviceId, cells: impl Into<Vec<u32>>) -> Self {
162        Self::FdtInterrupt(FdtIrqSpec {
163            controller,
164            cells: cells.into(),
165        })
166    }
167
168    pub fn as_irq_source(&self) -> Option<IrqSource> {
169        match self {
170            Self::AcpiGsi(gsi) => Some(IrqSource::AcpiGsi(*gsi)),
171            Self::AcpiGsiRoute(route) => Some(IrqSource::AcpiGsiRoute(*route)),
172            Self::FdtInterrupt(_) => None,
173        }
174    }
175}
176
177impl From<rdif_intc::AcpiGsiRoute> for BindingIrq {
178    fn from(route: rdif_intc::AcpiGsiRoute) -> Self {
179        Self::Source(BindingIrqSource::from(route))
180    }
181}
182
183impl From<rdif_intc::AcpiGsiRoute> for BindingIrqSource {
184    fn from(route: rdif_intc::AcpiGsiRoute) -> Self {
185        Self::AcpiGsiRoute(AcpiGsiRoute {
186            gsi: route.gsi,
187            vector: route.vector,
188            controller: match route.controller {
189                rdif_intc::AcpiGsiController::IoApic => irq_framework::AcpiGsiController::IoApic,
190                rdif_intc::AcpiGsiController::PchPic => irq_framework::AcpiGsiController::PchPic,
191            },
192            controller_id: route.controller_id,
193            controller_address: route.controller_address,
194            controller_input: route.controller_input,
195            trigger: match route.trigger {
196                rdif_intc::AcpiIrqTrigger::Edge => irq_framework::AcpiIrqTrigger::Edge,
197                rdif_intc::AcpiIrqTrigger::Level => irq_framework::AcpiIrqTrigger::Level,
198            },
199            polarity: match route.polarity {
200                rdif_intc::AcpiIrqPolarity::ActiveHigh => {
201                    irq_framework::AcpiIrqPolarity::ActiveHigh
202                }
203                rdif_intc::AcpiIrqPolarity::ActiveLow => irq_framework::AcpiIrqPolarity::ActiveLow,
204            },
205        })
206    }
207}