Compare commits

...

21 Commits

Author SHA1 Message Date
alnyan 717bbda123 block/mmc: improve sdhci initialization 2025-09-26 11:32:37 +03:00
alnyan aa86f377f2 block/mmc: fix sdhci on real hw (raspi4b) 2025-09-23 15:14:14 +03:00
alnyan e8acfb5a40 block/mmc: pio write functionality 2025-09-19 10:22:33 +03:00
alnyan b5d704064d block/mmc: pio read-only sdhci driver 2025-09-19 10:22:31 +03:00
alnyan e934b4d696 aarch64: simplify GIC init 2025-09-17 14:18:06 +03:00
alnyan 6d8d97d492 maint: remove raqote dependency 2025-09-17 11:23:29 +03:00
alnyan ca01f57873 ports: add GNU GMP 6.3.0 2025-08-28 16:34:14 +03:00
alnyan 9be467d5d5 ports: add doomgeneric 2025-08-25 10:39:59 +03:00
alnyan 0c2ebbf7b3 fat32: move cluster chain cache to FileNode 2025-08-16 09:05:39 +03:00
alnyan cb4c0bc4b0 sysutils: add tree utility 2025-08-15 08:38:38 +03:00
alnyan c1b62aef1d memfs: take mtimes from tar 2025-08-14 15:55:38 +03:00
alnyan 6469914be1 rtc: implement google,goldfish-rtc 2025-08-14 15:17:41 +03:00
alnyan 131e6adc3d riscv: fix irqhandle migration 2025-08-14 15:11:26 +03:00
alnyan 322cb0a958 rtc: improve rtc device drivers 2025-08-14 15:08:19 +03:00
alnyan 6552fa8059 irq: FullIrq -> IrqHandle 2025-08-14 11:34:00 +03:00
alnyan 0ff48fd520 sysutils: implement poweroff 2025-08-14 10:31:09 +03:00
alnyan 9c32c11b0b sysutils: add dummy chroot program 2025-08-02 20:43:21 +03:00
alnyan 3be32b7b8f maint: split peripheral drivers into bsp packages 2025-08-01 10:21:49 +03:00
alnyan 919d6d62ba pinctrl: basic gpio led support 2025-08-01 08:59:24 +03:00
alnyan 062db06473 pinctrl: basic support for gpio 2025-07-31 00:03:45 +03:00
alnyan 06a6e11dab pinctrl: basic pinctrl/pinmux support 2025-07-30 20:43:12 +03:00
113 changed files with 76344 additions and 432 deletions
Generated
+108
View File
@@ -258,6 +258,26 @@ version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61"
[[package]]
name = "bitfield"
version = "0.19.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db1bcd90f88eabbf0cadbfb87a45bceeaebcd3b4bc9e43da379cd2ef0162590d"
dependencies = [
"bitfield-macros",
]
[[package]]
name = "bitfield-macros"
version = "0.19.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3787a07661997bfc05dd3431e379c0188573f78857080cf682e1393ab8e4d64c"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.98",
]
[[package]]
name = "bitflags"
version = "1.3.2"
@@ -2638,6 +2658,70 @@ dependencies = [
"yggdrasil-abi",
]
[[package]]
name = "ygg_driver_bsp_arm"
version = "0.1.0"
dependencies = [
"bytemuck",
"device-api",
"device-tree",
"libk",
"libk-mm",
"libk-util",
"log",
"tock-registers",
"yggdrasil-abi",
]
[[package]]
name = "ygg_driver_bsp_bcm283x"
version = "0.1.0"
dependencies = [
"bytemuck",
"device-api",
"device-tree",
"futures-util",
"kernel-arch-aarch64",
"libk",
"libk-mm",
"libk-util",
"log",
"tock-registers",
"yggdrasil-abi",
]
[[package]]
name = "ygg_driver_bsp_jh7110"
version = "0.1.0"
dependencies = [
"bytemuck",
"device-api",
"device-tree",
"futures-util",
"libk",
"libk-mm",
"libk-util",
"log",
"tock-registers",
"yggdrasil-abi",
]
[[package]]
name = "ygg_driver_bsp_riscv"
version = "0.1.0"
dependencies = [
"bytemuck",
"device-api",
"device-tree",
"kernel-arch-riscv64",
"libk",
"libk-mm",
"libk-util",
"log",
"tock-registers",
"yggdrasil-abi",
]
[[package]]
name = "ygg_driver_fat32"
version = "0.1.0"
@@ -2663,6 +2747,25 @@ dependencies = [
"yggdrasil-abi",
]
[[package]]
name = "ygg_driver_mmc_generic_sdhci"
version = "0.1.0"
dependencies = [
"async-trait",
"bitfield",
"bitflags 2.8.0",
"bytemuck",
"device-api",
"device-tree",
"futures-util",
"libk",
"libk-mm",
"libk-util",
"log",
"tock-registers",
"yggdrasil-abi",
]
[[package]]
name = "ygg_driver_net_core"
version = "0.1.0"
@@ -2951,8 +3054,13 @@ dependencies = [
"yboot-proto",
"ygg_driver_acpi",
"ygg_driver_ahci",
"ygg_driver_bsp_arm",
"ygg_driver_bsp_bcm283x",
"ygg_driver_bsp_jh7110",
"ygg_driver_bsp_riscv",
"ygg_driver_fat32",
"ygg_driver_input",
"ygg_driver_mmc_generic_sdhci",
"ygg_driver_net_core",
"ygg_driver_net_igbe",
"ygg_driver_net_loopback",
+1
View File
@@ -7,6 +7,7 @@ exclude = [
"toolchain",
"userspace/dynload-program",
"userspace/lib/ygglibc",
"userspace",
"toolchain-c"
]
members = [
+54 -1
View File
@@ -42,4 +42,57 @@ $ booti ${loadaddr} ${initrd_addr_r}:<SIZE-PRINTED-WHEN-LOADING-INITRD> ${fdt_ad
###### a quick command for a development boot
###### (FIXME when initrd gets larger than 64MiB)
env set ipaddr 13.0.0.2; env set fdt_addr_r 0x11000000; env set initrd_addr_r 0x04000000; tftpboot ${initrd_addr_r} 13.0.0.1:initrd.tar; tftpboot ${loadaddr} 13.0.0.1:kernel.bin; load mmc 0:1 ${fdt_addr_r} bcm2711-rpi-4-b.dtb; fdt addr ${fdt_addr_r}; fdt resize; fdt memory 0x0 0x3C000000; booti ${loadaddr} ${initrd_addr_r}:67108864 ${fdt_addr_r}
env set ipaddr 13.0.0.2; env set fdt_addr_r 0x11000000; env set initrd_addr_r 0x20000000; tftpboot ${initrd_addr_r} 13.0.0.1:initrd.img; tftpboot ${loadaddr} 13.0.0.1:yggdrasil-kernel.bin; load mmc 1:1 ${fdt_addr_r} bcm2711-rpi-4-b.dtb; fdt addr ${fdt_addr_r}; fdt resize; fdt memory 0x0 0x3C000000; booti ${loadaddr} ${initrd_addr_r}:0x4000000 ${fdt_addr_r}
dhcp;
env set initrd_addr_r 0x20000000; tftpboot ${initrd_addr_r} 192.168.88.10:initrd.img; tftpboot ${loadaddr} 192.168.88.10:yggdrasil-kernel.bin; load mmc 1:1 ${fdt_addr_r} bcm2711-rpi-4-b.dtb; fdt addr ${fdt_addr_r}; fdt resize; fdt memory 0x0 0x3C000000; booti ${loadaddr} ${initrd_addr_r}:0x4000000 ${fdt_addr_r}
Missing drivers:
No driver for Some("hvs@7e400000") ("brcm,bcm2711-hvs")
No driver for Some("i2c@7e804000") ("brcm,bcm2711-i2c")
also "brcm,bcm2835-i2c"
No driver for Some("usb@7e980000") ("brcm,bcm2835-usb")
No driver for Some("local_intc@40000000") ("brcm,bcm2836-l1-intc")
: avs-monitor@7d5d2000: probed
No driver for Some("thermal") ("brcm,bcm2711-thermal")
No driver for Some("dma@7e007000") ("brcm,bcm2835-dma")
No driver for Some("watchdog@7e100000") ("brcm,bcm2835-pm")
also "brcm,bcm2835-pm-wdt"
No driver for Some("rng@7e104000") ("brcm,bcm2711-rng200")
No driver for Some("pixelvalve@7e206000") ("brcm,bcm2711-pixelvalve0")
No driver for Some("pixelvalve@7e207000") ("brcm,bcm2711-pixelvalve1")
No driver for Some("pixelvalve@7e20a000") ("brcm,bcm2711-pixelvalve2")
No driver for Some("pwm@7e20c800") ("brcm,bcm2835-pwm")
No driver for Some("pixelvalve@7e216000") ("brcm,bcm2711-pixelvalve4")
No driver for Some("clock@7ef00000") ("brcm,brcm2711-dvp")
No driver for Some("interrupt-controller@7ef00100") ("brcm,bcm2711-l2-intc")
also "brcm,l2-intc"
No driver for Some("hdmi@7ef00700") ("brcm,bcm2711-hdmi0")
No driver for Some("i2c@7ef04500") ("brcm,bcm2711-hdmi-i2c")
No driver for Some("hdmi@7ef05700") ("brcm,bcm2711-hdmi1")
No driver for Some("i2c@7ef09500") ("brcm,bcm2711-hdmi-i2c")
No driver for Some("firmware") ("raspberrypi,bcm2835-firmware")
also "simple-mfd"
No driver for Some("clocks") ("raspberrypi,firmware-clocks")
No driver for Some("gpio") ("raspberrypi,firmware-gpio")
No driver for Some("reset") ("raspberrypi,firmware-reset")
No driver for Some("power") ("raspberrypi,bcm2835-power")
No driver for Some("mailbox@7e00b840") ("brcm,bcm2835-vchiq")
No driver for Some("phy") ("usb-nop-xceiv")
No driver for Some("gpu") ("brcm,bcm2711-vc5")
No driver for Some("mmc@7e340000") ("brcm,bcm2711-emmc2")
No driver for Some("arm-pmu") ("arm,cortex-a72-pmu")
also "arm,armv8-pmuv3"
No driver for Some("cpu@0") ("arm,cortex-a72")
No driver for Some("cpu@1") ("arm,cortex-a72")
No driver for Some("cpu@2") ("arm,cortex-a72")
No driver for Some("cpu@3") ("arm,cortex-a72")
No driver for Some("pcie@7d500000") ("brcm,bcm2711-pcie")
No driver for Some("ethernet@7d580000") ("brcm,bcm2711-genet-v5")
No driver for Some("mdio@e14") ("brcm,genet-mdio-v5")
No driver for Some("leds") ("gpio-leds")
No driver for Some("wifi-pwrseq") ("mmc-pwrseq-simple")
No driver for Some("sd_io_1v8_reg") ("regulator-gpio")
No driver for Some("sd_vcc_reg") ("regulator-fixed")
+91
View File
@@ -37,3 +37,94 @@ env set ipaddr 13.0.0.2; env set initrd_addr_r 0x70000000; tftpboot ${initrd_add
dhcp
dhcp; env set initrd_addr_r 0x70000000; tftpboot ${initrd_addr_r} 192.168.88.10:initrd.img; tftpboot ${loadaddr} 192.168.88.10:yggdrasil-kernel.bin; load mmc 1:3 ${fdt_addr_r} dtbs/6.6.20-starfive/starfive/${fdtfile}; fdt resize; booti ${loadaddr} ${initrd_addr_r}:60000000 ${fdt_addr_r}
Missing drivers:
Clock/reset/pin:
No driver for Some("pinctrl@17020000") ("starfive,jh7110-aon-pinctrl")
No driver for Some("clock-controller@19810000") ("starfive,jh7110-ispcrg")
No driver for Some("clock-controller@295c0000") ("starfive,jh7110-voutcrg")
Power/reg/GPIO:
No driver for Some("opp-table-0") ("operating-points-v2")
No driver for Some("pmic@36") ("x-powers,axp15060")
No driver for Some("power-controller@17030000") ("starfive,jh7110-pmu")
No driver for Some("leds") ("gpio-leds")
No driver for Some("gpio-restart") ("gpio-restart")
Serial:
No driver for Some("i2c@10030000") ("snps,designware-i2c")
No driver for Some("i2c@10050000") ("snps,designware-i2c")
No driver for Some("i2c@12050000") ("snps,designware-i2c")
No driver for Some("i2c@12060000") ("snps,designware-i2c")
No driver for Some("spi@10060000") ("arm,pl022")
also "arm,primecell"
Bus:
No driver for Some("usb@10100000") ("starfive,jh7110-usb")
No driver for Some("usb@0") ("cdns,usb3")
No driver for Some("phy@10200000") ("starfive,jh7110-usb-phy")
No driver for Some("phy@10210000") ("starfive,jh7110-pcie-phy")
No driver for Some("phy@10220000") ("starfive,jh7110-pcie-phy")
No driver for Some("pcie@940000000") ("starfive,jh7110-pcie")
No driver for Some("pcie@9c0000000") ("starfive,jh7110-pcie")
Interrupt:
No driver for Some("interrupt-controller") ("riscv,cpu-intc")
No driver for Some("timer@2000000") ("starfive,jh7110-clint")
also "sifive,clint0"
Display/GPU subsystem:
No driver for Some("display-subsystem") ("starfive,jh7110-display")
also "verisilicon,display-subsystem"
No driver for Some("dsi-output") ("starfive,jh7110-display-encoder")
also "verisilicon,dsi-encoder"
No driver for Some("jpu@13090000") ("starfive,jpu")
No driver for Some("vpu_dec@130a0000") ("starfive,vdec")
No driver for Some("vpu_enc@130b0000") ("starfive,venc")
No driver for Some("gpu@18000000") ("img-gpu")
No driver for Some("vin_sysctl@19800000") ("starfive,jh7110-vin")
No driver for Some("phy@19820000") ("starfive,jh7110-dphy-rx")
No driver for Some("dc8200@29400000") ("starfive,jh7110-dc8200")
also "verisilicon,dc8200"
No driver for Some("hdmi@29590000") ("starfive,jh7110-hdmi")
also "inno,hdmi"
No driver for Some("mipi@295d0000") ("starfive,jh7110-mipi_dsi")
also "cdns,dsi"
No driver for Some("mipi-dphy@295e0000") ("starfive,jh7110-mipi-dphy-tx")
also "m31,mipi-dphy-tx"
Misc:
No driver for Some("mailbox_client") ("starfive,mailbox-test")
No driver for Some("cache-controller@2010000") ("starfive,jh7110-ccache")
also "sifive,ccache0"
also "cache"
No driver for Some("pwm@120d0000") ("starfive,jh7110-pwm")
also "opencores,pwm-v1"
No driver for Some("temperature-sensor@120e0000") ("starfive,jh7110-temp")
No driver for Some("timer@13050000") ("starfive,jh7110-timer")
No driver for Some("mailbox@13060000") ("starfive,mail_box")
No driver for Some("watchdog@13070000") ("starfive,jh7110-wdt")
No driver for Some("crypto@16000000") ("starfive,jh7110-crypto")
No driver for Some("rng@1600c000") ("starfive,jh7110-trng")
No driver for Some("mdio") ("snps,dwmac-mdio")
No driver for Some("mdio") ("snps,dwmac-mdio")
No driver for Some("dma-controller@16050000") ("starfive,jh7110-axi-dma")
No driver for Some("dma-controller@16008000") ("arm,pl080")
also "arm,primecell"
No driver for Some("rtc@17040000") ("starfive,jh7110-rtc")
No driver for Some("e24@6e210000") ("starfive,e24")
No driver for Some("linux,cma") ("shared-dma-pool")
Storage:
No driver for Some("spi@13010000") ("starfive,jh7110-qspi")
also "cdns,qspi-nor"
No driver for Some("flash@0") ("jedec,spi-nor")
No driver for Some("partitions") ("fixed-partitions")
No driver for Some("mmc@16010000") ("starfive,jh7110-mmc")
No driver for Some("mmc@16020000") ("starfive,jh7110-mmc")
Audio:
No driver for Some("pwmdac@100b0000") ("starfive,jh7110-pwmdac")
No driver for Some("i2s@120b0000") ("starfive,jh7110-i2stx0")
+10
View File
@@ -56,11 +56,17 @@ git-version = "0.3.9"
aarch64-cpu.workspace = true
device-tree.workspace = true
kernel-arch-aarch64.workspace = true
ygg_driver_bsp_arm.path = "driver/bsp/arm"
ygg_driver_bsp_bcm283x.path = "driver/bsp/bcm283x"
ygg_driver_mmc_generic_sdhci.path = "driver/mmc/generic-sdhci"
[target.'cfg(target_arch = "riscv64")'.dependencies]
device-tree.workspace = true
kernel-arch-riscv64.workspace = true
ygg_driver_bsp_arm.path = "driver/bsp/arm" # PrimeCell components
ygg_driver_bsp_riscv.path = "driver/bsp/riscv"
ygg_driver_net_stmmac.path = "driver/net/stmmac"
ygg_driver_bsp_jh7110.path = "driver/bsp/jh7110"
[target.'cfg(target_arch = "x86_64")'.dependencies]
yboot-proto.workspace = true
@@ -87,7 +93,11 @@ kernel-arch-aarch64.workspace = true
kernel-arch-riscv64.workspace = true
ygg_driver_acpi.path = "driver/acpi"
ygg_driver_bsp_arm.path = "driver/bsp/arm"
ygg_driver_bsp_riscv.path = "driver/bsp/riscv"
ygg_driver_net_stmmac.path = "driver/net/stmmac"
ygg_driver_bsp_bcm283x.path = "driver/bsp/bcm283x"
ygg_driver_bsp_jh7110.path = "driver/bsp/jh7110"
[features]
default = ["fb_console"]
+6
View File
@@ -44,6 +44,7 @@ pub struct PerCpuData {
}
pub static CPU_COUNT: AtomicUsize = AtomicUsize::new(1);
pub static mut BOOT_HART_ID: u64 = 0;
static IPI_QUEUES: OneTimeInit<Vec<IpiQueue<ArchitectureImpl>>> = OneTimeInit::new();
static HART_TO_QUEUE: IrqSafeSpinlock<ArchitectureImpl, BTreeMap<u32, usize>> =
IrqSafeSpinlock::new(BTreeMap::new());
@@ -60,6 +61,11 @@ impl CpuData for PerCpuData {
}
}
/// Returns the ID of the bootstrap HART
pub fn boot_hart_id() -> u64 {
unsafe { BOOT_HART_ID }
}
#[unsafe(naked)]
extern "C" fn idle_task(_: usize) -> ! {
core::arch::naked_asm!("1: nop; j 1b");
+8
View File
@@ -4,6 +4,8 @@ const EXT_HSM: u64 = 0x48534D;
const EXT_TIME: u64 = 0x54494D45;
const EXT_DBCN: u64 = 0x4442434E;
const EXT_SPI: u64 = 0x735049;
const EXT_SYSTEM_SHUTDOWN: u64 = 0x53525354;
const EXT_SYSTEM_SHUTDOWN_LEGACY: u64 = 0x08;
primitive_enum! {
pub enum Status: i64 {
@@ -108,3 +110,9 @@ pub fn sbi_debug_console_write_byte(byte: u8) {
pub fn sbi_set_timer(next_event: u64) {
unsafe { sbi_do_call(EXT_TIME, 0x00, next_event, 0, 0, 0, 0, 0) }.ok();
}
pub fn sbi_system_shutdown() -> ! {
unsafe { sbi_do_call(EXT_SYSTEM_SHUTDOWN, 0x00, 0, 0, 0, 0, 0, 0) }.ok();
unsafe { sbi_do_call(EXT_SYSTEM_SHUTDOWN_LEGACY, 0x00, 0, 0, 0, 0, 0, 0) }.ok();
unreachable!()
}
+13 -8
View File
@@ -1,8 +1,8 @@
#![feature(allocator_api)]
#![feature(allocator_api, never_type)]
#![no_std]
use acpi::AcpiTables;
use acpi_system::{AcpiInterruptMethod, AcpiSystem};
use acpi_system::{AcpiInterruptMethod, AcpiSleepState, AcpiSystem};
use alloc::boxed::Box;
use libk::error::Error;
use libk_util::{sync::IrqSafeSpinlock, OneTimeInit};
@@ -45,6 +45,17 @@ pub fn get_pci_route(
.ok()
}
pub fn power_off() -> Result<!, Error> {
let system = ACPI_SYSTEM.get();
unsafe {
system.lock().enter_sleep_state(AcpiSleepState::S5).ok();
loop {
core::arch::asm!("cli; hlt");
}
}
}
/// Initializes ACPI management
pub fn switch_to_acpi(tables: &'static AcpiTables<AcpiHandlerImpl>) -> Result<(), Error> {
// NOTE mostly broken for real HW
@@ -67,12 +78,6 @@ pub fn switch_to_acpi(tables: &'static AcpiTables<AcpiHandlerImpl>) -> Result<()
// // 6. Do something with the devices
// // 7. Actually enter the S5 state
// unsafe {
// PLATFORM
// .send_ipi(IpiDeliveryTarget::OtherCpus, IpiMessage::Shutdown)
// .unwrap();
// }
// SHUTDOWN_FENCE.signal();
// SHUTDOWN_FENCE.wait_all(CPU_COUNT.load(Ordering::Acquire));
+1 -1
View File
@@ -235,7 +235,7 @@ impl BlockDevice for ScsiUnit {
let mut transport = self.enclosure.transport.lock().await;
// TODO DmaSliceMut subslicing
let (buffer, range) = buffer.into_parts();
let (buffer, range) = unsafe { buffer.into_parts() };
let mut offset = range.start;
for i in (0..lba_count).step_by(self.max_lba_per_request) {
+16
View File
@@ -0,0 +1,16 @@
[package]
name = "ygg_driver_bsp_arm"
version = "0.1.0"
edition = "2024"
[dependencies]
device-tree.workspace = true
device-api.workspace = true
yggdrasil-abi.workspace = true
libk-mm.workspace = true
libk-util.workspace = true
libk.workspace = true
tock-registers.workspace = true
log.workspace = true
bytemuck.workspace = true
+8
View File
@@ -0,0 +1,8 @@
#![allow(unsafe_op_in_unsafe_fn)]
#![no_std]
extern crate alloc;
mod pl011;
mod pl031;
mod pl061;
@@ -1,14 +1,12 @@
//! ARM PL011 driver
use abi::{error::Error, io::TerminalOptions};
use alloc::sync::Arc;
use device_api::{
device::{Device, DeviceInitContext},
interrupt::{FullIrq, InterruptHandler, IrqVector},
interrupt::{InterruptHandler, IrqHandle, IrqVector},
};
use device_tree::driver::{device_tree_driver, Node, ProbeContext};
use libk::{
debug::DebugSink,
device::{external_interrupt_controller, manager::DEVICE_REGISTRY},
device::manager::DEVICE_REGISTRY,
vfs::{Terminal, TerminalInput, TerminalOutput},
};
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIo};
@@ -18,6 +16,7 @@ use tock_registers::{
register_bitfields, register_structs,
registers::{ReadOnly, ReadWrite, WriteOnly},
};
use yggdrasil_abi::{error::Error, io::TerminalOptions};
register_bitfields! {
u32,
@@ -69,7 +68,7 @@ struct Pl011Inner {
pub struct Pl011 {
inner: OneTimeInit<Arc<Terminal<Pl011Inner>>>,
base: PhysicalAddress,
irq: FullIrq,
irq: IrqHandle,
}
impl Io {
@@ -165,14 +164,12 @@ impl Device for Pl011 {
}
unsafe fn init_irq(self: Arc<Self>) -> Result<(), Error> {
let intc = external_interrupt_controller()?;
intc.register_irq(self.irq.irq, self.irq.options, self.clone())?;
self.irq.register(self.clone())?;
{
let io = self.inner.get().output().io.lock();
io.regs.IMSC.modify(IMSC::RXIM::SET);
}
intc.enable_irq(self.irq.irq)?;
self.irq.enable()?;
Ok(())
}
+66
View File
@@ -0,0 +1,66 @@
use alloc::sync::Arc;
use device_api::device::{Device, DeviceInitContext};
use device_tree::driver::{device_tree_driver, Node, ProbeContext};
use libk::{
error::Error,
fs::sysfs::{self, nodes::SysfsRtcNode},
};
use libk_mm::device::DeviceMemoryIo;
use libk_util::sync::IrqSafeSpinlock;
use tock_registers::{
interfaces::Readable,
register_structs,
registers::{ReadOnly, ReadWrite, WriteOnly},
};
register_structs! {
#[allow(non_snake_case)]
Regs {
(0x00 => RTCDR: ReadOnly<u32>),
(0x04 => RTCMR: ReadWrite<u32>),
(0x08 => RTCLR: ReadWrite<u32>),
(0x0C => RTCCR: ReadWrite<u32>),
(0x10 => RTCIMSC: ReadWrite<u32>),
(0x14 => RTCRIS: ReadOnly<u32>),
(0x18 => RTCMIS: ReadOnly<u32>),
(0x1C => RTCICR: WriteOnly<u32>),
(0x20 => @END),
}
}
struct Pl031 {
regs: IrqSafeSpinlock<DeviceMemoryIo<'static, Regs>>,
}
impl SysfsRtcNode for Pl031 {
fn read(&self) -> Result<u64, Error> {
let regs = self.regs.lock();
Ok(regs.RTCDR.get() as _)
}
}
impl Device for Pl031 {
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
sysfs::nodes::add_rtc_node(self.clone());
Ok(())
}
fn display_name(&self) -> &str {
"ARM PL031 RTC"
}
}
device_tree_driver! {
compatible: ["arm,pl031"],
driver: {
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
let base = node.map_base(context, 0)?;
let regs = unsafe { DeviceMemoryIo::map(base, Default::default()) }.ok()?;
let rtc = Arc::new(Pl031 { regs: IrqSafeSpinlock::new(regs) });
Some(rtc)
}
}
}
+232
View File
@@ -0,0 +1,232 @@
use core::sync::atomic::{AtomicU64, Ordering};
use alloc::{sync::Arc, vec::Vec};
use device_api::{
clock::{ClockHandle, ResetHandle},
device::{Device, DeviceInitContext},
gpio::{
GpioController, GpioInterruptEvent, GpioInterruptMode, GpioPinLevel, PinHandle,
SinglePinDirection,
},
interrupt::{InterruptHandler, IrqHandle, IrqVector},
};
use device_tree::{
driver::{
device_tree_driver, util::generic_gpio_config, DeviceTreeGpioPins, DeviceTreePinController,
Node, ProbeContext,
},
DeviceTreePropertyRead, TProp,
};
use libk::event::signal_gpio_event;
use libk_mm::device::DeviceMemoryIo;
use libk_util::{bit::BitField, sync::IrqSafeSpinlock};
use tock_registers::{
interfaces::{Readable, Writeable},
register_structs,
registers::{ReadOnly, ReadWrite, WriteOnly},
};
use yggdrasil_abi::error::Error;
register_structs! {
#[allow(non_snake_case)]
Regs {
(0x0000 => GPIODATA: [ReadWrite<u32>; 256]),
(0x0400 => GPIODIR: ReadWrite<u32>),
(0x0404 => GPIOIS: ReadWrite<u32>),
(0x0408 => GPIOIBE: ReadWrite<u32>),
(0x040C => GPIOIEV: ReadWrite<u32>),
(0x0410 => GPIOIE: ReadWrite<u32>),
(0x0414 => GPIORIS: ReadOnly<u32>),
(0x0418 => GPIOMIS: ReadOnly<u32>),
(0x041C => GPIOIC: WriteOnly<u32>),
(0x0420 => GPIOAFSEL: ReadWrite<u32>),
(0x0424 => _0),
(0x0FE0 => GPIOPERIPHID: [ReadOnly<u32>; 4]),
(0x0FF0 => GPIOPCELLID: [ReadOnly<u32>; 4]),
(0x1000 => @END),
}
}
struct Pl061 {
#[allow(unused)]
regs: IrqSafeSpinlock<DeviceMemoryIo<'static, Regs>>,
irq: IrqHandle,
clocks: Vec<ClockHandle>,
resets: Vec<ResetHandle>,
gpio_events: [AtomicU64; 8],
}
impl Device for Pl061 {
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
for clock in self.clocks.iter() {
clock.enable()?;
}
for reset in self.resets.iter() {
reset.deassert()?;
}
Ok(())
}
unsafe fn init_irq(self: Arc<Self>) -> Result<(), Error> {
self.irq.register(self.clone())?;
self.irq.enable()?;
Ok(())
}
fn display_name(&self) -> &str {
"PL061 GPIO Controller"
}
}
impl InterruptHandler for Pl061 {
fn handle_irq(self: Arc<Self>, _vector: IrqVector) -> bool {
let status = {
let lock = self.regs.lock();
let val = lock.GPIOMIS.get();
lock.GPIOIC.set(0xFF);
val
};
for bit in 0..8 {
let ev = self.gpio_events[bit].load(Ordering::Acquire);
if ev != 0 {
signal_gpio_event(ev);
}
}
status != 0
}
}
impl GpioController for Pl061 {
fn write_gpio(&self, _pin: &PinHandle, _value: bool) -> Result<(), Error> {
todo!()
}
fn read_gpio(&self, _pin: &PinHandle) -> Result<bool, Error> {
todo!()
}
fn setup_gpio(&self, pin: &PinHandle, event: Option<GpioInterruptEvent>) -> Result<(), Error> {
let regs = self.regs.lock();
let direction = pin
.config
.force_single_direction()
.ok_or(Error::InvalidArgument)
.inspect_err(|_| {
log::warn!(
"pl061: gpio #{} has invalid direction input={}, output={}",
pin.index,
pin.config.input,
pin.config.output
)
})?;
// Enable software control
regs.GPIOAFSEL.set(regs.GPIOAFSEL.get() & !(1 << pin.index));
match direction {
SinglePinDirection::Output => {
log::info!("pl061: gpio #{} set as output", pin.index);
regs.GPIODIR.set(regs.GPIODIR.get() | (1 << pin.index));
// Disable interrupt
regs.GPIOIE.set(regs.GPIOIE.get() & !(1 << pin.index));
let level = match pin.config.initial_level {
GpioPinLevel::Low => 0,
GpioPinLevel::High => 1,
};
let mut val = regs.GPIODATA[0].get();
val &= !(1 << pin.index);
val |= level << pin.index;
regs.GPIODATA[0].set(val);
}
SinglePinDirection::Input => {
log::info!("pl061: gpio #{} set as input", pin.index);
regs.GPIODIR.set(regs.GPIODIR.get() & !(1 << pin.index));
if let Some(event) = event {
let ibe = event.mode == GpioInterruptMode::BothEdges;
let is = event.mode == GpioInterruptMode::HighLevel
|| event.mode == GpioInterruptMode::LowLevel;
let iev = event.mode == GpioInterruptMode::HighLevel
|| event.mode == GpioInterruptMode::RisingEdge;
self.gpio_events[pin.index as usize].store(event.event, Ordering::Release);
regs.GPIOIS
.set(regs.GPIOIS.get().modify_bit(pin.index as usize, is));
regs.GPIOIBE
.set(regs.GPIOIBE.get().modify_bit(pin.index as usize, ibe));
regs.GPIOIEV
.set(regs.GPIOIEV.get().modify_bit(pin.index as usize, iev));
regs.GPIOIE.set(regs.GPIOIE.get() | (1 << pin.index));
regs.GPIOIC.set(1 << pin.index);
}
}
}
Ok(())
}
}
impl DeviceTreePinController for Pl061 {
fn configure_pin_group(self: Arc<Self>, _pins: &Arc<Node>) -> Result<(), Error> {
// TODO implement this when I get some board with this pinctrl
todo!()
}
fn map_gpio(
self: Arc<Self>,
property: &TProp,
offset: usize,
info: &DeviceTreeGpioPins,
) -> Option<(PinHandle, usize)> {
let (pin, options) = property.read_cells_at(offset, (1, 1))?;
let config = generic_gpio_config(options as u32, info);
Some((
PinHandle {
index: pin as u32,
config,
parent: self,
},
2,
))
}
}
device_tree_driver! {
compatible: ["arm,pl061"],
driver: {
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
let clocks = if let Some(clocks) = node.clocks() {
clocks.collect()
} else {
Vec::new()
};
let resets = if let Some(resets) = node.resets() {
resets.collect()
} else {
Vec::new()
};
let irq = node.interrupt(0)?;
let base = node.map_base(context, 0)?;
let regs = unsafe { DeviceMemoryIo::map(base, Default::default()) }.ok()?;
let pinctrl = Arc::new(Pl061 {
regs: IrqSafeSpinlock::new(regs),
irq,
clocks,
resets,
gpio_events: [const { AtomicU64::new(0) }; 8]
});
node.make_pin_controller(pinctrl.clone());
Some(pinctrl)
}
}
}
+18
View File
@@ -0,0 +1,18 @@
[package]
name = "ygg_driver_bsp_bcm283x"
version = "0.1.0"
edition = "2024"
[dependencies]
device-tree.workspace = true
device-api.workspace = true
yggdrasil-abi.workspace = true
libk-mm.workspace = true
libk-util.workspace = true
libk.workspace = true
kernel-arch-aarch64.workspace = true
tock-registers.workspace = true
log.workspace = true
bytemuck.workspace = true
futures-util.workspace = true
@@ -1,6 +1,3 @@
//! BCM283x AUX peripheral
use aarch64_cpu::registers::ReadWriteable;
use abi::error::Error;
use alloc::sync::Arc;
use device_api::{
clock::{ClockController, ClockHandle},
@@ -13,9 +10,11 @@ use device_tree::{
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIo};
use libk_util::{sync::IrqSafeSpinlock, OneTimeInit};
use tock_registers::{
interfaces::ReadWriteable,
register_bitfields, register_structs,
registers::{ReadOnly, ReadWrite},
};
use yggdrasil_abi::error::Error;
register_bitfields! {
u32,
@@ -1,19 +1,13 @@
//! Broadcom BCM2835 mini-UART driver
use abi::{
error::Error,
io::{TerminalOptions, TerminalOutputOptions},
};
use alloc::sync::Arc;
use device_api::{
clock::ClockHandle,
device::{Device, DeviceInitContext},
interrupt::{FullIrq, InterruptHandler, IrqVector},
interrupt::{InterruptHandler, IrqHandle, IrqVector},
};
use device_tree::driver::{device_tree_driver, Node, ProbeContext};
use libk::{
debug::DebugSink,
device::{external_interrupt_controller, manager::DEVICE_REGISTRY},
device::manager::DEVICE_REGISTRY,
vfs::{Terminal, TerminalInput, TerminalOutput},
};
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIo};
@@ -23,6 +17,10 @@ use tock_registers::{
register_bitfields, register_structs,
registers::{ReadOnly, ReadWrite},
};
use yggdrasil_abi::{
error::Error,
io::{TerminalOptions, TerminalOutputOptions},
};
register_bitfields! {
u32,
@@ -63,7 +61,7 @@ struct Inner {
/// Broadcom 283x mini-UART driver
pub struct Bcm2835AuxUart {
base: PhysicalAddress,
irq: FullIrq,
irq: IrqHandle,
clock: ClockHandle,
inner: OneTimeInit<Arc<Terminal<Inner>>>,
}
@@ -173,9 +171,8 @@ impl Device for Bcm2835AuxUart {
}
unsafe fn init_irq(self: Arc<Self>) -> Result<(), Error> {
let intc = external_interrupt_controller()?;
intc.register_irq(self.irq.irq, self.irq.options, self.clone())?;
intc.enable_irq(self.irq.irq)?;
self.irq.register(self.clone())?;
self.irq.enable()?;
let inner = self.inner.get().output();
let regs = inner.regs.lock();
+250
View File
@@ -0,0 +1,250 @@
use alloc::sync::Arc;
use device_api::{
device::{Device, DeviceInitContext},
gpio::{GpioController, GpioInterruptEvent, PinHandle},
interrupt::{InterruptHandler, IrqHandle, IrqVector},
};
use device_tree::{
driver::{
device_tree_driver, util::generic_gpio_config, DeviceTreeGpioPins, DeviceTreePinController,
Node, ProbeContext,
},
DeviceTreePropertyRead, TProp,
};
use libk_mm::device::DeviceMemoryIo;
use libk_util::sync::IrqSafeSpinlock;
use tock_registers::{
interfaces::{Readable, Writeable},
register_structs,
registers::{ReadOnly, ReadWrite, WriteOnly},
};
use yggdrasil_abi::error::Error;
const PUPD_NONE: u32 = 0b00;
register_structs! {
#[allow(non_snake_case)]
Regs {
// Pin function select
(0x00 => GPFSEL: [ReadWrite<u32>; 6]),
(0x18 => _0),
// Set pin
(0x1C => GPSET: [WriteOnly<u32>; 2]),
(0x24 => _1),
// Clear pin
(0x28 => GPCLR: [WriteOnly<u32>; 2]),
(0x30 => _2),
// Current pin level
(0x34 => GPLEV: [ReadOnly<u32>; 2]),
(0x3C => _3),
// Pin event detect status
(0x40 => GPEDS: [ReadWrite<u32>; 2]),
(0x48 => _4),
// Pin rising edge event enable
(0x4C => GPREN: [ReadWrite<u32>; 2]),
(0x54 => _5),
// Pin falling edge event enable
(0x58 => GPFEN: [ReadWrite<u32>; 2]),
(0x60 => _6),
// Pin high event enable
(0x64 => GPHEN: [ReadWrite<u32>; 2]),
(0x6C => _7),
// Pin low event enable
(0x70 => GPLEN: [ReadWrite<u32>; 2]),
(0x78 => _8),
// Pin async rising edge event enable
(0x7C => GPAREN: [ReadWrite<u32>; 2]),
(0x84 => _9),
// Pin async falling edge event enable
(0x88 => GPAFEN: [ReadWrite<u32>; 2]),
(0x90 => _10),
// Pin pull up/down control
(0xE4 => GPIO_PUP_PDN_CNTRL_REG: [ReadWrite<u32>; 4]),
(0xF4 => _11),
(0x100 => @END),
}
}
struct Bcm2711Gpio {
regs: IrqSafeSpinlock<DeviceMemoryIo<'static, Regs>>,
#[allow(unused)]
irqs: [IrqHandle; 2],
}
impl Regs {
fn write_fsel(&self, pin: usize, value: u32) {
let fsel_reg = pin / 10;
let fsel_shift = (pin % 10) * 3;
let mut fsel = self.GPFSEL[fsel_reg].get();
fsel &= !(0x7 << fsel_shift);
fsel |= (value & 0x7) << fsel_shift;
self.GPFSEL[fsel_reg].set(fsel);
}
fn write_pupd(&self, pin: usize, value: u32) {
let pupd_reg = pin / 16;
let pupd_shift = (pin % 16) * 2;
let mut pupd = self.GPIO_PUP_PDN_CNTRL_REG[pupd_reg].get();
pupd &= !(0x3 << pupd_shift);
pupd |= (value & 0x3) << pupd_shift;
self.GPIO_PUP_PDN_CNTRL_REG[pupd_reg].set(pupd);
}
fn configure_pin_interrupts(
&self,
pin: usize,
rising_edge: bool,
falling_edge: bool,
level_high: bool,
level_low: bool,
) {
#[inline]
fn modify_reg(reg: &ReadWrite<u32>, bit: u32, set: bool) {
if set {
reg.set(reg.get() | bit);
} else {
reg.set(reg.get() & !bit);
}
}
// TODO use async edge detection (likely have some limitations)?
let reg = pin / 32;
let bit = 1 << (pin % 32);
// Disable async edge events
modify_reg(&self.GPAREN[reg], bit, false);
modify_reg(&self.GPAFEN[reg], bit, false);
modify_reg(&self.GPREN[reg], bit, rising_edge);
modify_reg(&self.GPFEN[reg], bit, falling_edge);
modify_reg(&self.GPHEN[reg], bit, level_high);
modify_reg(&self.GPLEN[reg], bit, level_low);
// Clear interrupt status
self.GPEDS[reg].set(bit);
}
fn configure_pin_function(&self, pin: usize, function: u32, pull: u32) {
self.write_fsel(pin, function);
self.write_pupd(pin, pull);
}
}
impl Device for Bcm2711Gpio {
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
let regs = self.regs.lock();
// Disable all interrupts by default
for pin in 0..58 {
regs.configure_pin_interrupts(pin, false, false, false, false);
}
for irq in self.irqs.iter() {
irq.register(self.clone())?;
irq.enable()?;
}
Ok(())
}
fn display_name(&self) -> &str {
"bcm2711-gpio"
}
}
impl InterruptHandler for Bcm2711Gpio {
fn handle_irq(self: Arc<Self>, _vector: IrqVector) -> bool {
log::warn!("TODO: handle bcm2711-gpio interrupts");
false
}
}
impl GpioController for Bcm2711Gpio {
fn setup_gpio(&self, pin: &PinHandle, _event: Option<GpioInterruptEvent>) -> Result<(), Error> {
log::warn!(
"TOOD: bcm2711 gpio pin #{} setup: input={}, output={}",
pin.index,
pin.config.input,
pin.config.output
);
Ok(())
}
fn read_gpio(&self, _pin: &PinHandle) -> Result<bool, Error> {
Ok(false)
}
fn write_gpio(&self, _pin: &PinHandle, _value: bool) -> Result<(), Error> {
Ok(())
}
}
impl DeviceTreePinController for Bcm2711Gpio {
fn configure_pin_group(self: Arc<Self>, group: &Arc<Node>) -> Result<(), Error> {
let pins = group.property("brcm,pins").ok_or(Error::InvalidArgument)?;
let function = group.property("brcm,function").ok_or(Error::DoesNotExist)?;
let pull = group.property("brcm,pull");
if function.is_empty() || pins.is_empty() {
return Ok(());
}
let function = function.read_cell(0, 1).ok_or(Error::InvalidArgument)? as u32;
let regs = self.regs.lock();
for i in 0..pins.len() / 4 {
let pin = pins.read_cell(i, 1).ok_or(Error::InvalidArgument)? as u32;
let pull = if let Some(pull) = pull.as_ref().and_then(|p| p.read_cell(i, 1)) {
pull as u32
} else {
PUPD_NONE
};
log::info!("bcm2711-gpio: gpio{pin} function={function}");
regs.configure_pin_function(pin as usize, function, pull);
}
Ok(())
}
fn map_gpio(
self: Arc<Self>,
property: &TProp,
offset: usize,
info: &DeviceTreeGpioPins,
) -> Option<(PinHandle, usize)> {
let (pin, options) = property.read_cells_at(offset, (1, 1))?;
let config = generic_gpio_config(options as u32, info);
Some((
PinHandle {
index: pin as u32,
config,
parent: self,
},
2,
))
}
}
device_tree_driver! {
compatible: ["brcm,bcm2711-gpio"],
driver: {
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
let irq0 = node.interrupt(0)?;
let irq1 = node.interrupt(1)?;
let base = node.map_base(context, 0)?;
let regs = unsafe { DeviceMemoryIo::map(base, Default::default()) }.ok()?;
let gpio = Arc::new(Bcm2711Gpio {
regs: IrqSafeSpinlock::new(regs),
irqs: [irq0, irq1]
});
node.make_pin_controller(gpio.clone());
Some(gpio)
}
}
}
+8
View File
@@ -0,0 +1,8 @@
#![no_std]
extern crate alloc;
mod aux;
mod aux_uart;
mod gpio;
mod mbox;
@@ -1,8 +1,5 @@
#![allow(missing_docs)]
use core::{cell::UnsafeCell, mem::MaybeUninit};
use abi::error::Error;
use alloc::sync::Arc;
use device_api::device::{Device, DeviceInitContext};
use device_tree::driver::{device_tree_driver, Node, ProbeContext};
@@ -25,6 +22,7 @@ use tock_registers::{
register_bitfields, register_structs,
registers::{ReadOnly, ReadWrite, WriteOnly},
};
use yggdrasil_abi::error::Error;
register_bitfields! {
u32,
+17
View File
@@ -0,0 +1,17 @@
[package]
name = "ygg_driver_bsp_jh7110"
version = "0.1.0"
edition = "2024"
[dependencies]
device-tree.workspace = true
device-api.workspace = true
yggdrasil-abi.workspace = true
libk-mm.workspace = true
libk-util.workspace = true
libk.workspace = true
tock-registers.workspace = true
log.workspace = true
bytemuck.workspace = true
futures-util.workspace = true
@@ -1,8 +1,5 @@
//! StarFive JH7110 clock drivers
use core::{marker::PhantomData, ops::Index};
use abi::error::Error;
use alloc::sync::Arc;
use device_api::{
clock::{ClockController, ClockHandle, Hertz, ResetController, ResetHandle},
@@ -17,6 +14,7 @@ use device_tree::{
};
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIoMut};
use libk_util::sync::IrqSafeSpinlock;
use yggdrasil_abi::error::Error;
// SYSCRG clocks
+7
View File
@@ -0,0 +1,7 @@
#![allow(unsafe_op_in_unsafe_fn)]
#![no_std]
extern crate alloc;
mod clocks;
mod pinctrl;
+445
View File
@@ -0,0 +1,445 @@
use alloc::{sync::Arc, vec::Vec};
use device_api::{
clock::{ClockHandle, ResetHandle},
device::{Device, DeviceInitContext},
gpio::{
GpioController, GpioInterruptEvent, GpioPinLevel, OutputPinBias, PinHandle,
SinglePinDirection,
},
};
use device_tree::{
driver::{
device_tree_driver,
util::{generic_gpio_config, GenericPinctrlBiasConfig, GenericPinctrlConfig},
DeviceTreeGpioPins, DeviceTreePinController, Node, ProbeContext,
},
DeviceTreePropertyRead, TProp,
};
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIoMut};
use libk_util::{bit::BitField, sync::IrqSafeSpinlock, OneTimeInit};
use yggdrasil_abi::error::Error;
#[derive(Debug)]
struct PinMuxConfig {
pin: u8,
function: u8,
doen: u8,
dout: u8,
din: u8,
}
struct SysRegs(DeviceMemoryIoMut<'static, [u32]>);
struct AonRegs(DeviceMemoryIoMut<'static, [u32]>);
unsafe impl Send for SysRegs {}
unsafe impl Sync for SysRegs {}
unsafe impl Send for AonRegs {}
unsafe impl Sync for AonRegs {}
struct Gpio<R: GpioRegs> {
regs: IrqSafeSpinlock<R>,
init: OneTimeInit<Result<(), Error>>,
clocks: Vec<ClockHandle>,
resets: Vec<ResetHandle>,
}
trait GpioRegs: Sized + Send + 'static {
const NAME: &'static str;
const DOUT_OFFSET: usize;
const DOEN_OFFSET: usize;
const GPI_OFFSET: usize;
const DOUT_MASK: u32;
const DOEN_MASK: u32;
const GPI_MASK: u32;
unsafe fn map(base: PhysicalAddress) -> Result<Self, Error>;
fn reg_mut(&mut self, index: usize) -> *mut u32;
fn pin_functions(&self, pin: usize) -> Option<(usize, usize, u32)>;
fn write_output_pin(&mut self, pin: usize, value: bool) {
let reg = pin / 4;
let shift = (pin % 4) * 8;
let reg_dout = self.reg_mut(Self::DOUT_OFFSET + reg);
unsafe {
let mut val = reg_dout.read_volatile();
val.set_bit(shift, value);
reg_dout.write_volatile(val);
}
}
fn configure_pin(&mut self, pin: usize, doen: u32, dout: u32, din: u32) {
let reg = pin / 4;
let shift = (pin % 4) * 8;
let reg_doen = self.reg_mut(Self::DOEN_OFFSET + reg);
let reg_dout = self.reg_mut(Self::DOUT_OFFSET + reg);
unsafe {
let mut val = reg_dout.read_volatile();
val &= !(Self::DOUT_MASK << shift);
val |= dout << shift;
reg_dout.write_volatile(val);
let mut val = reg_doen.read_volatile();
val &= !(Self::DOEN_MASK << shift);
val |= doen << shift;
reg_doen.write_volatile(val);
if din != 0xFF {
let din_reg = din as usize / 4;
let din_shift = (din % 4) * 8;
let reg_din = self.reg_mut(Self::GPI_OFFSET + din_reg);
let mut val = reg_din.read_volatile();
val &= !(Self::GPI_MASK << din_shift);
val |= (pin as u32 + 2) << din_shift;
reg_din.write_volatile(val);
}
}
}
fn configure_pin_function(&mut self, pin: usize, func: u32) {
let Some((offset, shift, max)) = self.pin_functions(pin) else {
return;
};
if func > max {
return;
}
log::info!("jh7110-sys-pinctrl: set pad{pin} function={func}");
let reg = self.reg_mut(offset / 4);
unsafe {
let mut val = reg.read_volatile();
val &= !(0x3 << shift);
val |= func << shift;
reg.write_volatile(val);
}
}
fn set_input_enabled(&mut self, _pin: usize, _enabled: bool) {}
fn set_output_enabled(&mut self, _pin: usize, _enabled: bool) {}
fn set_bias(&mut self, _pin: usize, _bias: GenericPinctrlBiasConfig) {}
}
// sys-pinctrl pads
const fn pad_gpio(n: usize) -> usize {
n
}
const PAD_GMAC1_RXC: usize = 82;
const SYS_PIN_COUNT: usize = 95;
const SYS_PIN_FUNCTIONS: [Option<(usize, usize, u32)>; SYS_PIN_COUNT] = const {
let mut t = [const { None }; SYS_PIN_COUNT];
t[PAD_GMAC1_RXC] = Some((0x29C, 0, 1));
t[pad_gpio(10)] = Some((0x29C, 2, 3));
t[pad_gpio(11)] = Some((0x29C, 5, 3));
t[pad_gpio(12)] = Some((0x29C, 8, 3));
t[pad_gpio(13)] = Some((0x29C, 11, 3));
t[pad_gpio(14)] = Some((0x29C, 14, 3));
t[pad_gpio(15)] = Some((0x29C, 17, 3));
t[pad_gpio(16)] = Some((0x29C, 20, 3));
t[pad_gpio(17)] = Some((0x29C, 23, 3));
t[pad_gpio(18)] = Some((0x29C, 26, 3));
t[pad_gpio(19)] = Some((0x29C, 29, 3));
t[pad_gpio(20)] = Some((0x2A0, 0, 3));
t[pad_gpio(21)] = Some((0x2A0, 3, 3));
t[pad_gpio(22)] = Some((0x2A0, 6, 3));
t[pad_gpio(23)] = Some((0x2A0, 9, 3));
t[pad_gpio(24)] = Some((0x2A0, 12, 3));
t[pad_gpio(25)] = Some((0x2A0, 15, 3));
t[pad_gpio(26)] = Some((0x2A0, 18, 3));
t[pad_gpio(27)] = Some((0x2A0, 21, 3));
t[pad_gpio(28)] = Some((0x2A0, 24, 3));
t[pad_gpio(29)] = Some((0x2A0, 27, 3));
t[pad_gpio(30)] = Some((0x2A4, 0, 3));
t[pad_gpio(31)] = Some((0x2A4, 3, 3));
t[pad_gpio(32)] = Some((0x2A4, 6, 3));
t[pad_gpio(33)] = Some((0x2A4, 9, 3));
t[pad_gpio(34)] = Some((0x2A4, 12, 3));
t[pad_gpio(35)] = Some((0x2A4, 15, 3));
t[pad_gpio(36)] = Some((0x2A4, 17, 3));
t[pad_gpio(37)] = Some((0x2A4, 20, 3));
t[pad_gpio(38)] = Some((0x2A4, 23, 3));
t[pad_gpio(39)] = Some((0x2A4, 26, 3));
t[pad_gpio(40)] = Some((0x2A4, 29, 3));
t[pad_gpio(41)] = Some((0x2A8, 0, 3));
t[pad_gpio(42)] = Some((0x2A8, 3, 3));
t[pad_gpio(43)] = Some((0x2A8, 6, 3));
t[pad_gpio(44)] = Some((0x2A8, 9, 3));
t[pad_gpio(45)] = Some((0x2A8, 12, 3));
t[pad_gpio(46)] = Some((0x2A8, 15, 3));
t[pad_gpio(47)] = Some((0x2A8, 18, 3));
t[pad_gpio(48)] = Some((0x2A8, 21, 3));
t[pad_gpio(49)] = Some((0x2A8, 24, 3));
t[pad_gpio(50)] = Some((0x2A8, 27, 3));
t[pad_gpio(51)] = Some((0x2A8, 30, 3));
t[pad_gpio(52)] = Some((0x2AC, 0, 3));
t[pad_gpio(53)] = Some((0x2AC, 2, 3));
t[pad_gpio(54)] = Some((0x2AC, 4, 3));
t[pad_gpio(55)] = Some((0x2AC, 6, 3));
t[pad_gpio(56)] = Some((0x2AC, 9, 3));
t[pad_gpio(57)] = Some((0x2AC, 12, 3));
t[pad_gpio(58)] = Some((0x2AC, 15, 3));
t[pad_gpio(59)] = Some((0x2AC, 18, 3));
t[pad_gpio(60)] = Some((0x2AC, 21, 3));
t[pad_gpio(61)] = Some((0x2AC, 24, 3));
t[pad_gpio(62)] = Some((0x2AC, 27, 3));
t[pad_gpio(63)] = Some((0x2AC, 30, 3));
t[pad_gpio(6)] = Some((0x2B0, 0, 3));
t[pad_gpio(7)] = Some((0x2B0, 2, 3));
t[pad_gpio(8)] = Some((0x2B0, 5, 3));
t[pad_gpio(9)] = Some((0x2B0, 8, 3));
t
};
impl PinMuxConfig {
fn from_fdt(fdt: u32) -> Self {
let pin = fdt as u8;
let function = ((fdt >> 8) & 0x3) as u8;
let doen = ((fdt >> 10) & 0x3F) as u8;
let dout = (fdt >> 16) as u8;
let din = (fdt >> 24) as u8;
Self {
pin,
function,
doen,
dout,
din,
}
}
}
impl<R: GpioRegs> Gpio<R> {
fn from_device_tree(node: &Arc<Node>, context: &mut ProbeContext) -> Result<Arc<Self>, Error> {
let base = node.map_base(context, 0).ok_or(Error::DoesNotExist)?;
let regs = unsafe { R::map(base) }?;
let clocks = node.clocks().map(|c| c.collect()).unwrap_or_default();
let resets = node.resets().map(|c| c.collect()).unwrap_or_default();
let gpio = Arc::new(Self {
regs: IrqSafeSpinlock::new(regs),
init: OneTimeInit::new(),
clocks,
resets,
});
Ok(gpio)
}
fn configure_pinmux(&self, mux: PinMuxConfig, cfg: &GenericPinctrlConfig) {
let mut regs = self.regs.lock();
regs.configure_pin_function(mux.pin as _, mux.function as _);
regs.configure_pin(mux.pin as _, mux.doen as _, mux.dout as _, mux.din as _);
if let Some(enable) = cfg.input_enable {
regs.set_input_enabled(mux.pin as _, enable);
}
if let Some(enable) = cfg.output_enable {
regs.set_output_enabled(mux.pin as _, enable);
}
if let Some(bias) = cfg.bias {
regs.set_bias(mux.pin as _, bias);
}
}
fn configure_pin_output(
&self,
pin: usize,
_active_level: GpioPinLevel,
initial_level: GpioPinLevel,
_output_bias: Option<OutputPinBias>,
) -> Result<(), Error> {
// TODO configure pull up/pull down?
let mut regs = self.regs.lock();
let dout_val = initial_level == GpioPinLevel::High;
regs.configure_pin_function(pin, 0);
regs.configure_pin(pin, 0, dout_val as u32, 0);
Ok(())
}
fn ensure_init(&self) -> Result<(), Error> {
let res = self.init.or_init_with(|| {
for clock in self.clocks.iter() {
clock.enable()?;
}
for reset in self.resets.iter() {
reset.deassert()?;
}
// TODO disable all pin IRQs
Ok(())
});
*res
}
}
impl<R: GpioRegs> Device for Gpio<R> {
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
self.ensure_init()
}
fn display_name(&self) -> &str {
R::NAME
}
}
impl<R: GpioRegs> GpioController for Gpio<R> {
fn setup_gpio(&self, pin: &PinHandle, _event: Option<GpioInterruptEvent>) -> Result<(), Error> {
log::warn!(
"TODO: setup gpio #{} input={}, output={}",
pin.index,
pin.config.input,
pin.config.output
);
// TODO bidi pins?
match pin.config.force_single_direction() {
Some(SinglePinDirection::Input) => todo!(),
Some(SinglePinDirection::Output) => self.configure_pin_output(
pin.index as usize,
pin.config.active_level,
pin.config.initial_level,
pin.config.output_bias,
),
None => {
todo!("{}: support for bidi gpio", R::NAME);
}
}
}
fn read_gpio(&self, _pin: &PinHandle) -> Result<bool, Error> {
Err(Error::NotImplemented)
}
fn write_gpio(&self, pin: &PinHandle, value: bool) -> Result<(), Error> {
self.regs.lock().write_output_pin(pin.index as _, value);
Ok(())
}
}
impl<R: GpioRegs> DeviceTreePinController for Gpio<R> {
fn configure_pin_group(self: Arc<Self>, pins: &Arc<Node>) -> Result<(), Error> {
self.ensure_init()?;
for child in pins.children() {
let pinmux = child.property("pinmux").ok_or(Error::DoesNotExist)?;
let cfg = GenericPinctrlConfig::from_node(child);
for i in 0..pinmux.len() / 4 {
let mux = pinmux.read_cell(i, 1).unwrap() as u32;
let mux = PinMuxConfig::from_fdt(mux);
self.configure_pinmux(mux, &cfg);
}
}
Ok(())
}
fn map_gpio(
self: Arc<Self>,
property: &TProp,
offset: usize,
info: &DeviceTreeGpioPins,
) -> Option<(PinHandle, usize)> {
let (pin, options) = property.read_cells_at(offset, (1, 1))?;
let config = generic_gpio_config(options as u32, info);
Some((
PinHandle {
index: pin as u32,
config,
parent: self,
},
2,
))
}
}
impl GpioRegs for SysRegs {
const NAME: &'static str = "jh7110-sys-pinctrl";
const DOEN_OFFSET: usize = 0x00;
const DOUT_OFFSET: usize = 0x40 / 4;
const GPI_OFFSET: usize = 0x80 / 4;
const DOEN_MASK: u32 = 0x1F;
const DOUT_MASK: u32 = 0x3F;
const GPI_MASK: u32 = 0x3F;
unsafe fn map(base: PhysicalAddress) -> Result<Self, Error> {
DeviceMemoryIoMut::map_slice(base, 192, Default::default()).map(Self)
}
fn pin_functions(&self, pin: usize) -> Option<(usize, usize, u32)> {
SYS_PIN_FUNCTIONS.get(pin).copied()?
}
fn reg_mut(&mut self, index: usize) -> *mut u32 {
&raw mut self.0[index]
}
}
impl GpioRegs for AonRegs {
const NAME: &'static str = "jh7110-aon-pinctrl";
const DOEN_OFFSET: usize = 0x00;
const DOUT_OFFSET: usize = 0x04 / 4;
const GPI_OFFSET: usize = 0x08 / 4;
const DOEN_MASK: u32 = 0x7;
const DOUT_MASK: u32 = 0xF;
const GPI_MASK: u32 = 0xF;
unsafe fn map(base: PhysicalAddress) -> Result<Self, Error> {
DeviceMemoryIoMut::map_slice(base, 64, Default::default()).map(Self)
}
fn pin_functions(&self, _pin: usize) -> Option<(usize, usize, u32)> {
None
}
fn reg_mut(&mut self, index: usize) -> *mut u32 {
&raw mut self.0[index]
}
}
device_tree_driver! {
compatible: ["starfive,jh7110-sys-pinctrl"],
driver: {
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
let gpio = Gpio::<SysRegs>::from_device_tree(node, context)
.inspect_err(|e| log::error!("jh7110-sys-pinctrl: probe error: {e:?}"))
.ok()?;
node.make_pin_controller(gpio.clone());
Some(gpio)
}
}
}
device_tree_driver! {
compatible: ["starfive,jh7110-aon-pinctrl"],
driver: {
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
let gpio = Gpio::<AonRegs>::from_device_tree(node, context)
.inspect_err(|e| log::error!("jh7110-aon-pinctrl: probe error: {e:?}"))
.ok()?;
node.make_pin_controller(gpio.clone());
Some(gpio)
}
}
}
+17
View File
@@ -0,0 +1,17 @@
[package]
name = "ygg_driver_bsp_riscv"
version = "0.1.0"
edition = "2024"
[dependencies]
device-tree.workspace = true
device-api.workspace = true
yggdrasil-abi.workspace = true
libk-mm.workspace = true
libk-util.workspace = true
libk.workspace = true
kernel-arch-riscv64.workspace = true
tock-registers.workspace = true
log.workspace = true
bytemuck.workspace = true
@@ -0,0 +1,70 @@
use alloc::sync::Arc;
use device_api::device::{Device, DeviceInitContext};
use device_tree::driver::{device_tree_driver, Node, ProbeContext};
use libk::{
error::Error,
fs::sysfs::{self, nodes::SysfsRtcNode},
};
use libk_mm::device::DeviceMemoryIo;
use libk_util::sync::IrqSafeSpinlock;
use tock_registers::{
interfaces::Readable,
register_structs,
registers::{ReadOnly, ReadWrite},
};
use yggdrasil_abi::time::NANOSECONDS_IN_SECOND;
register_structs! {
#[allow(non_snake_case)]
Regs {
(0x00 => TIME_LOW: ReadOnly<u32>),
(0x04 => TIME_HIGH: ReadOnly<u32>),
(0x08 => ALARM_LOW: ReadWrite<u32>),
(0x0C => ALARM_HIGH: ReadWrite<u32>),
(0x10 => IRQ_ENABLED: ReadWrite<u32>),
(0x14 => CLEAR_ALARM: ReadWrite<u32>),
(0x18 => ALARM_STATUS: ReadOnly<u32>),
(0x1C => CLEAR_INTERRUPT: ReadWrite<u32>),
(0x20 => @END),
}
}
struct Rtc {
regs: IrqSafeSpinlock<DeviceMemoryIo<'static, Regs>>,
}
impl SysfsRtcNode for Rtc {
fn read(&self) -> Result<u64, Error> {
let regs = self.regs.lock();
let low = regs.TIME_LOW.get();
let high = regs.TIME_HIGH.get();
let t = ((high as u64) << 32) | (low as u64);
Ok(t / NANOSECONDS_IN_SECOND)
}
}
impl Device for Rtc {
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
sysfs::nodes::add_rtc_node(self.clone());
Ok(())
}
fn display_name(&self) -> &str {
"Google Goldfish RTC"
}
}
device_tree_driver! {
compatible: ["google,goldfish-rtc"],
driver: {
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
let base = node.map_base(context, 0)?;
let regs = unsafe { DeviceMemoryIo::map(base, Default::default()) }.ok()?;
let rtc = Arc::new(Rtc { regs: IrqSafeSpinlock::new(regs) });
Some(rtc)
}
}
}
+7
View File
@@ -0,0 +1,7 @@
#![allow(unsafe_op_in_unsafe_fn)]
#![no_std]
extern crate alloc;
mod goldfish_rtc;
mod plic;
@@ -1,12 +1,9 @@
//! RISC-V PLIC driver
use abi::{error::Error, primitive_enum};
use alloc::{sync::Arc, vec::Vec};
use device_api::{
device::{Device, DeviceInitContext},
interrupt::{
ExternalInterruptController, FixedInterruptTable, FullIrq, InterruptHandler,
InterruptTable, Irq, IrqOptions, IrqVector,
ExternalInterruptController, FixedInterruptTable, InterruptHandler, InterruptTable, Irq,
IrqHandle, IrqOptions, IrqVector,
},
};
use device_tree::{
@@ -15,6 +12,7 @@ use device_tree::{
},
DeviceTreePropertyRead, TProp,
};
use kernel_arch_riscv64::boot_hart_id;
use libk::{arch::Cpu, device::register_external_interrupt_controller};
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIo};
use libk_util::{sync::spin_rwlock::IrqSafeRwLock, OneTimeInit};
@@ -23,8 +21,7 @@ use tock_registers::{
register_structs,
registers::{ReadOnly, ReadWrite},
};
use crate::arch::riscv64::boot::boot_hart_id;
use yggdrasil_abi::{error::Error, primitive_enum};
const MAX_IRQS: usize = 1024;
@@ -264,11 +261,12 @@ impl Device for Plic {
}
impl DeviceTreeInterruptController for Plic {
fn map_interrupt(&self, property: &TProp, offset: usize) -> Option<FullIrq> {
fn map_interrupt(self: Arc<Self>, property: &TProp, offset: usize) -> Option<IrqHandle> {
let num = property.read_cell(offset, 1)?;
Some(FullIrq {
Some(IrqHandle {
irq: Irq::External(num as _),
options: IrqOptions::default(),
intc: self.clone(),
})
}
+3 -2
View File
@@ -1,9 +1,10 @@
use core::{any::Any, fmt, mem::MaybeUninit};
use alloc::sync::Arc;
use alloc::{sync::Arc, vec};
use libk::{
block,
error::Error,
task::sync::AsyncMutex,
vfs::{
CommonImpl, CreateInfo, DirectoryImpl, DirectoryOpenPosition, Filename, Metadata, Node,
NodeFlags, NodeRef,
@@ -281,10 +282,10 @@ impl DirectoryNode {
} else {
let file = FileNode {
fs: self.fs.clone(),
cluster,
size_bytes: size,
parent: Some(self.cluster),
metadata,
cluster_chain_cache: AsyncMutex::new(vec![cluster]),
};
Ok(Node::regular(
+17 -32
View File
@@ -1,6 +1,6 @@
use core::any::Any;
use alloc::{sync::Arc, vec, vec::Vec};
use alloc::{sync::Arc, vec::Vec};
use libk::{
block,
error::Error,
@@ -13,28 +13,22 @@ use crate::{data::ClusterNumber, Fat32Fs};
pub struct FileNode {
pub(crate) fs: Arc<Fat32Fs>,
pub(crate) cluster: ClusterNumber,
pub(crate) size_bytes: u32,
pub(crate) metadata: Metadata,
// Will be used when metadata needs to be updated
#[allow(unused)]
pub(crate) parent: Option<ClusterNumber>,
pub(crate) cluster_chain_cache: AsyncMutex<Vec<ClusterNumber>>,
}
// TODO use a "sliding window" to minimize memory usage when working with large files?
struct OpenedFile {
cluster_chain: AsyncMutex<Vec<ClusterNumber>>,
}
impl OpenedFile {
fn new(first_cluster: ClusterNumber) -> Self {
Self {
cluster_chain: AsyncMutex::new(vec![first_cluster]),
}
}
async fn seek(&self, file: &FileNode, cluster_index: usize) -> Result<ClusterNumber, Error> {
let mut chain = self.cluster_chain.lock().await;
impl FileNode {
async fn fetch_cluster_number(
&self,
file: &FileNode,
cluster_index: usize,
) -> Result<ClusterNumber, Error> {
let mut chain = self.cluster_chain_cache.lock().await;
if cluster_index >= chain.len() {
let last = *chain.last().unwrap();
file.fs
@@ -47,15 +41,8 @@ impl OpenedFile {
Ok(chain[cluster_index])
}
}
impl FileNode {
async fn read_inner(
&self,
instance: &OpenedFile,
mut pos: u64,
buffer: &mut [u8],
) -> Result<usize, Error> {
async fn read_inner(&self, mut pos: u64, buffer: &mut [u8]) -> Result<usize, Error> {
if pos >= self.size_bytes as u64 {
return Ok(0);
}
@@ -73,7 +60,9 @@ impl FileNode {
let offset_in_sector = (pos % bps) as usize;
let amount = rem.min(bps as usize - offset_in_sector);
let cluster = instance.seek(self, cluster_index as usize).await?;
let cluster = self
.fetch_cluster_number(self, cluster_index as usize)
.await?;
self.fs
.with_cluster_sector(cluster, sector_in_cluster, |data| {
@@ -119,8 +108,7 @@ impl RegularImpl for FileNode {
if opts.contains_any(OpenOptions::TRUNCATE | OpenOptions::APPEND | OpenOptions::WRITE) {
return Err(Error::ReadOnly);
}
let instance = Arc::new(OpenedFile::new(self.cluster));
Ok((0, Some(instance)))
Ok((0, None))
}
fn close(&self, _node: &NodeRef, _instance: Option<&InstanceData>) -> Result<(), Error> {
@@ -130,14 +118,11 @@ impl RegularImpl for FileNode {
fn read(
&self,
_node: &NodeRef,
instance: Option<&InstanceData>,
_instance: Option<&InstanceData>,
pos: u64,
buf: &mut [u8],
) -> Result<usize, Error> {
let instance = instance
.and_then(|p| p.downcast_ref::<OpenedFile>())
.ok_or(Error::InvalidFile)?;
block!(self.read_inner(instance, pos, buf).await)?
block!(self.read_inner(pos, buf).await)?
}
fn write(
+33 -12
View File
@@ -20,6 +20,7 @@ use yggdrasil_abi::{
error::Error,
io::{FileMode, FileType, GroupId, UserId},
path::Path,
time::SystemTime,
};
use crate::tar::TarIterator;
@@ -75,6 +76,7 @@ impl<A: BlockAllocator> MemoryFilesystem<A> {
path: &Path,
create: bool,
mode: FileMode,
mtime: SystemTime,
) -> Result<NodeRef, Error> {
let access = unsafe { AccessToken::authorized() };
if path.is_empty() {
@@ -97,7 +99,18 @@ impl<A: BlockAllocator> MemoryFilesystem<A> {
}
let ino = INO_COUNTER.fetch_add(1, Ordering::Relaxed);
let node = DirectoryNode::<A>::new(self.clone(), Metadata::now_root(mode, ino));
let metadata = Metadata {
uid: UserId::root(),
gid: GroupId::root(),
atime: mtime,
ctime: mtime,
mode,
mtime,
inode: Some(ino),
block_size: 512,
block_count: 0,
};
let node = DirectoryNode::<A>::new(self.clone(), metadata);
at.add_child(filename, node.clone())?;
node
@@ -112,7 +125,7 @@ impl<A: BlockAllocator> MemoryFilesystem<A> {
Ok(node)
} else {
assert!(node.is_directory());
self.make_path(&node, rest, create, mode)
self.make_path(&node, rest, create, mode, mtime)
}
}
@@ -120,16 +133,22 @@ impl<A: BlockAllocator> MemoryFilesystem<A> {
let kind = hdr.node_kind();
let mode = usize::from(&hdr.mode);
let mode = FileMode::new(0o777 & (mode as u32));
let mtime = SystemTime::new(usize::from(&hdr.mtime) as u64, 0);
let ino = INO_COUNTER.fetch_add(1, Ordering::Relaxed);
let metadata = Metadata {
uid: UserId::root(),
gid: GroupId::root(),
atime: mtime,
ctime: mtime,
mode,
mtime,
inode: Some(ino),
block_size: 512,
block_count: usize::from(&hdr.size).div_ceil(512) as u64,
};
match kind {
FileType::File => Ok(FileNode::<A>::new(
self.clone(),
Metadata::now_root(mode, ino),
)),
FileType::Directory => Ok(DirectoryNode::<A>::new(
self.clone(),
Metadata::now_root(mode, ino),
)),
FileType::File => Ok(FileNode::<A>::new(self.clone(), metadata)),
FileType::Directory => Ok(DirectoryNode::<A>::new(self.clone(), metadata)),
FileType::Symlink => {
let target = hdr.symlink_target()?;
Ok(fixed_path_symlink(target))
@@ -149,9 +168,10 @@ impl<A: BlockAllocator> MemoryFilesystem<A> {
};
let path = Path::from_str(hdr.name.as_str()?.trim_matches('/'));
let mtime = SystemTime::new(usize::from(&hdr.mtime) as u64, 0);
let (dirname, filename) = path.split_right();
let parent = self.make_path(&root, dirname, true, FileMode::new(0o755))?;
let parent = self.make_path(&root, dirname, true, FileMode::new(0o755), mtime)?;
let node = self.create_node_initial(hdr)?;
let filename = Filename::new(filename)?;
@@ -164,8 +184,9 @@ impl<A: BlockAllocator> MemoryFilesystem<A> {
panic!("Unreachable");
};
let mtime = SystemTime::new(usize::from(&hdr.mtime) as u64, 0);
let path = Path::from_str(hdr.name.as_str()?.trim_matches('/'));
let node = self.make_path(&root, path, false, FileMode::empty())?;
let node = self.make_path(&root, path, false, FileMode::empty(), mtime)?;
assert_eq!(node.ty(), hdr.node_kind());
let uid = unsafe { UserId::from_raw(usize::from(&hdr.uid) as u32) };
+1 -1
View File
@@ -23,7 +23,7 @@ pub(crate) struct TarEntry {
pub uid: OctalField<8>,
pub gid: OctalField<8>,
pub size: OctalField<12>,
_mtime: OctalField<12>,
pub mtime: OctalField<12>,
_checksum: OctalField<8>,
type_: u8,
link_name: TarString<100>,
@@ -0,0 +1,20 @@
[package]
name = "ygg_driver_mmc_generic_sdhci"
version = "0.1.0"
edition = "2024"
[dependencies]
device-tree.workspace = true
device-api.workspace = true
yggdrasil-abi.workspace = true
libk.workspace = true
libk-util.workspace = true
libk-mm.workspace = true
log.workspace = true
tock-registers.workspace = true
bitflags.workspace = true
bytemuck.workspace = true
async-trait.workspace = true
futures-util.workspace = true
bitfield = "0.19.1"
+230
View File
@@ -0,0 +1,230 @@
use core::{mem::MaybeUninit, time::Duration};
use alloc::{boxed::Box, sync::Arc};
use async_trait::async_trait;
use device_api::device::{Device, DeviceInitContext};
use libk::{
device::block::BlockDevice,
dma::{DmaBuffer, DmaSlice, DmaSliceMut},
error::Error,
task::runtime,
};
use libk_mm::{
address::PhysicalAddress, table::MapAttributes, OnDemandPage, PageProvider, VirtualPage,
};
use crate::{
command::{self, CsdStructure, OcrRegister},
host::SdhciHost,
Sdhci,
};
pub struct SdCard<H: SdhciHost> {
hc: Arc<Sdhci<H>>,
pub(crate) rca: u16,
pub(crate) csd: CsdStructure,
high_capacity: bool,
}
impl<H: SdhciHost> SdCard<H> {
/*
* SD/MMC card setup state machine:
*
* IDLE <------\ If card cannot handle req. voltage
* | |
* | <--- Send CMD8
* |
* No response | Voltage range not compatible
* | | |
* V V V
* /--------- ACMD41 --------> INA <-------- CMD15 from data xfer mode
* | |
* | |
* V V
* MMC card READY
* |
* V
* CMD2
* |
* V
* IDENT
* |
* V
* CMD3
* |
* | <--- Card responds with a RCA
* Ident mode |
* -----------------------+----------------------------------------
* Xfer mode |
* V
* STBY
*
*/
pub async fn setup(hc: Arc<Sdhci<H>>) -> Result<Arc<Self>, Error> {
// GO_IDLE_STATE
hc.submit_command::<command::CMD0, _>((), ()).await?;
// SEND_IF_COND, 2.7-3.3V range
// TODO: actually check result
hc.submit_command::<command::CMD8, _>(0x1AA, ()).await?;
// TODO adjust voltage as needed
// SEND_OP_COND
let mut attempts = 10;
let mut high_capacity = false;
while attempts != 0 {
let Ok(r3) = hc
.submit_command::<command::ACMD41, _>(0x40FF8000, ())
.await
else {
runtime::sleep(Duration::from_millis(200)).await;
attempts -= 1;
continue;
};
log::trace!("r3 = {r3:?}");
if r3.0.contains(OcrRegister::POWER_UP) {
high_capacity = r3.0.contains(OcrRegister::CCS);
break;
}
attempts -= 1;
runtime::sleep(Duration::from_millis(100)).await;
}
if attempts == 0 {
log::error!("ACMD41 timeout/failure");
return Err(Error::InvalidOperation);
}
// ALL_SEND_CID
hc.submit_command::<command::CMD2, _>((), ()).await?;
// SEND_RELATIVE_ADDR
let rca = hc
.submit_command::<command::CMD3, _>((), ())
.await?
.card_address;
// Card is now in data transfer mode
// Get card capacity
// SEND_CSD
let r2 = hc
.submit_command::<command::CMD9, _>((rca as u32) << 16, ())
.await?;
let csd = r2.parse_csd_structure()?;
hc.submit_command::<command::CMD7, _>((rca as u32) << 16, ())
.await?;
Ok(Arc::new(Self {
hc,
rca,
csd,
high_capacity,
}))
}
}
#[async_trait]
impl<H: SdhciHost> BlockDevice for SdCard<H> {
fn allocate_buffer(&self, size: usize) -> Result<DmaBuffer<[MaybeUninit<u8>]>, Error> {
DmaBuffer::new_uninit_slice(self.hc.dma_allocator().as_ref(), size)
}
// TODO read directly to cache
async fn read_aligned(
&self,
position: u64,
buffer: DmaSliceMut<'_, MaybeUninit<u8>>,
) -> Result<(), Error> {
assert!(self.high_capacity);
assert_eq!(position % 512, 0);
assert_eq!(buffer.len() % 512, 0);
let block_count = buffer.len() / 512;
let address: u32 = (position / 512)
.try_into()
.map_err(|_| Error::InvalidArgument)?;
let (buffer, range) = unsafe { buffer.into_parts() };
for i in 0..block_count {
let mut block_slice =
buffer.slice_mut(range.start + i * 512..range.start + i * 512 + 512);
self.hc
.read_single_block_pio(address + i as u32, &mut block_slice)
.await?;
}
Ok(())
}
async fn write_aligned(&self, position: u64, buffer: DmaSlice<'_, u8>) -> Result<(), Error> {
assert!(self.high_capacity);
assert_eq!(position % 512, 0);
assert_eq!(buffer.len() % 512, 0);
let block_count = buffer.len() / 512;
let address: u32 = (position / 512)
.try_into()
.map_err(|_| Error::InvalidArgument)?;
let (buffer, range) = unsafe { buffer.into_parts() };
for i in 0..block_count {
let block_slice = buffer.slice(range.start + i * 512..range.start + i * 512 + 512);
self.hc
.write_single_block_pio(address + i as u32, &block_slice)
.await?;
}
Ok(())
}
fn block_size(&self) -> usize {
self.csd.read_block_size
}
fn block_count(&self) -> u64 {
self.csd.capacity
}
fn max_blocks_per_request(&self) -> usize {
1
}
}
impl<H: SdhciHost> PageProvider for SdCard<H> {
fn ondemand_fetch(&self, _opaque: u64) -> Result<OnDemandPage, Error> {
unimplemented!()
}
fn get_page(&self, _offset: u64) -> Result<VirtualPage, Error> {
unimplemented!()
}
fn release_page(
&self,
_offset: u64,
_phys: PhysicalAddress,
_dirty: bool,
) -> Result<(), Error> {
unimplemented!()
}
fn clone_page(
&self,
_offset: u64,
_src_phys: PhysicalAddress,
_src_attrs: MapAttributes,
) -> Result<PhysicalAddress, Error> {
unimplemented!()
}
}
impl<H: SdhciHost> Device for SdCard<H> {
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
unreachable!()
}
fn display_name(&self) -> &str {
"SD Card"
}
}
@@ -0,0 +1,14 @@
pub trait SdArgument {
fn into_u32(self) -> u32;
}
impl SdArgument for u32 {
fn into_u32(self) -> u32 {
self
}
}
impl SdArgument for () {
fn into_u32(self) -> u32 {
0
}
}
@@ -0,0 +1,124 @@
pub mod argument;
pub mod register;
pub mod response;
pub mod transfer;
#[derive(Debug)]
pub enum SdCommandIndex {
ACmd(u8),
Cmd(u8),
}
pub trait SdCommand {
const INDEX: SdCommandIndex;
type Response: SdResponse = ();
type Argument: SdArgument = ();
type Transfer<'a>: SdTransfer = ();
}
pub use argument::SdArgument;
pub use register::{CsdStructure, CsdStructureV1, CsdStructureV2, OcrRegister};
pub use response::{R1b, SdResponse, SdResponseWidth, R1, R2, R3, R6, R7};
pub use transfer::{SdDeviceToHostTransfer, SdHostToDeviceTransfer, SdTransfer};
/// GO_IDLE_STATE
pub struct CMD0;
/// ALL_SEND_CID
pub struct CMD2;
/// SEND_RELATIVE_ADDR
pub struct CMD3;
/// SELECT/DESELECT_CARD
///
/// Argument: RCA
pub struct CMD7;
/// SEND_IF_COND
///
/// Argument: TODO
pub struct CMD8;
/// SEND_CSD
///
/// Argument: RCA
pub struct CMD9;
/// STOP_TRANSMISSION
pub struct CMD12;
/// READ_SINGLE_BLOCK
///
/// Argument: data address
pub struct CMD17;
/// READ_MULTIPLE_BLOCK
///
/// Argument: data address
pub struct CMD18;
/// WRITE_SINGLE_BLOCK
///
/// Argument: data address
pub struct CMD24;
/// APP_CMD
pub struct CMD55;
/// SD_SEND_OP_COND
///
/// Argument: TODO
pub struct ACMD41;
impl SdCommand for CMD0 {
const INDEX: SdCommandIndex = SdCommandIndex::Cmd(0);
}
impl SdCommand for CMD2 {
const INDEX: SdCommandIndex = SdCommandIndex::Cmd(2);
type Argument = ();
type Response = R2;
}
impl SdCommand for CMD3 {
const INDEX: SdCommandIndex = SdCommandIndex::Cmd(3);
type Argument = ();
type Response = R6;
}
impl SdCommand for CMD7 {
const INDEX: SdCommandIndex = SdCommandIndex::Cmd(7);
type Argument = u32;
type Response = R1b;
}
impl SdCommand for CMD8 {
const INDEX: SdCommandIndex = SdCommandIndex::Cmd(8);
type Argument = u32;
type Response = R7;
}
impl SdCommand for CMD9 {
const INDEX: SdCommandIndex = SdCommandIndex::Cmd(9);
type Argument = u32;
type Response = R2;
}
impl SdCommand for CMD12 {
const INDEX: SdCommandIndex = SdCommandIndex::Cmd(12);
type Argument = ();
type Response = R1b;
}
impl SdCommand for CMD17 {
const INDEX: SdCommandIndex = SdCommandIndex::Cmd(17);
type Argument = u32;
type Response = R1;
type Transfer<'a> = SdDeviceToHostTransfer<'a>;
}
impl SdCommand for CMD18 {
const INDEX: SdCommandIndex = SdCommandIndex::Cmd(18);
type Argument = u32;
type Response = R1;
type Transfer<'a> = SdDeviceToHostTransfer<'a>;
}
impl SdCommand for CMD24 {
const INDEX: SdCommandIndex = SdCommandIndex::Cmd(24);
type Argument = u32;
type Response = R1;
type Transfer<'a> = SdHostToDeviceTransfer<'a>;
}
impl SdCommand for CMD55 {
const INDEX: SdCommandIndex = SdCommandIndex::Cmd(55);
type Argument = u32;
type Response = R1;
}
impl SdCommand for ACMD41 {
const INDEX: SdCommandIndex = SdCommandIndex::ACmd(41);
type Argument = u32;
type Response = R3;
}
@@ -0,0 +1,91 @@
use core::fmt;
use bitfield::BitRange;
use bitflags::bitflags;
use bytemuck::{Pod, Zeroable};
#[derive(Debug)]
pub struct CsdStructure {
pub read_block_size: usize,
pub write_block_size: usize,
pub capacity: u64,
}
#[derive(Clone, Copy, PartialEq, Eq, Pod, Zeroable)]
#[repr(C)]
pub struct CsdStructureV1(u128);
#[derive(Clone, Copy, PartialEq, Eq, Pod, Zeroable)]
#[repr(C)]
pub struct CsdStructureV2(u128);
bitflags! {
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct OcrRegister: u32 {
const VDD_2_7V_2_8V = 1 << 15;
const VDD_2_8V_2_9V = 1 << 16;
const VDD_2_9V_3_0V = 1 << 17;
const VDD_3_0V_3_1V = 1 << 18;
const VDD_3_1V_3_2V = 1 << 19;
const VDD_3_2V_3_3V = 1 << 20;
const VDD_3_3V_3_4V = 1 << 21;
const VDD_3_4V_3_5V = 1 << 22;
const VDD_3_5V_3_6V = 1 << 23;
const CCS = 1 << 30;
const POWER_UP = 1 << 31;
}
}
unsafe impl Pod for OcrRegister {}
unsafe impl Zeroable for OcrRegister {}
impl CsdStructureV1 {
pub fn csd_structure(&self) -> u32 {
self.0.bit_range(127 - 8, 126 - 8)
}
pub fn read_bl_len(&self) -> u32 {
self.0.bit_range(83 - 8, 80 - 8)
}
pub fn c_size(&self) -> u32 {
self.0.bit_range(73 - 8, 62 - 8)
}
pub fn c_size_mult(&self) -> u32 {
self.0.bit_range(49 - 8, 47 - 8)
}
}
impl CsdStructureV2 {
pub fn csd_structure(&self) -> u32 {
self.0.bit_range(127 - 8, 126 - 8)
}
pub fn c_size(&self) -> u32 {
self.0.bit_range(69 - 8, 48 - 8)
}
pub fn read_bl_len(&self) -> u32 {
self.0.bit_range(83 - 8, 80 - 8)
}
}
impl fmt::Debug for CsdStructureV1 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("CsdStructureV1")
.field("CSD_STRUCTURE", &self.csd_structure())
.field("READ_BL_LEN", &self.read_bl_len())
.field("C_SIZE", &self.c_size())
.field("C_SIZE_MULT", &self.c_size_mult())
.finish_non_exhaustive()
}
}
impl fmt::Debug for CsdStructureV2 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("CsdStructureV2")
.field("CSD_STRUCTURE", &self.csd_structure())
.field("READ_BL_LEN", &self.read_bl_len())
.field("C_SIZE", &self.c_size())
.finish_non_exhaustive()
}
}
@@ -0,0 +1,122 @@
use libk::error::Error;
use super::{register::OcrRegister, CsdStructure, CsdStructureV1, CsdStructureV2};
pub enum SdResponseWidth {
None,
W48,
W48b,
W136,
}
pub trait SdResponse {
const BIT_WIDTH: SdResponseWidth;
const CRC_CHECK: bool = true;
const INDEX_CHECK: bool = false;
fn from_bits(bits: &[u32; 4]) -> Self;
}
#[derive(Debug)]
pub struct R1(pub u32);
#[derive(Debug)]
pub struct R1b(pub u32);
#[derive(Debug)]
pub struct R2(pub [u32; 4]);
#[derive(Debug)]
pub struct R3(pub OcrRegister);
/// R6 - Published RCA Response
#[derive(Debug)]
pub struct R6 {
pub card_status: u16,
pub card_address: u16,
}
#[derive(Debug)]
pub struct R7(pub u32);
impl SdResponse for () {
const BIT_WIDTH: SdResponseWidth = SdResponseWidth::None;
const CRC_CHECK: bool = false;
fn from_bits(_bits: &[u32; 4]) -> Self {
()
}
}
impl SdResponse for R1 {
const BIT_WIDTH: SdResponseWidth = SdResponseWidth::W48;
fn from_bits(bits: &[u32; 4]) -> Self {
Self(bits[0])
}
}
impl SdResponse for R1b {
const BIT_WIDTH: SdResponseWidth = SdResponseWidth::W48b;
fn from_bits(bits: &[u32; 4]) -> Self {
Self(bits[0])
}
}
impl SdResponse for R2 {
const BIT_WIDTH: SdResponseWidth = SdResponseWidth::W136;
fn from_bits(bits: &[u32; 4]) -> Self {
Self(*bits)
}
}
impl R2 {
pub fn parse_csd_structure(&self) -> Result<CsdStructure, Error> {
let v1 = bytemuck::cast::<_, CsdStructureV1>(self.0);
match v1.csd_structure() {
0 => {
// block_size = 1 << READ_BL_LEN
// size_mult = 1 << (C_SIZE_MULT + 2)
// capacity = (C_SIZE + 1) * size_mult [blocks]
let size_mult = 1usize << (v1.c_size_mult() + 2);
Ok(CsdStructure {
read_block_size: 1usize << v1.read_bl_len(),
write_block_size: 512, // TODO
capacity: (v1.c_size() as u64 + 1) * (size_mult as u64),
})
}
1 => {
// block_size = 512
// capacity = (C_SIZE + 1) * block_size * 1024 [bytes]
let v2 = bytemuck::cast::<_, CsdStructureV2>(self.0);
Ok(CsdStructure {
read_block_size: 512,
write_block_size: 512,
capacity: (v2.c_size() as u64 + 1) * 1024,
})
}
_ => Err(Error::InvalidArgument),
}
}
}
impl SdResponse for R3 {
const BIT_WIDTH: SdResponseWidth = SdResponseWidth::W48;
const CRC_CHECK: bool = false;
fn from_bits(bits: &[u32; 4]) -> Self {
Self(bytemuck::cast(bits[0]))
}
}
impl SdResponse for R6 {
const BIT_WIDTH: SdResponseWidth = SdResponseWidth::W48;
fn from_bits(bits: &[u32; 4]) -> Self {
Self {
card_address: (bits[0] >> 16) as u16,
card_status: bits[0] as u16,
}
}
}
impl SdResponse for R7 {
const BIT_WIDTH: SdResponseWidth = SdResponseWidth::W48;
const INDEX_CHECK: bool = true;
fn from_bits(bits: &[u32; 4]) -> Self {
Self(bits[0])
}
}
@@ -0,0 +1,140 @@
use core::{future, mem::MaybeUninit};
use libk::{
dma::{DmaSlice, DmaSliceMut},
error::Error,
};
use tock_registers::fields::FieldValue;
use crate::{host::SdhciHost, regs, Sdhci};
pub trait SdTransfer {
fn perform_transfer<H: SdhciHost>(
self,
hc: &Sdhci<H>,
) -> impl Future<Output = Result<(), Error>>;
fn command_bits(&self) -> FieldValue<u32, regs::COMMAND::Register>;
fn block_count(&self) -> u16;
fn block_size(&self) -> u16;
}
pub struct SdDeviceToHostTransfer<'a>(&'a mut DmaSliceMut<'a, MaybeUninit<u8>>);
pub struct SdHostToDeviceTransfer<'a>(&'a DmaSlice<'a, u8>);
impl<'a> From<&'a mut DmaSliceMut<'a, MaybeUninit<u8>>> for SdDeviceToHostTransfer<'a> {
fn from(value: &'a mut DmaSliceMut<'a, MaybeUninit<u8>>) -> Self {
Self(value)
}
}
impl SdTransfer for SdDeviceToHostTransfer<'_> {
fn perform_transfer<H: SdhciHost>(
self,
hc: &Sdhci<H>,
) -> impl Future<Output = Result<(), Error>> {
async move {
let block_count = self.block_count() as usize;
let block_size = self.block_size() as usize;
for i in 0..block_count {
let off = i * block_size;
let slice = &mut self.0[off..off + block_size];
hc.receive_single_block_pio(slice, block_size).await?;
}
hc.wait_for_interrupt(regs::EVENT_MASK::TRANSFER_COMPLETE::SET)
.await?;
Ok(())
}
}
fn command_bits(&self) -> FieldValue<u32, regs::COMMAND::Register> {
(if self.block_count() > 1 {
regs::COMMAND::MULTI_BLOCK_SELECT::MultiBlock
} else {
regs::COMMAND::MULTI_BLOCK_SELECT::SingleBlock
}) + regs::COMMAND::DATA_PRESENT_SELECT::SET
+ regs::COMMAND::DATA_TRANSFER_DIRECTION::CardToHost
}
fn block_size(&self) -> u16 {
// TODO
512
}
fn block_count(&self) -> u16 {
(self.0.len() / 512).try_into().unwrap()
}
}
impl<'a> From<&'a DmaSlice<'a, u8>> for SdHostToDeviceTransfer<'a> {
fn from(value: &'a DmaSlice<'a, u8>) -> Self {
Self(value)
}
}
impl SdTransfer for SdHostToDeviceTransfer<'_> {
fn perform_transfer<H: SdhciHost>(
self,
hc: &Sdhci<H>,
) -> impl Future<Output = Result<(), Error>> {
async move {
let block_count = self.block_count() as usize;
let block_size = self.block_size() as usize;
for i in 0..block_count {
let off = i * block_size;
let slice = &self.0[off..off + block_size];
hc.send_single_block_pio(slice, block_size).await?;
}
hc.wait_for_interrupt(regs::EVENT_MASK::TRANSFER_COMPLETE::SET)
.await?;
Ok(())
}
}
fn command_bits(&self) -> FieldValue<u32, regs::COMMAND::Register> {
(if self.block_count() > 1 {
regs::COMMAND::MULTI_BLOCK_SELECT::MultiBlock
} else {
regs::COMMAND::MULTI_BLOCK_SELECT::SingleBlock
}) + regs::COMMAND::DATA_PRESENT_SELECT::SET
+ regs::COMMAND::DATA_TRANSFER_DIRECTION::HostToCard
}
fn block_size(&self) -> u16 {
// TODO
512
}
fn block_count(&self) -> u16 {
(self.0.len() / 512).try_into().unwrap()
}
}
impl SdTransfer for () {
fn perform_transfer<H: SdhciHost>(
self,
_hc: &Sdhci<H>,
) -> impl Future<Output = Result<(), Error>> {
future::ready(Ok(()))
}
fn command_bits(&self) -> FieldValue<u32, regs::COMMAND::Register> {
FieldValue::none()
}
fn block_size(&self) -> u16 {
0
}
fn block_count(&self) -> u16 {
0
}
}
+147
View File
@@ -0,0 +1,147 @@
use core::cell::UnsafeCell;
use alloc::sync::Arc;
use device_api::{
device::{Device, DeviceInitContext},
dma::DmaAllocator,
interrupt::{InterruptHandler, IrqHandle},
};
use device_tree::driver::{device_tree_driver, Node, ProbeContext};
use libk::error::Error;
use libk_mm::device::DeviceMemoryIoMut;
use libk_util::OneTimeInit;
use crate::Sdhci;
pub trait SdhciHost: Send + Sync + 'static {
unsafe fn init(&self, cx: DeviceInitContext) -> Result<(), Error>;
unsafe fn init_irq(&self, handler: Arc<dyn InterruptHandler>) -> Result<(), Error>;
fn dma_allocator(&self) -> &Arc<dyn DmaAllocator>;
fn name(&self) -> &'static str;
fn read4(&self, reg: usize) -> u32;
fn read2(&self, reg: usize) -> u16;
fn read1(&self, reg: usize) -> u8;
fn write4(&self, reg: usize, val: u32);
fn write2(&self, reg: usize, val: u16);
fn write1(&self, reg: usize, val: u8);
// max_clock(), set_clock(...)
// reset()
// set_bus_width(...)
}
pub struct IprocSdhci {
// base: PhysicalAddress,
mmio: UnsafeCell<DeviceMemoryIoMut<'static, [u32]>>,
irq: IrqHandle,
name: &'static str,
dma: OneTimeInit<Arc<dyn DmaAllocator>>,
}
unsafe impl Sync for IprocSdhci {}
unsafe impl Send for IprocSdhci {}
impl SdhciHost for IprocSdhci {
fn name(&self) -> &'static str {
self.name
}
unsafe fn init(&self, cx: DeviceInitContext) -> Result<(), Error> {
self.dma.init(cx.dma_allocator);
Ok(())
}
unsafe fn init_irq(&self, handler: Arc<dyn InterruptHandler>) -> Result<(), Error> {
self.irq.register(handler)?;
self.irq.enable()?;
Ok(())
}
fn dma_allocator(&self) -> &Arc<dyn DmaAllocator> {
self.dma.get()
}
fn read4(&self, reg: usize) -> u32 {
assert_eq!(reg % 4, 0);
let mmio = unsafe { &mut *self.mmio.get() };
unsafe { core::ptr::read_volatile(&mmio[reg / 4]) }
}
fn read2(&self, reg: usize) -> u16 {
assert_eq!(reg % 2, 0);
let mmio = unsafe { &mut *self.mmio.get() };
let address32 = reg / 4;
let shift32 = (reg & 2) << 3;
let val32 = unsafe { core::ptr::read_volatile(&mmio[address32]) };
(val32 >> shift32) as u16
}
fn read1(&self, reg: usize) -> u8 {
let mmio = unsafe { &mut *self.mmio.get() };
let address32 = reg / 4;
let shift32 = (reg & 3) << 3;
let val32 = unsafe { core::ptr::read_volatile(&mmio[address32]) };
(val32 >> shift32) as u8
}
fn write4(&self, reg: usize, val: u32) {
assert_eq!(reg % 4, 0);
let mmio = unsafe { &mut *self.mmio.get() };
unsafe { core::ptr::write_volatile(&mut mmio[reg / 4], val) };
}
fn write2(&self, reg: usize, val: u16) {
let mmio = unsafe { &mut *self.mmio.get() };
let address32 = reg / 4;
let shift32 = (reg & 2) << 3;
let mut val32 = unsafe { core::ptr::read_volatile(&mmio[address32]) };
val32 &= !(0xFFFF << shift32);
val32 |= (val as u32) << shift32;
unsafe { core::ptr::write_volatile(&mut mmio[address32], val32) };
}
fn write1(&self, reg: usize, val: u8) {
let mmio = unsafe { &mut *self.mmio.get() };
let address32 = reg / 4;
let shift32 = (reg & 3) << 3;
let mut val32 = unsafe { core::ptr::read_volatile(&mmio[address32]) };
val32 &= !(0xFF << shift32);
val32 |= (val as u32) << shift32;
unsafe { core::ptr::write_volatile(&mut mmio[address32], val32) };
}
}
device_tree_driver! {
// compatible: ["brcm,bcm2835-mmc", "brcm,bcm2835-sdhci"],
compatible: ["brcm,bcm2711-emmc2"],
driver: {
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
let base = node.map_base(context, 0)?;
let irq = node.interrupt(0)?;
let name = node.name()?;
let mmio = UnsafeCell::new(unsafe {
DeviceMemoryIoMut::map_slice(
base, 0x100 / 4, Default::default()
)
.inspect_err(|e| log::error!("{name}: memory mapping error {e:?}"))
.ok()?
});
let hc = IprocSdhci {
mmio,
irq,
name,
dma: OneTimeInit::new(),
};
let sdhc = Sdhci::from_host(hc).inspect_err(|e| log::error!("{name}: {e:?}")).ok()?;
Some(Arc::new(sdhc))
}
}
}
+772
View File
@@ -0,0 +1,772 @@
#![feature(associated_type_defaults, maybe_uninit_slice)]
#![no_std]
use core::{
future::poll_fn,
mem::MaybeUninit,
ops::BitAndAssign,
sync::atomic::{AtomicBool, AtomicU32, Ordering},
task::Poll,
time::Duration,
};
use alloc::{format, sync::Arc};
use card::SdCard;
use command::{SdArgument, SdCommand, SdCommandIndex, SdResponse, SdResponseWidth, SdTransfer};
use device_api::{
clock::{Hertz, IntoHertz},
device::{Device, DeviceInitContext},
dma::DmaAllocator,
interrupt::{InterruptHandler, IrqVector},
};
use futures_util::task::AtomicWaker;
use host::SdhciHost;
use libk::{
device::manager::probe_partitions,
dma::{DmaSlice, DmaSliceMut},
error::Error,
fs::devfs,
task::{runtime, sync::AsyncMutex},
};
use libk_util::OneTimeInit;
use regs::{
Reg, CLOCK_CONTROL, EVENT_MASK, INTERRUPT_SIGNAL_ENABLE, INTERRUPT_STATUS_ENABLE,
POWER_CONTROL, PRESENT_STATE, SOFTWARE_RESET,
};
use tock_registers::{fields::FieldValue, LocalRegisterCopy, RegisterLongName, UIntLike};
use yggdrasil_abi::io::FileMode;
extern crate alloc;
pub mod card;
pub mod command;
pub mod host;
pub mod regs;
pub enum ClockMode {
None,
Internal,
All,
}
pub struct Capabilities {
pub version: HostVersion,
pub clock_multiplier: Option<u32>,
pub voltage_support_1_8v: bool,
pub voltage_support_3_0v: bool,
pub voltage_support_3_3v: bool,
pub sdma_support: bool,
pub adma2_support: bool,
pub base_frequency: Option<Hertz>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum HostVersion {
V1_00,
V2_00,
V3_00,
V4_00,
V4_10,
V4_20,
}
struct SdhciInner<H: SdhciHost> {
capabilities: Capabilities,
host: H,
}
struct InterruptStatus {
interrupt_status: AtomicU32,
notify: AtomicWaker,
}
pub struct Sdhci<H: SdhciHost> {
inner: SdhciInner<H>,
dma: OneTimeInit<Arc<dyn DmaAllocator>>,
interrupt_status: InterruptStatus,
command_lock: AsyncMutex<()>,
card_present: AtomicBool,
}
impl InterruptStatus {
const fn new() -> Self {
Self {
interrupt_status: AtomicU32::new(0),
notify: AtomicWaker::new(),
}
}
fn signal(&self, value: u32) {
self.interrupt_status.fetch_or(value, Ordering::Release);
self.notify.wake();
}
async fn wait(&self, mask: u32) -> LocalRegisterCopy<u32, EVENT_MASK::Register> {
let not_mask = !mask;
poll_fn(|cx| {
let value = self.interrupt_status.fetch_and(not_mask, Ordering::Acquire);
if value & mask != 0 {
Poll::Ready(LocalRegisterCopy::new(value))
} else {
self.notify.register(cx.waker());
Poll::Pending
}
})
.await
}
}
// impl<H: SdhciHost> From<H> for Sdhci<H> {
// fn from(value: H) -> Self {
// Self {
// inner: SdhciInner { host: value },
//
// interrupt_status: InterruptStatus::new(),
// command_lock: AsyncMutex::new(()),
//
// card_present: AtomicBool::new(false),
// }
// }
// }
impl TryFrom<u16> for HostVersion {
type Error = Error;
fn try_from(value: u16) -> Result<Self, Self::Error> {
match value & 0xFF {
0x00 => Ok(Self::V1_00),
0x01 => Ok(Self::V2_00),
0x02 => Ok(Self::V3_00),
0x03 => Ok(Self::V4_00),
0x04 => Ok(Self::V4_10),
0x05 => Ok(Self::V4_20),
_ => Err(Error::InvalidArgument),
}
}
}
impl Capabilities {
fn from_host<H: SdhciHost>(host: &H) -> Result<Self, Error> {
let version = HostVersion::try_from(host.read2(0xFE))?;
log::info!("HC version: {version:?}");
let reg_caps_0 = host.read4(regs::CAPABILITIES::Register::ADDRESS);
let reg_caps_1 = host.read4(regs::CAPABILITIES::Register::ADDRESS + 4);
let reg_caps = LocalRegisterCopy::<u64, regs::CAPABILITIES::Register>::new(
(reg_caps_0 as u64) | ((reg_caps_1 as u64) << 32),
);
let base_frequency = reg_caps.read(regs::CAPABILITIES::BASE_CLOCK_FREQ);
let base_frequency = if base_frequency == 0 {
None
} else {
if version < HostVersion::V3_00 {
assert_eq!(base_frequency & !0b111111, 0);
}
Some(base_frequency.mhz())
};
let clock_multiplier = match reg_caps.read(regs::CAPABILITIES::BASE_CLOCK_FREQ) {
0 => None,
n => Some(n as u32 + 1),
};
if let Some(base_frequency) = base_frequency {
log::info!("SD base frequency: {base_frequency}");
}
Ok(Self {
version,
base_frequency,
clock_multiplier,
adma2_support: reg_caps.matches_all(regs::CAPABILITIES::ADMA2_SUPPORT::SET),
sdma_support: reg_caps.matches_all(regs::CAPABILITIES::SDMA_SUPPORT::SET),
voltage_support_1_8v: reg_caps.matches_all(regs::CAPABILITIES::VOLTAGE_1_8V::SET),
voltage_support_3_0v: reg_caps.matches_all(regs::CAPABILITIES::VOLTAGE_3_0V::SET),
voltage_support_3_3v: reg_caps.matches_all(regs::CAPABILITIES::VOLTAGE_3_3V::SET),
})
}
}
impl<H: SdhciHost> Sdhci<H> {
pub fn from_host(host: H) -> Result<Self, Error> {
let capabilities = Capabilities::from_host(&host)?;
let inner = SdhciInner { capabilities, host };
Ok(Self {
inner,
dma: OneTimeInit::new(),
interrupt_status: InterruptStatus::new(),
command_lock: AsyncMutex::new(()),
card_present: AtomicBool::new(false),
})
}
async fn late_init(self: Arc<Self>) -> Result<(), Error> {
log::info!("{}: initialize", self.display_name());
self.inner.software_reset().await?;
self.inner.set_default_interrupt_mask();
self.inner.set_vdd1_power();
self.inner.set_clock_rate(400u64.khz()).await?;
self.inner.set_clock_mode(ClockMode::All).await?;
if self.inner.is_card_inserted() {
self.card_present.store(true, Ordering::Release);
runtime::spawn(self.clone().card_task())?;
}
Ok(())
}
async fn try_setup_card(self: &Arc<Self>) -> Result<Arc<SdCard<H>>, Error> {
// Enable SD bus clock
self.inner.set_clock_mode(ClockMode::All).await?;
let mut last_error = Error::InvalidArgument;
for _ in 0..3 {
match SdCard::setup(self.clone()).await {
Ok(card) => return Ok(card),
Err(error) => {
log::error!("Card setup error: {error:?}");
self.inner.set_clock_mode(ClockMode::Internal).await?;
last_error = error;
runtime::sleep(Duration::from_secs(1)).await;
}
}
}
Err(last_error)
}
async fn card_task(self: Arc<Self>) {
log::info!("Card inserted, try setup");
let card = match Self::try_setup_card(&self).await {
Ok(card) => card,
Err(error) => {
log::error!("Card setup error: {error:?}");
self.card_present.store(false, Ordering::Release);
return;
}
};
log::info!(
"Set up card: rca={:#04x}, capacity={}GiB",
card.rca,
(card.csd.capacity * card.csd.read_block_size as u64) / (1024 * 1024 * 1024)
);
// Register card as a block device
register_sdcard(card, true);
}
async fn receive_single_block_pio(
&self,
buffer: &mut [MaybeUninit<u8>],
block_size: usize,
) -> Result<(), Error> {
assert_eq!(block_size % 4, 0);
let words = bytemuck::cast_slice_mut::<_, u32>(unsafe { buffer.assume_init_mut() });
self.wait_for_interrupt(EVENT_MASK::BUFFER_READ_READY::SET)
.await?;
for word in words {
*word = self.inner.get::<regs::BUFFER_DATA::Register>();
}
Ok(())
}
async fn send_single_block_pio(&self, buffer: &[u8], block_size: usize) -> Result<(), Error> {
assert_eq!(block_size % 4, 0);
let words = bytemuck::cast_slice::<_, u32>(buffer);
for word in words {
self.wait_for_interrupt(EVENT_MASK::BUFFER_WRITE_READY::SET)
.await?;
self.inner.set::<regs::BUFFER_DATA::Register>(*word);
}
Ok(())
}
pub async fn read_single_block_pio<'a>(
&self,
address: u32,
buffer: &'a mut DmaSliceMut<'a, MaybeUninit<u8>>,
) -> Result<(), Error> {
assert_eq!(buffer.len(), 512);
self.submit_command::<command::CMD17, _>(address, buffer)
.await
.inspect_err(|e| log::error!("CMD17 error: {e:?}"))?;
Ok(())
}
pub async fn write_single_block_pio<'a>(
&self,
address: u32,
buffer: &'a DmaSlice<'a, u8>,
) -> Result<(), Error> {
assert_eq!(buffer.len(), 512);
self.submit_command::<command::CMD24, _>(address, buffer)
.await?;
Ok(())
}
pub async fn submit_command<'a, C: SdCommand, T: Into<C::Transfer<'a>>>(
&self,
argument: C::Argument,
transfer: T,
) -> Result<C::Response, Error> {
let transfer = transfer.into();
let argument = argument.into_u32();
let _guard = self.command_lock.lock().await;
let response_bits = match C::INDEX {
SdCommandIndex::Cmd(index) => {
log::trace!(">> CMD{index} : {argument:#x}");
self.submit_command_inner::<C>(argument, index, Some(&transfer))
.await
}
SdCommandIndex::ACmd(index) => {
log::trace!(">> ACMD{index} : {argument:#x}");
self.submit_command_inner::<command::CMD55>(0, 55, None)
.await?;
self.submit_command_inner::<C>(argument, index, Some(&transfer))
.await
}
}?;
log::trace!("<< {:x?}", response_bits);
let response = C::Response::from_bits(&response_bits);
transfer.perform_transfer(self).await?;
Ok(response)
}
async fn submit_command_inner<'a, C: SdCommand>(
&self,
argument: u32,
index: u8,
transfer: Option<&C::Transfer<'a>>,
) -> Result<[u32; 4], Error> {
// TODO only wait on DAT inhbit if DAT line is required
self.inner
.wait_inhibit(true, true, Duration::from_secs(1))
.await
.inspect_err(|_| log::warn!("CMD/DAT inhbit ready timeout"))?;
let response_type = match C::Response::BIT_WIDTH {
SdResponseWidth::None => regs::COMMAND::RESPONSE_TYPE::None,
SdResponseWidth::W48 => regs::COMMAND::RESPONSE_TYPE::Response48,
SdResponseWidth::W48b => regs::COMMAND::RESPONSE_TYPE::Response48b,
SdResponseWidth::W136 => regs::COMMAND::RESPONSE_TYPE::Response136,
};
// let status = self.grab_command().await;
let index_check = if C::Response::INDEX_CHECK {
regs::COMMAND::COMMAND_INDEX_CHECK::SET
} else {
regs::COMMAND::COMMAND_INDEX_CHECK::CLEAR
};
let crc_check = if C::Response::CRC_CHECK {
regs::COMMAND::COMMAND_CRC_CHECK::SET
} else {
regs::COMMAND::COMMAND_CRC_CHECK::CLEAR
};
let command_bits = if let Some(transfer) = transfer {
self.inner
.set::<regs::BLOCK_SIZE::Register>(transfer.block_size());
self.inner
.set::<regs::BLOCK_COUNT::Register>(transfer.block_count());
transfer.command_bits() + regs::COMMAND::DATA_PRESENT_SELECT::SET
} else {
FieldValue::none()
};
self.inner.set::<regs::ARGUMENT::Register>(argument);
self.inner.write(
response_type
+ index_check
+ crc_check
+ command_bits
+ regs::COMMAND::COMMAND_TYPE::Normal
+ regs::COMMAND::COMMAND_INDEX.val(index as u32),
);
match runtime::with_timeout(
self.wait_for_interrupt(EVENT_MASK::COMMAND_COMPLETE::SET),
Duration::from_secs(1),
)
.await
{
Ok(res) => {
res?;
}
Err(_) => {
// TODO abort the command?
log::warn!("Command complete timeout");
return Err(Error::TimedOut);
}
}
let response_bits = match C::Response::BIT_WIDTH {
SdResponseWidth::None => [0; 4],
SdResponseWidth::W48 | SdResponseWidth::W48b => self.inner.read_response_48(),
SdResponseWidth::W136 => self.inner.read_response_136(),
};
Ok(response_bits)
}
async fn wait_for_interrupt(
&self,
mask: FieldValue<u32, EVENT_MASK::Register>,
) -> Result<LocalRegisterCopy<u32, EVENT_MASK::Register>, Error> {
// Include errors
let mask = mask.value | 0xFFFF0000;
loop {
let status = self.interrupt_status.wait(mask).await;
if status.get() & 0xFFFF0000 != 0 {
todo!("Handle errors")
}
// Otherwise matches the expected mask
break Ok(status);
}
}
pub fn dma_allocator(&self) -> &Arc<dyn DmaAllocator> {
self.dma.get()
}
}
impl<H: SdhciHost> Device for Sdhci<H> {
unsafe fn init(self: Arc<Self>, cx: DeviceInitContext) -> Result<(), Error> {
self.dma.init(cx.dma_allocator.clone());
unsafe { self.inner.host.init(cx) }?;
unsafe { self.inner.host.init_irq(self.clone()) }?;
runtime::spawn(self.clone().late_init())?;
Ok(())
}
fn display_name(&self) -> &str {
self.inner.host.name()
}
}
impl<H: SdhciHost> InterruptHandler for Sdhci<H> {
fn handle_irq(self: Arc<Self>, _vector: IrqVector) -> bool {
let status = self.inner.clear_status();
self.interrupt_status.signal(status.get());
status.get() != 0
}
}
pub trait RegisterAccess<T: UIntLike> {
fn read_register(&self, address: usize) -> T;
fn write_register(&self, address: usize, value: T);
}
impl<H: SdhciHost> RegisterAccess<u8> for SdhciInner<H> {
fn read_register(&self, address: usize) -> u8 {
self.host.read1(address)
}
fn write_register(&self, address: usize, value: u8) {
self.host.write1(address, value);
}
}
impl<H: SdhciHost> RegisterAccess<u16> for SdhciInner<H> {
fn read_register(&self, address: usize) -> u16 {
self.host.read2(address)
}
fn write_register(&self, address: usize, value: u16) {
self.host.write2(address, value);
}
}
impl<H: SdhciHost> RegisterAccess<u32> for SdhciInner<H> {
fn read_register(&self, address: usize) -> u32 {
self.host.read4(address)
}
fn write_register(&self, address: usize, value: u32) {
self.host.write4(address, value);
}
}
impl<H: SdhciHost> SdhciInner<H> {
// async fn wait_command_inhibit(&self, timeout: Duration) -> Result<(), Error> {
// const POLL: Duration = Duration::from_millis(25);
// let deadline = monotonic_time() + timeout;
// while self.command_inhibit() || self.data_inhibit() {
// let now = monotonic_time();
// if now >= deadline {
// log::error!("sdhci: timeout waiting for command inhibit");
// return Err(Error::TimedOut);
// }
// let sleep = deadline.sub_time(&now).unwrap_or_default().min(POLL);
// runtime::sleep(sleep).await;
// }
// Ok(())
// }
// fn command_inhibit(&self) -> bool {
// self.matches_all(regs::PRESENT_STATE::COMMAND_INHIBIT_CMD::SET)
// }
// fn data_inhibit(&self) -> bool {
// self.matches_all(regs::PRESENT_STATE::COMMAND_INHIBIT_DAT::SET)
// }
fn write<R: Reg + RegisterLongName>(&self, value: FieldValue<R::Repr, R>)
where
Self: RegisterAccess<R::Repr>,
{
self.set::<R>(value.value);
}
fn modify<R: Reg + RegisterLongName>(&self, modify: FieldValue<R::Repr, R>)
where
Self: RegisterAccess<R::Repr>,
R::Repr: BitAndAssign,
{
let mut value = self.get::<R>();
value &= !modify.mask();
value |= modify.value;
self.set::<R>(value);
}
fn matches_all<R: Reg + RegisterLongName>(&self, pattern: FieldValue<R::Repr, R>) -> bool
where
Self: RegisterAccess<R::Repr>,
{
pattern.matches_all(self.get::<R>())
}
#[inline]
fn get<R: Reg>(&self) -> R::Repr
where
Self: RegisterAccess<R::Repr>,
{
self.read_register(R::ADDRESS)
}
#[inline]
fn set<R: Reg>(&self, value: R::Repr)
where
Self: RegisterAccess<R::Repr>,
{
self.write_register(R::ADDRESS, value);
}
pub async fn software_reset(&self) -> Result<(), Error> {
self.write(SOFTWARE_RESET::SOFTWARE_RESET_ALL::SET);
runtime::sleep_wait(Duration::from_secs(1), Duration::from_millis(20), || {
self.matches_all(SOFTWARE_RESET::SOFTWARE_RESET_ALL::CLEAR)
})
.await
// self.write(SOFTWARE_RESET::SOFTWARE_RESET_ALL::SET);
// let mut timeout = 1000000;
// while timeout > 0 {
// if self.matches_all(SOFTWARE_RESET::SOFTWARE_RESET_ALL::CLEAR) {
// break;
// }
// timeout -= 1;
// }
// if timeout == 0 {
// Err(Error::TimedOut)
// } else {
// Ok(())
// }
}
pub async fn wait_inhibit(
&self,
wait_cmd: bool,
wait_dat: bool,
timeout: Duration,
) -> Result<(), Error> {
runtime::sleep_wait(timeout, Duration::from_millis(20), || {
(!wait_cmd || self.matches_all(PRESENT_STATE::COMMAND_INHIBIT_CMD::CLEAR))
&& (!wait_dat || self.matches_all(PRESENT_STATE::COMMAND_INHIBIT_DAT::CLEAR))
})
.await
}
pub async fn wait_clock_stable(&self) -> Result<(), Error> {
// TODO doesn't work for some reason
runtime::sleep_wait(
Duration::from_millis(100),
Duration::from_millis(10),
|| self.matches_all(regs::CLOCK_CONTROL::INTERNAL_CLOCK_STABLE::SET),
)
.await
}
pub fn set_default_interrupt_mask(&self) {
let mask = 0xFFFF0000
| (EVENT_MASK::COMMAND_COMPLETE::SET
+ EVENT_MASK::TRANSFER_COMPLETE::SET
+ EVENT_MASK::BUFFER_READ_READY::SET
+ EVENT_MASK::BUFFER_WRITE_READY::SET)
.value;
self.set::<INTERRUPT_STATUS_ENABLE::Register>(mask);
self.set::<INTERRUPT_SIGNAL_ENABLE::Register>(mask);
}
pub async fn set_clock_rate(&self, freq: Hertz) -> Result<(), Error> {
let want_divider = if let Some(base_freq) = self.capabilities.base_frequency {
log::info!("Want frequency {freq}, base frequency {base_freq}");
base_freq / freq
} else {
log::warn!("Set SD clock without known base frequency");
16
};
let sd_was_enabled = self.matches_all(regs::CLOCK_CONTROL::SD_CLOCK_ENABLE::SET);
let internal_was_enabled =
self.matches_all(regs::CLOCK_CONTROL::INTERNAL_CLOCK_ENABLE::SET);
self.wait_inhibit(true, true, Duration::from_secs(1))
.await?;
if sd_was_enabled || internal_was_enabled {
self.modify(regs::CLOCK_CONTROL::SD_CLOCK_ENABLE::CLEAR);
}
if internal_was_enabled {
self.modify(regs::CLOCK_CONTROL::INTERNAL_CLOCK_ENABLE::CLEAR);
}
// Pick a matching divider
let div2 = want_divider / 2;
let divider_lo = (div2 & 0xFF) as u16;
let divider_hi = (div2 >> 8) as u16;
if self.capabilities.version < HostVersion::V3_00 {
todo!("8-bit divided clock mode");
}
assert_eq!(divider_hi & !0x3, 0);
self.modify(
regs::CLOCK_CONTROL::SDCLK_FREQ_LOWER.val(divider_lo)
+ regs::CLOCK_CONTROL::SDCLK_FREQ_UPPER.val(divider_hi)
+ regs::CLOCK_CONTROL::CLOCK_GENERATOR_SELECT::ModeDivided,
);
if internal_was_enabled {
// Wait for clock to stabilize
self.modify(regs::CLOCK_CONTROL::INTERNAL_CLOCK_ENABLE::SET);
self.wait_clock_stable()
.await
.inspect_err(|_| log::warn!("Clock stabilization timeout"))?;
}
if sd_was_enabled && internal_was_enabled {
self.modify(regs::CLOCK_CONTROL::SD_CLOCK_ENABLE::SET);
}
Ok(())
}
pub fn set_vdd1_power(&self) {
self.write(
POWER_CONTROL::SD_BUS_POWER_VDD1::SET + POWER_CONTROL::SD_BUS_VOLTAGE_VDD1::Voltage3_3v,
);
// // let tmp = self.host.read1(Self::REG_POWER_CONTROL);
// // self.host.write1(Self::REG_POWER_CONTROL, 0xF);
}
pub async fn set_clock_mode(&self, mode: ClockMode) -> Result<(), Error> {
match mode {
ClockMode::None => {
self.modify(
CLOCK_CONTROL::SD_CLOCK_ENABLE::CLEAR
+ CLOCK_CONTROL::INTERNAL_CLOCK_ENABLE::CLEAR,
);
Ok(())
}
ClockMode::Internal => {
self.modify(CLOCK_CONTROL::SD_CLOCK_ENABLE::CLEAR);
if self.matches_all(CLOCK_CONTROL::INTERNAL_CLOCK_ENABLE::CLEAR) {
self.modify(CLOCK_CONTROL::INTERNAL_CLOCK_ENABLE::SET);
self.wait_clock_stable().await?;
}
Ok(())
}
ClockMode::All => {
if self.matches_all(CLOCK_CONTROL::INTERNAL_CLOCK_ENABLE::CLEAR) {
self.modify(CLOCK_CONTROL::INTERNAL_CLOCK_ENABLE::SET);
self.wait_clock_stable().await?;
}
self.modify(CLOCK_CONTROL::SD_CLOCK_ENABLE::SET);
Ok(())
}
}
// if enable {
// self.modify(CLOCK_CONTROL::SD_CLOCK_ENABLE::SET);
// self.wait_clock_stable().await
// } else {
// self.modify(CLOCK_CONTROL::SD_CLOCK_ENABLE::CLEAR);
// Ok(())
// }
}
pub fn is_card_inserted(&self) -> bool {
self.matches_all(PRESENT_STATE::CARD_INSERTED::SET)
}
pub fn clear_status(&self) -> LocalRegisterCopy<u32, EVENT_MASK::Register> {
let value = self.get::<regs::INTERRUPT_STATUS::Register>();
self.set::<regs::INTERRUPT_STATUS::Register>(value);
LocalRegisterCopy::new(value)
}
pub fn read_response_136(&self) -> [u32; 4] {
[
self.get::<regs::RESPONSE0::Register>(),
self.get::<regs::RESPONSE1::Register>(),
self.get::<regs::RESPONSE2::Register>(),
self.get::<regs::RESPONSE3::Register>(),
]
}
pub fn read_response_48(&self) -> [u32; 4] {
[self.get::<regs::RESPONSE0::Register>(), 0, 0, 0]
}
}
pub fn register_sdcard<H: SdhciHost>(card: Arc<SdCard<H>>, probe: bool) {
// TODO card index allocation
let name = format!("mmc0");
log::info!("Register SD Card: {name}");
devfs::add_named_block_device(card.clone(), name.clone(), FileMode::new(0o600)).ok();
if probe {
runtime::spawn(async move {
let name = name;
log::info!("Probing partitions for {name}");
probe_partitions(card, |index, partition| {
let partition_name = format!("{name}p{}", index + 1);
devfs::add_named_block_device(
Arc::new(partition),
partition_name,
FileMode::new(0o600),
)
.ok();
})
.await
.inspect_err(|error| log::error!("{name}: partition probe failed: {error:?}"))
})
.ok();
}
}
+400
View File
@@ -0,0 +1,400 @@
#![allow(non_camel_case_types, non_snake_case)]
use tock_registers::{register_bitfields, UIntLike};
pub trait Reg {
const ADDRESS: usize;
type Repr: UIntLike;
}
macro_rules! define_register {
($vis:vis $name:ident: $repr:ident @ $address:literal) => {
$vis mod $name {
pub struct Register;
}
impl Reg for $name::Register {
const ADDRESS: usize = $address;
type Repr = $repr;
}
};
(
$vis:vis $name:ident: $repr:ident @ $address:literal {
$( $field:ident OFFSET($offset:literal) NUMBITS($numbits:literal) [
$( $fieldVariant:ident = $fieldVariantValue:literal ),* $(,)?
] ),+ $(,)?
}
) => {
tock_registers::register_bitfields! {
$repr,
$vis $name [
$( $field OFFSET($offset) NUMBITS($numbits) [
$( $fieldVariant = $fieldVariantValue ),*
] ),+
],
}
impl Reg for $name::Register {
const ADDRESS: usize = $address;
type Repr = $repr;
}
};
}
register_bitfields! {
u32,
pub EVENT_MASK [
BUFFER_READ_READY OFFSET(5) NUMBITS(1) [],
BUFFER_WRITE_READY OFFSET(4) NUMBITS(1) [],
DMA_INTERRUPT OFFSET(3) NUMBITS(1) [],
BLOCK_GAP_EVENT OFFSET(2) NUMBITS(1) [],
TRANSFER_COMPLETE OFFSET(1) NUMBITS(1) [],
COMMAND_COMPLETE OFFSET(0) NUMBITS(1) [],
],
}
define_register!(pub BLOCK_SIZE: u16 @ 0x04);
define_register!(pub BLOCK_COUNT: u16 @ 0x06);
define_register!(pub ARGUMENT: u32 @ 0x08);
define_register!(pub COMMAND: u32 @ 0x0C {
// Transfer mode
DMA_ENABLE OFFSET(0) NUMBITS(1) [],
BLOCK_COUNT_ENABLE OFFSET(1) NUMBITS(1) [],
AUTO_CMD_ENABLE OFFSET(2) NUMBITS(2) [],
DATA_TRANSFER_DIRECTION OFFSET(4) NUMBITS(1) [
HostToCard = 0,
CardToHost = 1,
],
MULTI_BLOCK_SELECT OFFSET(5) NUMBITS(1) [
SingleBlock = 0,
MultiBlock = 1,
],
RESPONSE_TYPE_R1_R5 OFFSET(6) NUMBITS(1) [
R1 = 0,
R5 = 1,
],
RESPONSE_ERROR_CHECK OFFSET(7) NUMBITS(1) [],
RESPONSE_INTERRUPT_DISABLE OFFSET(8) NUMBITS(1) [],
// Command
RESPONSE_TYPE OFFSET(16) NUMBITS(2) [
None = 0,
Response136 = 1,
Response48 = 2,
Response48b = 3,
],
SUB_COMMAND_FLAG OFFSET(18) NUMBITS(1) [],
COMMAND_CRC_CHECK OFFSET(19) NUMBITS(1) [],
COMMAND_INDEX_CHECK OFFSET(20) NUMBITS(1) [],
DATA_PRESENT_SELECT OFFSET(21) NUMBITS(1) [],
COMMAND_TYPE OFFSET(22) NUMBITS(2) [
Normal = 0,
Suspend = 1,
Resume = 2,
Abort = 3,
],
COMMAND_INDEX OFFSET(24) NUMBITS(6) [],
});
define_register!(pub RESPONSE0: u32 @ 0x10);
define_register!(pub RESPONSE1: u32 @ 0x14);
define_register!(pub RESPONSE2: u32 @ 0x18);
define_register!(pub RESPONSE3: u32 @ 0x1C);
define_register!(pub BUFFER_DATA: u32 @ 0x20);
define_register!(pub PRESENT_STATE: u32 @ 0x24 {
COMMAND_INHIBIT_CMD OFFSET(0) NUMBITS(1) [],
COMMAND_INHIBIT_DAT OFFSET(1) NUMBITS(1) [],
DAT_LINE_ACTIVE OFFSET(2) NUMBITS(1) [],
RETUNING_REQUEST OFFSET(3) NUMBITS(1) [],
DAT_LINE_UPPER OFFSET(4) NUMBITS(4) [],
WRITE_TRANSFER_ACTIVE OFFSET(8) NUMBITS(1) [],
READ_TRANSFER_ACTIVE OFFSET(9) NUMBITS(1) [],
BUFFER_WRITE_ENABLE OFFSET(10) NUMBITS(1) [],
BUFFER_READ_ENABLE OFFSET(11) NUMBITS(1) [],
CARD_INSERTED OFFSET(16) NUMBITS(1) [],
CARD_STATE_STABLE OFFSET(17) NUMBITS(1) [],
CARD_DETECT_PIN_LEVEL OFFSET(18) NUMBITS(1) [],
WRITE_PROTECT_LEVEL OFFSET(19) NUMBITS(1) [],
DAT_LINE_LOWER OFFSET(20) NUMBITS(4) [],
CMD_LINE_LEVEL OFFSET(24) NUMBITS(1) [],
HOST_REGULATOR_V_STABLE OFFSET(25) NUMBITS(1) [],
CMD_NOT_ISSUED_BY_ERROR OFFSET(27) NUMBITS(1) [],
SUB_CMD_STATUS OFFSET(28) NUMBITS(1) [],
DORMANT_STATE OFFSET(29) NUMBITS(1) [],
LANE_SYNC OFFSET(30) NUMBITS(1) [],
UHS2_IF_DETECT OFFSET(31) NUMBITS(1) [],
});
define_register!(pub HOST_CONTROL1: u8 @ 0x28 {
LED_CONTROL OFFSET(0) NUMBITS(1) [],
});
define_register!(pub POWER_CONTROL: u8 @ 0x29 {
SD_BUS_POWER_VDD1 OFFSET(0) NUMBITS(1) [],
SD_BUS_VOLTAGE_VDD1 OFFSET(1) NUMBITS(3) [
Voltage1_8v = 0b101,
Voltage3_0v = 0b110,
Voltage3_3v = 0b111,
],
SD_BUS_POWER_VDD2 OFFSET(4) NUMBITS(1) [],
SD_BUS_VOLTAGE_VDD2 OFFSET(5) NUMBITS(3) [
Voltage1_8v = 0b101,
],
});
define_register!(pub CLOCK_CONTROL: u16 @ 0x2C {
INTERNAL_CLOCK_ENABLE OFFSET(0) NUMBITS(1) [],
INTERNAL_CLOCK_STABLE OFFSET(1) NUMBITS(1) [],
SD_CLOCK_ENABLE OFFSET(2) NUMBITS(1) [],
PLL_ENABLE OFFSET(3) NUMBITS(1) [],
CLOCK_GENERATOR_SELECT OFFSET(5) NUMBITS(1) [
ModeDivided = 0,
ModeProgrammable = 1,
],
SDCLK_FREQ_UPPER OFFSET(6) NUMBITS(2) [],
SDCLK_FREQ_LOWER OFFSET(8) NUMBITS(8) [],
});
define_register!(pub SOFTWARE_RESET: u8 @ 0x2F {
SOFTWARE_RESET_ALL OFFSET(0) NUMBITS(1) [],
RESET_CMD_LINE OFFSET(1) NUMBITS(1) [],
RESET_DAT_LINE OFFSET(2) NUMBITS(1) [],
});
define_register!(pub INTERRUPT_STATUS: u32 @ 0x30);
define_register!(pub INTERRUPT_STATUS_ENABLE: u32 @ 0x34);
define_register!(pub INTERRUPT_SIGNAL_ENABLE: u32 @ 0x38);
define_register!(pub CAPABILITIES: u64 @ 0x40 {
BASE_CLOCK_FREQ OFFSET(8) NUMBITS(8) [],
ADMA2_SUPPORT OFFSET(19) NUMBITS(1) [],
SDMA_SUPPORT OFFSET(22) NUMBITS(1) [],
VOLTAGE_3_3V OFFSET(24) NUMBITS(1) [],
VOLTAGE_3_0V OFFSET(25) NUMBITS(1) [],
VOLTAGE_1_8V OFFSET(26) NUMBITS(1) [],
CLOCK_MULTIPLIER OFFSET(48) NUMBITS(8) [],
ADMA3_SUPPORT OFFSET(59) NUMBITS(1) [],
});
// use core::time::Duration;
//
// use libk::{
// error::Error,
// task::runtime::{psleep, pwait},
// };
// use tock_registers::{
// interfaces::{ReadWriteable, Readable, Writeable},
// register_bitfields, register_structs,
// registers::ReadWrite,
// };
//
// register_bitfields! {
// u32,
// WORD9 [
// COMMAND_INHIBIT_CMD OFFSET(0) NUMBITS(1) [],
// COMMAND_INHIBIT_DAT OFFSET(1) NUMBITS(1) [],
// DAT_LINE_HIGH OFFSET(2) NUMBITS(1) [],
// RETUNING_REQUEST OFFSET(3) NUMBITS(1) [],
// DAT_SIGNAL_LEVEL OFFSET(4) NUMBITS(4) [],
// WRITE_TRANSFER_ACTIVE OFFSET(8) NUMBITS(1) [],
// READ_TRANSFER_ACTIVE OFFSET(9) NUMBITS(1) [],
// BUFFER_WRITE_ENABLE OFFSET(10) NUMBITS(1) [],
// BUFFER_READ_ENABLE OFFSET(11) NUMBITS(1) [],
// CARD_INSERTED OFFSET(16) NUMBITS(1) [],
// CARD_STATE_STABLE OFFSET(17) NUMBITS(1) [],
// CARD_DETECT_PIN_LEVEL OFFSET(18) NUMBITS(1) [],
// WRITE_PROTECT_PIN_LEVEL OFFSET(19) NUMBITS(1) [],
// DAT_LINE_LOW OFFSET(20) NUMBITS(4) [],
// CMD_LINE OFFSET(24) NUMBITS(1) [],
// HOST_REGULATOR_V_STABLE OFFSET(25) NUMBITS(1) [],
// COMMAND_NOT_ISSUED OFFSET(27) NUMBITS(1) [],
// SUB_COMMAND_STATUS OFFSET(28) NUMBITS(1) [],
// DORMANT_STATE OFFSET(29) NUMBITS(1) [],
// LANE_SYNC OFFSET(30) NUMBITS(1) [],
// UHS2_IF_DETECT OFFSET(31) NUMBITS(1) [],
// ],
// WORD10 [
// LED_CONTROL OFFSET(0) NUMBITS(1) [],
// DATA_TRANSFER_WIDTH OFFSET(1) NUMBITS(1) [
// Width1b = 0,
// Width4b = 1,
// ],
// HIGH_SPEED_ENABLE OFFSET(2) NUMBITS(1) [],
// DMA_SELECT OFFSET(3) NUMBITS(2) [
// ModeSdma = 0,
// Adma2 = 2,
// Adma2or3 = 3,
// ],
// EXT_DATA_TRANSFER_WIDTH OFFSET(5) NUMBITS(1) [
// WidthNormal = 0,
// Width8b = 1,
// ],
// CARD_DETECT_TEST_LEVEL OFFSET(6) NUMBITS(1) [
// NotInserted = 0,
// Inserted = 1,
// ],
// CARD_DETECT_SIGNAL_SELECT OFFSET(7) NUMBITS(1) [
// SdcdSignal = 0,
// TestSignal = 1,
// ],
// SD_BUS_POWER_VDD1 OFFSET(8) NUMBITS(1) [],
// SD_BUS_VOLTAGE_VDD1 OFFSET(9) NUMBITS(3) [
// Voltage1_8v = 5,
// Voltage3_0v = 6,
// Voltage3_3v = 7,
// ],
// SD_BUS_POWER_VDD2 OFFSET(12) NUMBITS(1) [],
// SD_BUS_VOLTAGE_VDD2 OFFSET(13) NUMBITS(3) [
// Voltage1_8v = 5,
// NotSupported = 0,
// ],
// STOP_AT_BLOCK_GAP_REQ OFFSET(16) NUMBITS(1) [],
// CONTINUE_REQ OFFSET(17) NUMBITS(1) [],
// READ_WAIT_CONTROL OFFSET(18) NUMBITS(1) [],
// INTERRUPT_AT_BLOCK_GAP OFFSET(19) NUMBITS(1) [],
// WAKEUP_ON_CARD_INTERRUPT OFFSET(24) NUMBITS(1) [],
// WAKEUP_ON_CARD_INSERT OFFSET(25) NUMBITS(1) [],
// WAKEUP_ON_CARD_REMOVE OFFSET(26) NUMBITS(1) [],
// ],
// WORD11 [
// INTERNAL_CLOCK_ENABLE OFFSET(0) NUMBITS(1) [],
// INTERNAL_CLOCK_STABLE OFFSET(1) NUMBITS(1) [],
// SD_CLOCK_ENABLE OFFSET(2) NUMBITS(1) [],
// PLL_ENABLE OFFSET(3) NUMBITS(1) [],
// CLOCK_GENERATOR_SELECT OFFSET(5) NUMBITS(1) [
// DividedClock = 0,
// ProgrammableClock = 1,
// ],
// SDCLK_FREQ_UPPER OFFSET(6) NUMBITS(2) [],
// SDCLK_FREQ_LOWER OFFSET(8) NUMBITS(8) [],
// DATA_TIMEOUT_COUNTER OFFSET(16) NUMBITS(4) [],
// SOFTWARE_RESET_ALL OFFSET(24) NUMBITS(1) [],
// SOFTWARE_RESET_CMD_LINE OFFSET(25) NUMBITS(1) [],
// SOFTWARE_RESET_DAT_LINE OFFSET(26) NUMBITS(1) [],
// ],
// WORD12 [
// COMMAND_COMPLETE OFFSET(0) NUMBITS(1) [],
// TRANSFER_COMPLETE OFFSET(1) NUMBITS(1) [],
// BLOCK_GAP_EVENT OFFSET(2) NUMBITS(1) [],
// DMA_INTERRUPT OFFSET(3) NUMBITS(1) [],
// BUFFER_WRITE_READY OFFSET(4) NUMBITS(1) [],
// BUFFER_READ_READY OFFSET(5) NUMBITS(1) [],
// CARD_INSERTION OFFSET(6) NUMBITS(1) [],
// CARD_REMOVAL OFFSET(7) NUMBITS(1) [],
// CARD_INTERRUPT OFFSET(8) NUMBITS(1) [],
// INT_A OFFSET(9) NUMBITS(1) [],
// INT_B OFFSET(10) NUMBITS(1) [],
// INT_C OFFSET(11) NUMBITS(1) [],
// RETUNING_EVENT OFFSET(12) NUMBITS(1) [],
// FX_EVENT OFFSET(13) NUMBITS(1) [],
// COMMAND_TIMEOUT_ERROR OFFSET(16) NUMBITS(1) [],
// COMMAND_CRC_ERROR OFFSET(17) NUMBITS(1) [],
// COMMAND_END_BIT_ERROR OFFSET(18) NUMBITS(1) [],
// COMMAND_INDEX_ERROR OFFSET(19) NUMBITS(1) [],
// DATA_TIMEOUT_ERROR OFFSET(20) NUMBITS(1) [],
// DATA_CRC_ERROR OFFSET(21) NUMBITS(1) [],
// DATA_END_BIT_ERROR OFFSET(22) NUMBITS(1) [],
// CURRENT_LIMIT_ERROR OFFSET(23) NUMBITS(1) [],
// AUTO_CMD_ERROR OFFSET(24) NUMBITS(1) [],
// ADMA_ERROR OFFSET(25) NUMBITS(1) [],
// TUNING_ERROR OFFSET(26) NUMBITS(1) [],
// RESPONSE_ERROR OFFSET(27) NUMBITS(1) [],
// ],
// WORD15 [
// AUTO_CMD12_NOT_EXECUTED OFFSET(0) NUMBITS(1) [],
// AUTO_CMD_TIMEOUT_ERROR OFFSET(1) NUMBITS(1) [],
// AUTO_CMD_CRC_ERROR OFFSET(2) NUMBITS(1) [],
// AUTO_CMD_END_BIT_ERROR OFFSET(3) NUMBITS(1) [],
// AUTO_CMD_INDEX_ERROR OFFSET(4) NUMBITS(1) [],
// AUTO_CMD_RESPONSE_ERROR OFFSET(5) NUMBITS(1) [],
// COMMAND_NOT_ISSUED_CMD12 OFFSET(7) NUMBITS(1) [],
// UHS_MODE_SELECT OFFSET(16) NUMBITS(3) [
// ModeSdr12 = 0,
// ModeSdr25 = 1,
// ModeSdr50 = 2,
// ModeSdr104 = 3,
// ModeDdr50 = 4,
// Uhs2 = 7,
// ],
// SIGNALING_1_8V_ENABLE OFFSET(19) NUMBITS(1) [],
// DRIVER_SIGNAL_SELECT OFFSET(20) NUMBITS(2) [
// DriverTypeB = 0,
// DriverTypeA = 1,
// DriverTypeC = 2,
// DriverTypeD = 3,
// ],
// EXECUTE_TUNING OFFSET(22) NUMBITS(1) [],
// SAMPLING_CLOCK_SELECT OFFSET(23) NUMBITS(1) [],
// UHS2_INTERFACE_ENABLE OFFSET(24) NUMBITS(1) [],
// ADMA2_LENGTH_MODE OFFSET(26) NUMBITS(1) [
// Length16b = 0,
// Length26b = 1,
// ],
// CMD23_ENABLE OFFSET(27) NUMBITS(1) [],
// HOST_V4_ENABLE OFFSET(28) NUMBITS(1) [],
// ADDRESS_64_BIT OFFSET(29) NUMBITS(1) [],
// ASYNC_INTERRUPT_ENABLE OFFSET(30) NUMBITS(1) [],
// PRESET_VALUE_ENABLE OFFSET(31) NUMBITS(1) [],
// ],
// }
//
// register_structs! {
// #[allow(non_snake_case)]
// pub Regs {
// // SDMA system address
// (0x000 => WORD0: ReadWrite<u32>),
// // Block size, block count
// (0x004 => WORD1: ReadWrite<u32>),
// // Argument
// (0x008 => WORD2: ReadWrite<u32>),
// // Transfer mode, command
// (0x00C => WORD3: ReadWrite<u32>),
// // Response0, Response1
// (0x010 => WORD4: ReadWrite<u32>),
// // Response2, Response3
// (0x014 => WORD5: ReadWrite<u32>),
// // Response4, Response5
// (0x018 => WORD6: ReadWrite<u32>),
// // Response6, Response7
// (0x01C => WORD7: ReadWrite<u32>),
// // Buffer data port 0, port 1
// (0x020 => WORD8: ReadWrite<u32>),
// // Present state
// (0x024 => WORD9: ReadWrite<u32, WORD9::Register>),
// // Host control 1, power control, block gap control, wakeup control
// (0x028 => WORD10: ReadWrite<u32, WORD10::Register>),
// // Clock control, timeout control, software reset
// (0x02C => WORD11: ReadWrite<u32, WORD11::Register>),
// // Normal interrupt status, error interrupt status
// (0x030 => WORD12: ReadWrite<u32, WORD12::Register>),
// // Normal interrupt status enable, error interrupt status enable
// (0x034 => WORD13: ReadWrite<u32, WORD12::Register>),
// // Normal interrupt signal enable, error interrupt signal enable
// (0x038 => WORD14: ReadWrite<u32, WORD12::Register>),
// // Auto CMD error status, host control 2
// (0x03C => WORD15: ReadWrite<u32, WORD15::Register>),
// (0x040 => WORD16: ReadWrite<u32>),
// (0x044 => WORD17: ReadWrite<u32>),
// (0x048 => WORD18: ReadWrite<u32>),
// (0x04C => WORD19: ReadWrite<u32>),
// (0x050 => _0),
// (0x100 => @END),
// }
// }
//
// impl Regs {
// pub fn software_reset_all(&self, timeout: Duration) -> Result<(), Error> {
// self.WORD11.modify(WORD11::SOFTWARE_RESET_ALL::SET);
// pwait(timeout, Duration::from_millis(50), || {
// self.WORD11.matches_all(WORD11::SOFTWARE_RESET_ALL::CLEAR)
// })?;
// Ok(())
// }
//
// pub fn setup_default_interrupts(&self) {
// self.WORD13
// .write(WORD12::COMMAND_COMPLETE::SET + WORD12::TRANSFER_COMPLETE::SET);
//
// self.WORD11.modify(WORD11::SOFTWARE_RESET_ALL::CLEAR);
//
// // Setup VDD1 power
// self.WORD10
// .modify(WORD10::SD_BUS_POWER_VDD1::SET + WORD10::SD_BUS_VOLTAGE_VDD1::Voltage3_3v);
//
// loop {
// psleep(Duration::from_secs(1));
// log::info!("{:#x}", self.WORD9.get());
// }
// }
// }
+4 -7
View File
@@ -8,14 +8,13 @@ use device_api::{
clock::{ClockHandle, ResetHandle},
device::{Device, DeviceInitContext},
dma::DmaAllocator,
interrupt::{FullIrq, InterruptHandler, IrqVector},
interrupt::{InterruptHandler, IrqHandle, IrqVector},
};
use device_tree::driver::{
device_tree_driver, util::read_mac_address, InitSequence, Node, ProbeContext,
};
use futures_util::task::AtomicWaker;
use libk::{
device::external_interrupt_controller,
dma::DmaBuffer,
error::Error,
task::runtime::{self, psleep, pwait},
@@ -68,7 +67,7 @@ struct Stmmac {
rst_stmmaceth: ResetHandle,
rst_ahb: ResetHandle,
irq: FullIrq,
irq: IrqHandle,
softirq_events: BitmapEvent<AtomicWaker>,
inner: OneTimeInit<Inner>,
@@ -184,9 +183,7 @@ impl Device for Stmmac {
}
let dma = self.dma.init(cx.dma_allocator.clone());
let intc = external_interrupt_controller()?;
intc.register_irq(self.irq.irq, self.irq.options, self.clone())?;
self.irq.register(self.clone())?;
let tx_ring_capacity = 32;
let rx_ring_capacity = 32;
@@ -350,7 +347,7 @@ impl Device for Stmmac {
ygg_driver_net_core::register_interface(NetworkInterfaceType::Ethernet, self.clone());
self.iface_id.init(iface.id());
intc.enable_irq(self.irq.irq)?;
self.irq.enable()?;
let p = self.clone();
runtime::spawn(async move { p.softirq().await })?;
+8
View File
@@ -50,6 +50,14 @@ impl Div<u32> for Hertz {
}
}
impl Div<Hertz> for Hertz {
type Output = u32;
fn div(self, rhs: Hertz) -> Self::Output {
(self.0 / rhs.0) as u32
}
}
impl fmt::Display for Hertz {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let (scale, suffix) = match self.0 {
+96
View File
@@ -0,0 +1,96 @@
use alloc::{boxed::Box, sync::Arc};
use yggdrasil_abi::error::Error;
use crate::device::Device;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum InputPinBias {
PullUp,
PullDown,
Floating,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum OutputPinBias {
PushPull,
OpenDrain,
HighZ,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GpioPinLevel {
High,
Low,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SinglePinDirection {
Output,
Input,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GpioInterruptMode {
RisingEdge,
FallingEdge,
BothEdges,
HighLevel,
LowLevel,
}
pub struct GpioInterruptEvent {
pub mode: GpioInterruptMode,
pub event: u64,
}
pub struct GpioInterrupt {
pub mode: GpioInterruptMode,
pub handler: Box<dyn Fn() + Send>,
}
pub struct GpioPinConfig {
pub input: bool,
pub output: bool,
pub input_bias: Option<InputPinBias>,
pub output_bias: Option<OutputPinBias>,
pub active_level: GpioPinLevel,
pub initial_level: GpioPinLevel,
}
pub struct PinHandle {
pub index: u32,
pub config: GpioPinConfig,
pub parent: Arc<dyn GpioController>,
}
pub trait GpioController: Device {
fn write_gpio(&self, pin: &PinHandle, value: bool) -> Result<(), Error>;
fn read_gpio(&self, pin: &PinHandle) -> Result<bool, Error>;
fn setup_gpio(&self, pin: &PinHandle, event: Option<GpioInterruptEvent>) -> Result<(), Error>;
}
pub trait GpioOutput: Sync + Send {
fn write(&self, value: bool) -> Result<(), Error>;
}
impl PinHandle {
pub fn configure(&self) -> Result<(), Error> {
self.parent.setup_gpio(self, None)
}
pub fn write(&self, value: bool) -> Result<(), Error> {
self.parent.write_gpio(self, value)
}
}
impl GpioPinConfig {
pub fn force_single_direction(&self) -> Option<SinglePinDirection> {
match (self.input, self.output) {
(true, false) => Some(SinglePinDirection::Input),
(false, true) => Some(SinglePinDirection::Output),
(false, false) => None,
(true, true) => None,
}
}
}
+13 -2
View File
@@ -3,10 +3,11 @@ use yggdrasil_abi::error::Error;
use crate::device::Device;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct FullIrq {
#[derive(Clone)]
pub struct IrqHandle {
pub irq: Irq,
pub options: IrqOptions,
pub intc: Arc<dyn ExternalInterruptController>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -154,6 +155,16 @@ pub struct FixedInterruptTable<const N: usize> {
rows: [Option<Arc<dyn InterruptHandler>>; N],
}
impl IrqHandle {
pub fn register(&self, handler: Arc<dyn InterruptHandler>) -> Result<(), Error> {
self.intc.register_irq(self.irq, self.options, handler)
}
pub fn enable(&self) -> Result<(), Error> {
self.intc.enable_irq(self.irq)
}
}
impl<const N: usize> FixedInterruptTable<N> {
pub const fn new() -> Self {
Self {
+1
View File
@@ -7,6 +7,7 @@ extern crate alloc;
pub mod bus;
pub mod clock;
pub mod device;
pub mod gpio;
pub mod interrupt;
pub mod serial;
pub mod timer;
@@ -2,11 +2,12 @@
use alloc::sync::Arc;
use device_api::{
clock::{ClockHandle, ResetHandle},
interrupt::FullIrq,
gpio::PinHandle,
interrupt::IrqHandle,
};
use fdt_rs::spec::Phandle;
use crate::{DeviceTreePropertyRead, TProp};
use crate::{driver::DeviceTreeGpioPins, DeviceTreePropertyRead, TProp};
use super::{lookup_phandle, Node};
@@ -20,6 +21,12 @@ pub(crate) struct ResetIter<'dt> {
pub(crate) offset: usize,
}
pub(crate) struct GpioIter<'o, 'dt> {
pub(crate) options: &'o DeviceTreeGpioPins,
pub(crate) gpios: TProp<'dt>,
pub(crate) offset: usize,
}
impl Iterator for ClockIter<'_> {
type Item = ClockHandle;
@@ -52,6 +59,22 @@ impl Iterator for ResetIter<'_> {
}
}
impl Iterator for GpioIter<'_, '_> {
type Item = PinHandle;
fn next(&mut self) -> Option<Self::Item> {
if self.offset >= self.gpios.len() {
return None;
}
let phandle = self.gpios.read_cell(self.offset, 1)? as Phandle;
let gpioc = lookup_phandle(phandle, true)?;
let gpioc = gpioc.as_pin_controller()?;
let (pin, len) = gpioc.map_gpio(&self.gpios, self.offset + 1, self.options)?;
self.offset += len + 1;
Some(pin)
}
}
// Interrupt controller handling
/// Reads interrupt information, as interpreted by `interrupt_controller`, from `property` at a
@@ -60,14 +83,14 @@ pub fn map_interrupt_at(
interrupt_controller: &Arc<Node>,
property: &TProp,
offset: usize,
) -> Option<FullIrq> {
) -> Option<IrqHandle> {
let interrupt_controller = interrupt_controller.interrupt_controller.try_get()?;
interrupt_controller.map_interrupt(property, offset)
interrupt_controller.clone().map_interrupt(property, offset)
}
/// Same as [map_interrupt_at], but uses a phandle to address the `interrupt-controller` node
/// and a scaled `index`.
pub fn map_interrupt(phandle: Phandle, property: &TProp, index: usize) -> Option<FullIrq> {
pub fn map_interrupt(phandle: Phandle, property: &TProp, index: usize) -> Option<IrqHandle> {
let interrupt_controller = lookup_phandle(phandle, true)?;
let interrupt_cells = interrupt_controller.self_interrupt_cells()?;
map_interrupt_at(&interrupt_controller, property, index * interrupt_cells)
+14 -3
View File
@@ -1,11 +1,12 @@
//! Device tree-based driver definitions
use alloc::sync::Arc;
use device_api::gpio::GpioPinLevel;
use yggdrasil_abi::error::Error;
mod controller;
mod macros;
mod registry;
pub(crate) mod registry;
mod syscon;
mod traits;
mod tree;
@@ -17,11 +18,21 @@ pub use macros::device_tree_driver;
pub use registry::{lookup_phandle, register_driver};
pub use syscon::DeviceTreeSyscon;
pub use traits::{
DeviceTreeClockController, DeviceTreeInterruptController, DeviceTreeResetController, Driver,
ProbeContext,
DeviceTreeClockController, DeviceTreeInterruptController, DeviceTreePinController,
DeviceTreeResetController, Driver, ProbeContext,
};
pub use tree::{find_node, unflatten_device_tree, walk_device_tree, Node};
/// Contextual information about a GPIO pin being configured
pub struct DeviceTreeGpioPins {
/// Whether to enable input on the pin
pub input: bool,
/// Whether to enable output on the pin
pub output: bool,
/// Initial level to configure the GPIO with
pub initial_level: GpioPinLevel,
}
/// Specifies initialization sequence requirement for a driver
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum InitSequence {
+19 -3
View File
@@ -6,10 +6,12 @@ use device_api::{
bus::Bus,
clock::{ClockHandle, ResetHandle},
device::Device,
interrupt::{ExternalInterruptController, FullIrq},
gpio::PinHandle,
interrupt::{ExternalInterruptController, IrqHandle},
};
use libk::error::Error;
use crate::TProp;
use crate::{driver::DeviceTreeGpioPins, TProp};
use super::{InitSequence, Node};
@@ -23,7 +25,7 @@ pub trait Driver: Sync {
pub trait DeviceTreeInterruptController {
/// Reads interrupt information from `property` at given `offset` and maps it to
/// the interrupt used by the controller
fn map_interrupt(&self, property: &TProp, offset: usize) -> Option<FullIrq>;
fn map_interrupt(self: Arc<Self>, property: &TProp, offset: usize) -> Option<IrqHandle>;
/// Returns the [ExternalInterruptController] implementor of this node
fn as_interrupt_controller(self: Arc<Self>) -> Arc<dyn ExternalInterruptController>;
@@ -51,6 +53,20 @@ pub struct ProbeContext {
pub sequence: Option<InitSequence>,
}
/// `-pinctrl`-type devices, used for configuring pin groups
pub trait DeviceTreePinController {
/// Configure a pin group
fn configure_pin_group(self: Arc<Self>, pins: &Arc<Node>) -> Result<(), Error>;
/// Reads GPIO pin information from `property` at given `offset` and maps it to a
/// [PinHandle], returning the handle + the size of the GPIO entry.
fn map_gpio(
self: Arc<Self>,
property: &TProp,
offset: usize,
info: &DeviceTreeGpioPins,
) -> Option<(PinHandle, usize)>;
}
impl ProbeContext {
/// See [Node::map_range]
pub fn map_range(&self, range: Range<u64>) -> Option<Range<u64>> {
+74 -7
View File
@@ -9,7 +9,8 @@ use device_api::{
bus::Bus,
clock::{ClockController, ClockHandle, ResetHandle},
device::{Device, DeviceInitContext},
interrupt::{ExternalInterruptController, FullIrq, MessageInterruptController},
gpio::PinHandle,
interrupt::{ExternalInterruptController, IrqHandle, MessageInterruptController},
};
use fdt_rs::spec::Phandle;
use libk::dma::DummyDmaAllocator;
@@ -18,7 +19,10 @@ use libk_util::OneTimeInit;
use yggdrasil_abi::error::Error;
use crate::{
driver::DeviceTreeSyscon, DeviceTree, DeviceTreeNodeExt, DeviceTreePropertyRead, TNode, TProp,
driver::{
controller::GpioIter, traits::DeviceTreePinController, DeviceTreeGpioPins, DeviceTreeSyscon,
},
tree, DeviceTree, DeviceTreeNodeExt, DeviceTreePropertyRead, TNode, TProp,
};
use super::{
@@ -42,7 +46,7 @@ pub struct Node {
bus_size_cells: usize,
interrupt_parent: Option<Phandle>,
name: Option<&'static str>,
compatible: Option<TProp<'static>>,
pub(crate) compatible: Option<TProp<'static>>,
// Hierachy info
children: OneTimeInit<Vec<Arc<Node>>>,
@@ -55,6 +59,7 @@ pub struct Node {
pub(crate) clock_controler: OneTimeInit<Arc<dyn DeviceTreeClockController>>,
pub(crate) reset_controller: OneTimeInit<Arc<dyn DeviceTreeResetController>>,
pub(crate) system_controller: OneTimeInit<Arc<dyn DeviceTreeSyscon>>,
pub(crate) pin_controller: OneTimeInit<Arc<dyn DeviceTreePinController>>,
}
pub(crate) struct ProbedDevice {
@@ -90,6 +95,7 @@ impl NodeDevice {
impl Node {
fn probe_single(node: &Arc<Node>, cx: &mut ProbeContext) -> Option<ProbedDevice> {
let name = node.name();
let compatible = node.compatible.as_ref()?;
let drivers = DRIVERS.read();
@@ -100,7 +106,6 @@ impl Node {
if libk::config::get().device_tree.log_missing {
if driver.is_none() {
let name = node.name();
// FIXME don't spam virtio missing stuff
if !name.map_or(false, |n| n.starts_with("virtio_mmio")) {
for (i, compatible) in compatible.as_str_list().enumerate() {
@@ -115,6 +120,11 @@ impl Node {
}
let driver = driver?;
// Initialize default pinctrl before probing
node.setup_pins()
.inspect_err(|e| log::error!("{name:?}: pinctrl init error {e:?}"))
.ok()?;
let device = driver.imp.probe(node, cx);
// Move to early init unless driver wants to sleep
@@ -140,6 +150,7 @@ impl Node {
if let Some(status) = self.property("status") {
let status = status.as_str().unwrap_or("");
if status == "disabled" {
// log::info!("Not probing disabled {:?}", self.compatible);
return (None, parent_bus);
}
}
@@ -212,6 +223,11 @@ impl Node {
self.system_controller.init(syscon);
}
/// Informs the node of its capability as a pin controller.
pub fn make_pin_controller(&self, pinctrl: Arc<dyn DeviceTreePinController>) {
self.pin_controller.init(pinctrl);
}
/// Returns the device driver associated with this node, if any was probed.
pub fn driver(&self) -> Option<&'static dyn Driver> {
let probed = self.device.try_get()?.as_probed()?;
@@ -337,6 +353,24 @@ impl Node {
.map(|range| PhysicalAddress::from_u64(range.start))
}
/// Returns an iterator over the node's defined `gpios`
pub fn gpios<'a>(
&self,
options: &'a DeviceTreeGpioPins,
) -> Option<impl Iterator<Item = PinHandle> + 'a> {
let gpios = self.property("gpios")?;
Some(GpioIter {
gpios,
options,
offset: 0,
})
}
/// Returns a `gpios` entry with given index
pub fn gpio(&self, options: &DeviceTreeGpioPins, index: usize) -> Option<PinHandle> {
self.gpios(options)?.nth(index)
}
/// Returns an input `clock` handle for a given reset name
pub fn named_clock(&self, name: &str) -> Option<ClockHandle> {
self.property("clock-names")?
@@ -377,7 +411,7 @@ impl Node {
/// Reads interrupt information from `interrupts[index]` property, mapped by the node's
/// `interrupt-parent`.
pub fn interrupt(&self, index: usize) -> Option<FullIrq> {
pub fn interrupt(&self, index: usize) -> Option<IrqHandle> {
let interrupts = self.property("interrupts")?;
let phandle = self.interrupt_parent?;
map_interrupt(phandle, &interrupts, index)
@@ -385,7 +419,7 @@ impl Node {
/// Same as [Node::interrupt], but allows specifying other property to read the information
/// from.
pub fn interrupt_from(&self, property: &TProp, index: usize) -> Option<FullIrq> {
pub fn interrupt_from(&self, property: &TProp, index: usize) -> Option<IrqHandle> {
let phandle = self.interrupt_parent?;
map_interrupt(phandle, property, index)
}
@@ -409,11 +443,16 @@ impl Node {
.map(|e| e.clone().as_interrupt_controller())
}
/// Attempts to get an system controller represented by this node, if any
/// Attempts to get a system controller represented by this node, if any
pub fn as_system_controller(self: &Arc<Self>) -> Option<Arc<dyn DeviceTreeSyscon>> {
self.system_controller.try_get().cloned()
}
/// Attempts to get a pin controller represented by this node, if any
pub fn as_pin_controller(&self) -> Option<Arc<dyn DeviceTreePinController>> {
self.pin_controller.try_get().cloned()
}
/// Returns the `#address-cells` value of the node's parent bus
pub fn bus_address_cells(&self) -> usize {
self.bus_address_cells
@@ -444,6 +483,11 @@ impl Node {
self.dt_node.property(name)
}
/// Returns `true` if the node has a property `name` inside
pub fn has_property(&self, name: &str) -> bool {
self.dt_node.has_property(name)
}
/// Interprets property `name` as a single cell and casts it to usize
pub fn prop_usize(&self, name: &str) -> Option<usize> {
self.dt_node.prop_cell_usize(name)
@@ -479,6 +523,28 @@ impl Node {
Some(node)
}
/// Dump the contents of the device tree node into Info-level log
pub fn dump(&self) {
tree::dump(self.dt_node.clone(), 0);
}
fn setup_pins(&self) -> Result<(), Error> {
// TODO lookup pin state by name
let Some(pinctrl0) = self.prop_usize("pinctrl-0") else {
return Ok(());
};
let pinctrl0 = lookup_phandle(pinctrl0 as Phandle, false).ok_or(Error::DoesNotExist)?;
// Find pinctrl-group's parent pin controller
let pin_controller = pinctrl0.parent().ok_or(Error::DoesNotExist)?;
pin_controller.probe().ok_or(Error::DoesNotExist)?;
let pin_controller = pin_controller
.as_pin_controller()
.ok_or(Error::DoesNotExist)?;
pin_controller.configure_pin_group(&pinctrl0)
}
}
impl fmt::Debug for Node {
@@ -531,6 +597,7 @@ fn unflatten_node(
clock_controler: OneTimeInit::new(),
reset_controller: OneTimeInit::new(),
system_controller: OneTimeInit::new(),
pin_controller: OneTimeInit::new(),
});
if let Some(phandle) = phandle {
+120 -4
View File
@@ -1,19 +1,49 @@
//! General helpers and utilities for device tree drivers
use alloc::sync::Arc;
use device_api::interrupt::{FullIrq, MessageInterruptController};
use device_api::{
gpio::{GpioPinConfig, GpioPinLevel, InputPinBias, OutputPinBias},
interrupt::{IrqHandle, MessageInterruptController},
};
use fdt_rs::prelude::PropReader;
use yggdrasil_abi::net::MacAddress;
use crate::{
driver::{lookup_phandle, map_interrupt_at},
driver::{lookup_phandle, map_interrupt_at, DeviceTreeGpioPins},
DeviceTreePropertyRead, TProp,
};
use super::Node;
/// Defines how push-pull is configured for a pin
#[derive(Debug, Clone, Copy)]
pub enum GenericPinctrlBiasConfig {
/// Disable push-pull
Disable,
/// Pull pin up
PullUp,
/// Pull pin down
PullDown,
}
/// Represents pin config parameters most commonly used in FDTs
#[derive(Debug, Clone, Copy)]
pub struct GenericPinctrlConfig {
/// Whether and how to configure the push-pull on the pin
pub bias: Option<GenericPinctrlBiasConfig>,
/// Drive strength in pinmux-specific units
pub drive_strength: Option<u32>,
/// Whether to enable the pin as an input
pub input_enable: Option<bool>,
/// Whether to enable the Schmitt trigger on the pin
pub input_schmitt_enable: Option<bool>,
/// Whether to enable the pin as an output
pub output_enable: Option<bool>,
/// GPIO rise/fall time
pub slew_rate: Option<u32>,
}
/// Represents an entry in a PCIe-controller's `interrupt-map` field
#[derive(Debug)]
pub struct PcieInterruptEntry {
/// PCI address bus
pub bus: u8,
@@ -26,7 +56,7 @@ pub struct PcieInterruptEntry {
/// Destination interrupt controller
pub interrupt_controller: Arc<Node>,
/// Destination IRQ options
pub irq: FullIrq,
pub irq: IrqHandle,
}
/// Represents a single PCI address (Requester ID for a MSI(-x))
@@ -58,6 +88,55 @@ pub struct PcieMsiMapIter<'a> {
offset: usize,
}
impl GenericPinctrlConfig {
fn tristate(enable: bool, disable: bool) -> Option<bool> {
if enable {
Some(true)
} else if disable {
Some(false)
} else {
None
}
}
/// Retrieves a generic pin configuration from a device tree node
pub fn from_node(node: &Node) -> Self {
let bias_disable = node.has_property("bias-disable");
let bias_pull_up = node.has_property("bias-pull-up");
let bias_pull_down = node.has_property("bias-pull-down");
let drive_strength = node.prop_usize("drive-strength").map(|p| p as u32);
let input_disable = node.has_property("input-disable");
let input_enable = node.has_property("input-enable");
let input_schmitt_enable = node.has_property("input-schmitt-enable");
let input_schmitt_disable = node.has_property("input-schmitt-disable");
let output_disable = node.has_property("output-disable");
let output_enable = node.has_property("output-enable");
let slew_rate = node.prop_usize("slew-rate").map(|p| p as u32);
let bias = if bias_disable {
Some(GenericPinctrlBiasConfig::Disable)
} else if bias_pull_up {
Some(GenericPinctrlBiasConfig::PullUp)
} else if bias_pull_down {
Some(GenericPinctrlBiasConfig::PullDown)
} else {
None
};
let input_enable = Self::tristate(input_enable, input_disable);
let input_schmitt_enable = Self::tristate(input_schmitt_enable, input_schmitt_disable);
let output_enable = Self::tristate(output_enable, output_disable);
Self {
bias,
drive_strength,
input_enable,
input_schmitt_enable,
output_enable,
slew_rate,
}
}
}
impl Iterator for PcieInterruptMapIter<'_> {
type Item = PcieInterruptEntry;
@@ -195,3 +274,40 @@ pub fn read_mac_address(node: &Arc<Node>) -> Option<MacAddress> {
let mac_bytes: [u8; 6] = property.raw().try_into().ok()?;
Some(MacAddress::from(mac_bytes))
}
/// Parse a generic gpio configuration from the device tree 1-cell format
pub fn generic_gpio_config(cell: u32, gpio: &DeviceTreeGpioPins) -> GpioPinConfig {
const GPIO_ACTIVE_LOW: u32 = 1 << 0;
const GPIO_SINGLE_ENDED: u32 = 1 << 1;
const GPIO_LINE_OPEN_DRAIN: u32 = 1 << 2;
const GPIO_PULL_UP: u32 = 1 << 4;
const GPIO_PULL_DOWN: u32 = 1 << 5;
const GPIO_PULL_DISABLE: u32 = 1 << 6;
const GPIO_OPEN_DRAIN: u32 = GPIO_LINE_OPEN_DRAIN | GPIO_SINGLE_ENDED;
let active_level = if cell & GPIO_ACTIVE_LOW != 0 {
GpioPinLevel::Low
} else {
GpioPinLevel::High
};
let output_bias = match cell & (GPIO_SINGLE_ENDED | GPIO_LINE_OPEN_DRAIN) {
0 => Some(OutputPinBias::PushPull),
GPIO_OPEN_DRAIN | GPIO_SINGLE_ENDED => Some(OutputPinBias::OpenDrain),
_ => None,
};
let input_bias = match cell & (GPIO_PULL_UP | GPIO_PULL_DOWN | GPIO_PULL_DISABLE) {
GPIO_PULL_DISABLE => Some(InputPinBias::Floating),
GPIO_PULL_UP => Some(InputPinBias::PullUp),
GPIO_PULL_DOWN => Some(InputPinBias::PullDown),
_ => None,
};
GpioPinConfig {
input: gpio.input,
output: gpio.output,
initial_level: gpio.initial_level,
input_bias,
output_bias,
active_level,
}
}
+55
View File
@@ -23,7 +23,62 @@ pub mod property;
pub mod tree;
pub mod util;
use core::fmt;
pub use node::DeviceTreeNodeExt;
pub use property::DeviceTreePropertyRead;
pub use tree::{DeviceTree, TNode, TProp};
pub use util::DeviceTreeMemoryRegionIter;
fn dump_node(level: log::Level, node: &driver::Node, depth: usize) {
fn indent(level: log::Level, x: usize) {
for _ in 0..x {
log::log!(target: ":raw", level, " ");
}
}
// fn field<D: fmt::Debug>(level: log::Level, depth: usize, name: &str, value: &D) {
// indent(level, depth);
// log::log!(target: ":raw", level, "{name} = {value:?},\n");
// }
fn field_list<'a, D: fmt::Debug + ?Sized + 'a, I: IntoIterator<Item = &'a D>>(
level: log::Level,
depth: usize,
name: &str,
value: I,
) {
indent(level, depth);
log::log!(target: ":raw", level, "{name} = ");
for (i, item) in value.into_iter().enumerate() {
if i != 0 {
log::log!(target: ":raw", level, ", ");
}
log::log!(target: ":raw", level, "{item:?}");
}
log::log!(target: ":raw", level, ",\n");
}
indent(level, depth);
log::log!(target: ":raw", level, "\"{}\" {{\n", node.name().unwrap_or(""));
if let Some(compatible) = node.compatible.as_ref() {
field_list(level, depth + 1, "compatible", compatible.as_str_list());
}
// for value in node.compatible
for (i, node) in node.children().enumerate() {
if i == 0 {
log::log!(target: ":raw", level, "\n");
}
dump_node(level, node, depth + 1);
}
indent(level, depth);
log::log!(target: ":raw", level, "}},\n");
}
/// Dump the device tree to debug output
pub fn dump(level: log::Level) {
let root = driver::registry::ROOT.get();
dump_node(level, root, 0);
}
+20 -20
View File
@@ -197,7 +197,7 @@ impl<'a> DeviceTree<'a> {
unsafe impl Sync for DeviceTree<'_> {}
fn indent(amount: usize) {
log::info!(target: "raw", "{0:1$}", "", amount * 2);
log::info!(target: ":raw", "{0:1$}", "", amount * 2);
}
fn dump_property(node: &TNode, prop: TProp, level: usize) {
@@ -211,14 +211,14 @@ fn dump_property(node: &TNode, prop: TProp, level: usize) {
let name = prop.name().unwrap_or("<unknown-prop>");
indent(level);
log::info!(target: "raw", "{name}");
log::info!(target: ":raw", "{name}");
if prop.len() == 0 {
log::info!(target: "raw", ";\n");
log::info!(target: ":raw", ";\n");
return;
}
log::info!(target: "raw", " = ");
log::info!(target: ":raw", " = ");
let ty = match name {
"model" | "compatible" | "clock-output-names" | "clock-names" | "device_type"
@@ -234,48 +234,48 @@ fn dump_property(node: &TNode, prop: TProp, level: usize) {
// "..."
for (i, string) in prop.as_str_list().enumerate() {
if i != 0 {
log::info!(target: "raw", ", ");
log::info!(target: ":raw", ", ");
}
log::info!(target: "raw", "{string:?}");
log::info!(target: ":raw", "{string:?}");
}
}
Type::Cells => {
// <...>
log::info!(target: "raw", "<");
log::info!(target: ":raw", "<");
let mut i = 0;
while let Some(value) = prop.read_cell(i, 1) {
if i != 0 {
log::info!(target: "raw", ", ");
log::info!(target: ":raw", ", ");
}
log::info!(target: "raw", "{value:#x}");
log::info!(target: ":raw", "{value:#x}");
i += 1;
}
log::info!(target: "raw", ">");
log::info!(target: ":raw", ">");
}
Type::Bytes => {
// <...>
log::info!(target: "raw", "<");
log::info!(target: ":raw", "<");
for (i, byte) in prop.raw().iter().enumerate() {
if i != 0 {
log::info!(target: "raw", ", ");
log::info!(target: ":raw", ", ");
}
log::info!(target: "raw", "{byte:#x}");
log::info!(target: ":raw", "{byte:#x}");
}
log::info!(target: "raw", ">");
log::info!(target: ":raw", ">");
}
}
log::info!(target: "raw", ";\n");
log::info!(target: ":raw", ";\n");
}
fn dump(node: TNode, level: usize) {
pub(crate) fn dump(node: TNode, level: usize) {
let name = node.name().unwrap_or("<unknown>");
indent(level);
if name.is_empty() {
log::info!(target: "raw", "{{\n");
log::info!(target: ":raw", "{{\n");
} else {
log::info!(target: "raw", "{name}: {{\n");
log::info!(target: ":raw", "{name}: {{\n");
}
let mut do_break = false;
@@ -286,12 +286,12 @@ fn dump(node: TNode, level: usize) {
for child in node.children() {
if do_break {
log::info!(target: "raw", "\n");
log::info!(target: ":raw", "\n");
do_break = false;
}
dump(child, level + 1);
}
indent(level);
log::info!(target: "raw", "}}\n");
log::info!(target: ":raw", "}}\n");
}
+35
View File
@@ -0,0 +1,35 @@
pub trait BitField: Sized {
fn modify_bit(&self, bit: usize, value: bool) -> Self;
fn set_bit(&mut self, bit: usize, value: bool);
fn get_bit(&self, bit: usize) -> bool;
}
macro_rules! impl_bit_field {
($($ty:ty),+ $(,)?) => {
$(
impl BitField for $ty {
#[inline]
fn modify_bit(&self, bit: usize, value: bool) -> Self {
if value {
*self | (1 << bit)
} else {
*self & !(1 << bit)
}
}
#[inline]
fn set_bit(&mut self, bit: usize, value: bool) {
*self = self.modify_bit(bit, value);
}
#[inline]
fn get_bit(&self, bit: usize) -> bool {
*self & (1 << bit) != 0
}
}
)+
};
}
impl_bit_field!(u8, u16, u32, u64, usize);
impl_bit_field!(i8, i16, i32, i64, isize);
+1
View File
@@ -18,6 +18,7 @@ use core::{
panic,
};
pub mod bit;
pub mod event;
pub mod ext;
pub mod hash_table;
+2 -2
View File
@@ -324,7 +324,7 @@ impl<'a, T> DmaSlice<'a, T> {
}
// TODO subslicing
pub fn into_parts(self) -> (&'a DmaBuffer<[T]>, Range<usize>) {
pub unsafe fn into_parts(self) -> (&'a DmaBuffer<[T]>, Range<usize>) {
(self.buffer, self.range)
}
@@ -353,7 +353,7 @@ impl<'a, T> DmaSliceMut<'a, T> {
}
// TODO subslicing
pub fn into_parts(self) -> (&'a mut DmaBuffer<[T]>, Range<usize>) {
pub unsafe fn into_parts(self) -> (&'a mut DmaBuffer<[T]>, Range<usize>) {
(self.buffer, self.range)
}
+81
View File
@@ -0,0 +1,81 @@
use core::{
sync::atomic::{AtomicU64, Ordering},
time::Duration,
};
use alloc::{boxed::Box, collections::BTreeMap, sync::Arc, vec::Vec};
use device_api::gpio::{GpioInterrupt, GpioInterruptEvent, GpioOutput, PinHandle};
use libk_util::{ring::LossyRingQueue, sync::spin_rwlock::IrqSafeRwLock};
use yggdrasil_abi::error::Error;
use crate::task::runtime;
static GPIO_EVENT_QUEUE: LossyRingQueue<u64> = LossyRingQueue::with_capacity(64);
static GPIO_EVENT_MAP: IrqSafeRwLock<BTreeMap<u64, Box<dyn Fn() + Send>>> =
IrqSafeRwLock::new(BTreeMap::new());
static GPIO_EVENT_ID: AtomicU64 = AtomicU64::new(1);
static GPIO_HEARTBEATS: IrqSafeRwLock<Vec<Arc<dyn GpioOutput>>> = IrqSafeRwLock::new(Vec::new());
pub trait GpioEvent {
fn configure_with_interrupt(&self, int: GpioInterrupt) -> Result<(), Error>;
}
impl GpioEvent for PinHandle {
fn configure_with_interrupt(&self, int: GpioInterrupt) -> Result<(), Error> {
let event = bind_gpio_event(int.handler);
self.parent.setup_gpio(
self,
Some(GpioInterruptEvent {
mode: int.mode,
event,
}),
)
}
}
pub async fn gpio_event_handler_task() {
loop {
let id = GPIO_EVENT_QUEUE.read().await;
let lock = GPIO_EVENT_MAP.read();
if let Some(handler) = lock.get(&id) {
handler();
}
}
}
pub async fn heartbeat_handler_task() {
const LEVELS: &[(Duration, bool)] = &[
(Duration::from_millis(50), true),
(Duration::from_millis(200), false),
(Duration::from_millis(50), true),
(Duration::from_millis(700), false),
];
let mut index = 0;
loop {
let (timeout, level) = LEVELS[index];
for pin in GPIO_HEARTBEATS.read().iter() {
pin.write(level).ok();
}
runtime::sleep(timeout).await;
index = (index + 1) % LEVELS.len();
}
}
pub fn register_heartbeat_gpio(gpio: Arc<dyn GpioOutput>) {
GPIO_HEARTBEATS.write().push(gpio);
}
pub fn bind_gpio_event(handler: Box<dyn Fn() + Send>) -> u64 {
let id = GPIO_EVENT_ID.fetch_add(1, Ordering::Acquire);
GPIO_EVENT_MAP.write().insert(id, handler);
id
}
pub fn signal_gpio_event(ev: u64) {
GPIO_EVENT_QUEUE.write(ev);
}
+1
View File
@@ -5,6 +5,7 @@ use object::KObject;
use crate::vfs::NodeRef;
pub mod attribute;
pub mod nodes;
pub mod object;
static ROOT: OneTimeInit<NodeRef> = OneTimeInit::new();
+52
View File
@@ -0,0 +1,52 @@
use core::{
marker::PhantomData,
sync::atomic::{AtomicUsize, Ordering},
};
use alloc::{format, string::String, sync::Arc};
use libk_util::OneTimeInit;
use yggdrasil_abi::error::Error;
use crate::fs::sysfs::{
attribute::{StringAttribute, StringAttributeOps},
device,
object::KObject,
};
pub trait SysfsRtcNode: Send + Sync + 'static {
fn read(&self) -> Result<u64, Error>;
}
static RTC_NODES: AtomicUsize = AtomicUsize::new(0);
pub fn add_rtc_node<N: SysfsRtcNode>(node: Arc<N>) {
struct Time<N>(PhantomData<N>);
impl<N: SysfsRtcNode> StringAttributeOps for Time<N> {
type Data = Arc<N>;
const NAME: &'static str = "time";
fn read(state: &Self::Data) -> Result<String, Error> {
let time = state.read()?;
Ok(format!("{time}"))
}
}
static RTC_OBJECT: OneTimeInit<Arc<KObject<()>>> = OneTimeInit::new();
let rtc_object = RTC_OBJECT.or_init_with(|| {
let device_object = device().unwrap();
let rtc_object = KObject::new(());
device_object.add_object("rtc", rtc_object.clone()).ok();
rtc_object
});
let rtc_node = KObject::new(node);
rtc_node
.add_attribute(StringAttribute::from(Time(PhantomData)))
.ok();
let index = RTC_NODES.fetch_add(1, Ordering::AcqRel);
let name = format!("rtc{index}");
rtc_object.add_object(name, rtc_node).ok();
}
+1
View File
@@ -36,6 +36,7 @@ pub mod arch;
pub mod config;
pub mod debug;
pub mod device;
pub mod event;
pub mod fs;
pub mod module;
pub mod random;
+2 -1
View File
@@ -9,5 +9,6 @@ mod timer;
pub use executor::{run_to_completion, spawn, spawn_async_worker};
pub use task_queue::init_task_queue;
pub use timer::{
maybe_timeout, psleep, pwait, pwait_try, sleep, sleep_until, tick, with_timeout, SleepFuture,
maybe_timeout, psleep, pwait, pwait_try, sleep, sleep_until, sleep_wait, tick, with_timeout,
SleepFuture,
};
+19
View File
@@ -201,6 +201,25 @@ pub fn pwait_try<P: Fn() -> Result<bool, Error>>(
}
}
pub async fn sleep_wait<P: Fn() -> bool>(
timeout: Duration,
poll_rate: Duration,
predicate: P,
) -> Result<(), Error> {
let now = monotonic_time();
let deadline = now + timeout;
loop {
if predicate() {
return Ok(());
}
if monotonic_time() > deadline {
return Err(Error::TimedOut);
}
sleep(poll_rate).await;
}
}
pub fn with_timeout<'a, T: 'a, F: Future<Output = T> + Send + 'a>(
fut: F,
timeout: Duration,
+1
View File
@@ -310,6 +310,7 @@ fn el0_sync_inner(frame: &mut ExceptionFrame) {
}
fn irq_common() {
// TODO some concept of "root" interrupt controller
external_interrupt_controller()
.unwrap()
.handle_pending_irqs();
+52 -54
View File
@@ -8,9 +8,9 @@ use alloc::sync::Arc;
use device_api::{
device::{Device, DeviceInitContext},
interrupt::{
ExternalInterruptController, FixedInterruptTable, FullIrq, InterruptHandler,
InterruptTable, IpiDeliveryTarget, IpiMessage, Irq, IrqLevel, IrqOptions, IrqTrigger,
IrqVector, LocalInterruptController,
ExternalInterruptController, FixedInterruptTable, InterruptHandler, InterruptTable,
IpiDeliveryTarget, IpiMessage, Irq, IrqHandle, IrqLevel, IrqOptions, IrqTrigger, IrqVector,
LocalInterruptController,
},
};
use device_tree::{
@@ -23,7 +23,7 @@ use libk_mm::{
address::PhysicalAddress,
device::{DeviceMemoryIo, RawDeviceMemoryMapping},
};
use libk_util::{sync::spin_rwlock::IrqSafeRwLock, OneTimeInit};
use libk_util::sync::spin_rwlock::IrqSafeRwLock;
use self::{gicc::Gicc, gicd::Gicd};
@@ -41,13 +41,13 @@ pub mod gicv2m;
/// ARM Generic Interrupt Controller v2
pub struct Gic {
gicc: OneTimeInit<Gicc>,
gicd: OneTimeInit<Gicd>,
gicd_base: PhysicalAddress,
gicc_base: PhysicalAddress,
gicc: Gicc,
gicd: Gicd,
table: IrqSafeRwLock<FixedInterruptTable<MAX_IRQ>>,
}
unsafe impl Sync for Gic {}
/// Per-CPU GIC information
pub struct GicPerCpu {}
@@ -59,32 +59,8 @@ impl Device for Gic {
}
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
log::debug!(
"Init GIC: gicd={:#x}, gicc={:#x}",
self.gicd_base,
self.gicc_base
);
let gicd_mmio = Arc::new(RawDeviceMemoryMapping::map(
self.gicd_base.into_u64(),
0x1000,
Default::default(),
)?);
let gicd_mmio_shared = DeviceMemoryIo::from_raw(gicd_mmio.clone())?;
let gicd_mmio_banked = DeviceMemoryIo::from_raw(gicd_mmio)?;
let gicc_mmio = DeviceMemoryIo::map(self.gicc_base, Default::default())?;
let gicd = Gicd::new(gicd_mmio_shared, gicd_mmio_banked);
let gicc = Gicc::new(gicc_mmio);
gicd.init();
gicc.init();
self.gicd.init(gicd);
self.gicc.init(gicc);
register_external_interrupt_controller(self.clone());
AArch64::set_gic(self.clone());
Ok(())
}
}
@@ -97,7 +73,6 @@ impl ExternalInterruptController for Gic {
handler: Arc<dyn InterruptHandler>,
) -> Result<(), Error> {
let mut table = self.table.write();
let gicd = self.gicd.get();
let index = match irq {
Irq::External(i) => i + GIC_SPI_START,
@@ -112,7 +87,7 @@ impl ExternalInterruptController for Gic {
options.level
);
if index >= GIC_SPI_START as usize {
gicd.configure_irq(index, options);
self.gicd.configure_irq(index, options);
}
table.insert(index, handler)?;
@@ -120,29 +95,27 @@ impl ExternalInterruptController for Gic {
}
fn enable_irq(&self, irq: Irq) -> Result<(), Error> {
let gicd = self.gicd.get();
let index = match irq {
Irq::External(i) => i + GIC_SPI_START,
Irq::Private(i) => i + GIC_PPI_START,
} as usize;
log::debug!("Enable irq{index} ({irq:?})");
gicd.enable_irq(index);
self.gicd.enable_irq(index);
Ok(())
}
fn handle_pending_irqs(&self) {
let gicc = self.gicc.get();
let irq_number = gicc.pending_irq_number();
let irq_number = self.gicc.pending_irq_number();
if irq_number >= MAX_IRQ {
return;
}
gicc.clear_irq(irq_number);
self.gicc.clear_irq(irq_number);
if irq_number == IPI_VECTOR as usize {
// TODO pop entry
crate::panic::panic_secondary();
let ipi = Cpu::local().get_ipi();
AArch64::handle_ipi(ipi);
return;
}
if irq_number < GIC_PPI_START as usize {
@@ -190,20 +163,20 @@ impl LocalInterruptController for Gic {
barrier::isb(barrier::SY);
unsafe {
self.gicd.get().set_sgir(target, IPI_VECTOR);
self.gicd.set_sgir(target, IPI_VECTOR);
}
Ok(())
}
unsafe fn init_ap(&self) -> Result<(), Error> {
self.gicc.get().init();
self.gicc.init();
Ok(())
}
}
impl DeviceTreeInterruptController for Gic {
fn map_interrupt(&self, property: &TProp, offset: usize) -> Option<FullIrq> {
fn map_interrupt(self: Arc<Self>, property: &TProp, offset: usize) -> Option<IrqHandle> {
// IRQ_TYPE_NONE - 0
// IRQ_TYPE_EDGE_RISING - 1
// IRQ_TYPE_EDGE_FALLING - 2
@@ -229,9 +202,10 @@ impl DeviceTreeInterruptController for Gic {
_ => return None,
};
Some(FullIrq {
Some(IrqHandle {
irq,
options: IrqOptions { trigger, level },
intc: self.clone(),
})
}
@@ -246,14 +220,34 @@ impl Gic {
/// # Safety
///
/// The caller must ensure the addresses actually point to the GIC components.
pub unsafe fn new(gicd_base: PhysicalAddress, gicc_base: PhysicalAddress) -> Self {
Self {
gicc: OneTimeInit::new(),
gicd: OneTimeInit::new(),
gicd_base,
gicc_base,
pub unsafe fn new(
gicd_base: PhysicalAddress,
gicc_base: PhysicalAddress,
) -> Result<Self, Error> {
log::debug!("Init GIC: gicd={:#x}, gicc={:#x}", gicd_base, gicc_base);
let gicd_mmio = Arc::new(RawDeviceMemoryMapping::map(
gicd_base.into_u64(),
0x1000,
Default::default(),
)?);
let gicd_mmio_shared = DeviceMemoryIo::from_raw(gicd_mmio.clone())?;
let gicd_mmio_banked = DeviceMemoryIo::from_raw(gicd_mmio)?;
let gicc_mmio = DeviceMemoryIo::map(gicc_base, Default::default())?;
let gicd = Gicd::new(gicd_mmio_shared, gicd_mmio_banked);
let gicc = Gicc::new(gicc_mmio);
gicd.init();
gicc.init();
// self.gicd.init(gicd);
// self.gicc.init(gicc);
Ok(Self {
gicd,
gicc,
table: IrqSafeRwLock::new(FixedInterruptTable::new()),
}
})
}
}
@@ -274,7 +268,11 @@ device_tree_driver! {
let gicd_base = PhysicalAddress::from_u64(gicd_range.start);
let gicc_base = PhysicalAddress::from_u64(gicc_range.start);
let gic = Arc::new(unsafe { Gic::new(gicd_base, gicc_base) });
let gic = Arc::new(unsafe {
Gic::new(gicd_base, gicc_base)
.inspect_err(|e| log::error!("GIC probe/init error: {e:?}"))
.ok()?
});
// Register device-tree interrupt controller
node.make_interrupt_controller(gic.clone());
+54 -3
View File
@@ -5,14 +5,15 @@ use core::sync::atomic::{self, Ordering};
use aarch64_cpu::registers::{CNTP_CTL_EL0, CNTP_TVAL_EL0};
use alloc::sync::Arc;
use device_api::{
interrupt::{Irq, LocalInterruptController},
interrupt::{IpiDeliveryTarget, IpiMessage, Irq, LocalInterruptController},
ResetDevice,
};
use device_tree::{
driver::{unflatten_device_tree, InitSequence},
DeviceTree, DeviceTreeNodeExt,
};
use kernel_arch_aarch64::{mem, ArchitectureImpl, PerCpuData};
use kernel_arch::Architecture;
use kernel_arch_aarch64::{mem, ArchitectureImpl, PerCpuData, CPU_COUNT};
use libk::{arch::Cpu, config, debug, device::external_interrupt_controller, error::Error};
use libk_mm::{
address::PhysicalAddress,
@@ -20,7 +21,7 @@ use libk_mm::{
pointer::PhysicalRef,
table::EntryLevelExt,
};
use libk_util::OneTimeInit;
use libk_util::{sync::SpinFence, OneTimeInit};
use tock_registers::interfaces::Writeable;
use ygg_driver_pci::PciBusManager;
@@ -28,6 +29,7 @@ use crate::{
arch::{aarch64::gic::Gic, Platform},
device::{power::arm_psci::Psci, MACHINE_NAME},
fs::{Initrd, INITRD_DATA},
panic,
util::call_init_array,
};
@@ -68,6 +70,8 @@ impl<const SIZE: usize> BootStack<SIZE> {
}
}
static SHUTDOWN_FENCE: SpinFence = SpinFence::new();
impl Platform for AArch64 {
unsafe fn start_application_processors(&self) {
if let Some(compatible) = self.machine_compatible.try_get() {
@@ -96,6 +100,18 @@ impl Platform for AArch64 {
psci.reset()
}
}
unsafe fn power_off(&self) -> Result<!, Error> {
Self::halt_aps()?;
if let Some(psci) = self.psci.try_get() {
log::info!("Powering off");
psci.power_off()
} else {
log::warn!("No power off method, halting");
ArchitectureImpl::halt();
}
}
}
impl AArch64 {
@@ -103,6 +119,41 @@ impl AArch64 {
GIC.init(gic);
}
fn handle_ipi(ipi: Option<IpiMessage>) {
let Some(ipi) = ipi else {
log::warn!("Spurious IPI received, assuming panic");
panic::panic_secondary();
};
match ipi {
IpiMessage::Panic => panic::panic_secondary(),
IpiMessage::Shutdown => Self::secondary_halt_handler(),
}
}
fn secondary_halt_handler() {
log::info!("CPU halted");
SHUTDOWN_FENCE.signal();
ArchitectureImpl::halt();
}
unsafe fn halt_aps() -> Result<(), Error> {
let ap_count = CPU_COUNT.load(Ordering::Acquire) - 1;
if ap_count > 0 {
if let Some(gic) = GIC.try_get() {
gic.send_ipi(IpiDeliveryTarget::OtherCpus, IpiMessage::Shutdown)?;
SHUTDOWN_FENCE.wait_all(ap_count);
Ok(())
} else {
log::warn!("No way to handle secondary CPUs");
Err(Error::NotImplemented)
}
} else {
Ok(())
}
}
#[inline(never)]
unsafe fn init_memory_management(
&'static self,
+6 -8
View File
@@ -7,16 +7,16 @@ use abi::{error::Error, time::NANOSECONDS_IN_SECOND};
use alloc::sync::Arc;
use device_api::{
device::{Device, DeviceInitContext},
interrupt::{FullIrq, InterruptHandler, IrqVector},
interrupt::{InterruptHandler, IrqHandle, IrqVector},
};
use device_tree::driver::{device_tree_driver, Node, ProbeContext};
use kernel_arch::task::Scheduler;
use libk::{arch::Cpu, device::external_interrupt_controller, task::runtime, time};
use libk::{arch::Cpu, task::runtime, time};
use tock_registers::interfaces::{ReadWriteable, Readable, Writeable};
/// ARM Generic Timer driver
pub struct ArmTimer {
irq: FullIrq,
irq: IrqHandle,
}
/// ARM timer tick interval (in some time units?)
@@ -63,14 +63,12 @@ impl Device for ArmTimer {
unsafe fn init_irq(self: Arc<Self>) -> Result<(), Error> {
log::info!("ARM Generic Timer frequency={}Hz", CNTFRQ_EL0.get());
let intc = external_interrupt_controller()?;
intc.register_irq(self.irq.irq, self.irq.options, self.clone())?;
self.irq.register(self.clone())?;
CNTP_CTL_EL0.modify(CNTP_CTL_EL0::IMASK::CLEAR);
CNTP_TVAL_EL0.set(TICK_INTERVAL);
intc.enable_irq(self.irq.irq)?;
self.irq.enable()?;
Ok(())
}
@@ -82,7 +80,7 @@ impl ArmTimer {
/// # Safety
///
/// The caller must ensure the function has not been called before.
pub const unsafe fn new(irq: FullIrq) -> Self {
pub const unsafe fn new(irq: IrqHandle) -> Self {
Self { irq }
}
}
+14
View File
@@ -1,5 +1,6 @@
//! Provides architecture/platform-specific implementation details
use abi::error::Error;
use kernel_arch::{Architecture, ArchitectureImpl};
#[cfg(any(target_arch = "aarch64", rust_analyzer))]
@@ -51,6 +52,19 @@ pub trait Platform {
}
}
/// Performs full system powerdown.
///
/// # Safety
///
/// The caller must ensure it is actually safe to power down, i.e. no critical processes will be
/// aborted and no data will be lost.
unsafe fn power_off(&self) -> Result<!, Error> {
ArchitectureImpl::set_interrupt_mask(true);
loop {
ArchitectureImpl::wait_for_interrupt();
}
}
// /// Adds a reset device to the system
// fn register_reset_device(&self, reset: Arc<dyn ResetDevice>) -> Result<(), Error> {
// Err(Error::NotImplemented)
+1 -7
View File
@@ -7,7 +7,7 @@ use core::{
use elf::{abi, relocation::Elf64_Rela};
use kernel_arch::{Architecture, ArchitectureImpl};
use kernel_arch_riscv64::{mem, CPU_COUNT};
use kernel_arch_riscv64::{mem, BOOT_HART_ID, CPU_COUNT};
use libk::{
debug,
fs::{devfs, sysfs},
@@ -28,7 +28,6 @@ const BOOT_STACK_SIZE: usize = 65536;
static mut KERNEL_LOAD_BASE: u64 = 0;
static mut DTB_ADDRESS: PhysicalAddress = PhysicalAddress::ZERO;
static mut BOOT_HART_ID: u64 = 0;
static mut BOOT_STACK: BootStack<BOOT_STACK_SIZE> = BootStack::zeroed();
#[repr(C, align(0x10))]
@@ -58,11 +57,6 @@ unsafe extern "C" fn relocate_kernel(image_base: i64, rela_start: usize, rela_en
}
}
/// Returns the ID of the bootstrap HART
pub fn boot_hart_id() -> u64 {
unsafe { BOOT_HART_ID }
}
unsafe fn long_jump(pc: usize, sp: usize, a0: usize) -> ! {
core::arch::asm!("mv sp, {sp}; jr {pc}", in("a0") a0, sp = in(reg) sp, pc = in(reg) pc, options(noreturn))
}
+5 -1
View File
@@ -11,7 +11,7 @@ use kernel_arch::{Architecture, ArchitectureImpl};
use kernel_arch_riscv64::{
mem,
registers::{SIE, SSTATUS},
PerCpuData,
sbi, PerCpuData,
};
use libk::{arch::Cpu, config};
use libk_mm::{
@@ -51,6 +51,10 @@ impl Platform for Riscv64 {
ArchitectureImpl::halt();
}
unsafe fn power_off(&self) -> Result<!, Error> {
sbi::sbi_system_shutdown()
}
unsafe fn start_application_processors(&self) {
// TODO asymmetric systems with different hart types are not yet supported.
// e.g., in JH7110 there're two different types of cores
+2 -2
View File
@@ -6,7 +6,7 @@ use abi::error::Error;
use device_api::interrupt::{IpiDeliveryTarget, IpiMessage};
use device_tree::{DeviceTree, DeviceTreeNodeExt};
use kernel_arch_riscv64::{
mem::auto_lower_address, registers::SIP, sbi, ArchitectureImpl, CPU_COUNT,
boot_hart_id, mem::auto_lower_address, registers::SIP, sbi, ArchitectureImpl, CPU_COUNT,
};
use libk::arch::Cpu;
use libk_mm::{
@@ -15,7 +15,7 @@ use libk_mm::{
};
use tock_registers::interfaces::ReadWriteable;
use crate::{arch::riscv64::boot::boot_hart_id, panic};
use crate::panic;
#[allow(missing_docs)]
pub const SECONDARY_STACK_SIZE: usize = 32768;
+20 -73
View File
@@ -1,18 +1,11 @@
use abi::error::Error;
use alloc::sync::Arc;
use device_api::{
device::Device,
interrupt::{InterruptHandler, Irq, IrqVector},
};
use device_api::device::Device;
use kernel_arch::{Architecture, ArchitectureImpl};
use kernel_arch_x86::{
intrinsics::{io_wait, IoPort, IoPortAccess},
ISA_IRQ_OFFSET,
};
use libk::{device::external_interrupt_controller, time};
use kernel_arch_x86::intrinsics::{io_wait, IoPort, IoPortAccess};
use libk::fs::sysfs::{self, nodes::SysfsRtcNode};
use libk_util::sync::IrqSafeSpinlock;
const NMI_DISABLE: u8 = 1 << 7;
const CMOS_REG_SEC: u8 = 0x00;
const CMOS_REG_MIN: u8 = 0x02;
const CMOS_REG_HOUR: u8 = 0x04;
@@ -20,13 +13,8 @@ const CMOS_REG_DAY: u8 = 0x07;
const CMOS_REG_MON: u8 = 0x08;
const CMOS_REG_YEAR: u8 = 0x09;
const CMOS_REG_STATUS_A: u8 = 0x0A;
const CMOS_REG_STATUS_B: u8 = 0x0B;
const CMOS_REG_STATUS_C: u8 = 0x0C;
const STATUS_A_UPDATE_IN_PROGRESS: u8 = 1 << 7;
// Refresh every 4 ticks (every 2s)
const REFRESH_INTERVAL: u32 = 4;
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
struct DateTime {
seconds: u8,
@@ -40,14 +28,18 @@ struct DateTime {
struct Inner {
command: IoPort<u8>,
data: IoPort<u8>,
counter: u32,
last_timestamp: Option<DateTime>,
}
pub struct Rtc {
inner: IrqSafeSpinlock<Inner>,
}
fn bcd_to_dec(x: u8) -> u8 {
let a = (x >> 4) & 0xF;
let b = x & 0xF;
a * 10 + b
}
impl Inner {
fn read_date_time(&mut self) -> DateTime {
self.wait_for_update();
@@ -71,12 +63,12 @@ impl Inner {
}
fn try_read_time(&mut self) -> DateTime {
let seconds = self.read_reg(CMOS_REG_SEC);
let minutes = self.read_reg(CMOS_REG_MIN);
let hours = self.read_reg(CMOS_REG_HOUR);
let day_of_month = self.read_reg(CMOS_REG_DAY);
let month = self.read_reg(CMOS_REG_MON);
let year = self.read_reg(CMOS_REG_YEAR);
let seconds = bcd_to_dec(self.read_reg(CMOS_REG_SEC));
let minutes = bcd_to_dec(self.read_reg(CMOS_REG_MIN));
let hours = bcd_to_dec(self.read_reg(CMOS_REG_HOUR));
let day_of_month = bcd_to_dec(self.read_reg(CMOS_REG_DAY));
let month = bcd_to_dec(self.read_reg(CMOS_REG_MON));
let year = bcd_to_dec(self.read_reg(CMOS_REG_YEAR));
DateTime {
seconds,
minutes,
@@ -99,59 +91,16 @@ impl Inner {
}
value
}
fn write_reg(&mut self, reg: u8, value: u8) {
assert!(ArchitectureImpl::interrupt_mask());
self.command.write(reg);
for _ in 0..10 {
io_wait();
}
self.data.write(value);
for _ in 0..10 {
io_wait();
}
}
fn setup_irq(&mut self) {
let rate = 15; // freq = 2Hz
let old_a = self.read_reg(CMOS_REG_STATUS_A | NMI_DISABLE);
let old_b = self.read_reg(CMOS_REG_STATUS_B | NMI_DISABLE);
let new_a = (old_a & 0xF0) | rate;
let new_b = old_b | (1 << 6) | (1 << 2) | (1 << 1);
self.write_reg(CMOS_REG_STATUS_A | NMI_DISABLE, new_a);
self.write_reg(CMOS_REG_STATUS_B | NMI_DISABLE, new_b);
}
}
impl InterruptHandler for Rtc {
fn handle_irq(self: Arc<Self>, _vector: IrqVector) -> bool {
let mut inner = self.inner.lock();
if inner.counter == 0 {
let time = inner.read_date_time();
inner.last_timestamp = Some(time);
time::set_real_seconds(time.to_seconds(), true);
}
inner.read_reg(CMOS_REG_STATUS_C);
inner.counter += 1;
if inner.counter == REFRESH_INTERVAL {
inner.counter = 0;
}
true
impl SysfsRtcNode for Rtc {
fn read(&self) -> Result<u64, Error> {
let time = self.inner.lock().read_date_time();
Ok(time.to_seconds())
}
}
impl Device for Rtc {
unsafe fn init_irq(self: Arc<Self>) -> Result<(), Error> {
let irq = Irq::External(ISA_IRQ_OFFSET + 8);
let intc = external_interrupt_controller()?;
self.inner.lock().setup_irq();
intc.register_irq(irq, Default::default(), self.clone())?;
intc.enable_irq(irq)?;
Ok(())
}
fn display_name(&self) -> &str {
"x86 RTC"
}
@@ -163,15 +112,13 @@ impl Rtc {
inner: IrqSafeSpinlock::new(Inner {
command: IoPort::new(0x70),
data: IoPort::new(0x71),
counter: 0,
last_timestamp: None,
}),
}
}
pub fn setup() -> Result<Arc<Self>, Error> {
let this = Arc::new(Self::new());
unsafe { this.clone().init_irq() }?;
sysfs::nodes::add_rtc_node(this.clone());
Ok(this)
}
}
+4 -2
View File
@@ -11,6 +11,8 @@ use libk::{
};
use static_assertions::{const_assert, const_assert_eq};
use crate::arch::x86_64::X86_64;
use super::exception;
pub mod ioapic;
@@ -115,8 +117,8 @@ unsafe extern "C" fn dummy_irq_handler() {
}
unsafe extern "C" fn ipi_handler() {
let cpu = Cpu::local();
todo!("Processor {} received an IPI", cpu.id());
let ipi = Cpu::local().get_ipi();
X86_64::handle_ipi(ipi);
}
global_asm!(
+9 -3
View File
@@ -4,12 +4,12 @@ use core::{arch::global_asm, mem::size_of};
use abi::{bitflags, primitive_enum, process::Signal};
use kernel_arch_x86::registers::{CR2, CR3};
use kernel_arch_x86_64::context::ExceptionFrame;
use libk::task::thread::Thread;
use libk::{arch::Cpu, task::thread::Thread};
use libk_mm::PageFaultKind;
use libk_util::sync::spin_rwlock::IrqSafeRwLock;
use tock_registers::interfaces::Readable;
use crate::arch::x86_64::apic;
use crate::arch::x86_64::{apic, X86_64};
primitive_enum! {
enum ExceptionKind: u64 {
@@ -231,7 +231,13 @@ extern "C" fn __x86_64_exception_handler(frame: *mut ExceptionFrame) {
}
extern "C" fn __x86_64_nmi_handler() -> ! {
crate::panic::panic_secondary();
let ipi = Cpu::local().get_ipi();
if let Some(ipi) = ipi {
X86_64::handle_ipi(Some(ipi));
unreachable!();
} else {
panic!("Spurious/unknown NMI received");
}
}
/// Initializes the interrupt descriptor table for the given CPU.
+54 -5
View File
@@ -1,16 +1,20 @@
//! x86-64 architecture implementation
use core::ptr::null_mut;
use core::{ptr::null_mut, sync::atomic::Ordering};
use abi::error::Error;
use acpi::{mcfg::Mcfg, AcpiTables, HpetInfo, InterruptModel};
use alloc::sync::Arc;
use device_api::device::Device;
use device_api::{
device::Device,
interrupt::{IpiDeliveryTarget, IpiMessage},
};
use kernel_arch::{Architecture, ArchitectureImpl};
use kernel_arch_x86::{
cpuid::{self, CpuFeatures, EcxFeatures, EdxFeatures, ExtEdxFeatures},
gdt,
};
use kernel_arch_x86_64::{mem, LocalApicInterface, PerCpuData};
use kernel_arch_x86_64::{mem, LocalApicInterface, PerCpuData, CPU_COUNT};
use libk::{
arch::Cpu,
config, debug,
@@ -27,7 +31,10 @@ use libk_mm::{
pointer::PhysicalRef,
table::EntryLevel,
};
use libk_util::OneTimeInit;
use libk_util::{
sync::{IrqGuard, SpinFence},
OneTimeInit,
};
use yboot_proto::{
v1::{self, AvailableMemoryRegion},
LoadProtocolV1,
@@ -45,6 +52,7 @@ use crate::{
Platform,
},
device::display::linear_fb::LinearFramebuffer,
panic,
util::call_init_array,
};
@@ -72,7 +80,9 @@ pub static PLATFORM: X86_64 = X86_64 {
fbconsole: OneTimeInit::new(),
};
//
static SHUTDOWN_FENCE: SpinFence = SpinFence::new();
impl Platform for X86_64 {
unsafe fn start_application_processors(&self) {
if let Some(acpi) = self.acpi.try_get() {
@@ -87,9 +97,42 @@ impl Platform for X86_64 {
smp::start_ap_cores(&pinfo);
}
}
unsafe fn power_off(&self) -> Result<!, Error> {
let _guard = IrqGuard::acquire();
let ap_count = CPU_COUNT.load(Ordering::Acquire) - 1;
if ap_count > 0 {
self.send_ipi(IpiDeliveryTarget::OtherCpus, IpiMessage::Shutdown)?;
SHUTDOWN_FENCE.wait_all(ap_count);
}
ygg_driver_acpi::power_off()
}
}
impl X86_64 {
fn handle_ipi(ipi: Option<IpiMessage>) {
let Some(ipi) = ipi else {
log::warn!("Spurious IPI received, assuming panic");
panic::panic_secondary();
};
match ipi {
IpiMessage::Shutdown => {
log::info!("Core halted");
SHUTDOWN_FENCE.signal();
ArchitectureImpl::halt()
}
IpiMessage::Panic => panic::panic_secondary(),
}
}
fn send_ipi(&self, target: IpiDeliveryTarget, message: IpiMessage) -> Result<(), Error> {
let cpu = Cpu::local();
cpu.local_apic.send_ipi(target, message)
}
fn set_boot_data(&self, data: BootData) {
match data {
BootData::YBoot(data) => {
@@ -196,6 +239,12 @@ impl X86_64 {
call_init_array();
x86::init_platform_devices(early);
extern "C" {
static __kernel_start: u8;
}
log::info!("Load base: {:p}", &raw const __kernel_start);
}
Ok(())
-6
View File
@@ -1,10 +1,4 @@
//! Clock controller drivers
#[cfg(any(target_arch = "aarch64", rust_analyzer))]
pub mod bcm2835_aux;
#[cfg(any(target_arch = "riscv64", rust_analyzer))]
pub mod jh7110_clocks;
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64", rust_analyzer))]
pub mod fixed;
+165
View File
@@ -0,0 +1,165 @@
//! GPIO drivers
use abi::error::Error;
use alloc::{boxed::Box, string::String, sync::Arc, vec::Vec};
use device_api::{
device::{Device, DeviceInitContext},
gpio::{GpioInterrupt, GpioInterruptMode, GpioOutput, GpioPinLevel, PinHandle},
};
use device_tree::driver::{device_tree_driver, DeviceTreeGpioPins, Node, ProbeContext};
use libk::event::{self, GpioEvent};
enum LedFunction {
Heartbeat,
Other,
}
struct LedPin {
name: String,
function: LedFunction,
handle: PinHandle,
}
struct KeyPin {
name: String,
handle: PinHandle,
}
trait Pin: Sized + Send + Sync + 'static {
fn from_device_tree(node: &Arc<Node>) -> Option<Arc<Self>>;
fn configure(self: &Arc<Self>) -> Result<(), Error>;
fn name(&self) -> &str;
}
struct PinGroup<P: Pin> {
name: String,
pins: Vec<Arc<P>>,
}
impl<P: Pin> PinGroup<P> {
fn from_device_tree(node: &Arc<Node>) -> Option<Arc<dyn Device>> {
let pins = node
.children()
.filter_map(P::from_device_tree)
.collect::<Vec<_>>();
if pins.is_empty() {
return None;
}
let name = node.name().unwrap_or("pin-group");
Some(Arc::new(Self {
pins,
name: name.into(),
}))
}
}
impl<P: Pin> Device for PinGroup<P> {
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
for pin in self.pins.iter() {
if let Err(error) = pin.configure() {
log::error!("gpio {} setup error: {:?}", pin.name(), error);
}
}
Ok(())
}
fn display_name(&self) -> &str {
&self.name
}
}
impl Pin for LedPin {
fn from_device_tree(node: &Arc<Node>) -> Option<Arc<Self>> {
let handle = node.gpio(
&DeviceTreeGpioPins {
input: false,
output: true,
initial_level: GpioPinLevel::Low,
},
0,
)?;
let function = match node.prop_str("function") {
Some("heartbeat") => LedFunction::Heartbeat,
_ => LedFunction::Other,
};
let name = node.name().unwrap_or("gpio-led").into();
Some(Arc::new(Self {
name,
function,
handle,
}))
}
fn configure(self: &Arc<Self>) -> Result<(), Error> {
self.handle.configure()?;
match self.function {
LedFunction::Heartbeat => {
log::info!("Register led {:?} as heartbeat indicator", self.name);
event::register_heartbeat_gpio(self.clone())
}
_ => (),
}
Ok(())
}
fn name(&self) -> &str {
&self.name
}
}
impl GpioOutput for LedPin {
fn write(&self, value: bool) -> Result<(), Error> {
self.handle.write(value)
}
}
impl Pin for KeyPin {
fn from_device_tree(node: &Arc<Node>) -> Option<Arc<Self>> {
let handle = node.gpio(
&DeviceTreeGpioPins {
input: true,
output: false,
initial_level: GpioPinLevel::Low,
},
0,
)?;
let name = node.name().unwrap_or("gpio-key").into();
Some(Arc::new(Self { name, handle }))
}
fn configure(self: &Arc<Self>) -> Result<(), Error> {
let key = self.clone();
self.handle.configure_with_interrupt(GpioInterrupt {
mode: GpioInterruptMode::FallingEdge,
handler: Box::new(move || {
log::info!("GPIO key triggered: {:?}", key.name);
}),
})
}
fn name(&self) -> &str {
&self.name
}
}
device_tree_driver! {
compatible: ["gpio-leds"],
driver: {
fn probe(&self, node: &Arc<Node>, _context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
PinGroup::<LedPin>::from_device_tree(node)
}
}
}
device_tree_driver! {
compatible: ["gpio-keys"],
driver: {
fn probe(&self, node: &Arc<Node>, _context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
PinGroup::<KeyPin>::from_device_tree(node)
}
}
}
-4
View File
@@ -1,4 +0,0 @@
//! Interrupt controller drivers
#[cfg(any(target_arch = "riscv64", rust_analyzer))]
pub mod riscv_plic;
-4
View File
@@ -1,4 +0,0 @@
//! Mailbox interfaces
#[cfg(any(rust_analyzer, target_arch = "aarch64"))]
mod bcm2835_mbox;
+3 -3
View File
@@ -6,12 +6,12 @@ use libk_util::OneTimeInit;
pub mod bus;
pub mod clock;
pub mod display;
pub mod interrupt;
pub mod mbox;
pub mod pinctrl;
pub mod power;
pub mod serial;
// pub mod timer;
#[cfg(any(rust_analyzer, not(target_arch = "x86_64")))]
pub mod gpio;
/// Generic machine description string
pub static MACHINE_NAME: OneTimeInit<String> = OneTimeInit::new();
@@ -1,49 +0,0 @@
use abi::error::Error;
use alloc::sync::Arc;
use device_api::{
clock::ClockHandle,
device::{Device, DeviceInitContext},
};
use device_tree::driver::{device_tree_driver, Node, ProbeContext};
use libk_mm::device::DeviceMemoryIoMut;
use libk_util::sync::IrqSafeSpinlock;
struct Jh7110SysPinctrl {
#[allow(unused)]
regs: IrqSafeSpinlock<DeviceMemoryIoMut<'static, [u32]>>,
clock: ClockHandle,
}
unsafe impl Send for Jh7110SysPinctrl {}
unsafe impl Sync for Jh7110SysPinctrl {}
impl Device for Jh7110SysPinctrl {
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
self.clock.enable()?;
Ok(())
}
fn display_name(&self) -> &str {
"jh7110-sys-pinctrl"
}
}
device_tree_driver! {
compatible: ["starfive,jh7110-sys-pinctrl"],
driver: {
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
let clock = node.clock(0).unwrap();
let base = node.map_base(context, 0)?;
let regs = unsafe { DeviceMemoryIoMut::map_slice(base, 0x2B8 / 4, Default::default()) }.ok()?;
let pinctrl = Arc::new(Jh7110SysPinctrl {
clock,
regs: IrqSafeSpinlock::new(regs)
});
Some(pinctrl)
}
}
}
-4
View File
@@ -1,4 +0,0 @@
//! GPIO/Pin controller, multiplexer drivers
#[cfg(any(target_arch = "riscv64", rust_analyzer))]
mod jh7110_pinctrl;
+7
View File
@@ -61,6 +61,7 @@ impl ResetDevice for Psci {
impl Psci {
const SYSTEM_RESET: u32 = 0x84000009;
const SYSTEM_OFF: u32 = 0x84000008;
#[inline]
unsafe fn call(&self, mut x0: u64, x1: u64, x2: u64, x3: u64) -> u64 {
@@ -74,6 +75,12 @@ impl Psci {
}
x0
}
/// Shut down the system
pub unsafe fn power_off(&self) -> Result<!, Error> {
self.call(Self::SYSTEM_OFF as _, 0, 0, 0);
unreachable!()
}
}
device_tree_driver! {
-6
View File
@@ -1,11 +1,5 @@
//! Serial device interfaces
#[cfg(any(target_arch = "aarch64", rust_analyzer))]
pub mod bcm2835_aux_uart;
#[cfg(any(target_arch = "aarch64", rust_analyzer))]
pub mod pl011;
#[cfg(any(target_arch = "riscv64", rust_analyzer))]
pub mod ns16550a;
#[cfg(any(target_arch = "riscv64", rust_analyzer))]
+5 -6
View File
@@ -3,12 +3,12 @@ use abi::{error::Error, io::TerminalOptions};
use alloc::sync::Arc;
use device_api::{
device::{Device, DeviceInitContext},
interrupt::{FullIrq, InterruptHandler, IrqVector},
interrupt::{InterruptHandler, IrqHandle, IrqVector},
};
use device_tree::driver::{device_tree_driver, Node, ProbeContext};
use libk::{
debug::DebugSink,
device::{external_interrupt_controller, manager::DEVICE_REGISTRY},
device::manager::DEVICE_REGISTRY,
vfs::{Terminal, TerminalInput, TerminalOutput},
};
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIo};
@@ -79,7 +79,7 @@ struct Inner {
pub struct Ns16550a {
inner: OneTimeInit<Arc<Terminal<Inner>>>,
base: PhysicalAddress,
irq: FullIrq,
irq: IrqHandle,
}
impl Io {
@@ -135,9 +135,8 @@ impl Device for Ns16550a {
}
unsafe fn init_irq(self: Arc<Self>) -> Result<(), Error> {
let intc = external_interrupt_controller()?;
intc.register_irq(self.irq.irq, self.irq.options, self.clone())?;
intc.enable_irq(self.irq.irq)?;
self.irq.register(self.clone())?;
self.irq.enable()?;
let io = self.inner.get().output().io.lock();
io.regs.IER.modify(IER::RDR::SET);
Ok(())
+5 -6
View File
@@ -4,12 +4,12 @@ use alloc::sync::Arc;
use device_api::{
clock::{ClockHandle, Hertz, ResetHandle},
device::{Device, DeviceInitContext},
interrupt::{FullIrq, InterruptHandler, IrqVector},
interrupt::{InterruptHandler, IrqHandle, IrqVector},
};
use device_tree::driver::{device_tree_driver, Node, ProbeContext};
use libk::{
debug::{self, DebugSink},
device::{external_interrupt_controller, manager::DEVICE_REGISTRY},
device::manager::DEVICE_REGISTRY,
vfs::{Terminal, TerminalInput, TerminalOutput},
};
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIo};
@@ -110,7 +110,7 @@ struct Inner {
/// Synopsys DesignWare 8250 UART
pub struct DwUart {
base: PhysicalAddress,
irq: FullIrq,
irq: IrqHandle,
clk_baud: ClockHandle,
#[allow(unused)]
clk_apb: Option<ClockHandle>,
@@ -241,9 +241,8 @@ impl Device for DwUart {
}
unsafe fn init_irq(self: Arc<Self>) -> Result<(), Error> {
let intc = external_interrupt_controller()?;
intc.register_irq(self.irq.irq, Default::default(), self.clone())?;
intc.enable_irq(self.irq.irq)?;
self.irq.register(self.clone())?;
self.irq.enable()?;
let output = self.inner.get().output();
let io = output.io.lock();
+4
View File
@@ -37,6 +37,7 @@ pub fn kinit() -> Result<(), Error> {
#[cfg(any(rust_analyzer, target_arch = "riscv64", target_arch = "aarch64"))]
{
use device_tree::driver::InitSequence;
use libk::event;
device_tree::driver::lazy_init(
|_| (),
@@ -55,6 +56,9 @@ pub fn kinit() -> Result<(), Error> {
},
InitSequence::Late,
);
runtime::spawn(event::gpio_event_handler_task()).ok();
runtime::spawn(event::heartbeat_handler_task()).ok();
}
// Initialize PCI devices
if let Err(error) = PciBusManager::setup_bus_devices(false) {
+10
View File
@@ -71,15 +71,25 @@ extern crate ygg_driver_virtio_blk;
extern crate ygg_driver_virtio_gpu;
extern crate ygg_driver_virtio_net;
#[cfg(any(target_arch = "aarch64", rust_analyzer))]
extern crate ygg_driver_mmc_generic_sdhci;
cfg_if::cfg_if! {
if #[cfg(target_arch = "x86_64")] {
extern crate ygg_driver_net_igbe;
} else if #[cfg(target_arch = "aarch64")] {
extern crate ygg_driver_bsp_arm;
extern crate ygg_driver_bsp_bcm283x;
} else if #[cfg(target_arch = "riscv64")] {
extern crate ygg_driver_bsp_riscv;
extern crate ygg_driver_bsp_jh7110;
extern crate ygg_driver_net_stmmac;
}
}
pub mod arch;
pub mod device;
pub mod fs;
pub mod init;
+15 -2
View File
@@ -16,7 +16,7 @@ use abi::{
option::OptionValue,
path::Path,
process::{ExecveOptions, ProcessId},
system::{self, SystemInfoVariant},
system::{self, SystemControlVariant, SystemInfoVariant},
time::{ClockType, SystemTime},
};
use libk::{
@@ -26,7 +26,10 @@ use libk::{
};
use libk_mm::phys;
use crate::fs;
use crate::{
arch::{Platform, PLATFORM},
fs,
};
use super::run_with_io;
@@ -86,6 +89,16 @@ pub(crate) fn set_clock(ty: ClockType, value: &SystemTime) -> Result<(), Error>
}
}
pub(crate) fn system_control(option: u32, value: &mut [u8], size: usize) -> Result<usize, Error> {
let _ = (value, size);
let option = SystemControlVariant::try_from(option)?;
match option {
SystemControlVariant::PowerOff => {
unsafe { PLATFORM.power_off() }?;
}
}
}
pub(crate) fn get_system_info(option: u32, buffer: &mut [u8]) -> Result<usize, Error> {
let option = SystemInfoVariant::try_from(option)?;
match option {
+2
View File
@@ -47,6 +47,8 @@ struct SpawnOptions<'a> {
pub environment: &'a [&'a str],
/// Working directory
pub directory: Option<&'a str>,
/// Root path
pub root: Option<&'a str>,
/// Optional arguments to specify details of the creation
pub optional: &'a [SpawnOption],
/// Extra flags controlling the new process behavior
+1
View File
@@ -80,6 +80,7 @@ syscall unmount(opts: &UnmountOptions) -> Result<()>;
syscall load_module(path: &str) -> Result<()>;
syscall filesystem_control(fd: Option<RawFd>, option: u32, value: &mut [u8], len: usize) -> Result<usize>;
syscall system_control(option: u32, value: &mut [u8], len: usize) -> Result<usize>;
syscall get_system_info(option: u32, value: &mut [u8]) -> Result<usize>;
+8
View File
@@ -21,6 +21,14 @@ option_group!(
}
);
request_group!(
#[doc = "Common system controls"]
pub enum SystemControlVariant<'de> {
#[doc = "Power down the system"]
0x1000: PowerOff((), ()),
}
);
abi_serde::impl_struct_serde!(
SystemMemoryStats: [total_usable_pages, allocated_pages, free_pages, page_size]
);
+14 -1
View File
@@ -1,7 +1,10 @@
//! System-related parameters
pub use abi::system::*;
use abi::{error::Error, option::OptionValue};
use abi::{
error::Error,
option::{OptionValue, RequestValue},
};
/// Helper macro for [get_system_info]
pub macro get_system_info($variant_ty:ty) {{
@@ -14,3 +17,13 @@ pub fn get_system_info<'de, T: OptionValue<'de>>(buffer: &'de mut [u8]) -> Resul
let len = unsafe { crate::sys::get_system_info(T::VARIANT.into(), buffer) }?;
T::load(&buffer[..len])
}
/// Performs an operation on the system, like power management, reset etc
pub fn system_control<'de, T: RequestValue<'de>>(
buffer: &'de mut [u8],
request: &T::Request,
) -> Result<T::Response, Error> {
let len = T::store_request(request, buffer)?;
let len = unsafe { crate::sys::system_control(T::VARIANT.into(), buffer, len) }?;
T::load_response(&buffer[..len])
}
+8
View File
@@ -0,0 +1,8 @@
#!/bin/sh
set -e
build_dir=$3
cd "$build_dir/doomgeneric/doomgeneric"
make -f Makefile.yggdrasil SYSROOT=$Y_SYSROOT TARGET=$Y_TRIPLE
+17
View File
@@ -0,0 +1,17 @@
#!/bin/sh
set -e
REPO_URL="https://git.alnyan.me/yggdrasil/doomgeneric.git"
REPO_BRANCH="alnyan/yggdrasil"
build_dir=$3
mkdir -p "$build_dir"
if [ ! -f "$build_dir/.source-ready" ]; then
cd "$build_dir"
git clone --branch="$REPO_BRANCH" "$REPO_URL"
ln -s "$Y_WORKSPACE_ROOT" "$build_dir/doomgeneric/doomgeneric/doomgeneric-yggdrasil/yggdrasil-root"
touch "$build_dir/.source-ready"
fi
+8
View File
@@ -0,0 +1,8 @@
#!/bin/sh
set -e
build_dir=$3
mkdir -p $Y_SYSROOT/bin
install -m0755 $build_dir/doomgeneric/doomgeneric/doomgeneric $Y_SYSROOT/bin/doomgeneric
+2
View File
@@ -0,0 +1,2 @@
description = "Doom port"
version = "1.0.0"
+1
View File
@@ -0,0 +1 @@
../meta-port-scripts/gnu-compile.sh
+1
View File
@@ -0,0 +1 @@
../meta-port-scripts/gnu-fetch.sh
+4
View File
@@ -0,0 +1,4 @@
#!/bin/sh
export GNU_PROJECT=gmp
export SRC_SHA256=e56fd59d76810932a0555aa15a14b61c16bed66110d3c75cc2ac49ddaa9ab24c
export GNU_CONFIGURE_OPTIONS=

Some files were not shown because too many files have changed in this diff Show More