aarch64: add irqchip module to initialize all irq chip.

This commit is contained in:
Ivan Tan
2023-09-20 10:24:49 +08:00
committed by Jeremy Soller
parent 78a8568932
commit 01d0e9a4b5
3 changed files with 261 additions and 0 deletions
+195
View File
@@ -0,0 +1,195 @@
use core::ptr::{read_volatile, write_volatile};
use fdt::DeviceTree;
use crate::device::io_mmap;
use syscall::{Result, error::{Error, EINVAL}};
use super::InterruptController;
static GICD_CTLR: u32 = 0x000;
static GICD_TYPER: u32 = 0x004;
static GICD_ISENABLER: u32 = 0x100;
static GICD_ICENABLER: u32 = 0x180;
static GICD_IPRIORITY: u32 = 0x400;
static GICD_ITARGETSR: u32 = 0x800;
static GICD_ICFGR: u32 = 0xc00;
static GICC_EOIR: u32 = 0x0010;
static GICC_IAR: u32 = 0x000c;
static GICC_CTLR: u32 = 0x0000;
static GICC_PMR: u32 = 0x0004;
pub struct GenericInterruptController {
gic_dist_if: GicDistIf,
gic_cpu_if: GicCpuIf,
}
impl GenericInterruptController {
pub fn new() -> Self {
let gic_dist_if = GicDistIf {
address: 0,
ncpus: 0,
nirqs: 0,
};
let gic_cpu_if = GicCpuIf {
address: 0,
};
GenericInterruptController { gic_dist_if, gic_cpu_if }
}
pub fn parse(fdt: Option<&DeviceTree>) -> Result<(usize, usize, usize, usize)> {
match fdt {
//TODO: remove hard code for qemu-virt
None => Ok((0x800_0000, 0x1_0000, 0x801_0000, 0x1_0000)),
Some(dtb) => {
//TODO: try to parse dtb using stable library
Err(Error::new(EINVAL))
}
}
}
}
impl InterruptController for GenericInterruptController {
fn irq_init(&mut self, fdt: Option<&DeviceTree>) -> Result<()> {
let (dist_addr, dist_size, cpu_addr, cpu_size) =
GenericInterruptController::parse(fdt).unwrap();
unsafe {
//TODO: do kernel memory map using node.ranges
// Map in the Distributor interface
io_mmap(dist_addr, dist_size);
// Map in CPU0's interface
io_mmap(cpu_addr, cpu_size);
self.gic_cpu_if.init(cpu_addr);
self.gic_dist_if.init(dist_addr);
// Enable CPU0's GIC interface
self.gic_dist_if.write(GICC_CTLR, 1);
// Set CPU0's Interrupt Priority Mask
self.gic_dist_if.write(GICC_PMR, 0xff);
}
Ok(())
}
fn irq_ack(&mut self) -> u32 {
unsafe { self.gic_cpu_if.irq_ack() }
}
fn irq_eoi(&mut self, irq_num: u32) {
unsafe { self.gic_cpu_if.irq_eoi(irq_num) }
}
fn irq_enable(&mut self, irq_num: u32) {
unsafe { self.gic_dist_if.irq_enable(irq_num) }
}
fn irq_disable(&mut self, irq_num: u32) {
unsafe { self.gic_dist_if.irq_disable(irq_num) }
}
}
pub struct GicDistIf {
pub address: usize,
pub ncpus: u32,
pub nirqs: u32,
}
impl GicDistIf {
unsafe fn init(&mut self, addr: usize) {
self.address = addr;
// Disable IRQ Distribution
self.write(GICD_CTLR, 0);
let typer = self.read(GICD_TYPER);
self.ncpus = ((typer & (0x7 << 5)) >> 5) + 1;
self.nirqs = ((typer & 0x1f) + 1) * 32;
println!("gic: Distributor supports {:?} CPUs and {:?} IRQs", self.ncpus, self.nirqs);
// Set all SPIs to level triggered
for irq in (32..self.nirqs).step_by(16) {
self.write(GICD_ICFGR + ((irq / 16) * 4), 0);
}
// Disable all SPIs
for irq in (32..self.nirqs).step_by(32) {
self.write(GICD_ICENABLER + ((irq / 32) * 4), 0xffff_ffff);
}
// Affine all SPIs to CPU0 and set priorities for all IRQs
for irq in 0..self.nirqs {
if irq > 31 {
let ext_offset = GICD_ITARGETSR + (4 * (irq / 4));
let int_offset = irq % 4;
let mut val = self.read(ext_offset);
val |= 0b0000_0001 << (8 * int_offset);
self.write(ext_offset, val);
}
let ext_offset = GICD_IPRIORITY + (4 * (irq / 4));
let int_offset = irq % 4;
let mut val = self.read(ext_offset);
val |= 0b0000_0000 << (8 * int_offset);
self.write(ext_offset, val);
}
// Enable IRQ distribution
self.write(GICD_CTLR, 0x1);
}
unsafe fn irq_enable(&mut self, irq: u32) {
let offset = GICD_ISENABLER + (4 * (irq / 32));
let shift = 1 << (irq % 32);
let mut val = self.read(offset);
val |= shift;
self.write(offset, val);
}
unsafe fn irq_disable(&mut self, irq: u32) {
let offset = GICD_ICENABLER + (4 * (irq / 32));
let shift = 1 << (irq % 32);
let mut val = self.read(offset);
val |= shift;
self.write(offset, val);
}
unsafe fn read(&self, reg: u32) -> u32 {
let val = read_volatile((self.address + reg as usize) as *const u32);
val
}
unsafe fn write(&mut self, reg: u32, value: u32) {
write_volatile((self.address + reg as usize) as *mut u32, value);
}
}
pub struct GicCpuIf {
pub address: usize,
}
impl GicCpuIf {
fn init(&mut self, addr: usize) {
self.address = addr;
}
unsafe fn irq_ack(&mut self) -> u32 {
let irq = self.read(GICC_IAR) & 0x1ff;
if irq == 1023 {
panic!("irq_ack: got ID 1023!!!");
}
irq
}
unsafe fn irq_eoi(&mut self, irq: u32) {
self.write(GICC_EOIR, irq);
}
unsafe fn read(&self, reg: u32) -> u32 {
let val = read_volatile((self.address + reg as usize) as *const u32);
val
}
unsafe fn write(&mut self, reg: u32, value: u32) {
write_volatile((self.address + reg as usize) as *mut u32, value);
}
}
+44
View File
@@ -0,0 +1,44 @@
use alloc::{boxed::Box, vec::Vec};
use syscall::Result;
use fdt::DeviceTree;
mod gic;
trait InterruptController {
fn irq_init(&mut self, fdt: Option<&DeviceTree>) -> Result<()>;
fn irq_ack(&mut self) -> u32;
fn irq_eoi(&mut self, irq_num: u32);
fn irq_enable(&mut self, irq_num: u32);
fn irq_disable(&mut self, irq_num: u32);
}
struct IrqChipCore {
//TODO: support multi level interrupt constrollers
ic: Vec<Box<dyn InterruptController>>,
main_ic_idx: usize,
}
impl IrqChipCore {
pub fn irq_ack(&mut self) -> u32 {
self.ic[self.main_ic_idx].irq_ack()
}
pub fn irq_eoi(&mut self, irq_num: u32) {
self.ic[self.main_ic_idx].irq_eoi(irq_num)
}
pub fn irq_enable(&mut self, irq_num: u32) {
self.ic[self.main_ic_idx].irq_enable(irq_num)
}
pub fn irq_disable(&mut self, irq_num: u32) {
self.ic[self.main_ic_idx].irq_disable(irq_num)
}
}
static IRQ_CHIP = IrqChipCore { ic: Vec::new(), main_ic_idx: 0 };
pub fn init(fdt: Option<&DeviceTree>) {
let ic = Box::new(gic::GenericInterruptController::new());
let irq_chip_core = IrqChipCore { ic };
}
+22
View File
@@ -1,5 +1,9 @@
use crate::memory::Frame;
use crate::paging::{KernelMapper, PhysicalAddress, Page, PageFlags, VirtualAddress};
pub mod cpu;
pub mod gic;
pub mod irqchip;
pub mod generic_timer;
pub mod serial;
pub mod rtc;
@@ -21,3 +25,21 @@ pub unsafe fn init_noncore() {
pub unsafe fn init_ap() {
}
//map physical addr X to virtual addr PHYS_OFFSET + X
pub unsafe fn io_mmap(addr: usize, io_size: usize) {
let mut mapper = KernelMapper::lock();
let start_frame = Frame::containing_address(PhysicalAddress::new(addr));
let end_frame = Frame::containing_address(PhysicalAddress::new(addr + io_size - 1));
for frame in Frame::range_inclusive(start_frame, end_frame) {
let page = Page::containing_address(VirtualAddress::new(frame.start_address().data() + crate::PHYS_OFFSET));
mapper
.get_mut()
.expect("failed to access KernelMapper for mapping GIC distributor")
.map_phys(page.start_address(), frame.start_address(), PageFlags::new().write(true))
.expect("failed to map GIC distributor")
.flush();
}
}