Skip to content

Commit 610381e

Browse files
committed
feat(driver): Add PCIe, RP1 GPIO and UART config for BCM2712
1 parent 971ee8e commit 610381e

File tree

5 files changed

+353
-6
lines changed

5 files changed

+353
-6
lines changed
Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
11
// SPDX-License-Identifier: MIT OR Apache-2.0
22
//
33
// Copyright (c) 2018-2023 Andre Richter <andre.o.richter@gmail.com>
4+
// Copyright (c) 2026 Devansh Lodha <devanshlodha12@gmail.com>
45

56
//! Device driver.
67
7-
#[cfg(feature = "bsp_rpi4")]
8+
#[cfg(any(feature = "bsp_rpi4", feature = "bsp_rpi5"))]
89
mod arm;
9-
#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))]
10+
#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4", feature = "bsp_rpi5"))]
1011
mod bcm;
1112
mod common;
1213

13-
#[cfg(feature = "bsp_rpi4")]
14+
#[cfg(any(feature = "bsp_rpi4", feature = "bsp_rpi5"))]
1415
pub use arm::*;
15-
#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))]
16+
#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4", feature = "bsp_rpi5"))]
1617
pub use bcm::*;
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,33 @@
11
// SPDX-License-Identifier: MIT OR Apache-2.0
22
//
33
// Copyright (c) 2018-2023 Andre Richter <andre.o.richter@gmail.com>
4+
// Copyright (c) 2026 Devansh Lodha <devanshlodha12@gmail.com>
45

56
//! BCM driver top level.
67
8+
#[cfg(not(feature = "bsp_rpi5"))]
79
mod bcm2xxx_gpio;
810
#[cfg(feature = "bsp_rpi3")]
911
mod bcm2xxx_interrupt_controller;
1012
mod bcm2xxx_pl011_uart;
1113

14+
#[cfg(not(feature = "bsp_rpi5"))]
1215
pub use bcm2xxx_gpio::*;
1316
#[cfg(feature = "bsp_rpi3")]
1417
pub use bcm2xxx_interrupt_controller::*;
1518
pub use bcm2xxx_pl011_uart::*;
19+
20+
#[cfg(feature = "bsp_rpi5")]
21+
mod rp1_gpio;
22+
#[cfg(feature = "bsp_rpi5")]
23+
pub use rp1_gpio::*;
24+
25+
#[cfg(feature = "bsp_rpi5")]
26+
mod bcm2712_ic;
27+
#[cfg(feature = "bsp_rpi5")]
28+
pub use bcm2712_ic::*;
29+
30+
#[cfg(feature = "bsp_rpi5")]
31+
mod bcm2712_pcie;
32+
#[cfg(feature = "bsp_rpi5")]
33+
pub use bcm2712_pcie::*;
Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
// SPDX-License-Identifier: MIT OR Apache-2.0
2+
//
3+
// Copyright (c) 2026 Devansh Lodha <devanshlodha12@gmail.com>
4+
5+
//! PCIe Root Complex & RP1 Southbridge Driver.
6+
7+
use crate::{
8+
bsp::device_driver::common::MMIODerefWrapper,
9+
driver, exception,
10+
memory::{Address, Virtual},
11+
};
12+
use tock_registers::{
13+
interfaces::{ReadWriteable, Readable, Writeable},
14+
register_bitfields, register_structs,
15+
registers::{ReadOnly, ReadWrite},
16+
};
17+
18+
//--------------------------------------------------------------------------------------------------
19+
// Private Definitions
20+
//--------------------------------------------------------------------------------------------------
21+
22+
// PCIe Root Complex Registers
23+
register_bitfields! {
24+
u32,
25+
MISC_CTRL [
26+
SCB_ACCESS_EN OFFSET(12) NUMBITS(1) []
27+
]
28+
}
29+
30+
register_structs! {
31+
#[allow(non_snake_case)]
32+
pub PcieRootComplex {
33+
(0x0000 => _reserved_padding),
34+
(0x4008 => pub MISC_CTRL: ReadWrite<u32, MISC_CTRL::Register>),
35+
(0x400C => _reserved1),
36+
(0x4034 => pub BAR2_LO: ReadWrite<u32>),
37+
(0x4038 => pub BAR2_HI: ReadWrite<u32>),
38+
(0x403C => _reserved2),
39+
(0x40B4 => pub UBUS_BAR2_LO: ReadWrite<u32>),
40+
(0x40B8 => pub UBUS_BAR2_HI: ReadWrite<u32>),
41+
(0x40BC => @END),
42+
}
43+
}
44+
45+
// RP1 Configuration Space Registers
46+
register_bitfields! {
47+
u32,
48+
CMD [
49+
BUS_MASTER OFFSET(2) NUMBITS(1) [],
50+
MEM_ACCESS OFFSET(1) NUMBITS(1) []
51+
]
52+
}
53+
54+
register_structs! {
55+
#[allow(non_snake_case)]
56+
pub Rp1Config {
57+
(0x00 => pub VENDOR_ID: ReadOnly<u16>),
58+
(0x02 => pub DEVICE_ID: ReadOnly<u16>),
59+
(0x04 => pub COMMAND: ReadWrite<u32, CMD::Register>),
60+
(0x08 => _reserved0),
61+
(0x34 => pub CAP_PTR: ReadOnly<u32>),
62+
(0x38 => @END),
63+
}
64+
}
65+
66+
type RcRegisters = MMIODerefWrapper<PcieRootComplex>;
67+
type Rp1Registers = MMIODerefWrapper<Rp1Config>;
68+
69+
// Constants
70+
const UART0_VECTOR: usize = 25;
71+
const MIP_DOORBELL_PCI_LO: u32 = 0xFFFF_F000;
72+
const MIP_DOORBELL_PCI_HI: u32 = 0x0000_00FF;
73+
const MIP_PHYS_ADDR: u64 = 0x10_0013_0000;
74+
75+
// Offsets relative to the RP1_CFG_START mapping (0x1F_0010_0000)
76+
const OFFSET_RP1_CFG: usize = 0x9000;
77+
const OFFSET_MSIX_TABLE: usize = 0x31_0000;
78+
const OFFSET_APB_INTERNAL: usize = 0x8000; // 0x1F_0010_8000 relative to 0x1F_0010_0000
79+
80+
//--------------------------------------------------------------------------------------------------
81+
// Public Definitions
82+
//--------------------------------------------------------------------------------------------------
83+
84+
pub struct BCM2712PCIe {
85+
rc_regs: RcRegisters,
86+
rp1_regs: Rp1Registers,
87+
rp1_mapping_base: Address<Virtual>,
88+
}
89+
90+
//--------------------------------------------------------------------------------------------------
91+
// Public Code
92+
//--------------------------------------------------------------------------------------------------
93+
94+
impl BCM2712PCIe {
95+
pub const COMPATIBLE: &'static str = "BCM2712 PCIe RC";
96+
97+
/// Create an instance.
98+
///
99+
/// # Safety
100+
///
101+
/// - The user must ensure to provide correct MMIO start addresses.
102+
pub unsafe fn new(rc_base: Address<Virtual>, rp1_mapping_base: Address<Virtual>) -> Self {
103+
// RP1 Config is at offset 0x9000 in the mapped region
104+
let rp1_cfg_addr = rp1_mapping_base + OFFSET_RP1_CFG;
105+
106+
Self {
107+
rc_regs: RcRegisters::new(rc_base),
108+
rp1_regs: Rp1Registers::new(rp1_cfg_addr),
109+
rp1_mapping_base,
110+
}
111+
}
112+
113+
/// Configure the MSI-X table for a specific vector.
114+
unsafe fn configure_msix(&self, vector: usize, addr_lo: u32, addr_hi: u32, data: u32) {
115+
let table_base = (self.rp1_mapping_base + OFFSET_MSIX_TABLE).as_usize() as *mut u32;
116+
let entry_ptr = table_base.add(vector * 4);
117+
118+
core::ptr::write_volatile(entry_ptr.add(0), addr_lo);
119+
core::ptr::write_volatile(entry_ptr.add(1), addr_hi);
120+
core::ptr::write_volatile(entry_ptr.add(2), data);
121+
core::ptr::write_volatile(entry_ptr.add(3), 0); // Unmasked
122+
}
123+
124+
/// Unmask the interrupt at the RP1 internal APB controller.
125+
unsafe fn unmask_rp1_irq(&self, vector: usize) {
126+
let apb_base = (self.rp1_mapping_base + OFFSET_APB_INTERNAL).as_usize() as *mut u32;
127+
// The interrupt controller registers start at offset 0x8 inside the APB block?
128+
// Reference: "let ctrl_base = (RP1_APB_BASE + 0x008) as *mut u32;"
129+
let ctrl_base = apb_base.add(2); // 0x8 / 4 = 2
130+
let ctrl_reg = ctrl_base.add(vector);
131+
132+
// 0x9: Bit 3 (Enable) | Bit 0 (Target)
133+
core::ptr::write_volatile(ctrl_reg, (1 << 3) | (1 << 0));
134+
}
135+
}
136+
137+
impl driver::interface::DeviceDriver for BCM2712PCIe {
138+
type IRQNumberType = exception::asynchronous::IRQNumber;
139+
140+
fn compatible(&self) -> &'static str {
141+
Self::COMPATIBLE
142+
}
143+
144+
unsafe fn init(&self) -> Result<(), &'static str> {
145+
// 1. Enable System Core Bus (SCB) Access
146+
self.rc_regs.MISC_CTRL.modify(MISC_CTRL::SCB_ACCESS_EN::SET);
147+
148+
// 2. Configure BAR2 for Inbound Translation
149+
// 0x1C = 64-bit | Prefetchable
150+
self.rc_regs.BAR2_LO.set(MIP_DOORBELL_PCI_LO | 0x1C);
151+
self.rc_regs.BAR2_HI.set(MIP_DOORBELL_PCI_HI);
152+
153+
// Map to MIP Physical Address
154+
self.rc_regs.UBUS_BAR2_LO.set((MIP_PHYS_ADDR as u32) | 1); // Enable
155+
self.rc_regs.UBUS_BAR2_HI.set((MIP_PHYS_ADDR >> 32) as u32);
156+
157+
// 3. Enable RP1 Bus Mastering
158+
self.rp1_regs
159+
.COMMAND
160+
.modify(CMD::BUS_MASTER::SET + CMD::MEM_ACCESS::SET);
161+
162+
// 4. Enable MSI-X Capability on RP1
163+
// We need to walk the capability list.
164+
let mut cap_offset = (self.rp1_regs.CAP_PTR.get() & 0xFF) as usize;
165+
// Start of config space in our virtual mapping
166+
let base_ptr = (self.rp1_mapping_base + OFFSET_RP1_CFG).as_usize() as *const u8;
167+
168+
while cap_offset != 0 {
169+
let cap_hdr_ptr = base_ptr.add(cap_offset) as *const u32;
170+
let cap_hdr = core::ptr::read_volatile(cap_hdr_ptr);
171+
172+
if (cap_hdr & 0xFF) == 0x11 {
173+
// MSI-X ID
174+
// Bit 31 is Enable
175+
if (cap_hdr & (1 << 31)) == 0 {
176+
core::ptr::write_volatile(cap_hdr_ptr as *mut u32, cap_hdr | (1 << 31));
177+
}
178+
break;
179+
}
180+
cap_offset = ((cap_hdr >> 8) & 0xFF) as usize;
181+
}
182+
183+
// 5. Configure UART0 Interrupt (Vector 25)
184+
self.configure_msix(UART0_VECTOR, MIP_DOORBELL_PCI_LO, MIP_DOORBELL_PCI_HI, 0);
185+
self.unmask_rp1_irq(UART0_VECTOR);
186+
187+
Ok(())
188+
}
189+
}

19_kernel_heap/kernel/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// SPDX-License-Identifier: MIT OR Apache-2.0
22
//
33
// Copyright (c) 2018-2023 Andre Richter <andre.o.richter@gmail.com>
4+
// Copyright (c) 2026 Devansh Lodha <devanshlodha12@gmail.com>
45

56
//! PL011 UART driver.
67
//!
@@ -294,8 +295,20 @@ impl PL011UartInner {
294295
// contents of IBRD or FBRD, a LCR_H write must always be performed at the end.
295296
//
296297
// Set the baud rate, 8N1 and FIFO enabled.
297-
self.registers.IBRD.write(IBRD::BAUD_DIVINT.val(3));
298-
self.registers.FBRD.write(FBRD::BAUD_DIVFRAC.val(16));
298+
#[cfg(feature = "bsp_rpi5")]
299+
{
300+
// RP1 UART Clock: 50MHz
301+
// Target Baud: 115200
302+
// Divider: 50,000,000 / (16 * 115200) = 27.1267
303+
// IBRD = 26, FBRD = 3 (Adjusted for timing margin)
304+
self.registers.IBRD.write(IBRD::BAUD_DIVINT.val(26));
305+
self.registers.FBRD.write(FBRD::BAUD_DIVFRAC.val(3));
306+
}
307+
#[cfg(not(feature = "bsp_rpi5"))]
308+
{
309+
self.registers.IBRD.write(IBRD::BAUD_DIVINT.val(3));
310+
self.registers.FBRD.write(FBRD::BAUD_DIVFRAC.val(16));
311+
}
299312
self.registers
300313
.LCR_H
301314
.write(LCR_H::WLEN::EightBit + LCR_H::FEN::FifosEnabled);
@@ -384,6 +397,9 @@ impl PL011UartInner {
384397
impl fmt::Write for PL011UartInner {
385398
fn write_str(&mut self, s: &str) -> fmt::Result {
386399
for c in s.chars() {
400+
if c == '\n' {
401+
self.write_char('\r');
402+
}
387403
self.write_char(c);
388404
}
389405

0 commit comments

Comments
 (0)