Update the init of pci device for LoongArch
This commit is contained in:
parent
3391863312
commit
0370f8fdf3
|
|
@ -2,10 +2,16 @@
|
|||
|
||||
//! PCI bus access
|
||||
|
||||
use core::alloc::Layout;
|
||||
|
||||
use align_ext::AlignExt;
|
||||
use fdt::node::FdtNode;
|
||||
use spin::Once;
|
||||
|
||||
use super::boot::DEVICE_TREE;
|
||||
use crate::{bus::pci::PciDeviceLocation, io::IoMem, mm::VmIoOnce, prelude::*, Error};
|
||||
use crate::{
|
||||
bus::pci::PciDeviceLocation, io::IoMem, mm::VmIoOnce, prelude::*, sync::SpinLock, Error,
|
||||
};
|
||||
|
||||
static PCI_IO_MEM: Once<IoMem> = Once::new();
|
||||
|
||||
|
|
@ -64,6 +70,9 @@ pub(crate) fn init() -> Result<()> {
|
|||
return Err(Error::IoError);
|
||||
}
|
||||
|
||||
// Initialize the MMIO allocator
|
||||
init_mmio_allocator_from_fdt(&pci);
|
||||
|
||||
PCI_IO_MEM.call_once(|| unsafe {
|
||||
IoMem::acquire(
|
||||
(region.starting_address as usize)
|
||||
|
|
@ -80,3 +89,74 @@ pub(crate) const MSIX_DEFAULT_MSG_ADDR: u32 = 0x2ff0_0000;
|
|||
pub(crate) fn construct_remappable_msix_address(remapping_index: u32) -> u32 {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
/// A simple MMIO allocator managing a linear region.
|
||||
///
|
||||
/// In loongarch, the starting address of the memory bar of the PCI device
|
||||
/// needs to be allocated within the specified range
|
||||
struct MmioAllocator {
|
||||
base: Paddr,
|
||||
size: Paddr,
|
||||
offset: Paddr,
|
||||
}
|
||||
|
||||
impl MmioAllocator {
|
||||
/// Creates a new MMIO allocator with a given base and size.
|
||||
const fn new(base: Paddr, size: Paddr) -> Self {
|
||||
MmioAllocator {
|
||||
base,
|
||||
size,
|
||||
offset: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Allocates a physical address range with the specified alignment and size.
|
||||
fn allocate(&mut self, layout: Layout) -> Option<Paddr> {
|
||||
let align = layout.align();
|
||||
let size = layout.size();
|
||||
|
||||
let current = self.base + self.offset;
|
||||
let aligned = current.align_up(align);
|
||||
let aligned_offset = aligned - self.base;
|
||||
|
||||
if aligned_offset + size > self.size {
|
||||
return None;
|
||||
}
|
||||
self.offset = aligned_offset + size;
|
||||
Some(aligned)
|
||||
}
|
||||
}
|
||||
|
||||
static MMIO_ALLOCATOR: Once<SpinLock<MmioAllocator>> = Once::new();
|
||||
|
||||
/// Initializes the MMIO allocator from the PCIe node's "ranges" property.
|
||||
fn init_mmio_allocator_from_fdt(node: &FdtNode) {
|
||||
let ranges = node
|
||||
.property("ranges")
|
||||
.expect("Missing 'ranges' property in PCIe node");
|
||||
let data = ranges.value;
|
||||
|
||||
let entry_size = 7 * 4; // Each entry is 7 x u32 = 28 bytes
|
||||
let mut i = 0;
|
||||
|
||||
while i + entry_size <= data.len() {
|
||||
let pci_space = u32::from_be_bytes(data[i..i + 4].try_into().unwrap());
|
||||
let pci_addr = u64::from_be_bytes(data[i + 4..i + 12].try_into().unwrap());
|
||||
let cpu_addr = u64::from_be_bytes(data[i + 12..i + 20].try_into().unwrap());
|
||||
let size = u64::from_be_bytes(data[i + 20..i + 28].try_into().unwrap());
|
||||
|
||||
// Only initialize with memory-type region
|
||||
if (pci_space >> 24) == 0x2 {
|
||||
MMIO_ALLOCATOR
|
||||
.call_once(|| SpinLock::new(MmioAllocator::new(cpu_addr as usize, size as usize)));
|
||||
break;
|
||||
}
|
||||
|
||||
i += entry_size;
|
||||
}
|
||||
}
|
||||
|
||||
/// Allocates an MMIO address range using the global allocator.
|
||||
pub(crate) fn alloc_mmio(layout: Layout) -> Option<Paddr> {
|
||||
MMIO_ALLOCATOR.get().unwrap().lock().allocate(layout)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -166,8 +166,12 @@ impl Bar {
|
|||
return Err(Error::InvalidArgs);
|
||||
}
|
||||
// Get the original value first, then write all 1 to the register to get the length
|
||||
let raw = location.read32(index as u16 * 4 + PciDeviceCommonCfgOffset::Bar0 as u16);
|
||||
if raw == 0 {
|
||||
let offset = index as u16 * 4 + PciDeviceCommonCfgOffset::Bar0 as u16;
|
||||
let raw = location.read32(offset);
|
||||
location.write32(offset, !0);
|
||||
let len_encoded = location.read32(offset);
|
||||
location.write32(offset, raw);
|
||||
if len_encoded == 0 {
|
||||
// no BAR
|
||||
return Err(Error::InvalidArgs);
|
||||
}
|
||||
|
|
@ -243,7 +247,7 @@ impl MemoryBar {
|
|||
location.write32(offset, raw);
|
||||
let mut address_length = AddrLen::Bits32;
|
||||
// base address, it may be bit64 or bit32
|
||||
let base: u64 = match (raw & 0b110) >> 1 {
|
||||
let raw_base: u64 = match (raw & 0b110) >> 1 {
|
||||
// bits32
|
||||
0 => (raw & !0xF) as u64,
|
||||
// bits64
|
||||
|
|
@ -257,6 +261,34 @@ impl MemoryBar {
|
|||
};
|
||||
// length
|
||||
let size = (!(len_encoded & !0xF)).wrapping_add(1);
|
||||
|
||||
// Some architectures may not initialize PCI devices at all. Even in x86-64 systems, the
|
||||
// BIOS may leave some devices uninitialized. If support is available, we can allocate the
|
||||
// region ourselves. Otherwise, if the BAR is not properly initialized, we will ignore it
|
||||
// and return an error.
|
||||
#[cfg(not(target_arch = "loongarch64"))]
|
||||
let base = if raw_base == 0 {
|
||||
return Err(Error::InvalidArgs);
|
||||
} else {
|
||||
raw_base
|
||||
};
|
||||
// In LoongArch, the BAR base address needs to be allocated manually.
|
||||
#[cfg(target_arch = "loongarch64")]
|
||||
let base = {
|
||||
use core::alloc::Layout;
|
||||
crate::arch::pci::alloc_mmio(
|
||||
Layout::from_size_align(size as usize, size as usize).unwrap(),
|
||||
)
|
||||
.unwrap() as u64
|
||||
};
|
||||
|
||||
if address_length == AddrLen::Bits64 {
|
||||
location.write32(offset, base as u32);
|
||||
location.write32(offset + 4, (base >> 32) as u32);
|
||||
} else {
|
||||
location.write32(offset, base as u32);
|
||||
}
|
||||
|
||||
let prefetchable = raw & 0b1000 != 0;
|
||||
// The BAR is located in I/O memory region
|
||||
Ok(MemoryBar {
|
||||
|
|
|
|||
|
|
@ -72,13 +72,19 @@ impl PciCommonDevice {
|
|||
|
||||
let capabilities = Vec::new();
|
||||
let device_id = PciDeviceId::new(location);
|
||||
let bar_manager = BarManager::new(location);
|
||||
let bar_manager = BarManager {
|
||||
bars: [const { None }; 6],
|
||||
};
|
||||
let mut device = Self {
|
||||
device_id,
|
||||
location,
|
||||
bar_manager,
|
||||
capabilities,
|
||||
};
|
||||
device.set_command(
|
||||
device.command() | Command::MEMORY_SPACE | Command::BUS_MASTER | Command::IO_SPACE,
|
||||
);
|
||||
device.bar_manager = BarManager::new(location);
|
||||
device.capabilities = Capability::device_capabilities(&mut device);
|
||||
Some(device)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue