Better parsing of IRQ specifications in DTB
Fixes Raspberry 3B+ DTB parsing (as generated by Qemu)
This commit is contained in:
@@ -6,11 +6,13 @@ use crate::{
|
||||
context,
|
||||
context::timeout,
|
||||
device::cpu::registers::control_regs,
|
||||
dtb::irqchip::{register_irq, InterruptHandler, IRQ_CHIP},
|
||||
dtb::{
|
||||
get_interrupt,
|
||||
irqchip::{register_irq, InterruptHandler, IRQ_CHIP},
|
||||
},
|
||||
interrupt::irq::trigger,
|
||||
time,
|
||||
};
|
||||
use byteorder::{ByteOrder, BE};
|
||||
use fdt::Fdt;
|
||||
|
||||
bitflags! {
|
||||
@@ -28,19 +30,12 @@ pub unsafe fn init(fdt: &Fdt) {
|
||||
};
|
||||
timer.init();
|
||||
if let Some(node) = fdt.find_compatible(&["arm,armv7-timer"]) {
|
||||
let interrupts = node.property("interrupts").unwrap();
|
||||
let irq = interrupts
|
||||
.value
|
||||
.array_chunks::<4>()
|
||||
.map(|f| BE::read_u32(f))
|
||||
.skip(3)
|
||||
.next_chunk::<3>()
|
||||
.unwrap();
|
||||
let irq = get_interrupt(fdt, &node, 0).unwrap();
|
||||
if let Some(ic_idx) = ic_for_chip(&fdt, &node) {
|
||||
//PHYS_NONSECURE_PPI only
|
||||
let virq = IRQ_CHIP.irq_chip_list.chips[ic_idx]
|
||||
.ic
|
||||
.irq_xlate(&irq)
|
||||
.irq_xlate(irq)
|
||||
.unwrap();
|
||||
info!("generic_timer virq = {}", virq);
|
||||
register_irq(virq as u32, Box::new(timer));
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use super::InterruptController;
|
||||
use crate::dtb::{
|
||||
get_mmio_address,
|
||||
irqchip::{InterruptHandler, IrqDesc},
|
||||
irqchip::{InterruptHandler, IrqCell, IrqDesc},
|
||||
};
|
||||
use core::ptr::{read_volatile, write_volatile};
|
||||
use fdt::{node::FdtNode, Fdt};
|
||||
@@ -132,14 +132,13 @@ impl InterruptController for GenericInterruptController {
|
||||
fn irq_disable(&mut self, irq_num: u32) {
|
||||
unsafe { self.gic_dist_if.irq_disable(irq_num) }
|
||||
}
|
||||
fn irq_xlate(&self, irq_data: &[u32; 3]) -> Result<usize> {
|
||||
let mut off = match irq_data[0] {
|
||||
0 => irq_data[1] as usize + 32, //SPI
|
||||
1 => irq_data[1] as usize + 16, //PPI,
|
||||
fn irq_xlate(&self, irq_data: IrqCell) -> Result<usize> {
|
||||
let off = match irq_data {
|
||||
IrqCell::L3(0, irq, _flags) => irq as usize + 32, // SPI
|
||||
IrqCell::L3(1, irq, _flags) => irq as usize + 16, // PPI
|
||||
_ => return Err(Error::new(EINVAL)),
|
||||
};
|
||||
off += self.irq_range.0;
|
||||
return Ok(off);
|
||||
return Ok(off + self.irq_range.0);
|
||||
}
|
||||
fn irq_to_virq(&self, hwirq: u32) -> Option<usize> {
|
||||
if hwirq >= self.gic_dist_if.nirqs {
|
||||
|
||||
@@ -5,7 +5,7 @@ use fdt::{node::NodeProperty, Fdt};
|
||||
use super::{gic::GicDistIf, InterruptController};
|
||||
use crate::dtb::{
|
||||
get_mmio_address,
|
||||
irqchip::{InterruptHandler, IrqDesc},
|
||||
irqchip::{InterruptHandler, IrqCell, IrqDesc},
|
||||
};
|
||||
use syscall::{
|
||||
error::{Error, EINVAL},
|
||||
@@ -127,14 +127,13 @@ impl InterruptController for GicV3 {
|
||||
fn irq_disable(&mut self, irq_num: u32) {
|
||||
unsafe { self.gic_dist_if.irq_disable(irq_num) }
|
||||
}
|
||||
fn irq_xlate(&self, irq_data: &[u32; 3]) -> Result<usize> {
|
||||
let mut off = match irq_data[0] {
|
||||
0 => irq_data[1] as usize + 32, //SPI
|
||||
1 => irq_data[1] as usize + 16, //PPI,
|
||||
fn irq_xlate(&self, irq_data: IrqCell) -> Result<usize> {
|
||||
let off = match irq_data {
|
||||
IrqCell::L2(0, irq) => irq as usize + 32, // SPI
|
||||
IrqCell::L2(1, irq) => irq as usize + 16, // PPI
|
||||
_ => return Err(Error::new(EINVAL)),
|
||||
};
|
||||
off += self.irq_range.0;
|
||||
return Ok(off);
|
||||
return Ok(off + self.irq_range.0);
|
||||
}
|
||||
fn irq_to_virq(&self, hwirq: u32) -> Option<usize> {
|
||||
if hwirq >= self.gic_dist_if.nirqs {
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
use byteorder::{ByteOrder, BE};
|
||||
use core::ptr::{read_volatile, write_volatile};
|
||||
use fdt::{node::FdtNode, Fdt};
|
||||
use log::{debug, error, info};
|
||||
|
||||
use super::InterruptController;
|
||||
use crate::dtb::{
|
||||
get_mmio_address,
|
||||
irqchip::{InterruptHandler, IrqDesc, IRQ_CHIP},
|
||||
get_interrupt, get_mmio_address,
|
||||
irqchip::{InterruptHandler, IrqCell, IrqDesc, IRQ_CHIP},
|
||||
};
|
||||
use syscall::{
|
||||
error::{Error, EINVAL},
|
||||
@@ -81,18 +80,12 @@ impl Bcm2835ArmInterruptController {
|
||||
|
||||
if let Some(interrupt_parent) = node.property("interrupt-parent") {
|
||||
let phandle = interrupt_parent.as_usize().unwrap() as u32;
|
||||
let interrupts = node.property("interrupts").unwrap();
|
||||
let irq = interrupts
|
||||
.value
|
||||
.array_chunks::<4>()
|
||||
.map(|f| BE::read_u32(f))
|
||||
.next_chunk::<3>()
|
||||
.unwrap();
|
||||
let irq = get_interrupt(fdt, node, 0).unwrap();
|
||||
let ic_idx = IRQ_CHIP.phandle_to_ic_idx(phandle).unwrap();
|
||||
//PHYS_NONSECURE_PPI only
|
||||
let virq = IRQ_CHIP.irq_chip_list.chips[ic_idx]
|
||||
.ic
|
||||
.irq_xlate(&irq)
|
||||
.irq_xlate(irq)
|
||||
.unwrap();
|
||||
info!("bcm2835arm_ctrl virq = {}", virq);
|
||||
ret_virq = Some(virq);
|
||||
@@ -252,14 +245,17 @@ impl InterruptController for Bcm2835ArmInterruptController {
|
||||
_ => return,
|
||||
}
|
||||
}
|
||||
fn irq_xlate(&self, irq_data: &[u32; 3]) -> Result<usize> {
|
||||
fn irq_xlate(&self, irq_data: IrqCell) -> Result<usize> {
|
||||
//assert interrupt-cells == 0x2
|
||||
let bank = irq_data[0] as usize;
|
||||
let irq = irq_data[1] as usize;
|
||||
//TODO: check bank && irq
|
||||
let hwirq = bank << 5 | irq;
|
||||
let off = hwirq + self.irq_range.0;
|
||||
return Ok(off);
|
||||
match irq_data {
|
||||
IrqCell::L2(bank, irq) => {
|
||||
//TODO: check bank && irq
|
||||
let hwirq = (bank as usize) << 5 | (irq as usize);
|
||||
let off = hwirq + self.irq_range.0;
|
||||
Ok(off)
|
||||
}
|
||||
_ => Err(Error::new(EINVAL)),
|
||||
}
|
||||
}
|
||||
|
||||
fn irq_to_virq(&self, hwirq: u32) -> Option<usize> {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use super::InterruptController;
|
||||
use crate::dtb::{
|
||||
get_mmio_address,
|
||||
irqchip::{InterruptHandler, IrqDesc},
|
||||
irqchip::{InterruptHandler, IrqCell, IrqDesc},
|
||||
};
|
||||
use core::{
|
||||
arch::asm,
|
||||
@@ -198,10 +198,12 @@ impl InterruptController for Bcm2836ArmInterruptController {
|
||||
}
|
||||
}
|
||||
}
|
||||
fn irq_xlate(&self, irq_data: &[u32; 3]) -> Result<usize> {
|
||||
fn irq_xlate(&self, irq_data: IrqCell) -> Result<usize> {
|
||||
//assert interrupt-cells == 0x2
|
||||
let off = irq_data[0] as usize + self.irq_range.0;
|
||||
return Ok(off);
|
||||
match irq_data {
|
||||
IrqCell::L2(irq, _) => Ok(irq as usize + self.irq_range.0),
|
||||
_ => Err(Error::new(EINVAL)),
|
||||
}
|
||||
}
|
||||
fn irq_to_virq(&self, hwirq: u32) -> Option<usize> {
|
||||
if hwirq > LOCAL_IRQ_LAST {
|
||||
|
||||
@@ -5,7 +5,7 @@ use syscall::{
|
||||
};
|
||||
|
||||
use super::InterruptController;
|
||||
use crate::dtb::irqchip::{InterruptHandler, IrqDesc};
|
||||
use crate::dtb::irqchip::{InterruptHandler, IrqCell, IrqDesc};
|
||||
|
||||
pub struct Null;
|
||||
|
||||
@@ -29,7 +29,7 @@ impl InterruptController for Null {
|
||||
fn irq_eoi(&mut self, _irq_num: u32) {}
|
||||
fn irq_enable(&mut self, _irq_num: u32) {}
|
||||
fn irq_disable(&mut self, _irq_num: u32) {}
|
||||
fn irq_xlate(&self, _irq_data: &[u32; 3]) -> Result<usize> {
|
||||
fn irq_xlate(&self, _irq_data: IrqCell) -> Result<usize> {
|
||||
Err(Error::new(EINVAL))
|
||||
}
|
||||
fn irq_to_virq(&self, _hwirq: u32) -> Option<usize> {
|
||||
|
||||
@@ -6,13 +6,12 @@ use crate::{
|
||||
device::uart_pl011,
|
||||
devices::uart_16550,
|
||||
dtb::{
|
||||
diag_uart_range,
|
||||
diag_uart_range, get_interrupt,
|
||||
irqchip::{register_irq, InterruptHandler, IRQ_CHIP},
|
||||
},
|
||||
interrupt::irq::trigger,
|
||||
scheme::debug::{debug_input, debug_notify},
|
||||
};
|
||||
use byteorder::{ByteOrder, BE};
|
||||
use fdt::Fdt;
|
||||
use log::{error, info};
|
||||
use syscall::Mmio;
|
||||
@@ -120,17 +119,11 @@ pub unsafe fn init_early(dtb: &Fdt) {
|
||||
pub unsafe fn init(fdt: &Fdt) {
|
||||
//TODO: find actual serial device, not just any PL011
|
||||
if let Some(node) = fdt.find_compatible(&["arm,pl011"]) {
|
||||
let interrupts = node.property("interrupts").unwrap();
|
||||
let irq = interrupts
|
||||
.value
|
||||
.array_chunks::<4>()
|
||||
.map(|f| BE::read_u32(f))
|
||||
.next_chunk::<3>()
|
||||
.unwrap();
|
||||
let irq = get_interrupt(fdt, &node, 0).unwrap();
|
||||
if let Some(ic_idx) = ic_for_chip(&fdt, &node) {
|
||||
let virq = IRQ_CHIP.irq_chip_list.chips[ic_idx]
|
||||
.ic
|
||||
.irq_xlate(&irq)
|
||||
.irq_xlate(irq)
|
||||
.unwrap();
|
||||
info!("serial_port virq = {}", virq);
|
||||
register_irq(virq as u32, Box::new(Com1Irq {}));
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::{
|
||||
context,
|
||||
context::timeout,
|
||||
dtb::irqchip::{register_irq, InterruptHandler, IRQ_CHIP},
|
||||
dtb::irqchip::{register_irq, InterruptHandler, IrqCell, IRQ_CHIP},
|
||||
};
|
||||
use alloc::{boxed::Box, vec::Vec};
|
||||
use byteorder::{ByteOrder, BE};
|
||||
@@ -91,16 +91,16 @@ impl Clint {
|
||||
|
||||
// FIXME dirty hack map M-mode interrupts (handled by SBI) to S-mode interrupts we get from SBI
|
||||
// Why aren't S-mode interrupts in the DTB already?
|
||||
let irq0 = map_interrupt(irq0);
|
||||
let irq1 = map_interrupt(irq1);
|
||||
let irq0 = IrqCell::L1(map_interrupt(irq0));
|
||||
let irq1 = IrqCell::L1(map_interrupt(irq1));
|
||||
|
||||
let virq0 = hlic
|
||||
.ic
|
||||
.irq_xlate(&[irq0, 0, 0])
|
||||
.irq_xlate(irq0)
|
||||
.expect("Couldn't get virq 0 from HLIC");
|
||||
let virq1 = hlic
|
||||
.ic
|
||||
.irq_xlate(&[irq1, 0, 0])
|
||||
.irq_xlate(irq1)
|
||||
.expect("Couldn't get virq 1 from HLIC");
|
||||
register_irq(virq0 as u32, Box::new(ClintConnector { hart_id, irq: 0 }));
|
||||
register_irq(virq1 as u32, Box::new(ClintConnector { hart_id, irq: 1 }));
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use crate::dtb::irqchip::{InterruptController, InterruptHandler, IrqDesc, IRQ_CHIP};
|
||||
use crate::dtb::irqchip::{InterruptController, InterruptHandler, IrqCell, IrqDesc, IRQ_CHIP};
|
||||
use alloc::vec::Vec;
|
||||
use core::arch::asm;
|
||||
use fdt::{node::NodeProperty, Fdt};
|
||||
use syscall::{Error, ENOENT};
|
||||
use syscall::{Error, EINVAL};
|
||||
|
||||
// This is a hart-local interrupt controller, a root of irqchip tree
|
||||
// An example DTS:
|
||||
@@ -137,11 +137,10 @@ impl InterruptController for Hlic {
|
||||
// Not bothering with this, all interrupts are enabled at all times
|
||||
}
|
||||
|
||||
fn irq_xlate(&self, irq_data: &[u32; 3]) -> syscall::Result<usize> {
|
||||
let irq = irq_data[0];
|
||||
match irq {
|
||||
0..=0xF => Ok(self.virq_base + irq as usize),
|
||||
_ => Err(Error::new(ENOENT)),
|
||||
fn irq_xlate(&self, irq_data: IrqCell) -> syscall::Result<usize> {
|
||||
match irq_data {
|
||||
IrqCell::L1(irq) if irq <= 0xF => Ok(self.virq_base + irq as usize),
|
||||
_ => Err(Error::new(EINVAL)),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,13 +2,13 @@ use crate::{
|
||||
arch::{device::irqchip::hlic, start::BOOT_HART_ID},
|
||||
dtb::{
|
||||
get_mmio_address,
|
||||
irqchip::{InterruptController, InterruptHandler, IrqDesc, IRQ_CHIP},
|
||||
irqchip::{InterruptController, InterruptHandler, IrqCell, IrqDesc, IRQ_CHIP},
|
||||
},
|
||||
};
|
||||
use core::{mem, num::NonZero, sync::atomic::Ordering};
|
||||
use fdt::Fdt;
|
||||
use log::{error, info};
|
||||
use syscall::{Error, Io, Mmio, ENODEV};
|
||||
use syscall::{Error, Io, Mmio, EINVAL};
|
||||
|
||||
#[repr(packed(4))]
|
||||
#[repr(C)]
|
||||
@@ -147,7 +147,7 @@ impl InterruptController for Plic {
|
||||
self.context = desc
|
||||
.parents
|
||||
.iter()
|
||||
.position(|x| x.parent_interrupt[0] != u32::MAX && x.parent == hlic_ic_idx)
|
||||
.position(|x| x.parent_interrupt.is_some() && x.parent == hlic_ic_idx)
|
||||
.unwrap();
|
||||
info!("PLIC: using context {}", self.context);
|
||||
|
||||
@@ -181,11 +181,10 @@ impl InterruptController for Plic {
|
||||
regs.enable(self.context, NonZero::new(irq_num as usize).unwrap(), false);
|
||||
}
|
||||
|
||||
fn irq_xlate(&self, irq_data: &[u32; 3]) -> syscall::Result<usize> {
|
||||
if (irq_data[0] as usize) < self.ndev {
|
||||
Ok(self.virq_base + irq_data[0] as usize)
|
||||
} else {
|
||||
Err(Error::new(ENODEV))
|
||||
fn irq_xlate(&self, irq_data: IrqCell) -> syscall::Result<usize> {
|
||||
match irq_data {
|
||||
IrqCell::L1(irq) => Ok(self.virq_base + irq as usize),
|
||||
_ => Err(Error::new(EINVAL)),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use alloc::boxed::Box;
|
||||
use byteorder::{ByteOrder, BE};
|
||||
use fdt::Fdt;
|
||||
use log::info;
|
||||
use spin::Mutex;
|
||||
@@ -8,7 +7,7 @@ use syscall::Mmio;
|
||||
use crate::{
|
||||
devices::uart_16550,
|
||||
dtb::{
|
||||
diag_uart_range,
|
||||
diag_uart_range, get_interrupt, interrupt_parent,
|
||||
irqchip::{register_irq, InterruptHandler, IRQ_CHIP},
|
||||
},
|
||||
scheme::{
|
||||
@@ -74,23 +73,14 @@ pub unsafe fn init_early(dtb: &Fdt) {
|
||||
|
||||
pub unsafe fn init(fdt: &Fdt) -> Option<()> {
|
||||
if let Some(node) = fdt.find_compatible(&["ns16550a"]) {
|
||||
let interrupts = node.property("interrupts").unwrap();
|
||||
let mut intr_data: [u32; 3] = [0, 0, 0];
|
||||
for (idx, chunk) in interrupts.value.chunks(4).enumerate() {
|
||||
if idx >= intr_data.len() {
|
||||
break;
|
||||
}
|
||||
let val = BE::read_u32(chunk);
|
||||
intr_data[idx] = val;
|
||||
}
|
||||
|
||||
let interrupt_parent = node.interrupt_parent()?;
|
||||
let intr = get_interrupt(fdt, &node, 0).unwrap();
|
||||
let interrupt_parent = interrupt_parent(fdt, &node)?;
|
||||
let phandle = interrupt_parent.property("phandle")?.as_usize()? as u32;
|
||||
let ic_idx = IRQ_CHIP.phandle_to_ic_idx(phandle)?;
|
||||
|
||||
let virq = IRQ_CHIP.irq_chip_list.chips[ic_idx]
|
||||
.ic
|
||||
.irq_xlate(&intr_data)
|
||||
.irq_xlate(intr)
|
||||
.unwrap();
|
||||
info!("serial_port virq = {}", virq);
|
||||
register_irq(virq as u32, Box::new(Com1Irq {}));
|
||||
|
||||
+38
-15
@@ -4,12 +4,20 @@ use alloc::{boxed::Box, vec::Vec};
|
||||
use byteorder::{ByteOrder, BE};
|
||||
use fdt::{node::NodeProperty, Fdt};
|
||||
use log::{debug, error};
|
||||
use syscall::{Error, Result};
|
||||
use syscall::{Error, Result, EINVAL};
|
||||
|
||||
pub trait InterruptHandler {
|
||||
fn irq_handler(&mut self, irq: u32);
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[allow(dead_code)]
|
||||
pub enum IrqCell {
|
||||
L1(u32),
|
||||
L2(u32, u32),
|
||||
L3(u32, u32, u32),
|
||||
}
|
||||
|
||||
pub trait InterruptController: InterruptHandler {
|
||||
fn irq_init(
|
||||
&mut self,
|
||||
@@ -23,14 +31,14 @@ pub trait InterruptController: InterruptHandler {
|
||||
fn irq_enable(&mut self, irq_num: u32);
|
||||
#[allow(unused)]
|
||||
fn irq_disable(&mut self, irq_num: u32);
|
||||
fn irq_xlate(&self, irq_data: &[u32; 3]) -> Result<usize>;
|
||||
fn irq_xlate(&self, irq_data: IrqCell) -> Result<usize>;
|
||||
fn irq_to_virq(&self, hwirq: u32) -> Option<usize>;
|
||||
}
|
||||
|
||||
pub struct IrqConnection {
|
||||
pub parent_phandle: u32,
|
||||
pub parent: usize, // parent idx in chiplist
|
||||
pub parent_interrupt: [u32; 3],
|
||||
pub parent_interrupt: Option<IrqCell>,
|
||||
}
|
||||
|
||||
pub struct IrqChipItem {
|
||||
@@ -79,27 +87,36 @@ impl IrqChipList {
|
||||
fn interrupt_address(
|
||||
iter: &mut impl Iterator<Item = u32>,
|
||||
interrupt_cells: usize,
|
||||
) -> Option<[u32; 3]> {
|
||||
) -> Option<IrqCell> {
|
||||
match interrupt_cells {
|
||||
1 if let Some(a) = iter.next() => Some([a, 0, 0]),
|
||||
2 if let Ok([a, b]) = iter.next_chunk() => Some([a, b, 0]),
|
||||
3 => iter.next_chunk::<3>().ok(),
|
||||
1 => Some(IrqCell::L1(iter.next()?)),
|
||||
2 if let Ok([a, b]) = iter.next_chunk() => Some(IrqCell::L2(a, b)),
|
||||
3 if let Ok([a, b, c]) = iter.next_chunk() => Some(IrqCell::L3(a, b, c)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn gate_interrupt_address(addr: IrqCell) -> Option<IrqCell> {
|
||||
match addr {
|
||||
IrqCell::L1(u32::MAX)
|
||||
| IrqCell::L2(u32::MAX, _)
|
||||
| IrqCell::L3(u32::MAX, _, _) => None,
|
||||
_ => Some(addr),
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(parent) = node.interrupt_parent()
|
||||
&& let Some(intr_data) = node.property("interrupts")
|
||||
{
|
||||
// FIXME use interrupts() helper when fixed (see gh#12)
|
||||
let parent_interrupt_cells = parent.interrupt_cells().unwrap();
|
||||
let mut intr_data = intr_data.value.chunks(4).map(|x| BE::read_u32(x));
|
||||
let parent_phandle = parent
|
||||
.property("phandle")
|
||||
.and_then(NodeProperty::as_usize)
|
||||
.unwrap() as u32;
|
||||
let parent_interrupt_cells = parent.interrupt_cells().unwrap();
|
||||
debug!("interrupt-parent = 0x{:08x}", parent_phandle);
|
||||
debug!("interrupts begin:");
|
||||
let mut intr_data = intr_data.value.chunks(4).map(|x| BE::read_u32(x));
|
||||
while let Some(parent_interrupt) =
|
||||
interrupt_address(&mut intr_data, parent_interrupt_cells)
|
||||
{
|
||||
@@ -107,7 +124,7 @@ impl IrqChipList {
|
||||
item.parents.push(IrqConnection {
|
||||
parent_phandle,
|
||||
parent: 0,
|
||||
parent_interrupt,
|
||||
parent_interrupt: gate_interrupt_address(parent_interrupt),
|
||||
});
|
||||
}
|
||||
debug!("interrupts end");
|
||||
@@ -126,7 +143,7 @@ impl IrqChipList {
|
||||
item.parents.push(IrqConnection {
|
||||
parent_phandle,
|
||||
parent: 0,
|
||||
parent_interrupt,
|
||||
parent_interrupt: gate_interrupt_address(parent_interrupt),
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -201,15 +218,15 @@ impl IrqChipList {
|
||||
let cur_chip = &self.chips[cur_idx];
|
||||
for connection in &cur_chip.parents {
|
||||
debug_assert!(queue[0..queue_idx].contains(&connection.parent));
|
||||
if connection.parent_interrupt[0] != u32::MAX {
|
||||
if let Some(parent_interrupt) = connection.parent_interrupt {
|
||||
let parent = &self.chips[connection.parent];
|
||||
if let Ok(virq) = parent.ic.irq_xlate(&connection.parent_interrupt) {
|
||||
if let Ok(virq) = parent.ic.irq_xlate(parent_interrupt) {
|
||||
// assert is unused
|
||||
irq_desc[virq].basic.child_ic_idx = Some(cur_idx);
|
||||
} else {
|
||||
error!(
|
||||
"Cannot connect irq chip {} to parent irq {} : {:?}",
|
||||
cur_chip.phandle, parent.phandle, connection.parent_interrupt
|
||||
cur_chip.phandle, parent.phandle, parent_interrupt
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -257,7 +274,13 @@ impl IrqChipCore {
|
||||
self.irq_chip_list.chips[ic_idx].ic.irq_to_virq(hwirq)
|
||||
}
|
||||
|
||||
pub fn irq_xlate(&self, ic_idx: usize, irq_data: &[u32; 3]) -> Result<usize, Error> {
|
||||
pub fn irq_xlate(&self, ic_idx: usize, irq_data: &[u32]) -> Result<usize, Error> {
|
||||
let irq_data = match irq_data.len() {
|
||||
1 => IrqCell::L1(irq_data[0]),
|
||||
2 => IrqCell::L2(irq_data[0], irq_data[1]),
|
||||
3 => IrqCell::L3(irq_data[0], irq_data[1], irq_data[2]),
|
||||
_ => return Err(Error::new(EINVAL)),
|
||||
};
|
||||
self.irq_chip_list.chips[ic_idx].ic.irq_xlate(irq_data)
|
||||
}
|
||||
|
||||
|
||||
+30
-1
@@ -1,6 +1,9 @@
|
||||
pub mod irqchip;
|
||||
|
||||
use crate::startup::memory::{register_memory_region, BootloaderMemoryKind};
|
||||
use crate::{
|
||||
dtb::irqchip::IrqCell,
|
||||
startup::memory::{register_memory_region, BootloaderMemoryKind},
|
||||
};
|
||||
use alloc::vec::Vec;
|
||||
use byteorder::{ByteOrder, BE};
|
||||
use core::slice;
|
||||
@@ -168,6 +171,32 @@ pub fn get_mmio_address(fdt: &Fdt, _device: &FdtNode, region: &MemoryRegion) ->
|
||||
Some(mapped_addr)
|
||||
}
|
||||
|
||||
pub fn interrupt_parent<'a>(fdt: &'a Fdt, node: &'a FdtNode) -> Option<FdtNode<'a, 'a>> {
|
||||
// FIXME traverse device tree up
|
||||
node.interrupt_parent()
|
||||
.or_else(|| fdt.find_node("/soc").and_then(|soc| soc.interrupt_parent()))
|
||||
.or_else(|| fdt.find_node("/").and_then(|node| node.interrupt_parent()))
|
||||
}
|
||||
|
||||
pub fn get_interrupt(fdt: &Fdt, node: &FdtNode, idx: usize) -> Option<IrqCell> {
|
||||
let interrupts = node.property("interrupts").unwrap();
|
||||
let parent_interrupt_cells = interrupt_parent(fdt, node)
|
||||
.unwrap()
|
||||
.interrupt_cells()
|
||||
.unwrap();
|
||||
let mut intr = interrupts
|
||||
.value
|
||||
.array_chunks::<4>()
|
||||
.map(|f| BE::read_u32(f))
|
||||
.skip(parent_interrupt_cells * idx);
|
||||
match parent_interrupt_cells {
|
||||
1 => Some(IrqCell::L1(intr.next()?)),
|
||||
2 if let Ok([a, b]) = intr.next_chunk() => Some(IrqCell::L2(a, b)),
|
||||
3 if let Ok([a, b, c]) = intr.next_chunk() => Some(IrqCell::L3(a, b, c)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn diag_uart_range<'a>(dtb: &'a Fdt) -> Option<(usize, usize, bool, bool, &'a str)> {
|
||||
let stdout_path = dtb.chosen().stdout()?;
|
||||
let uart_node = stdout_path.node();
|
||||
|
||||
+1
-1
@@ -57,7 +57,7 @@
|
||||
#![feature(option_get_or_insert_default)]
|
||||
#![feature(array_chunks)]
|
||||
#![feature(if_let_guard)]
|
||||
|
||||
#![feature(iterator_try_collect)]
|
||||
#[macro_use]
|
||||
extern crate alloc;
|
||||
|
||||
|
||||
+5
-11
@@ -165,16 +165,10 @@ impl IrqScheme {
|
||||
phandle: usize,
|
||||
path_str: &str,
|
||||
) -> Result<(Handle, InternalFlags)> {
|
||||
let mut path_iter = path_str.split(',');
|
||||
let addr = path_iter.next_chunk::<3>().or(Err(Error::new(ENOENT)))?;
|
||||
if path_iter.next().is_some() {
|
||||
return Err(Error::new(ENOENT));
|
||||
}
|
||||
let addr = [
|
||||
u32::from_str(addr[0]).or(Err(Error::new(ENOENT)))?,
|
||||
u32::from_str(addr[1]).or(Err(Error::new(ENOENT)))?,
|
||||
u32::from_str(addr[2]).or(Err(Error::new(ENOENT)))?,
|
||||
];
|
||||
let addr: Vec<u32> = path_str
|
||||
.split(',')
|
||||
.map(|x| u32::from_str(x).or(Err(Error::new(ENOENT))))
|
||||
.try_collect()?;
|
||||
let ic_idx = IRQ_CHIP
|
||||
.phandle_to_ic_idx(phandle as u32)
|
||||
.ok_or(Error::new(ENOENT))?;
|
||||
@@ -183,7 +177,7 @@ impl IrqScheme {
|
||||
return Err(Error::new(EINVAL));
|
||||
}
|
||||
let irq_number = IRQ_CHIP
|
||||
.irq_xlate(ic_idx, &addr)
|
||||
.irq_xlate(ic_idx, addr.as_slice())
|
||||
.or(Err(Error::new(ENOENT)))?;
|
||||
log::debug!("open_phandle_irq virq={}", irq_number);
|
||||
if flags & O_STAT == 0 {
|
||||
|
||||
Reference in New Issue
Block a user