Compare commits

...

40 Commits

Author SHA1 Message Date
alnyan 0e979a9e09 spi: initial spi device support 2026-02-04 14:46:57 +02:00
alnyan ed9d7a7145 usb: add ft232 driver 2026-02-03 17:28:15 +02:00
alnyan 6b5dd9f673 irq: more flexible interrupt tables 2026-02-03 13:29:49 +02:00
alnyan 58dbaddf11 i2c: implement initial support for i2c devices 2026-02-03 12:08:15 +02:00
alnyan 218e391505 aarch64: better page fault info 2026-02-03 12:07:52 +02:00
alnyan 2a49c655c2 8250: fix reg-io-width/reg-shift for raspi4b 2026-01-19 11:36:01 +02:00
alnyan d108494314 8250: merge bcm aux uart into 8250 driver 2026-01-19 11:06:05 +02:00
alnyan 195c19e225 serial: unify 8250 drivers, better dts support 2026-01-16 23:18:46 +02:00
alnyan 21a8361eec usb: more keys for hid keyboard 2026-01-14 09:11:00 +02:00
alnyan fd0a3f50ea maint: update README.md 2026-01-14 09:06:03 +02:00
alnyan c72c8f88d7 usb: add support for usb hubs 2026-01-14 08:58:46 +02:00
alnyan e44b616998 sysutils: add a showkey program 2026-01-08 14:45:14 +02:00
alnyan 7abb950a8f block: fix rustc migration error 2026-01-06 18:17:11 +02:00
alnyan 57143f9d8d maint: migrate to rustc 1.94.0-nightly 2026-01-06 14:41:08 +02:00
alnyan 3491e1a227 graphics/iv: add image viewer program 2025-12-29 17:17:42 +02:00
alnyan 61644bdef5 netutils/ping: add sigint handler 2025-12-04 08:30:08 +02:00
alnyan e1b905c65a shell: improve interactive mode 2025-11-19 08:57:47 +02:00
alnyan 9855142c25 cross: *nix support for serial module 2025-10-17 15:25:11 +03:00
alnyan bb4e805733 tar: better error reporting 2025-10-17 09:38:28 +03:00
alnyan ecf1c18240 shell: add basic tab completion (single-option) 2025-10-17 09:24:54 +03:00
alnyan a87c8a7ee2 WIP: Add tar utility 2025-10-16 10:42:41 +03:00
alnyan 312458b8f0 sysutils: add serial console program 2025-10-10 09:21:20 +03:00
alnyan 33474c10d3 bcm2711: update dtb 2025-09-26 16:51:43 +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
528 changed files with 86347 additions and 6201 deletions
Generated
+114 -11
View File
@@ -469,6 +469,7 @@ dependencies = [
name = "device-api"
version = "0.1.0"
dependencies = [
"async-trait",
"device-api-macros",
"yggdrasil-abi",
]
@@ -499,7 +500,7 @@ dependencies = [
[[package]]
name = "discrete_range_map"
version = "0.6.2"
source = "git+https://git.alnyan.me/yggdrasil/discrete_range_map.git#6b54882b190b02fb013f22cbe9664f6273e846ae"
source = "git+https://git.alnyan.me/yggdrasil/discrete_range_map.git#0c932f7cc7ff55253519e3465ddeea8fe69083be"
dependencies = [
"btree_monstrousity",
"either",
@@ -1199,9 +1200,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "libc"
version = "0.2.169"
version = "0.2.180"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc"
dependencies = [
"rustc-std-workspace-core",
]
@@ -1353,6 +1354,12 @@ version = "0.4.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
[[package]]
name = "linux-raw-sys"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039"
[[package]]
name = "litemap"
version = "0.7.4"
@@ -1787,7 +1794,20 @@ dependencies = [
"bitflags 2.8.0",
"errno",
"libc",
"linux-raw-sys",
"linux-raw-sys 0.4.15",
"windows-sys",
]
[[package]]
name = "rustix"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34"
dependencies = [
"bitflags 2.8.0",
"errno",
"libc",
"linux-raw-sys 0.11.0",
"windows-sys",
]
@@ -2004,15 +2024,14 @@ dependencies = [
[[package]]
name = "tempfile"
version = "3.16.0"
version = "3.24.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38c246215d7d24f48ae091a2902398798e05d978b24315d6efbc00ede9a8bb91"
checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c"
dependencies = [
"cfg-if",
"fastrand",
"getrandom 0.3.1",
"once_cell",
"rustix",
"rustix 1.1.3",
"windows-sys",
]
@@ -2388,7 +2407,7 @@ checksum = "b4ee928febd44d98f2f459a4a79bd4d928591333a494a10a868418ac1b39cf1f"
dependencies = [
"either",
"home",
"rustix",
"rustix 0.38.44",
"winsafe",
]
@@ -2557,8 +2576,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e105d177a3871454f754b33bb0ee637ecaaac997446375fd3e5d43a2ed00c909"
dependencies = [
"libc",
"linux-raw-sys",
"rustix",
"linux-raw-sys 0.4.15",
"rustix 0.38.44",
]
[[package]]
@@ -2589,6 +2608,7 @@ dependencies = [
"semver 1.0.25",
"serde",
"tar",
"tempfile",
"thiserror",
"toml",
"walkdir",
@@ -2638,6 +2658,71 @@ 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 = [
"async-trait",
"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"
@@ -2791,6 +2876,19 @@ dependencies = [
"yggdrasil-abi",
]
[[package]]
name = "ygg_driver_serial_8250"
version = "0.1.0"
dependencies = [
"device-api",
"device-tree",
"kernel-arch-x86",
"libk",
"libk-mm",
"libk-util",
"yggdrasil-abi",
]
[[package]]
name = "ygg_driver_usb"
version = "0.1.0"
@@ -2951,6 +3049,10 @@ 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_net_core",
@@ -2960,6 +3062,7 @@ dependencies = [
"ygg_driver_net_stmmac",
"ygg_driver_nvme",
"ygg_driver_pci",
"ygg_driver_serial_8250",
"ygg_driver_usb",
"ygg_driver_usb_xhci",
"ygg_driver_virtio_blk",
+1
View File
@@ -7,6 +7,7 @@ exclude = [
"toolchain",
"userspace/dynload-program",
"userspace/lib/ygglibc",
"userspace",
"toolchain-c"
]
members = [
+18 -14
View File
@@ -9,9 +9,9 @@ Main features
-------------
* Architecture support:
* [aarch64](kernel/src/arch/aarch64)
* [x86_64](kernel/src/arch/x86_64)
* [i686](kernel/src/arch/i686) (Pentium Pro and later)
* [aarch64](kernel/arch/aarch64)
* [x86_64](kernel/arch/x86_64)
* [riscv64](kernel/arch/riscv64)
* Core features:
* Kernel/userspace preemptive multithreading
* Kernel-space multitasking with `async`/`await` runtime
@@ -24,6 +24,7 @@ Main features
* sysfs
* devfs
* ext2
* fat32 (read-only)
* Userspace features:
* [Kernel-user ABI](lib/abi-def/yggdrasil.abi) generated from a rust-like description language
* Sanitized system calls better suited for use in Rust
@@ -33,33 +34,37 @@ Main features
* Synchronization primitives through futex-like interface
* Unix-like signals and exceptions
* [Dynamic loader](userspace/dyn-loader) for linking with shared libraries
* Runs DOOM
* Hardware features:
* PCI Express devices
* NVMe drive support (read/write, currently x86_64 only, due to lack of MSI-X support on aarch64/i686).
* AHCI SATA drive support (read/write)
* NVMe drive support
* AHCI SATA drive support
* xHCI USB host controller
* VirtIO Network + GPU framebuffer support
* USB HID keyboards
* USB device support
* Hub driver
* HID keyboards and mice
* Mass storage (BBB)
* Partial hardware support for aarch64/riscv64 SBCs like StarFive VisionFive 2 and Raspberry Pi 4
aarch64-specific:
* PSCI for SMP start-up and power control
* PL011 serial port
* PL061 GPIO controller
* PL031 RTC
* ARM generic timer as system/monotonic timer
* GICv2 IRQ controller
* GICv2 IRQ controller + GICv2m MSI interrupts
x86-specific:
* Boot options:
* x86_64: UEFI [yboot](https://git.alnyan.me/yggdrasil/yboot)
* i686: multiboot/grub
* Boot via UEFI [yboot](https://git.alnyan.me/yggdrasil/yboot)
* I/O and Local APIC IRQ controllers
* PS/2 keyboard
* HPET for x86_64
* i8253-based timer for i686 or as a fallback timer
* i8253 as a fallback timer
* COM ports
* ACPI, [work in progress](https://github.com/rust-osdev/acpi), mostly broken
on real hardware, so currently disabled
* ACPI, [work in progress](https://github.com/rust-osdev/acpi)
* ACPI shutdown
* PCI IRQ pin routing
* Events like power button, etc.
@@ -122,7 +127,6 @@ General plans (in no particular order)
2. Get a full LLVM build to work
3. Get rustc to work
4. Get self-hosted
5. Run doom (?)
In addition to eternal code cleanup, I've been doing quite a lazy job at that lately...
+53
View File
@@ -43,3 +43,56 @@ $ booti ${loadaddr} ${initrd_addr_r}:<SIZE-PRINTED-WHEN-LOADING-INITRD> ${fdt_ad
###### (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}
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")
+94 -2
View File
@@ -33,7 +33,99 @@ $ booti ${loadaddr} ${initrd_addr_r}:<initrd-size> ${fdt_addr_r}
env set ipaddr 13.0.0.2; env set initrd_addr_r 0x70000000; tftpboot ${initrd_addr_r} 13.0.0.1:initrd.img; tftpboot ${loadaddr} 13.0.0.1: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}
env set ipaddr 13.0.0.2; env set initrd_addr_r 0x70000000; tftpboot ${initrd_addr_r} 13.0.0.1:initrd.img; tftpboot ${loadaddr} 13.0.0.1:yggdrasil-kernel.bin; tftpboot ${fdt_addr_r} 13.0.0.1:vf2.dtb; fdt resize; booti ${loadaddr} ${initrd_addr_r}:60000000 ${fdt_addr_r}
#### For DHCP boot with BUILD-MACHINE-IP-ADDR 192.168.88.10
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}
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; tftpboot ${fdt_addr_r} 192.168.88.10:vf2.dtb; 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")
+1 -1
View File
@@ -5,7 +5,7 @@
"llvm-target": "aarch64-unknown-none",
"data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32",
"max-atomic-width": 128,
"target-pointer-width": "64",
"target-pointer-width": 64,
"features": "+v8a,+strict-align,-neon,-fp-armv8",
"disable-redzone": true,
Binary file not shown.
@@ -264,16 +264,15 @@
compatible = "gpio-keys";
poweroff {
gpios = <0x8007 0x03 0x00>;
gpios = <&gpio 0x03 0x00>;
linux,code = <0x74>;
label = "GPIO Key Poweroff";
};
};
pl061@9030000 {
phandle = <0x8007>;
gpio: pl061@9030000 {
clock-names = "apb_pclk";
clocks = <0x8000>;
clocks = <&clk_24mhz>;
interrupts = <0x00 0x07 0x04>;
gpio-controller;
#gpio-cells = <0x02>;
@@ -299,15 +298,15 @@
pl031@9010000 {
clock-names = "apb_pclk";
clocks = <0x8000>;
clocks = <&clk_24mhz>;
interrupts = <0x00 0x02 0x04>;
reg = <0x00 0x9010000 0x00 0x1000>;
compatible = "arm,pl031", "arm,primecell";
};
pl011@9000000 {
uart0: pl011@9000000 {
clock-names = "uartclk", "apb_pclk";
clocks = <0x8000 0x8000>;
clocks = <&clk_24mhz &clk_24mhz>;
interrupts = <0x00 0x01 0x04>;
reg = <0x00 0x9000000 0x00 0x1000>;
compatible = "arm,pl011", "arm,primecell";
@@ -411,8 +410,7 @@
compatible = "arm,armv8-timer", "arm,armv7-timer";
};
apb-pclk {
phandle = <0x8000>;
clk_24mhz: apb-pclk {
clock-output-names = "clk24mhz";
clock-frequency = <0x16e3600>;
#clock-cells = <0x00>;
+669
View File
@@ -0,0 +1,669 @@
/dts-v1/;
#include <aarch64/gicv2.h>
/memreserve/ 0x0000000000000000 0x0000000000001000;
/ {
compatible = "raspberrypi,4-model-b", "brcm,bcm2711";
model = "Raspberry Pi 4 Model B";
#address-cells = <0x02>;
#size-cells = <0x01>;
interrupt-parent = <&gicv2>;
aliases {
serial0 = "/soc/serial@7e201000";
serial1 = "/soc/serial@7e215040";
blconfig = "/reserved-memory/nvram@0";
};
chosen {
stdout-path = "serial1:115200n8";
};
reserved-memory {
#address-cells = <0x02>;
#size-cells = <0x01>;
ranges;
linux,cma {
compatible = "shared-dma-pool";
size = <0x4000000>;
reusable;
linux,cma-default;
alloc-ranges = <0x00 0x00 0x40000000>;
};
nvram@0 {
compatible = "raspberrypi,bootloader-config", "nvmem-rmem";
#address-cells = <0x01>;
#size-cells = <0x01>;
reg = <0x00 0x00 0x00>;
no-map;
status = "disabled";
};
};
soc {
compatible = "simple-bus";
#address-cells = <0x01>;
#size-cells = <0x01>;
ranges = <0x7e000000 0x00 0xfe000000 0x01800000>,
<0x7c000000 0x00 0xfc000000 0x02000000>,
<0x40000000 0x00 0xff800000 0x00800000>;
dma-ranges = <0xc0000000 0x00 0x00 0x40000000>;
timer@7e003000 {
compatible = "brcm,bcm2835-system-timer";
reg = <0x7e003000 0x1000>;
interrupts = <GIC_SPI 64 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 65 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 66 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 67 IRQ_TYPE_LEVEL_HIGH>;
clock-frequency = <0xf4240>;
};
txp@7e004000 {
compatible = "brcm,bcm2835-txp";
reg = <0x7e004000 0x20>;
interrupts = <GIC_SPI 75 IRQ_TYPE_LEVEL_HIGH>;
};
dsi0: dsi@7e209000 {
compatible = "brcm,bcm2835-dsi0";
reg = <0x7e209000 0x78>;
interrupts = <GIC_SPI 100 IRQ_TYPE_LEVEL_HIGH>;
#address-cells = <0x01>;
#size-cells = <0x00>;
#clock-cells = <0x01>;
clocks = <&cprman 0x20>,
<&cprman 0x2f>,
<&cprman 0x31>;
clock-names = "phy", "escape", "pixel";
clock-output-names = "dsi0_byte", "dsi0_ddr2", "dsi0_ddr";
status = "disabled";
power-domains = <&power 0x11>;
};
dsi1: dsi@7e700000 {
compatible = "brcm,bcm2711-dsi1";
reg = <0x7e700000 0x8c>;
interrupts = <GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH>;
#address-cells = <0x01>;
#size-cells = <0x00>;
#clock-cells = <0x01>;
clocks = <&cprman 0x23>,
<&cprman 0x30>,
<&cprman 0x32>;
clock-names = "phy", "escape", "pixel";
clock-output-names = "dsi1_byte", "dsi1_ddr2", "dsi1_ddr";
status = "disabled";
power-domains = <&power 0x12>;
};
cprman: cprman@7e101000 {
compatible = "brcm,bcm2711-cprman";
#clock-cells = <0x01>;
reg = <0x7e101000 0x2000>;
clocks = <&clk_osc>,
<&dsi0 0x00>,
<&dsi0 0x01>,
<&dsi0 0x02>,
<&dsi1 0x00>,
<&dsi1 0x01>,
<&dsi1 0x02>;
};
mbox: mailbox@7e00b880 {
compatible = "brcm,bcm2835-mbox";
reg = <0x7e00b880 0x40>;
interrupts = <GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>;
#mbox-cells = <0x00>;
};
gpio: gpio@7e200000 {
compatible = "brcm,bcm2711-gpio";
reg = <0x7e200000 0xb4>;
interrupts = <GIC_SPI 113 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 114 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 115 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 116 IRQ_TYPE_LEVEL_HIGH>;
gpio-controller;
#gpio-cells = <0x02>;
interrupt-controller;
#interrupt-cells = <0x02>;
pinctrl-names = "default";
bootph-all;
// UART
uart0_ctsrts_gpio30: uart0_ctsrts_gpio30 {
brcm,pins = <30>, <31>;
brcm,pull = <2>, <0>;
brcm,function = <7>;
};
uart0_gpio32: uart0_gpio32 {
brcm,pins = <32>, <33>;
brcm,pull = <0>, <2>;
brcm,function = <8>;
};
uart1_gpio14: uart1_gpio14 {
brcm,pins = <14>, <15>;
brcm,function = <2>;
bootph-all;
};
uart2_gpio: uart2_pins {
brcm,pins = <0>, <1>;
brcm,function = <3>;
brcm,pull = <0>, <2>;
};
// I²C
i2c0if_gpio0: i2c0if-gpio0 {
brcm,pins = <0>, <1>;
brcm,function = <4>;
};
i2c0if_gpio44: i2c0if-gpio44 {
brcm,pins = <44>, <45>;
brcm,function = <5>;
};
i2c1_gpio: i2c1 {
brcm,pins = <2>, <3>;
brcm,function = <4>;
brcm,pull = <2>;
};
i2c3_gpio: i2c3 {
brcm,pins = <4>, <5>;
brcm,function = <2>;
brcm,pull = <2>;
};
i2c4_gpio: i2c4 {
brcm,pins = <8>, <9>;
brcm,function = <2>;
brcm,pull = <2>;
};
i2c5_gpio: i2c5 {
brcm,pins = <12>, <13>;
brcm,function = <2>;
brcm,pull = <2>;
};
i2c6_gpio: i2c6 {
brcm,pins = <22>, <23>;
brcm,function = <2>;
brcm,pull = <2>;
};
// SPI
spi0_gpio: spi0_pins {
brcm,pins = <9>, <10>, <11>;
brcm,function = <4>;
};
spi0_cs_gpio: spi0_cs_pins {
brcm,pins = <8>, <7>;
brcm,function = <1>;
};
};
uart0: serial@7e201000 {
compatible = "arm,pl011", "arm,primecell";
reg = <0x7e201000 0x200>;
interrupts = <GIC_SPI 121 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cprman 0x13>,
<&cprman 0x14>;
clock-names = "uartclk",
"apb_pclk";
arm,primecell-periphid = <0x241011>;
pinctrl-names = "default";
pinctrl-0 = <&uart0_ctsrts_gpio30>, <&uart0_gpio32>;
uart-has-rtscts;
status = "okay";
skip-init;
bootph-all;
};
aux: aux@7e215000 {
compatible = "brcm,bcm2835-aux";
#clock-cells = <0x01>;
reg = <0x7e215000 0x08>;
clocks = <&cprman 0x14>;
};
uart1: serial@7e215040 {
compatible = "brcm,bcm2835-aux-uart";
reg = <0x7e215040 0x40>;
interrupts = <GIC_SPI 93 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&aux 0x00>;
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&uart1_gpio14>;
skip-init;
bootph-all;
};
i2c0if: i2c@7e205000 {
compatible = "brcm,bcm2711-i2c", "brcm,bcm2835-i2c";
reg = <0x7e205000 0x200>;
interrupts = <GIC_SPI 117 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cprman 0x14>;
#address-cells = <0x01>;
#size-cells = <0x00>;
status = "disabled";
clock-frequency = <0x186a0>;
};
i2c0mux: i2c0mux {
compatible = "i2c-mux-pinctrl";
#address-cells = <0x01>;
#size-cells = <0x00>;
i2c-parent = <&i2c0if>;
status = "disabled";
pinctrl-names = "i2c0", "i2c_csi_dsi";
pinctrl-0 = <&i2c0if_gpio0>;
pinctrl-1 = <&i2c0if_gpio44>;
i2c0: i2c@0 {
reg = <0x00>;
#address-cells = <0x01>;
#size-cells = <0x00>;
};
i2c_csi_dsi: i2c@1 {
reg = <0x01>;
#address-cells = <0x01>;
#size-cells = <0x00>;
};
};
i2c1: i2c@7e804000 {
compatible = "brcm,bcm2711-i2c", "brcm,bcm2835-i2c";
reg = <0x7e804000 0x1000>;
interrupts = <GIC_SPI 117 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cprman 0x14>;
#address-cells = <0x01>;
#size-cells = <0x00>;
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&i2c1_gpio>;
clock-frequency = <0x186a0>;
};
// TODO: memory access crashes on qemu (not implemented?)
i2c3: i2c@7e205600 {
compatible = "brcm,bcm2711-i2c", "brcm,bcm2835-i2c";
reg = <0x7e205600 0x200>;
interrupts = <GIC_SPI 117 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cprman 0x14>;
#address-cells = <0x01>;
#size-cells = <0x00>;
status = "disabled";
pinctrl-0 = <&i2c3_gpio>;
pinctrl-names = "default";
};
// TODO: memory access crashes on qemu (not implemented?)
i2c4: i2c@7e205800 {
compatible = "brcm,bcm2711-i2c", "brcm,bcm2835-i2c";
reg = <0x7e205800 0x200>;
interrupts = <GIC_SPI 117 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cprman 0x14>;
#address-cells = <0x01>;
#size-cells = <0x00>;
status = "disabled";
pinctrl-0 = <&i2c4_gpio>;
pinctrl-names = "default";
};
// TODO: memory access crashes on qemu (not implemented?)
i2c5: i2c@7e205a00 {
compatible = "brcm,bcm2711-i2c", "brcm,bcm2835-i2c";
reg = <0x7e205a00 0x200>;
interrupts = <GIC_SPI 117 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cprman 0x14>;
#address-cells = <0x01>;
#size-cells = <0x00>;
status = "disabled";
pinctrl-0 = <&i2c5_gpio>;
pinctrl-names = "default";
};
// TODO: memory access crashes on qemu (not implemented?)
i2c6: i2c@7e205c00 {
compatible = "brcm,bcm2711-i2c", "brcm,bcm2835-i2c";
reg = <0x7e205c00 0x200>;
interrupts = <GIC_SPI 117 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cprman 0x14>;
#address-cells = <0x01>;
#size-cells = <0x00>;
status = "disabled";
pinctrl-0 = <&i2c6_gpio>;
pinctrl-names = "default";
};
spi0: spi@7e204000 {
compatible = "brcm,bcm2835-spi";
reg = <0x7e204000 0x200>;
interrupts = <GIC_SPI 118 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cprman 0x14>;
#address-cells = <0x01>;
#size-cells = <0x00>;
status = "okay";
dmas = <&dma 0x06>,
<&dma 0x07>;
dma-names = "tx", "rx";
pinctrl-names = "default";
pinctrl-0 = <&spi0_gpio>, <&spi0_cs_gpio>;
cs-gpios = <&gpio 8 1>, <&gpio 7 1>;
spi0_0: spidev@0 {
compatible = "spidev";
reg = <0x00>;
#address-cells = <0x01>;
#size-cells = <0x00>;
spi-max-frequency = <125000000>;
};
spi0_1: spidev@1 {
compatible = "spidev";
reg = <0x01>;
#address-cells = <0x01>;
#size-cells = <0x00>;
spi-max-frequency = <125000000>;
};
};
l1_intc: local_intc@40000000 {
compatible = "brcm,bcm2836-l1-intc";
reg = <0x40000000 0x100>;
};
gicv2: interrupt-controller@40041000 {
interrupt-controller;
#interrupt-cells = <0x03>;
compatible = "arm,gic-400";
reg = <0x40041000 0x1000>,
<0x40042000 0x2000>,
<0x40044000 0x2000>,
<0x40046000 0x2000>;
interrupts = <GIC_PPI 9 (IRQ_TYPE_LEVEL_HIGH | GIC_CPU_MASK_SIMPLE(4))>;
};
avs_monitor: avs-monitor@7d5d2000 {
compatible = "brcm,bcm2711-avs-monitor", "syscon", "simple-mfd";
reg = <0x7d5d2000 0xf00>;
};
dma: dma@7e007000 {
compatible = "brcm,bcm2835-dma";
reg = <0x7e007000 0xb00>;
interrupts = <GIC_SPI 80 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 81 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 82 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 83 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 84 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 85 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 86 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 87 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 87 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 88 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 88 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "dma0",
"dma1",
"dma2",
"dma3",
"dma4",
"dma5",
"dma6",
"dma7",
"dma8",
"dma9",
"dma10";
#dma-cells = <0x01>;
brcm,dma-channel-mask = <0x7f5>;
};
pm_wdt: watchdog@7e100000 {
compatible = "brcm,bcm2835-pm", "brcm,bcm2835-pm-wdt";
#power-domain-cells = <0x01>;
#reset-cells = <0x01>;
reg = <0x7e100000 0x114>,
<0x7e00a000 0x24>,
<0x7ec11000 0x20>;
clocks = <&cprman 0x15>,
<&cprman 0x1d>,
<&cprman 0x17>,
<&cprman 0x16>;
clock-names = "v3d", "peri_image", "h264", "isp";
system-power-controller;
};
uart2: serial@7e201400 {
compatible = "arm,pl011", "arm,primecell";
reg = <0x7e201400 0x200>;
interrupts = <GIC_SPI 121 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cprman 0x13>,
<&cprman 0x14>;
clock-names = "uartclk",
"apb_pclk";
arm,primecell-periphid = <0x241011>;
pinctrl-0 = <&uart2_gpio>;
pinctrl-names = "default";
status = "disabled";
};
uart3: serial@7e201600 {
compatible = "arm,pl011", "arm,primecell";
reg = <0x7e201600 0x200>;
interrupts = <GIC_SPI 121 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cprman 0x13>,
<&cprman 0x14>;
clock-names = "uartclk", "apb_pclk";
arm,primecell-periphid = <0x241011>;
status = "disabled";
};
uart4: serial@7e201800 {
compatible = "arm,pl011", "arm,primecell";
reg = <0x7e201800 0x200>;
interrupts = <GIC_SPI 121 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cprman 0x13>,
<&cprman 0x14>;
clock-names = "uartclk", "apb_pclk";
arm,primecell-periphid = <0x241011>;
status = "disabled";
};
uart5: serial@7e201a00 {
compatible = "arm,pl011", "arm,primecell";
reg = <0x7e201a00 0x200>;
interrupts = <GIC_SPI 121 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cprman 0x13>,
<&cprman 0x14>;
clock-names = "uartclk", "apb_pclk";
arm,primecell-periphid = <0x241011>;
status = "disabled";
};
clk_dvp: clock@7ef00000 {
compatible = "brcm,brcm2711-dvp";
reg = <0x7ef00000 0x10>;
clocks = <&clk_108m>;
#clock-cells = <0x01>;
#reset-cells = <0x01>;
};
l2_intc: interrupt-controller@7ef00100 {
compatible = "brcm,bcm2711-l2-intc", "brcm,l2-intc";
reg = <0x7ef00100 0x30>;
interrupts = <GIC_SPI 96 IRQ_TYPE_LEVEL_HIGH>;
interrupt-controller;
#interrupt-cells = <0x01>;
};
firmware: firmware {
compatible = "raspberrypi,bcm2835-firmware", "simple-mfd";
#address-cells = <0x01>;
#size-cells = <0x01>;
mboxes = <&mbox>;
dma-ranges;
clk_firmware: clocks {
compatible = "raspberrypi,firmware-clocks";
#clock-cells = <0x01>;
};
gpio_firmware: gpio {
compatible = "raspberrypi,firmware-gpio";
gpio-controller;
#gpio-cells = <0x02>;
status = "okay";
gpio-line-names = "BT_ON", "WL_ON", "PWR_LED_OFF", "GLOBAL_RESET", "VDD_SD_IO_SEL", "CAM_GPIO", "SD_PWR_ON", "";
};
firmware_reset: reset {
compatible = "raspberrypi,firmware-reset";
#reset-cells = <0x01>;
};
};
power: power {
compatible = "raspberrypi,bcm2835-power";
firmware = <&firmware>;
#power-domain-cells = <0x01>;
};
vchiq: mailbox@7e00b840 {
compatible = "brcm,bcm2835-vchiq";
reg = <0x7e00b840 0x3c>;
interrupts = <GIC_SPI 34 IRQ_TYPE_LEVEL_HIGH>;
};
};
clocks {
clk_osc: clk-osc {
compatible = "fixed-clock";
#clock-cells = <0x00>;
clock-output-names = "osc";
clock-frequency = <54000000>;
};
clk_usb: clk-usb {
compatible = "fixed-clock";
#clock-cells = <0x00>;
clock-output-names = "otg";
clock-frequency = <480000000>;
};
};
clk_27m: clk-27M {
#clock-cells = <0x00>;
compatible = "fixed-clock";
clock-frequency = <27000000>;
clock-output-names = "27MHz-clock";
};
clk_108m: clk-108M {
#clock-cells = <0x00>;
compatible = "fixed-clock";
clock-frequency = <108000000>;
clock-output-names = "108MHz-clock";
};
pmu: arm-pmu {
compatible = "arm,cortex-a72-pmu", "arm,armv8-pmuv3";
interrupts = <GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 17 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 18 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 19 IRQ_TYPE_LEVEL_HIGH>;
interrupt-affinity = <0x21>,
<0x22>,
<0x23>,
<0x24>;
};
arm_timer: timer {
compatible = "arm,armv8-timer";
interrupts = <GIC_PPI 13 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>,
<GIC_PPI 14 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>,
<GIC_PPI 15 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>,
<GIC_PPI 16 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>;
arm,cpu-registers-not-fw-configured;
};
cpus {
#address-cells = <0x01>;
#size-cells = <0x00>;
enable-method = "brcm,bcm2836-smp";
cpu0: cpu@0 {
device_type = "cpu";
compatible = "arm,cortex-a72";
reg = <0x00>;
enable-method = "spin-table";
cpu-release-addr = <0x00 0xd8>;
};
cpu1: cpu@1 {
device_type = "cpu";
compatible = "arm,cortex-a72";
reg = <0x01>;
enable-method = "spin-table";
cpu-release-addr = <0x00 0xe0>;
};
cpu2: cpu@2 {
device_type = "cpu";
compatible = "arm,cortex-a72";
reg = <0x02>;
enable-method = "spin-table";
cpu-release-addr = <0x00 0xe8>;
};
cpu3: cpu@3 {
device_type = "cpu";
compatible = "arm,cortex-a72";
reg = <0x03>;
enable-method = "spin-table";
cpu-release-addr = <0x00 0xf0>;
};
};
leds {
compatible = "gpio-leds";
led_act: led-act {
label = "ACT";
default-state = "keep";
linux,default-trigger = "heartbeat";
gpios = <&gpio 0x2a 0x00>;
};
led_pwr: led-pwr {
label = "PWR";
gpios = <&gpio_firmware 0x02 0x01>;
default-state = "keep";
linux,default-trigger = "default-on";
};
};
memory@0 {
device_type = "memory";
reg = <0x00 0x00 0x00>;
};
sd_io_1v8_reg {
compatible = "regulator-gpio";
regulator-name = "vdd-sd-io";
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <3300000>;
regulator-boot-on;
regulator-always-on;
regulator-settling-time-us = <0x1388>;
gpios = <&gpio_firmware 0x04 0x00>;
states = <3300000 0x01>,
<1800000 0x00>;
status = "okay";
};
sd_vcc_reg {
compatible = "regulator-fixed";
regulator-name = "vcc-sd";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
regulator-boot-on;
enable-active-high;
gpio = <&gpio_firmware 0x06 0x00>;
};
};
+14
View File
@@ -0,0 +1,14 @@
#pragma once
#define GIC_SPI 0
#define GIC_PPI 1
#define GIC_CPU_MASK_RAW(x) ((x) << 8)
#define GIC_CPU_MASK_SIMPLE(num) GIC_CPU_MASK_RAW((1 << (num)) - 1)
#define IRQ_TYPE_NONE 0
#define IRQ_TYPE_EDGE_RISING 1
#define IRQ_TYPE_EDGE_FALLING 2
#define IRQ_TYPE_EDGE_BOTH (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)
#define IRQ_TYPE_LEVEL_HIGH 4
#define IRQ_TYPE_LEVEL_LOW 8
Binary file not shown.
File diff suppressed because it is too large Load Diff
Binary file not shown.
+308
View File
@@ -0,0 +1,308 @@
/* SPDX-License-Identifier: GPL-2.0 OR MIT */
/*
* Copyright (C) 2022 Emil Renner Berthing <kernel@esmil.dk>
* Copyright (C) 2022 StarFive Technology Co., Ltd.
*/
#ifndef __JH7110_PINFUNC_H__
#define __JH7110_PINFUNC_H__
/*
* mux bits:
* | 31 - 24 | 23 - 16 | 15 - 10 | 9 - 8 | 7 - 0 |
* | din | dout | doen | function | gpio nr |
*
* dout: output signal
* doen: output enable signal
* din: optional input signal, 0xff = none
* function: function selector
* gpio nr: gpio number, 0 - 63
*/
#define GPIOMUX(n, dout, doen, din) ( \
(((din) & 0xff) << 24) | \
(((dout) & 0xff) << 16) | \
(((doen) & 0x3f) << 10) | \
((n) & 0x3f))
#define PINMUX(n, func) ((1 << 10) | (((func) & 0x3) << 8) | ((n) & 0xff))
/* sys_iomux dout */
#define GPOUT_LOW 0
#define GPOUT_HIGH 1
#define GPOUT_SYS_WAVE511_UART_TX 2
#define GPOUT_SYS_CAN0_STBY 3
#define GPOUT_SYS_CAN0_TST_NEXT_BIT 4
#define GPOUT_SYS_CAN0_TST_SAMPLE_POINT 5
#define GPOUT_SYS_CAN0_TXD 6
#define GPOUT_SYS_USB_DRIVE_VBUS 7
#define GPOUT_SYS_QSPI_CS1 8
#define GPOUT_SYS_SPDIF 9
#define GPOUT_SYS_HDMI_CEC_SDA 10
#define GPOUT_SYS_HDMI_DDC_SCL 11
#define GPOUT_SYS_HDMI_DDC_SDA 12
#define GPOUT_SYS_WATCHDOG 13
#define GPOUT_SYS_I2C0_CLK 14
#define GPOUT_SYS_I2C0_DATA 15
#define GPOUT_SYS_SDIO0_BACK_END_POWER 16
#define GPOUT_SYS_SDIO0_CARD_POWER_EN 17
#define GPOUT_SYS_SDIO0_CCMD_OD_PULLUP_EN 18
#define GPOUT_SYS_SDIO0_RST 19
#define GPOUT_SYS_UART0_TX 20
#define GPOUT_SYS_HIFI4_JTAG_TDO 21
#define GPOUT_SYS_JTAG_TDO 22
#define GPOUT_SYS_PDM_MCLK 23
#define GPOUT_SYS_PWM_CHANNEL0 24
#define GPOUT_SYS_PWM_CHANNEL1 25
#define GPOUT_SYS_PWM_CHANNEL2 26
#define GPOUT_SYS_PWM_CHANNEL3 27
#define GPOUT_SYS_PWMDAC_LEFT 28
#define GPOUT_SYS_PWMDAC_RIGHT 29
#define GPOUT_SYS_SPI0_CLK 30
#define GPOUT_SYS_SPI0_FSS 31
#define GPOUT_SYS_SPI0_TXD 32
#define GPOUT_SYS_GMAC_PHYCLK 33
#define GPOUT_SYS_I2SRX_BCLK 34
#define GPOUT_SYS_I2SRX_LRCK 35
#define GPOUT_SYS_I2STX0_BCLK 36
#define GPOUT_SYS_I2STX0_LRCK 37
#define GPOUT_SYS_MCLK 38
#define GPOUT_SYS_TDM_CLK 39
#define GPOUT_SYS_TDM_SYNC 40
#define GPOUT_SYS_TDM_TXD 41
#define GPOUT_SYS_TRACE_DATA0 42
#define GPOUT_SYS_TRACE_DATA1 43
#define GPOUT_SYS_TRACE_DATA2 44
#define GPOUT_SYS_TRACE_DATA3 45
#define GPOUT_SYS_TRACE_REF 46
#define GPOUT_SYS_CAN1_STBY 47
#define GPOUT_SYS_CAN1_TST_NEXT_BIT 48
#define GPOUT_SYS_CAN1_TST_SAMPLE_POINT 49
#define GPOUT_SYS_CAN1_TXD 50
#define GPOUT_SYS_I2C1_CLK 51
#define GPOUT_SYS_I2C1_DATA 52
#define GPOUT_SYS_SDIO1_BACK_END_POWER 53
#define GPOUT_SYS_SDIO1_CARD_POWER_EN 54
#define GPOUT_SYS_SDIO1_CLK 55
#define GPOUT_SYS_SDIO1_CMD_OD_PULLUP_EN 56
#define GPOUT_SYS_SDIO1_CMD 57
#define GPOUT_SYS_SDIO1_DATA0 58
#define GPOUT_SYS_SDIO1_DATA1 59
#define GPOUT_SYS_SDIO1_DATA2 60
#define GPOUT_SYS_SDIO1_DATA3 61
#define GPOUT_SYS_SDIO1_DATA4 62
#define GPOUT_SYS_SDIO1_DATA5 63
#define GPOUT_SYS_SDIO1_DATA6 64
#define GPOUT_SYS_SDIO1_DATA7 65
#define GPOUT_SYS_SDIO1_RST 66
#define GPOUT_SYS_UART1_RTS 67
#define GPOUT_SYS_UART1_TX 68
#define GPOUT_SYS_I2STX1_SDO0 69
#define GPOUT_SYS_I2STX1_SDO1 70
#define GPOUT_SYS_I2STX1_SDO2 71
#define GPOUT_SYS_I2STX1_SDO3 72
#define GPOUT_SYS_SPI1_CLK 73
#define GPOUT_SYS_SPI1_FSS 74
#define GPOUT_SYS_SPI1_TXD 75
#define GPOUT_SYS_I2C2_CLK 76
#define GPOUT_SYS_I2C2_DATA 77
#define GPOUT_SYS_UART2_RTS 78
#define GPOUT_SYS_UART2_TX 79
#define GPOUT_SYS_SPI2_CLK 80
#define GPOUT_SYS_SPI2_FSS 81
#define GPOUT_SYS_SPI2_TXD 82
#define GPOUT_SYS_I2C3_CLK 83
#define GPOUT_SYS_I2C3_DATA 84
#define GPOUT_SYS_UART3_TX 85
#define GPOUT_SYS_SPI3_CLK 86
#define GPOUT_SYS_SPI3_FSS 87
#define GPOUT_SYS_SPI3_TXD 88
#define GPOUT_SYS_I2C4_CLK 89
#define GPOUT_SYS_I2C4_DATA 90
#define GPOUT_SYS_UART4_RTS 91
#define GPOUT_SYS_UART4_TX 92
#define GPOUT_SYS_SPI4_CLK 93
#define GPOUT_SYS_SPI4_FSS 94
#define GPOUT_SYS_SPI4_TXD 95
#define GPOUT_SYS_I2C5_CLK 96
#define GPOUT_SYS_I2C5_DATA 97
#define GPOUT_SYS_UART5_RTS 98
#define GPOUT_SYS_UART5_TX 99
#define GPOUT_SYS_SPI5_CLK 100
#define GPOUT_SYS_SPI5_FSS 101
#define GPOUT_SYS_SPI5_TXD 102
#define GPOUT_SYS_I2C6_CLK 103
#define GPOUT_SYS_I2C6_DATA 104
#define GPOUT_SYS_SPI6_CLK 105
#define GPOUT_SYS_SPI6_FSS 106
#define GPOUT_SYS_SPI6_TXD 107
/* aon_iomux dout */
#define GPOUT_AON_CLK_32K_OUT 2
#define GPOUT_AON_PTC0_PWM4 3
#define GPOUT_AON_PTC0_PWM5 4
#define GPOUT_AON_PTC0_PWM6 5
#define GPOUT_AON_PTC0_PWM7 6
#define GPOUT_AON_CLK_GCLK0 7
#define GPOUT_AON_CLK_GCLK1 8
#define GPOUT_AON_CLK_GCLK2 9
/* sys_iomux doen */
#define GPOEN_ENABLE 0
#define GPOEN_DISABLE 1
#define GPOEN_SYS_HDMI_CEC_SDA 2
#define GPOEN_SYS_HDMI_DDC_SCL 3
#define GPOEN_SYS_HDMI_DDC_SDA 4
#define GPOEN_SYS_I2C0_CLK 5
#define GPOEN_SYS_I2C0_DATA 6
#define GPOEN_SYS_HIFI4_JTAG_TDO 7
#define GPOEN_SYS_JTAG_TDO 8
#define GPOEN_SYS_PWM0_CHANNEL0 9
#define GPOEN_SYS_PWM0_CHANNEL1 10
#define GPOEN_SYS_PWM0_CHANNEL2 11
#define GPOEN_SYS_PWM0_CHANNEL3 12
#define GPOEN_SYS_SPI0_NSSPCTL 13
#define GPOEN_SYS_SPI0_NSSP 14
#define GPOEN_SYS_TDM_SYNC 15
#define GPOEN_SYS_TDM_TXD 16
#define GPOEN_SYS_I2C1_CLK 17
#define GPOEN_SYS_I2C1_DATA 18
#define GPOEN_SYS_SDIO1_CMD 19
#define GPOEN_SYS_SDIO1_DATA0 20
#define GPOEN_SYS_SDIO1_DATA1 21
#define GPOEN_SYS_SDIO1_DATA2 22
#define GPOEN_SYS_SDIO1_DATA3 23
#define GPOEN_SYS_SDIO1_DATA4 24
#define GPOEN_SYS_SDIO1_DATA5 25
#define GPOEN_SYS_SDIO1_DATA6 26
#define GPOEN_SYS_SDIO1_DATA7 27
#define GPOEN_SYS_SPI1_NSSPCTL 28
#define GPOEN_SYS_SPI1_NSSP 29
#define GPOEN_SYS_I2C2_CLK 30
#define GPOEN_SYS_I2C2_DATA 31
#define GPOEN_SYS_SPI2_NSSPCTL 32
#define GPOEN_SYS_SPI2_NSSP 33
#define GPOEN_SYS_I2C3_CLK 34
#define GPOEN_SYS_I2C3_DATA 35
#define GPOEN_SYS_SPI3_NSSPCTL 36
#define GPOEN_SYS_SPI3_NSSP 37
#define GPOEN_SYS_I2C4_CLK 38
#define GPOEN_SYS_I2C4_DATA 39
#define GPOEN_SYS_SPI4_NSSPCTL 40
#define GPOEN_SYS_SPI4_NSSP 41
#define GPOEN_SYS_I2C5_CLK 42
#define GPOEN_SYS_I2C5_DATA 43
#define GPOEN_SYS_SPI5_NSSPCTL 44
#define GPOEN_SYS_SPI5_NSSP 45
#define GPOEN_SYS_I2C6_CLK 46
#define GPOEN_SYS_I2C6_DATA 47
#define GPOEN_SYS_SPI6_NSSPCTL 48
#define GPOEN_SYS_SPI6_NSSP 49
/* aon_iomux doen */
#define GPOEN_AON_PTC0_OE_N_4 2
#define GPOEN_AON_PTC0_OE_N_5 3
#define GPOEN_AON_PTC0_OE_N_6 4
#define GPOEN_AON_PTC0_OE_N_7 5
/* sys_iomux gin */
#define GPI_NONE 255
#define GPI_SYS_WAVE511_UART_RX 0
#define GPI_SYS_CAN0_RXD 1
#define GPI_SYS_USB_OVERCURRENT 2
#define GPI_SYS_SPDIF 3
#define GPI_SYS_JTAG_RST 4
#define GPI_SYS_HDMI_CEC_SDA 5
#define GPI_SYS_HDMI_DDC_SCL 6
#define GPI_SYS_HDMI_DDC_SDA 7
#define GPI_SYS_HDMI_HPD 8
#define GPI_SYS_I2C0_CLK 9
#define GPI_SYS_I2C0_DATA 10
#define GPI_SYS_SDIO0_CD 11
#define GPI_SYS_SDIO0_INT 12
#define GPI_SYS_SDIO0_WP 13
#define GPI_SYS_UART0_RX 14
#define GPI_SYS_HIFI4_JTAG_TCK 15
#define GPI_SYS_HIFI4_JTAG_TDI 16
#define GPI_SYS_HIFI4_JTAG_TMS 17
#define GPI_SYS_HIFI4_JTAG_RST 18
#define GPI_SYS_JTAG_TDI 19
#define GPI_SYS_JTAG_TMS 20
#define GPI_SYS_PDM_DMIC0 21
#define GPI_SYS_PDM_DMIC1 22
#define GPI_SYS_I2SRX_SDIN0 23
#define GPI_SYS_I2SRX_SDIN1 24
#define GPI_SYS_I2SRX_SDIN2 25
#define GPI_SYS_SPI0_CLK 26
#define GPI_SYS_SPI0_FSS 27
#define GPI_SYS_SPI0_RXD 28
#define GPI_SYS_JTAG_TCK 29
#define GPI_SYS_MCLK_EXT 30
#define GPI_SYS_I2SRX_BCLK 31
#define GPI_SYS_I2SRX_LRCK 32
#define GPI_SYS_I2STX1_BCLK 33
#define GPI_SYS_I2STX1_LRCK 34
#define GPI_SYS_TDM_CLK 35
#define GPI_SYS_TDM_RXD 36
#define GPI_SYS_TDM_SYNC 37
#define GPI_SYS_CAN1_RXD 38
#define GPI_SYS_I2C1_CLK 39
#define GPI_SYS_I2C1_DATA 40
#define GPI_SYS_SDIO1_CD 41
#define GPI_SYS_SDIO1_INT 42
#define GPI_SYS_SDIO1_WP 43
#define GPI_SYS_SDIO1_CMD 44
#define GPI_SYS_SDIO1_DATA0 45
#define GPI_SYS_SDIO1_DATA1 46
#define GPI_SYS_SDIO1_DATA2 47
#define GPI_SYS_SDIO1_DATA3 48
#define GPI_SYS_SDIO1_DATA4 49
#define GPI_SYS_SDIO1_DATA5 50
#define GPI_SYS_SDIO1_DATA6 51
#define GPI_SYS_SDIO1_DATA7 52
#define GPI_SYS_SDIO1_STRB 53
#define GPI_SYS_UART1_CTS 54
#define GPI_SYS_UART1_RX 55
#define GPI_SYS_SPI1_CLK 56
#define GPI_SYS_SPI1_FSS 57
#define GPI_SYS_SPI1_RXD 58
#define GPI_SYS_I2C2_CLK 59
#define GPI_SYS_I2C2_DATA 60
#define GPI_SYS_UART2_CTS 61
#define GPI_SYS_UART2_RX 62
#define GPI_SYS_SPI2_CLK 63
#define GPI_SYS_SPI2_FSS 64
#define GPI_SYS_SPI2_RXD 65
#define GPI_SYS_I2C3_CLK 66
#define GPI_SYS_I2C3_DATA 67
#define GPI_SYS_UART3_RX 68
#define GPI_SYS_SPI3_CLK 69
#define GPI_SYS_SPI3_FSS 70
#define GPI_SYS_SPI3_RXD 71
#define GPI_SYS_I2C4_CLK 72
#define GPI_SYS_I2C4_DATA 73
#define GPI_SYS_UART4_CTS 74
#define GPI_SYS_UART4_RX 75
#define GPI_SYS_SPI4_CLK 76
#define GPI_SYS_SPI4_FSS 77
#define GPI_SYS_SPI4_RXD 78
#define GPI_SYS_I2C5_CLK 79
#define GPI_SYS_I2C5_DATA 80
#define GPI_SYS_UART5_CTS 81
#define GPI_SYS_UART5_RX 82
#define GPI_SYS_SPI5_CLK 83
#define GPI_SYS_SPI5_FSS 84
#define GPI_SYS_SPI5_RXD 85
#define GPI_SYS_I2C6_CLK 86
#define GPI_SYS_I2C6_DATA 87
#define GPI_SYS_SPI6_CLK 88
#define GPI_SYS_SPI6_FSS 89
#define GPI_SYS_SPI6_RXD 90
/* aon_iomux gin */
#define GPI_AON_PMU_GPIO_WAKEUP_0 0
#define GPI_AON_PMU_GPIO_WAKEUP_1 1
#define GPI_AON_PMU_GPIO_WAKEUP_2 2
#define GPI_AON_PMU_GPIO_WAKEUP_3 3
#endif
File diff suppressed because it is too large Load Diff
@@ -38,7 +38,7 @@
#size-cells = <0x00>;
timebase-frequency = <0x989680>;
cpu@0 {
cpu0: cpu@0 {
phandle = <0x01>;
device_type = "cpu";
reg = <0x00>;
@@ -52,7 +52,7 @@
riscv,isa = "rv64imafdch_zic64b_zicbom_zicbop_zicboz_ziccamoa_ziccif_zicclsm_ziccrse_zicntr_zicsr_zifencei_zihintntl_zihintpause_zihpm_zmmul_za64rs_zaamo_zalrsc_zawrs_zfa_zca_zcd_zba_zbb_zbc_zbs_shcounterenw_shgatpa_shtvala_shvsatpa_shvstvala_shvstvecd_ssccptr_sscounterenw_sstc_sstvala_sstvecd_ssu64xl_svadu_svvptc";
mmu-type = "riscv,sv57";
interrupt-controller {
cpu0_intc: interrupt-controller {
#interrupt-cells = <0x01>;
interrupt-controller;
compatible = "riscv,cpu-intc";
@@ -61,11 +61,9 @@
};
cpu-map {
cluster0 {
core0 {
cpu = <0x01>;
cpu = <&cpu0>;
};
};
};
@@ -186,7 +184,8 @@
phandle = <0x03>;
riscv,ndev = <0x5f>;
reg = <0x00 0xc000000 0x00 0x600000>;
interrupts-extended = <0x02 0x0b 0x02 0x09>;
interrupts-extended = <&cpu0_intc 0x0b>,
<&cpu0_intc 0x09>;
interrupt-controller;
compatible = "sifive,plic-1.0.0", "riscv,plic0";
#address-cells = <0x00>;
@@ -194,7 +193,8 @@
};
clint@2000000 {
interrupts-extended = <0x02 0x03 0x02 0x07>;
interrupts-extended = <&cpu0_intc 0x03>,
<&cpu0_intc 0x07>;
reg = <0x00 0x2000000 0x00 0x10000>;
compatible = "sifive,clint0", "riscv,clint0";
};
Binary file not shown.
@@ -68,7 +68,7 @@
chosen {
stdout-path = "serial0:115200n8";
bootargs = "coherent_pool=1M 8250.nr_uarts=1 snd_bcm2835.enable_headphones=0 cgroup_disable=memory numa_policy=interleave";
bootargs = "coherent_pool=1M 8250.nr_uarts=1 snd_bcm2835.enable_headphones=0 cgroup_disable=memory numa_policy=interleave nvme.max_host_mem_size_mb=0";
phandle = <0x54>;
};
@@ -172,7 +172,7 @@
reg = <0x7e00b880 0x40>;
interrupts = <0x00 0x21 0x04>;
#mbox-cells = <0x00>;
phandle = <0x35>;
phandle = <0x31>;
};
gpio@7e200000 {
@@ -252,7 +252,7 @@
i2c0if-gpio0 {
brcm,pins = <0x00 0x01>;
brcm,function = <0x04>;
phandle = <0x33>;
phandle = <0x35>;
};
i2c0if-gpio28 {
@@ -264,7 +264,7 @@
i2c0if-gpio44 {
brcm,pins = <0x2c 0x2d>;
brcm,function = <0x05>;
phandle = <0x34>;
phandle = <0x36>;
};
i2c1-gpio2 {
@@ -958,64 +958,64 @@
};
};
dpi_18bit_cpadhi_gpio0 {
brcm,pins = <0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0c 0x0d 0x0e 0x0f 0x10 0x11 0x14 0x15 0x16 0x17 0x18 0x19>;
brcm,function = <0x06>;
brcm,pull = <0x00>;
phandle = <0xb2>;
};
dpi_18bit_cpadhi_gpio2 {
brcm,pins = <0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0c 0x0d 0x0e 0x0f 0x10 0x11 0x14 0x15 0x16 0x17 0x18 0x19>;
brcm,function = <0x06>;
phandle = <0xb3>;
};
dpi_18bit_gpio0 {
brcm,pins = <0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0a 0x0b 0x0c 0x0d 0x0e 0x0f 0x10 0x11 0x12 0x13 0x14 0x15>;
brcm,function = <0x06>;
phandle = <0xb4>;
};
dpi_18bit_gpio2 {
brcm,pins = <0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0a 0x0b 0x0c 0x0d 0x0e 0x0f 0x10 0x11 0x12 0x13 0x14 0x15>;
brcm,function = <0x06>;
phandle = <0xb5>;
};
dpi_16bit_gpio0 {
brcm,pins = <0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0a 0x0b 0x0c 0x0d 0x0e 0x0f 0x10 0x11 0x12 0x13>;
brcm,function = <0x06>;
phandle = <0xb6>;
};
dpi_16bit_gpio2 {
brcm,pins = <0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0a 0x0b 0x0c 0x0d 0x0e 0x0f 0x10 0x11 0x12 0x13>;
brcm,function = <0x06>;
phandle = <0xb7>;
};
dpi_16bit_cpadhi_gpio0 {
brcm,pins = <0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x0c 0x0d 0x0e 0x0f 0x10 0x11 0x14 0x15 0x16 0x17 0x18>;
brcm,function = <0x06>;
phandle = <0xb8>;
};
dpi_16bit_cpadhi_gpio2 {
brcm,pins = <0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x0c 0x0d 0x0e 0x0f 0x10 0x11 0x14 0x15 0x16 0x17 0x18>;
brcm,function = <0x06>;
phandle = <0xb9>;
};
gpioout {
brcm,pins = <0x06>;
brcm,function = <0x01>;
phandle = <0xba>;
phandle = <0xb2>;
};
alt0 {
brcm,pins = <0x04 0x05 0x07 0x08 0x09 0x0a 0x0b>;
brcm,function = <0x04>;
phandle = <0xb3>;
};
dpi_18bit_cpadhi_gpio0 {
brcm,pins = <0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0c 0x0d 0x0e 0x0f 0x10 0x11 0x14 0x15 0x16 0x17 0x18 0x19>;
brcm,function = <0x06>;
brcm,pull = <0x00>;
phandle = <0xb4>;
};
dpi_18bit_cpadhi_gpio2 {
brcm,pins = <0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0c 0x0d 0x0e 0x0f 0x10 0x11 0x14 0x15 0x16 0x17 0x18 0x19>;
brcm,function = <0x06>;
phandle = <0xb5>;
};
dpi_18bit_gpio0 {
brcm,pins = <0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0a 0x0b 0x0c 0x0d 0x0e 0x0f 0x10 0x11 0x12 0x13 0x14 0x15>;
brcm,function = <0x06>;
phandle = <0xb6>;
};
dpi_18bit_gpio2 {
brcm,pins = <0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0a 0x0b 0x0c 0x0d 0x0e 0x0f 0x10 0x11 0x12 0x13 0x14 0x15>;
brcm,function = <0x06>;
phandle = <0xb7>;
};
dpi_16bit_gpio0 {
brcm,pins = <0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0a 0x0b 0x0c 0x0d 0x0e 0x0f 0x10 0x11 0x12 0x13>;
brcm,function = <0x06>;
phandle = <0xb8>;
};
dpi_16bit_gpio2 {
brcm,pins = <0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0a 0x0b 0x0c 0x0d 0x0e 0x0f 0x10 0x11 0x12 0x13>;
brcm,function = <0x06>;
phandle = <0xb9>;
};
dpi_16bit_cpadhi_gpio0 {
brcm,pins = <0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x0c 0x0d 0x0e 0x0f 0x10 0x11 0x14 0x15 0x16 0x17 0x18>;
brcm,function = <0x06>;
phandle = <0xba>;
};
dpi_16bit_cpadhi_gpio2 {
brcm,pins = <0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x0c 0x0d 0x0e 0x0f 0x10 0x11 0x14 0x15 0x16 0x17 0x18>;
brcm,function = <0x06>;
phandle = <0xbb>;
};
@@ -1131,7 +1131,7 @@
brcm,pins = <0x22 0x23 0x24 0x25 0x26 0x27>;
brcm,function = <0x07>;
brcm,pull = <0x00 0x02 0x02 0x02 0x02 0x02>;
phandle = <0x31>;
phandle = <0x33>;
};
uart2_pins {
@@ -1194,17 +1194,17 @@
brcm,pins = <0x28 0x29>;
brcm,function = <0x04>;
brcm,pull = <0x00>;
phandle = <0x36>;
phandle = <0x32>;
};
};
serial@7e201000 {
compatible = "arm,pl011", "arm,primecell";
compatible = "arm,pl011-axi";
reg = <0x7e201000 0x200>;
interrupts = <0x00 0x79 0x04>;
clocks = <0x08 0x13 0x08 0x14>;
clock-names = "uartclk", "apb_pclk";
arm,primecell-periphid = <0x241011>;
arm,primecell-periphid = <0x341011>;
status = "okay";
cts-event-workaround;
pinctrl-names = "default";
@@ -1295,7 +1295,7 @@
#size-cells = <0x00>;
status = "disabled";
clock-frequency = <0x186a0>;
phandle = <0x32>;
phandle = <0x34>;
};
dpi@7e208000 {
@@ -1404,8 +1404,8 @@
compatible = "brcm,bcm2711-hvs";
reg = <0x7e400000 0x8000>;
interrupts = <0x00 0x61 0x04>;
status = "disabled";
clocks = <0x14 0x04>;
status = "disabled";
phandle = <0xc4>;
};
@@ -1424,6 +1424,38 @@
phandle = <0x05>;
};
csi@7e800000 {
compatible = "brcm,bcm2835-unicam";
reg = <0x7e800000 0x800 0x7e802000 0x04>;
reg-names = "unicam", "cmi";
interrupts = <0x00 0x66 0x04>;
brcm,num-data-lanes = <0x02>;
status = "disabled";
clocks = <0x08 0x2d 0x14 0x04>;
clock-names = "lp", "vpu";
power-domains = <0x10 0x0c>;
phandle = <0x51>;
port {
};
};
csi@7e801000 {
compatible = "brcm,bcm2835-unicam";
reg = <0x7e801000 0x800 0x7e802004 0x04>;
reg-names = "unicam", "cmi";
interrupts = <0x00 0x67 0x04>;
brcm,num-data-lanes = <0x02>;
status = "disabled";
clocks = <0x08 0x2e 0x14 0x04>;
clock-names = "lp", "vpu";
power-domains = <0x10 0x0d>;
phandle = <0x50>;
port {
};
};
i2c@7e804000 {
compatible = "brcm,bcm2711-i2c", "brcm,bcm2835-i2c";
reg = <0x7e804000 0x1000>;
@@ -1446,10 +1478,11 @@
#size-cells = <0x00>;
clocks = <0x16>;
clock-names = "otg";
dr_mode = "host";
phys = <0x17>;
phy-names = "usb2-phy";
interrupt-names = "usb", "soft";
power-domains = <0x10 0x06>;
interrupt-names = "usb", "soft";
status = "disabled";
phandle = <0xc5>;
};
@@ -1511,12 +1544,12 @@
};
serial@7e201400 {
compatible = "arm,pl011", "arm,primecell";
compatible = "arm,pl011-axi";
reg = <0x7e201400 0x200>;
interrupts = <0x00 0x79 0x04>;
clocks = <0x08 0x13 0x08 0x14>;
clock-names = "uartclk", "apb_pclk";
arm,primecell-periphid = <0x241011>;
arm,primecell-periphid = <0x341011>;
status = "disabled";
pinctrl-0 = <0x18>;
pinctrl-names = "default";
@@ -1524,12 +1557,12 @@
};
serial@7e201600 {
compatible = "arm,pl011", "arm,primecell";
compatible = "arm,pl011-axi";
reg = <0x7e201600 0x200>;
interrupts = <0x00 0x79 0x04>;
clocks = <0x08 0x13 0x08 0x14>;
clock-names = "uartclk", "apb_pclk";
arm,primecell-periphid = <0x241011>;
arm,primecell-periphid = <0x341011>;
status = "disabled";
pinctrl-0 = <0x19>;
pinctrl-names = "default";
@@ -1537,12 +1570,12 @@
};
serial@7e201800 {
compatible = "arm,pl011", "arm,primecell";
compatible = "arm,pl011-axi";
reg = <0x7e201800 0x200>;
interrupts = <0x00 0x79 0x04>;
clocks = <0x08 0x13 0x08 0x14>;
clock-names = "uartclk", "apb_pclk";
arm,primecell-periphid = <0x241011>;
arm,primecell-periphid = <0x341011>;
status = "disabled";
pinctrl-0 = <0x1a>;
pinctrl-names = "default";
@@ -1550,12 +1583,12 @@
};
serial@7e201a00 {
compatible = "arm,pl011", "arm,primecell";
compatible = "arm,pl011-axi";
reg = <0x7e201a00 0x200>;
interrupts = <0x00 0x79 0x04>;
clocks = <0x08 0x13 0x08 0x14>;
clock-names = "uartclk", "apb_pclk";
arm,primecell-periphid = <0x241011>;
arm,primecell-periphid = <0x341011>;
status = "disabled";
pinctrl-0 = <0x1b>;
pinctrl-names = "default";
@@ -1803,113 +1836,9 @@
phandle = <0x30>;
};
mmcnr@7e300000 {
compatible = "brcm,bcm2835-mmc", "brcm,bcm2835-sdhci";
reg = <0x7e300000 0x100>;
interrupts = <0x00 0x7e 0x04>;
clocks = <0x08 0x1c>;
dmas = <0x0c 0x0b>;
dma-names = "rx-tx";
brcm,overclock-50 = <0x00>;
non-removable;
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <0x31>;
bus-width = <0x04>;
phandle = <0x4d>;
};
firmwarekms@7e600000 {
compatible = "raspberrypi,rpi-firmware-kms-2711";
reg = <0x7e600000 0x100>;
interrupts = <0x00 0x70 0x04>;
brcm,firmware = <0x06>;
status = "disabled";
phandle = <0xdb>;
};
smi@7e600000 {
compatible = "brcm,bcm2835-smi";
reg = <0x7e600000 0x100>;
interrupts = <0x00 0x70 0x04>;
clocks = <0x08 0x2a>;
assigned-clocks = <0x08 0x2a>;
assigned-clock-rates = <0x7735940>;
dmas = <0x0c 0x04>;
dma-names = "rx-tx";
status = "disabled";
phandle = <0xdc>;
};
csi@7e800000 {
compatible = "brcm,bcm2835-unicam";
reg = <0x7e800000 0x800 0x7e802000 0x04>;
interrupts = <0x00 0x66 0x04>;
clocks = <0x08 0x2d 0x14 0x04>;
clock-names = "lp", "vpu";
power-domains = <0x10 0x0c>;
#address-cells = <0x01>;
#size-cells = <0x00>;
#clock-cells = <0x01>;
status = "disabled";
phandle = <0x51>;
};
csi@7e801000 {
compatible = "brcm,bcm2835-unicam";
reg = <0x7e801000 0x800 0x7e802004 0x04>;
interrupts = <0x00 0x67 0x04>;
clocks = <0x08 0x2e 0x14 0x04>;
clock-names = "lp", "vpu";
power-domains = <0x10 0x0d>;
#address-cells = <0x01>;
#size-cells = <0x00>;
#clock-cells = <0x01>;
status = "disabled";
brcm,num-data-lanes = <0x02>;
phandle = <0x50>;
};
axiperf {
compatible = "brcm,bcm2711-axiperf";
reg = <0x7e009800 0x100 0x7ee08000 0x100>;
firmware = <0x06>;
status = "disabled";
phandle = <0x4e>;
};
i2c0mux {
compatible = "i2c-mux-pinctrl";
#address-cells = <0x01>;
#size-cells = <0x00>;
i2c-parent = <0x32>;
status = "disabled";
pinctrl-names = "i2c0", "i2c_csi_dsi";
pinctrl-0 = <0x33>;
pinctrl-1 = <0x34>;
phandle = <0x47>;
i2c@0 {
reg = <0x00>;
#address-cells = <0x01>;
#size-cells = <0x00>;
phandle = <0xdd>;
};
i2c@1 {
reg = <0x01>;
#address-cells = <0x01>;
#size-cells = <0x00>;
phandle = <0xde>;
};
};
firmware {
compatible = "raspberrypi,bcm2835-firmware", "simple-mfd";
#address-cells = <0x01>;
#size-cells = <0x01>;
mboxes = <0x35>;
dma-ranges;
mboxes = <0x31>;
phandle = <0x06>;
clocks {
@@ -1935,7 +1864,7 @@
vcio {
compatible = "raspberrypi,vcio";
phandle = <0xdf>;
phandle = <0xdb>;
};
};
@@ -1951,8 +1880,88 @@
reg = <0x7e00b840 0x3c>;
interrupts = <0x00 0x22 0x04>;
pinctrl-names = "default";
pinctrl-0 = <0x36>;
phandle = <0xe0>;
pinctrl-0 = <0x32>;
phandle = <0xdc>;
};
mmcnr@7e300000 {
compatible = "brcm,bcm2835-mmc", "brcm,bcm2835-sdhci";
reg = <0x7e300000 0x100>;
interrupts = <0x00 0x7e 0x04>;
clocks = <0x08 0x1c>;
dmas = <0x0c 0x0b>;
dma-names = "rx-tx";
brcm,overclock-50 = <0x00>;
non-removable;
status = "okay";
#address-cells = <0x01>;
#size-cells = <0x00>;
pinctrl-names = "default";
pinctrl-0 = <0x33>;
bus-width = <0x04>;
phandle = <0x4d>;
wifi@1 {
reg = <0x01>;
compatible = "brcm,bcm4329-fmac";
phandle = <0xdd>;
};
};
firmwarekms@7e600000 {
compatible = "raspberrypi,rpi-firmware-kms-2711";
reg = <0x7e600000 0x100>;
interrupts = <0x00 0x70 0x04>;
brcm,firmware = <0x06>;
status = "disabled";
phandle = <0xde>;
};
smi@7e600000 {
compatible = "brcm,bcm2835-smi";
reg = <0x7e600000 0x100>;
interrupts = <0x00 0x70 0x04>;
clocks = <0x08 0x2a>;
assigned-clocks = <0x08 0x2a>;
assigned-clock-rates = <0x7735940>;
dmas = <0x0c 0x04>;
dma-names = "rx-tx";
status = "disabled";
phandle = <0xdf>;
};
axiperf {
compatible = "brcm,bcm2711-axiperf";
reg = <0x7e009800 0x100 0x7ee08000 0x100>;
firmware = <0x06>;
status = "disabled";
phandle = <0x4e>;
};
i2c0mux {
compatible = "i2c-mux-pinctrl";
#address-cells = <0x01>;
#size-cells = <0x00>;
i2c-parent = <0x34>;
status = "disabled";
pinctrl-names = "i2c0", "i2c_csi_dsi";
pinctrl-0 = <0x35>;
pinctrl-1 = <0x36>;
phandle = <0x47>;
i2c@0 {
reg = <0x00>;
#address-cells = <0x01>;
#size-cells = <0x00>;
phandle = <0xe0>;
};
i2c@1 {
reg = <0x01>;
#address-cells = <0x01>;
#size-cells = <0x00>;
phandle = <0xe1>;
};
};
gpiomem {
@@ -1964,12 +1973,12 @@
compatible = "brcm,bcm2708-fb";
firmware = <0x06>;
status = "okay";
phandle = <0xe1>;
phandle = <0xe2>;
};
sound {
status = "disabled";
phandle = <0xe2>;
phandle = <0xe3>;
};
nvmem {
@@ -1982,7 +1991,7 @@
firmware = <0x06>;
reg = <0x00 0xa6>;
status = "okay";
phandle = <0xe3>;
phandle = <0xe4>;
};
nvmem_cust {
@@ -2035,7 +2044,7 @@
compatible = "brcm,bcm2711-vc5";
status = "disabled";
raspberrypi,firmware = <0x06>;
phandle = <0xe4>;
phandle = <0xe5>;
};
clk-27M {
@@ -2060,7 +2069,7 @@
#size-cells = <0x01>;
ranges = <0x00 0x7e000000 0x00 0xfe000000 0x1800000>;
dma-ranges = <0x00 0xc0000000 0x00 0x00 0x40000000>;
phandle = <0xe5>;
phandle = <0xe6>;
mmc@7e340000 {
compatible = "brcm,bcm2711-emmc2";
@@ -2076,8 +2085,8 @@
};
};
arm-pmu {
compatible = "arm,cortex-a72-pmu", "arm,armv8-pmuv3", "arm,cortex-a7-pmu";
pmu {
compatible = "arm,cortex-a72-pmu";
interrupts = <0x00 0x10 0x04 0x00 0x11 0x04 0x00 0x12 0x04 0x00 0x13 0x04>;
interrupt-affinity = <0x39 0x3a 0x3b 0x3c>;
};
@@ -2085,14 +2094,13 @@
timer {
compatible = "arm,armv8-timer";
interrupts = <0x01 0x0d 0xf08 0x01 0x0e 0xf08 0x01 0x0b 0xf08 0x01 0x0a 0xf08>;
arm,cpu-registers-not-fw-configured;
};
cpus {
#address-cells = <0x01>;
#size-cells = <0x00>;
enable-method = "brcm,bcm2836-smp";
phandle = <0xe6>;
phandle = <0xe7>;
cpu@0 {
device_type = "cpu";
@@ -2173,9 +2181,9 @@
compatible = "simple-bus";
#address-cells = <0x02>;
#size-cells = <0x02>;
ranges = <0x00 0x7c000000 0x00 0xfc000000 0x00 0x3800000 0x00 0x40000000 0x00 0xff800000 0x00 0x800000 0x06 0x00 0x06 0x00 0x00 0x80000000 0x00 0x00 0x00 0x00 0x00 0xfc000000>;
ranges = <0x00 0x7c000000 0x00 0xfc000000 0x00 0x3800000 0x00 0x40000000 0x00 0xff800000 0x00 0x800000 0x06 0x00 0x06 0x00 0x00 0x40000000 0x00 0x00 0x00 0x00 0x00 0xfc000000>;
dma-ranges = <0x04 0x7c000000 0x00 0xfc000000 0x00 0x3800000 0x00 0x00 0x00 0x00 0x04 0x00>;
phandle = <0xe7>;
phandle = <0xe8>;
pcie@7d500000 {
compatible = "brcm,bcm2711-pcie";
@@ -2190,7 +2198,7 @@
interrupt-map = <0x00 0x00 0x00 0x01 0x01 0x00 0x8f 0x04 0x00 0x00 0x00 0x02 0x01 0x00 0x90 0x04 0x00 0x00 0x00 0x03 0x01 0x00 0x91 0x04 0x00 0x00 0x00 0x04 0x01 0x00 0x92 0x04>;
msi-controller;
msi-parent = <0x3e>;
ranges = <0x2000000 0x00 0x80000000 0x06 0x00 0x00 0x80000000>;
ranges = <0x2000000 0x00 0xc0000000 0x06 0x00 0x00 0x40000000>;
dma-ranges = <0x2000000 0x00 0x00 0x00 0x00 0x00 0xc0000000>;
brcm,enable-ssc;
phandle = <0x3e>;
@@ -2218,7 +2226,7 @@
status = "okay";
phy-handle = <0x40>;
phy-mode = "rgmii-rxid";
phandle = <0xe8>;
phandle = <0xe9>;
mdio@e14 {
compatible = "brcm,genet-mdio-v5";
@@ -2226,7 +2234,7 @@
reg-names = "mdio";
#address-cells = <0x01>;
#size-cells = <0x00>;
phandle = <0xe9>;
phandle = <0xea>;
ethernet-phy@1 {
reg = <0x01>;
@@ -2236,6 +2244,15 @@
};
};
codec@7eb10000 {
compatible = "brcm,bcm2711-hevc-dec", "raspberrypi,hevc-dec";
reg = <0x00 0x7eb00000 0x00 0x10000 0x00 0x7eb10000 0x00 0x1000>;
reg-names = "hevc", "intc";
interrupts = <0x00 0x62 0x04>;
clocks = <0x14 0x0b>;
phandle = <0xeb>;
};
dma@7e007b00 {
compatible = "brcm,bcm2711-dma";
reg = <0x00 0x7e007b00 0x00 0x400>;
@@ -2252,33 +2269,41 @@
reg = <0x00 0x7e9c0000 0x00 0x100000>;
interrupts = <0x00 0xb0 0x04>;
power-domains = <0x10 0x06>;
phandle = <0xea>;
};
codec@7eb10000 {
compatible = "raspberrypi,rpivid-vid-decoder";
reg = <0x00 0x7eb10000 0x00 0x1000 0x00 0x7eb00000 0x00 0x10000>;
reg-names = "intc", "hevc";
interrupts = <0x00 0x62 0x04>;
clocks = <0x14 0x0b>;
clock-names = "hevc";
phandle = <0xec>;
};
};
cam1_regulator {
compatible = "regulator-fixed";
regulator-name = "cam1-reg";
enable-active-high;
status = "okay";
gpio = <0x0b 0x05 0x00>;
phandle = <0x53>;
memory@0 {
device_type = "memory";
reg = <0x00 0x00 0x00>;
};
leds {
compatible = "gpio-leds";
phandle = <0xed>;
led-act {
label = "ACT";
default-state = "off";
linux,default-trigger = "mmc0";
gpios = <0x07 0x2a 0x00>;
phandle = <0x5a>;
};
led-pwr {
label = "PWR";
gpios = <0x0b 0x02 0x01>;
default-state = "off";
linux,default-trigger = "default-on";
phandle = <0x5b>;
};
};
cam1_clk {
compatible = "fixed-clock";
#clock-cells = <0x00>;
status = "disabled";
phandle = <0xeb>;
phandle = <0xee>;
};
cam0_regulator {
@@ -2286,14 +2311,14 @@
regulator-name = "cam0-reg";
enable-active-high;
status = "disabled";
phandle = <0xec>;
phandle = <0xef>;
};
cam0_clk {
compatible = "fixed-clock";
#clock-cells = <0x00>;
status = "disabled";
phandle = <0xed>;
phandle = <0xf0>;
};
cam_dummy_reg {
@@ -2316,17 +2341,17 @@
uart1 = "", "", "", "Dstatus";
i2s = "", "", "", "Estatus";
spi = "", "", "", "Fstatus";
i2c0 = "", "", "", "2status", "", "", "", "Gstatus";
i2c0 = "", "", "", "4status", "", "", "", "Gstatus";
i2c1 = "", "", "", "Hstatus";
i2c = "", "", "", "Hstatus";
i2c_arm = "", "", "", "Hstatus";
i2c_vc = "", "", "", "2status", "", "", "", "Gstatus";
i2c0_baudrate = "", "", "", "2clock-frequency:0";
i2c_vc = "", "", "", "4status", "", "", "", "Gstatus";
i2c0_baudrate = "", "", "", "4clock-frequency:0";
i2c1_baudrate = "", "", "", "Hclock-frequency:0";
i2c_baudrate = "", "", "", "Hclock-frequency:0";
i2c_arm_baudrate = "", "", "", "Hclock-frequency:0";
i2c_vc_baudrate = "", "", "", "2clock-frequency:0";
watchdog = "", "", "", "Istatus";
i2c_vc_baudrate = "", "", "", "4clock-frequency:0";
watchdog = "", "", "", "Iearly-watchdog?";
random = "", "", "", "Jstatus";
sd_overclock = "", "", "", "Kbrcm,overclock-50:0";
sd_force_pio = "", "", "", "Kbrcm,force-pio?";
@@ -2365,32 +2390,15 @@
pwr_led_trigger = "", "", "", "[linux,default-trigger";
eth_led0 = "", "", "", "@led-modes:0";
eth_led1 = "", "", "", "@led-modes:4";
eth_max_speed = "", "", "", "@max-speed:0";
};
memory@0 {
device_type = "memory";
reg = <0x00 0x00 0x00>;
};
leds {
compatible = "gpio-leds";
phandle = <0xee>;
led-act {
label = "ACT";
default-state = "off";
linux,default-trigger = "mmc0";
gpios = <0x07 0x2a 0x00>;
phandle = <0x5a>;
};
led-pwr {
label = "PWR";
gpios = <0x0b 0x02 0x01>;
default-state = "off";
linux,default-trigger = "default-on";
phandle = <0x5b>;
};
regulator-cam1 {
compatible = "regulator-fixed";
regulator-name = "cam1-reg";
enable-active-high;
gpio = <0x0b 0x05 0x00>;
phandle = <0x53>;
};
regulator-sd-io-1v8 {
@@ -2424,7 +2432,7 @@
regulator-max-microvolt = <0x325aa0>;
regulator-min-microvolt = <0x325aa0>;
regulator-name = "3v3";
phandle = <0xef>;
phandle = <0xf1>;
};
fixedregulator_5v0 {
@@ -2433,7 +2441,7 @@
regulator-max-microvolt = <0x4c4b40>;
regulator-min-microvolt = <0x4c4b40>;
regulator-name = "5v0";
phandle = <0xf0>;
phandle = <0xf2>;
};
zone_dma {
@@ -2448,7 +2456,7 @@
#size-cells = <0x02>;
ranges = <0x7c500000 0x00 0xfc500000 0x00 0x3300000 0x40000000 0x00 0xff800000 0x00 0x800000>;
dma-ranges = <0x00 0x00 0x00 0x04 0x00>;
phandle = <0xf1>;
phandle = <0xf3>;
v3d@7ec04000 {
compatible = "brcm,2711-v3d";
@@ -2460,7 +2468,7 @@
clocks-names = "v3d";
interrupts = <0x00 0x4a 0x04>;
status = "disabled";
phandle = <0xf2>;
phandle = <0xf4>;
};
};
@@ -2561,6 +2569,8 @@
uart4_ctsrts_gpio10 = "/soc/gpio@7e200000/uart4-ctsrts-gpio10";
uart5_gpio12 = "/soc/gpio@7e200000/uart5-gpio12";
uart5_ctsrts_gpio14 = "/soc/gpio@7e200000/uart5-ctsrts-gpio14";
gpioout = "/soc/gpio@7e200000/gpioout";
alt0 = "/soc/gpio@7e200000/alt0";
dpi_18bit_cpadhi_gpio0 = "/soc/gpio@7e200000/dpi_18bit_cpadhi_gpio0";
dpi_18bit_cpadhi_gpio2 = "/soc/gpio@7e200000/dpi_18bit_cpadhi_gpio2";
dpi_18bit_gpio0 = "/soc/gpio@7e200000/dpi_18bit_gpio0";
@@ -2569,8 +2579,6 @@
dpi_16bit_gpio2 = "/soc/gpio@7e200000/dpi_16bit_gpio2";
dpi_16bit_cpadhi_gpio0 = "/soc/gpio@7e200000/dpi_16bit_cpadhi_gpio0";
dpi_16bit_cpadhi_gpio2 = "/soc/gpio@7e200000/dpi_16bit_cpadhi_gpio2";
gpioout = "/soc/gpio@7e200000/gpioout";
alt0 = "/soc/gpio@7e200000/alt0";
spi0_pins = "/soc/gpio@7e200000/spi0_pins";
spi0_cs_pins = "/soc/gpio@7e200000/spi0_cs_pins";
spi3_pins = "/soc/gpio@7e200000/spi3_pins";
@@ -2621,6 +2629,8 @@
sdhci = "/soc/mmc@7e300000";
hvs = "/soc/hvs@7e400000";
dsi1 = "/soc/dsi@7e700000";
csi0 = "/soc/csi@7e800000";
csi1 = "/soc/csi@7e801000";
i2c_arm = "/soc/i2c@7e804000";
i2c1 = "/soc/i2c@7e804000";
usb = "/soc/usb@7e980000";
@@ -2657,17 +2667,6 @@
ddc0 = "/soc/i2c@7ef04500";
hdmi1 = "/soc/hdmi@7ef05700";
ddc1 = "/soc/i2c@7ef09500";
mmcnr = "/soc/mmcnr@7e300000";
firmwarekms = "/soc/firmwarekms@7e600000";
smi = "/soc/smi@7e600000";
csi0 = "/soc/csi@7e800000";
csi1 = "/soc/csi@7e801000";
axiperf = "/soc/axiperf";
i2c0mux = "/soc/i2c0mux";
i2c_csi_dsi0 = "/soc/i2c0mux/i2c@0";
i2c_vc = "/soc/i2c0mux/i2c@0";
i2c0 = "/soc/i2c0mux/i2c@0";
i2c_csi_dsi = "/soc/i2c0mux/i2c@1";
firmware = "/soc/firmware";
firmware_clocks = "/soc/firmware/clocks";
expgpio = "/soc/firmware/gpio";
@@ -2675,6 +2674,16 @@
vcio = "/soc/firmware/vcio";
power = "/soc/power";
vchiq = "/soc/mailbox@7e00b840";
mmcnr = "/soc/mmcnr@7e300000";
brcmf = "/soc/mmcnr@7e300000/wifi@1";
firmwarekms = "/soc/firmwarekms@7e600000";
smi = "/soc/smi@7e600000";
axiperf = "/soc/axiperf";
i2c0mux = "/soc/i2c0mux";
i2c_csi_dsi0 = "/soc/i2c0mux/i2c@0";
i2c_vc = "/soc/i2c0mux/i2c@0";
i2c0 = "/soc/i2c0mux/i2c@0";
i2c_csi_dsi = "/soc/i2c0mux/i2c@1";
fb = "/soc/fb";
sound = "/soc/sound";
nvmem_otp = "/soc/nvmem/nvmem_otp";
@@ -2699,17 +2708,18 @@
genet = "/scb/ethernet@7d580000";
genet_mdio = "/scb/ethernet@7d580000/mdio@e14";
phy1 = "/scb/ethernet@7d580000/mdio@e14/ethernet-phy@1";
hevc_dec = "/scb/codec@7eb10000";
dma40 = "/scb/dma@7e007b00";
xhci = "/scb/xhci@7e9c0000";
cam1_reg = "/cam1_regulator";
leds = "/leds";
led_act = "/leds/led-act";
led_pwr = "/leds/led-pwr";
cam1_clk = "/cam1_clk";
cam0_regulator = "/cam0_regulator";
cam0_clk = "/cam0_clk";
cam0_reg = "/cam_dummy_reg";
cam_dummy_reg = "/cam_dummy_reg";
leds = "/leds";
led_act = "/leds/led-act";
led_pwr = "/leds/led-pwr";
cam1_reg = "/regulator-cam1";
sd_io_1v8_reg = "/regulator-sd-io-1v8";
sd_vcc_reg = "/regulator-sd-vcc";
vdd_3v3_reg = "/fixedregulator_3v3";
+1 -1
View File
@@ -6,7 +6,7 @@
"llvm-target": "riscv64",
"data-layout": "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128",
"max-atomic-width": 64,
"target-pointer-width": "64",
"target-pointer-width": 64,
"features": "+m,+a,+c",
"disable-redzone": true,
+1 -1
View File
@@ -7,7 +7,7 @@
"llvm-target": "x86_64-unknown-linux-gnu",
"data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128",
"max-atomic-width": 64,
"target-pointer-width": "64",
"target-pointer-width": 64,
"features": "-avx,-sse,-avx2,+soft-float",
"disable-redzone": true,
+11 -1
View File
@@ -1,7 +1,7 @@
[package]
name = "yggdrasil-kernel"
version = "0.1.0"
edition = "2021"
edition = "2024"
build = "build.rs"
authors = ["Mark Poliakov <mark@alnyan.me>"]
@@ -35,6 +35,7 @@ ygg_driver_ahci = { path = "driver/block/ahci" }
ygg_driver_input = { path = "driver/input" }
ygg_driver_usb_xhci.path = "driver/usb/xhci"
ygg_driver_net_rtl81xx.path = "driver/net/rtl81xx"
ygg_driver_serial_8250.path = "driver/serial/uart8250"
memfs = { path = "driver/fs/memfs" }
ext2 = { path = "driver/fs/ext2" }
@@ -56,11 +57,16 @@ 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"
[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"]
+1 -1
View File
@@ -1,7 +1,7 @@
[package]
name = "kernel-arch"
version = "0.1.0"
edition = "2021"
edition = "2024"
[target.'cfg(all(target_os = "none", target_arch = "x86_64"))'.dependencies]
kernel-arch-x86_64.path = "x86_64"
+1 -1
View File
@@ -1,7 +1,7 @@
[package]
name = "kernel-arch-aarch64"
version = "0.1.0"
edition = "2021"
edition = "2024"
[dependencies]
yggdrasil-abi.workspace = true
+21 -16
View File
@@ -66,7 +66,7 @@ impl FpContext {
///
/// It is up to the caller to ensure `this` is a valid pointer to store the FPU context in.
pub unsafe fn store(this: *mut Self) {
__aarch64_fp_store_context(this as _)
unsafe { __aarch64_fp_store_context(this as _) }
}
/// Loads the FPU with the context stored in `this` pointer.
@@ -75,7 +75,7 @@ impl FpContext {
///
/// It is up to the caller to ensure `this` is a valid pointer to load the FPU context from.
pub unsafe fn restore(this: *const Self) {
__aarch64_fp_restore_context(this as _)
unsafe { __aarch64_fp_restore_context(this as _) }
}
}
@@ -177,7 +177,12 @@ impl<K: KernelTableManager, PA: PhysicalMemoryAllocator<Address = PhysicalAddres
stack.push(entry as _);
stack.push(arg);
setup_common_context(&mut stack, __aarch64_task_enter_kernel as _, 0, 0);
setup_common_context(
&mut stack,
(__aarch64_task_enter_kernel as *const ()).addr(),
0,
0,
);
let sp = stack.build();
@@ -212,7 +217,7 @@ impl<K: KernelTableManager, PA: PhysicalMemoryAllocator<Address = PhysicalAddres
setup_common_context(
&mut stack,
__aarch64_task_enter_user as _,
(__aarch64_task_enter_user as *const ()).addr(),
ttbr0,
context.thread_pointer as _,
);
@@ -231,9 +236,8 @@ impl<K: KernelTableManager, PA: PhysicalMemoryAllocator<Address = PhysicalAddres
}
unsafe fn enter(&self) -> ! {
FpContext::restore(self.fp_context.get());
__aarch64_enter_task(self.inner.get())
unsafe { FpContext::restore(self.fp_context.get()) };
unsafe { __aarch64_enter_task(self.inner.get()) }
}
unsafe fn switch(&self, from: &Self) {
@@ -241,19 +245,20 @@ impl<K: KernelTableManager, PA: PhysicalMemoryAllocator<Address = PhysicalAddres
let src = from.inner.get();
if dst != src {
// Save the old context
FpContext::store(from.fp_context.get());
// Load next context
FpContext::restore(self.fp_context.get());
unsafe {
// Save the old context
FpContext::store(from.fp_context.get());
// Load next context
FpContext::restore(self.fp_context.get());
__aarch64_switch_task(self.inner.get(), from.inner.get())
__aarch64_switch_task(self.inner.get(), from.inner.get())
}
}
}
unsafe fn switch_and_drop(&self, thread: *const ()) {
FpContext::restore(self.fp_context.get());
__aarch64_switch_task_and_drop(self.inner.get(), thread);
unsafe { FpContext::restore(self.fp_context.get()) };
unsafe { __aarch64_switch_task_and_drop(self.inner.get(), thread) };
}
fn set_thread_pointer(&self, _tp: usize) {
@@ -293,7 +298,7 @@ fn setup_common_context(builder: &mut StackBuilder, entry: usize, ttbr0: u64, tp
builder.push(0); // x19
}
extern "C" {
unsafe extern "C" {
fn __aarch64_enter_task(to: *mut TaskContextInner) -> !;
fn __aarch64_switch_task(to: *mut TaskContextInner, from: *mut TaskContextInner);
fn __aarch64_switch_task_and_drop(to: *mut TaskContextInner, thread: *const ()) -> !;
+3 -3
View File
@@ -13,11 +13,11 @@ use aarch64_cpu::{
use alloc::{boxed::Box, sync::Arc, vec::Vec};
use device_api::interrupt::LocalInterruptController;
use kernel_arch_interface::{
Architecture,
cpu::{CpuData, CpuImpl, IpiQueue},
guard::IrqGuard,
task::Scheduler,
util::OneTimeInit,
Architecture,
};
use tock_registers::interfaces::{ReadWriteable, Readable, Writeable};
@@ -25,7 +25,7 @@ pub mod context;
pub mod mem;
pub use context::TaskContextImpl;
pub use mem::{process::ProcessAddressSpaceImpl, KernelTableManagerImpl};
pub use mem::{KernelTableManagerImpl, process::ProcessAddressSpaceImpl};
pub struct ArchitectureImpl;
@@ -101,7 +101,7 @@ impl Architecture for ArchitectureImpl {
let id = (MPIDR_EL1.get() & 0xFF) as u32;
let cpu = Box::leak(Box::new(CpuImpl::<Self, S>::new(id, data)));
cpu.set_local();
unsafe { cpu.set_local() };
}
fn local_cpu() -> *mut () {
+19 -15
View File
@@ -1,7 +1,7 @@
use core::ops::Range;
use aarch64_cpu::registers::{TTBR0_EL1, TTBR1_EL1};
use kernel_arch_interface::{mem::DeviceMemoryAttributes, KERNEL_VIRT_OFFSET};
use kernel_arch_interface::{KERNEL_VIRT_OFFSET, mem::DeviceMemoryAttributes};
use libk_mm_interface::{
address::PhysicalAddress,
device::{DevicePageManager, DevicePageTableLevel},
@@ -10,7 +10,7 @@ use libk_mm_interface::{
use crate::mem::{
auto_lower_address,
table::{PageAttributes, PageEntry, PageTable, L1, L2, L3},
table::{L1, L2, L3, PageAttributes, PageEntry, PageTable},
tlb_flush_range_va,
};
@@ -98,22 +98,26 @@ impl DevicePageTableLevel for L3DeviceMemory {
}
pub unsafe fn setup() {
// 0..IDENTITY_SIZE_L1 -> lower RAM region
for i in 0..IDENTITY_SIZE_L1 {
let phys = PhysicalAddress::from_usize(i << L1::SHIFT);
KERNEL_L1[i] = PageEntry::normal_block(phys, PageAttributes::empty());
}
unsafe {
// 0..IDENTITY_SIZE_L1 -> lower RAM region
for i in 0..IDENTITY_SIZE_L1 {
let phys = PhysicalAddress::from_usize(i << L1::SHIFT);
KERNEL_L1[i] = PageEntry::normal_block(phys, PageAttributes::empty());
}
// DEVICE_L1 -> Device L2 table
// 0..DEVICE_MAPPING_L3_COUNT -> Device L3 tables -> Device L3 pages
// ..512 -> Device L2 pages
for i in 0..DEVICE_MAPPING_L3_COUNT {
// DEVICE_L1 -> Device L2 table
// 0..DEVICE_MAPPING_L3_COUNT -> Device L3 tables -> Device L3 pages
// ..512 -> Device L2 pages
for i in 0..DEVICE_MAPPING_L3_COUNT {
let phys = PhysicalAddress::from_usize(auto_lower_address(
&raw const DEVICE_MEMORY.normal.0[i],
));
DEVICE_MEMORY.large.0[i] = PageEntry::table(phys, PageAttributes::empty());
}
let phys =
PhysicalAddress::from_usize(auto_lower_address(&raw const DEVICE_MEMORY.normal.0[i]));
DEVICE_MEMORY.large.0[i] = PageEntry::table(phys, PageAttributes::empty());
PhysicalAddress::from_usize(auto_lower_address(&raw const DEVICE_MEMORY.large.0));
KERNEL_L1[DEVICE_L1] = PageEntry::table(phys, PageAttributes::empty());
}
let phys = PhysicalAddress::from_usize(auto_lower_address(&raw const DEVICE_MEMORY.large.0));
KERNEL_L1[DEVICE_L1] = PageEntry::table(phys, PageAttributes::empty());
}
pub unsafe fn load() {
+20 -12
View File
@@ -4,9 +4,9 @@ use aarch64_cpu::{
registers::{MAIR_EL1, SCTLR_EL1, TCR_EL1},
};
use kernel_arch_interface::{
KERNEL_VIRT_OFFSET,
mem::{DeviceMemoryAttributes, KernelTableManager, RawDeviceMemoryMapping},
sync::IrqSafeSpinlock,
KERNEL_VIRT_OFFSET,
};
use libk_mm_interface::{address::PhysicalAddress, table::EntryLevel};
use tock_registers::interfaces::{ReadWriteable, Writeable};
@@ -14,7 +14,7 @@ use yggdrasil_abi::error::Error;
pub use intrinsics::*;
use crate::{mem::table::L1, ArchitectureImpl};
use crate::{ArchitectureImpl, mem::table::L1};
pub mod fixed;
pub mod intrinsics;
@@ -52,14 +52,18 @@ impl KernelTableManager for KernelTableManagerImpl {
attrs: DeviceMemoryAttributes,
) -> Result<RawDeviceMemoryMapping<Self>, Error> {
let _lock = KERNEL_MEMORY_LOCK.lock();
#[allow(static_mut_refs)]
fixed::DEVICE_MEMORY.map_device_pages(PhysicalAddress::from_u64(base), count, attrs)
unsafe {
#[allow(static_mut_refs)]
fixed::DEVICE_MEMORY.map_device_pages(PhysicalAddress::from_u64(base), count, attrs)
}
}
unsafe fn unmap_device_pages(mapping: &RawDeviceMemoryMapping<Self>) {
let _lock = KERNEL_MEMORY_LOCK.lock();
#[allow(static_mut_refs)]
fixed::DEVICE_MEMORY.unmap_device_pages(mapping);
unsafe {
#[allow(static_mut_refs)]
fixed::DEVICE_MEMORY.unmap_device_pages(mapping);
}
}
}
@@ -132,15 +136,19 @@ unsafe fn enable_mmu() {
SCTLR_EL1.modify(SCTLR_EL1::M::Enable);
// Enable caches
enable_icache();
enable_dcache();
unsafe {
enable_icache();
enable_dcache();
}
}
pub unsafe fn init_lower(bsp: bool) {
setup_memory_attributes();
if bsp {
fixed::setup();
unsafe {
if bsp {
fixed::setup();
}
fixed::load();
enable_mmu();
}
fixed::load();
enable_mmu();
}
+6 -4
View File
@@ -14,11 +14,11 @@ use libk_mm_interface::{
};
use yggdrasil_abi::error::Error;
use crate::{mem::table::PageEntry, KernelTableManagerImpl};
use crate::{KernelTableManagerImpl, mem::table::PageEntry};
use super::{
dc_cvac, ic_iallu,
table::{PageAttributes, PageTable, L1, L2, L3},
table::{L1, L2, L3, PageAttributes, PageTable},
tlb_flush_asid, tlb_flush_vaae1,
};
@@ -97,8 +97,10 @@ impl<TA: TableAllocator> ProcessAddressSpaceManager<TA> for ProcessAddressSpaceI
}
unsafe fn clear(&mut self) {
self.l1
.drop_range::<TA>(0..((Self::UPPER_LIMIT_PFN * L3::SIZE).page_index::<L1>()));
unsafe {
self.l1
.drop_range::<TA>(0..((Self::UPPER_LIMIT_PFN * L3::SIZE).page_index::<L1>()))
};
}
}
+41 -14
View File
@@ -22,7 +22,7 @@ use crate::KernelTableManagerImpl;
use super::dc_cvac;
bitflags! {
#[derive(Clone, Copy, PartialEq, Eq)]
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct PageAttributes: u64 {
const PRESENT = 1 << 0;
@@ -71,8 +71,8 @@ pub struct L3;
#[derive(Debug, Clone, Copy)]
pub enum EntryType {
Table(PhysicalAddress),
Page(PhysicalAddress),
Table(PageAttributes, PhysicalAddress),
Page(PageAttributes, PhysicalAddress),
Invalid,
}
@@ -103,8 +103,8 @@ impl<L: EntryLevel> PageTable<L> {
}
}
pub fn new_zeroed<'a, TA: TableAllocator>(
) -> Result<PhysicalRefMut<'a, Self, KernelTableManagerImpl>, Error> {
pub fn new_zeroed<'a, TA: TableAllocator>()
-> Result<PhysicalRefMut<'a, Self, KernelTableManagerImpl>, Error> {
let physical = TA::allocate_page_table()?;
let mut table =
unsafe { PhysicalRefMut::<'a, Self, KernelTableManagerImpl>::map(physical) };
@@ -132,7 +132,7 @@ impl<L: EntryLevel> PageTable<L> {
return None;
}
let inner = PhysicalRefMut::map(physical);
let inner = unsafe { PhysicalRefMut::map(physical) };
Some(inner)
}
}
@@ -234,12 +234,16 @@ where
let entry = self[index];
if let Some(table) = entry.as_table() {
let mut table_ref: PhysicalRefMut<PageTable<L::NextLevel>, KernelTableManagerImpl> =
PhysicalRefMut::map(table);
unsafe {
let mut table_ref: PhysicalRefMut<
PageTable<L::NextLevel>,
KernelTableManagerImpl,
> = PhysicalRefMut::map(table);
table_ref.drop_all::<TA>();
table_ref.drop_all::<TA>();
TA::free_page_table(table);
TA::free_page_table(table);
}
} else if entry.is_present() {
// Memory must've been cleared beforehand, so no non-table entries must be present
panic!(
@@ -307,9 +311,14 @@ impl<L: NonTerminalEntryLevel> PageEntry<L> {
if !self.is_present() {
EntryType::Invalid
} else if let Some(table) = self.as_table() {
EntryType::Table(table)
let attributes = self.attributes();
EntryType::Table(attributes, table)
} else {
EntryType::Page(PhysicalAddress::from_u64(self.0 & !Self::ATTR_MASK))
let attributes = self.attributes();
EntryType::Page(
attributes,
PhysicalAddress::from_u64(self.0 & !Self::ATTR_MASK),
)
}
}
}
@@ -426,8 +435,26 @@ impl From<PageAttributes> for MapAttributes {
impl fmt::Display for EntryType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Table(address) => write!(f, "table @ {address:#x}"),
Self::Page(address) => write!(f, "page @ {address:#x}"),
&Self::Table(attrs, address) => {
let mask = match attrs & PageAttributes::AP_ACCESS_MASK {
PageAttributes::AP_BOTH_READONLY => "r- r-",
PageAttributes::AP_BOTH_READWRITE => "rw rw",
PageAttributes::AP_KERNEL_READONLY => "r- --",
PageAttributes::AP_KERNEL_READWRITE => "rw --",
_ => unreachable!(),
};
write!(f, "table @ {address:#010x} {mask}")
}
&Self::Page(attrs, address) => {
let mask = match attrs & PageAttributes::AP_ACCESS_MASK {
PageAttributes::AP_BOTH_READONLY => "r- r-",
PageAttributes::AP_BOTH_READWRITE => "rw rw",
PageAttributes::AP_KERNEL_READONLY => "r- --",
PageAttributes::AP_KERNEL_READWRITE => "rw --",
_ => unreachable!(),
};
write!(f, "page @ {address:#010x} {mask}")
}
Self::Invalid => f.write_str("<invalid>"),
}
}
+10 -2
View File
@@ -7,16 +7,16 @@ use std::{
use device_api::dma::{DmaAllocation, DmaAllocator};
use kernel_arch_interface::{
Architecture,
cpu::{CpuData, IpiQueue},
mem::{
DeviceMemoryAttributes, KernelTableManager, PhysicalMemoryAllocator, RawDeviceMemoryMapping,
},
task::{Scheduler, TaskContext, UserContextInfo},
Architecture,
};
use libk_mm_interface::{
address::PhysicalAddress,
process::ProcessAddressSpaceManager,
process::{PageAttributeUpdate, ProcessAddressSpaceManager},
table::{MapAttributes, TableAllocator},
};
use yggdrasil_abi::{
@@ -164,6 +164,14 @@ impl<TA: TableAllocator> ProcessAddressSpaceManager<TA> for ProcessAddressSpaceI
unimplemented!()
}
unsafe fn update_page_attributes(
&mut self,
_address: usize,
_update: &PageAttributeUpdate,
) -> Result<(), Error> {
unimplemented!()
}
fn translate(&self, _address: usize) -> Result<(PhysicalAddress, MapAttributes), Error> {
unimplemented!()
}
+1 -1
View File
@@ -7,7 +7,7 @@ use alloc::vec::Vec;
use device_api::interrupt::IpiMessage;
use crate::{
guard::IrqGuard, sync::IrqSafeSpinlock, task::Scheduler, util::OneTimeInit, Architecture,
Architecture, guard::IrqGuard, sync::IrqSafeSpinlock, task::Scheduler, util::OneTimeInit,
};
#[repr(C, align(0x10))]
+1 -1
View File
@@ -6,7 +6,7 @@ use core::{
sync::atomic::{AtomicBool, Ordering},
};
use crate::{guard::IrqGuard, Architecture};
use crate::{Architecture, guard::IrqGuard};
pub struct Spinlock<A: Architecture, T> {
value: UnsafeCell<T>,
+4 -4
View File
@@ -1,9 +1,9 @@
use core::{arch::global_asm, cell::UnsafeCell, marker::PhantomData};
use kernel_arch_interface::{
Architecture,
mem::{KernelTableManager, PhysicalMemoryAllocator},
task::{StackBuilder, TaskContext, UserContextInfo},
Architecture,
};
use libk_mm_interface::address::PhysicalAddress;
use tock_registers::{
@@ -13,9 +13,9 @@ use tock_registers::{
use yggdrasil_abi::error::Error;
use crate::{
ArchitectureImpl, PerCpuData,
mem::{self},
registers::SATP,
ArchitectureImpl, PerCpuData,
};
pub const CONTEXT_SIZE: usize = 14 * size_of::<usize>();
@@ -86,7 +86,7 @@ impl<K: KernelTableManager, PA: PhysicalMemoryAllocator<Address = PhysicalAddres
stack.push(context.entry);
stack.push(context.argument);
setup_common_context(&mut stack, __rv64_task_enter_user as _);
setup_common_context(&mut stack, (__rv64_task_enter_user as *const ()).addr());
let sp = stack.build();
let satp = InMemoryRegister::new(0);
@@ -118,7 +118,7 @@ impl<K: KernelTableManager, PA: PhysicalMemoryAllocator<Address = PhysicalAddres
stack.push(entry as _);
stack.push(arg);
setup_common_context(&mut stack, __rv64_task_enter_kernel as _);
setup_common_context(&mut stack, (__rv64_task_enter_kernel as *const ()).addr());
let sp = stack.build();
+8 -2
View File
@@ -11,18 +11,18 @@ use core::{
use alloc::{boxed::Box, collections::btree_map::BTreeMap, vec::Vec};
use device_api::interrupt::LocalInterruptController;
use kernel_arch_interface::{
Architecture,
cpu::{CpuData, CpuImpl, IpiQueue},
sync::IrqSafeSpinlock,
task::Scheduler,
util::OneTimeInit,
Architecture,
};
use tock_registers::interfaces::{ReadWriteable, Readable};
use registers::SSTATUS;
pub mod mem;
pub use mem::{process::ProcessAddressSpaceImpl, KernelTableManagerImpl};
pub use mem::{KernelTableManagerImpl, process::ProcessAddressSpaceImpl};
pub mod context;
pub use context::TaskContextImpl;
pub mod intrinsics;
@@ -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");
+4 -5
View File
@@ -2,12 +2,11 @@ use kernel_arch_interface::sync::IrqSafeSpinlock;
use libk_mm_interface::{address::PhysicalAddress, table::EntryLevel};
use crate::{
mem::{
auto_lower_address,
table::{PageEntry, PageTable, L1},
KERNEL_VIRT_OFFSET,
},
ArchitectureImpl,
mem::{
KERNEL_VIRT_OFFSET, auto_lower_address,
table::{L1, PageEntry, PageTable},
},
};
pub const IDENTITY_SIZE_L1: usize = 64;
+2 -2
View File
@@ -3,13 +3,13 @@ use kernel_arch_interface::mem::{
};
use libk_mm_interface::{
address::PhysicalAddress,
table::{page_index, EntryLevel, EntryLevelExt},
table::{EntryLevel, EntryLevelExt, page_index},
};
use tock_registers::interfaces::Writeable;
use yggdrasil_abi::error::Error;
use crate::{
mem::table::{PageTable, L1, L3},
mem::table::{L1, L3, PageTable},
registers::SATP,
};
+1 -1
View File
@@ -19,8 +19,8 @@ use crate::mem::{
};
use super::{
table::{DroppableRange, PageTable, L1, L2, L3},
KernelTableManagerImpl, USER_BOUNDARY,
table::{DroppableRange, L1, L2, L3, PageTable},
};
pub struct ProcessAddressSpaceImpl<TA: TableAllocator> {
+4 -4
View File
@@ -10,8 +10,8 @@ use libk_mm_interface::{
pointer::{PhysicalRef, PhysicalRefMut},
process::PageAttributeUpdate,
table::{
page_index, EntryLevel, EntryLevelDrop, NextPageTable, NonTerminalEntryLevel,
TableAllocator,
EntryLevel, EntryLevelDrop, NextPageTable, NonTerminalEntryLevel, TableAllocator,
page_index,
},
};
use yggdrasil_abi::error::Error;
@@ -109,8 +109,8 @@ impl<L: EntryLevel> PageTable<L> {
}
}
pub fn new_zeroed<'a, TA: TableAllocator>(
) -> Result<PhysicalRefMut<'a, PageTable<L>, KernelTableManagerImpl>, Error> {
pub fn new_zeroed<'a, TA: TableAllocator>()
-> Result<PhysicalRefMut<'a, PageTable<L>, KernelTableManagerImpl>, Error> {
let physical = TA::allocate_page_table()?;
let mut table =
unsafe { PhysicalRefMut::<'a, Self, KernelTableManagerImpl>::map(physical) };
+9 -5
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 {
@@ -76,11 +78,7 @@ unsafe fn sbi_do_call(
);
}
let a0 = a0 as i64;
if a0 == 0 {
Ok(a1)
} else {
Err(a0.into())
}
if a0 == 0 { Ok(a1) } else { Err(a0.into()) }
}
pub fn sbi_hart_start(hart_id: u64, start_addr: u64, opaque: u64) -> Result<(), Error> {
@@ -108,3 +106,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!()
}
+1 -1
View File
@@ -35,7 +35,7 @@ cfg_if! {
pub use imp::{ArchitectureImpl, KernelTableManagerImpl, ProcessAddressSpaceImpl, TaskContextImpl};
pub use kernel_arch_interface::{guard, mem, sync, task, util, Architecture, KERNEL_VIRT_OFFSET};
pub use kernel_arch_interface::{Architecture, KERNEL_VIRT_OFFSET, guard, mem, sync, task, util};
pub type CpuImpl<S> = kernel_arch_interface::cpu::CpuImpl<ArchitectureImpl, S>;
pub type LocalCpuImpl<'a, S> = kernel_arch_interface::cpu::LocalCpuImpl<'a, ArchitectureImpl, S>;
+1 -1
View File
@@ -1,4 +1,4 @@
#![feature(iter_chain, new_zeroed_alloc, box_as_ptr)]
#![feature(box_as_ptr)]
#![allow(clippy::new_without_default)]
#![no_std]
+4 -4
View File
@@ -4,14 +4,14 @@ use kernel_arch_interface::{
mem::{KernelTableManager, PhysicalMemoryAllocator},
task::{ForkFrame, StackBuilder, TaskContext, TaskFrame, UserContextInfo},
};
use kernel_arch_x86::registers::{FpuContext, CR3, MSR_IA32_FS_BASE};
use kernel_arch_x86::registers::{CR3, FpuContext, MSR_IA32_FS_BASE};
use libk_mm_interface::address::PhysicalAddress;
use tock_registers::interfaces::Writeable;
use yggdrasil_abi::{arch::SavedFrame, error::Error};
use crate::{
mem::{auto_lower_address, fixed},
ArchitectureImpl,
mem::{auto_lower_address, fixed},
};
/// Frame saved onto the stack when taking an IRQ
@@ -445,7 +445,7 @@ impl<K: KernelTableManager, PA: PhysicalMemoryAllocator<Address = PhysicalAddres
stack.push(entry as _);
stack.push(arg);
setup_common_context(&mut stack, __x86_64_task_enter_kernel as _);
setup_common_context(&mut stack, (__x86_64_task_enter_kernel as *const ()).addr());
let sp = stack.build();
@@ -483,7 +483,7 @@ impl<K: KernelTableManager, PA: PhysicalMemoryAllocator<Address = PhysicalAddres
stack.push(context.argument);
stack.push(context.stack_pointer);
setup_common_context(&mut stack, __x86_64_task_enter_user as _);
setup_common_context(&mut stack, (__x86_64_task_enter_user as *const ()).addr());
let sp = stack.build();
let rsp0 = stack_base + USER_TASK_PAGES * 0x1000;
+2 -2
View File
@@ -11,10 +11,10 @@ use core::{
use alloc::{sync::Arc, vec::Vec};
use device_api::interrupt::{LocalInterruptController, MessageInterruptController};
use kernel_arch_interface::{
Architecture,
cpu::{CpuData, CpuImpl, IpiQueue},
task::Scheduler,
util::OneTimeInit,
Architecture,
};
use kernel_arch_x86::{cpuid::CpuFeatures, registers::MSR_IA32_KERNEL_GS_BASE};
use libk_mm_interface::address::PhysicalAddress;
@@ -24,7 +24,7 @@ pub mod context;
pub mod mem;
pub use context::TaskContextImpl;
pub use mem::{process::ProcessAddressSpaceImpl, KernelTableManagerImpl};
pub use mem::{KernelTableManagerImpl, process::ProcessAddressSpaceImpl};
pub struct ArchitectureImpl;
+4 -4
View File
@@ -1,19 +1,19 @@
use core::ops::Range;
use kernel_arch_interface::{mem::DeviceMemoryAttributes, sync::IrqSafeSpinlock, Architecture};
use kernel_arch_interface::{Architecture, mem::DeviceMemoryAttributes, sync::IrqSafeSpinlock};
use kernel_arch_x86::registers::CR3;
use libk_mm_interface::{
address::PhysicalAddress,
device::{DevicePageManager, DevicePageTableLevel},
table::{page_index, EntryLevel},
table::{EntryLevel, page_index},
};
use crate::{
ArchitectureImpl, KERNEL_VIRT_OFFSET,
mem::{
auto_lower_address,
table::{PageAttributes, PageEntry, PageTable, L0, L1, L2, L3},
table::{L0, L1, L2, L3, PageAttributes, PageEntry, PageTable},
},
ArchitectureImpl, KERNEL_VIRT_OFFSET,
};
pub const IDENTITY_SIZE_L1: usize = 64;
+1 -1
View File
@@ -6,7 +6,7 @@ use yggdrasil_abi::error::Error;
use crate::KERNEL_VIRT_OFFSET;
use self::table::{PageTable, L0, L1};
use self::table::{L0, L1, PageTable};
pub mod fixed;
pub mod process;
+1 -1
View File
@@ -15,7 +15,7 @@ use crate::KernelTableManagerImpl;
use super::{
clone_kernel_tables, flush_tlb_entry,
table::{PageEntry, PageTable, L0, L1, L2, L3},
table::{L0, L1, L2, L3, PageEntry, PageTable},
};
/// Represents a process or kernel address space. Because x86-64 does not have cool stuff like
+2 -2
View File
@@ -224,8 +224,8 @@ impl<L: EntryLevel> PageTable<L> {
}
/// Allocates a new page table, filling it with non-preset entries
pub fn new_zeroed<'a, TA: TableAllocator>(
) -> Result<PhysicalRefMut<'a, Self, KernelTableManagerImpl>, Error> {
pub fn new_zeroed<'a, TA: TableAllocator>()
-> Result<PhysicalRefMut<'a, Self, KernelTableManagerImpl>, Error> {
let physical = TA::allocate_page_table()?;
let mut table =
unsafe { PhysicalRefMut::<'a, Self, KernelTableManagerImpl>::map(physical) };
+2 -2
View File
@@ -6,9 +6,9 @@ use std::{
};
use abi_generator::{
abi::{ty::TypeWidth, AbiBuilder},
syntax::UnwrapFancy,
TargetEnv,
abi::{AbiBuilder, ty::TypeWidth},
syntax::UnwrapFancy,
};
fn build_x86_64() {
+2 -2
View File
@@ -7,7 +7,7 @@ use device_api::{
device::Device,
interrupt::{InterruptHandler, Irq, IrqVector},
};
use kernel_arch_x86::{intrinsics, ISA_IRQ_OFFSET};
use kernel_arch_x86::{ISA_IRQ_OFFSET, intrinsics};
use libk::device::external_interrupt_controller;
use libk_mm::{
address::{PhysicalAddress, Virtualize},
@@ -15,8 +15,8 @@ use libk_mm::{
};
use crate::{
mem::{read_memory, write_memory},
ACPI_SYSTEM,
mem::{read_memory, write_memory},
};
#[derive(Clone, Copy)]
+14 -9
View File
@@ -1,11 +1,11 @@
#![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};
use libk_util::{OneTimeInit, sync::IrqSafeSpinlock};
extern crate alloc;
@@ -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));
+2 -2
View File
@@ -1,10 +1,10 @@
use core::mem::{size_of, MaybeUninit};
use core::mem::{MaybeUninit, size_of};
use device_api::dma::DmaAllocator;
use libk::dma::{BusAddress, DmaBuffer, DmaSliceMut};
use tock_registers::register_structs;
use crate::{data::AtaString, error::AhciError, MAX_PRD_SIZE};
use crate::{MAX_PRD_SIZE, data::AtaString, error::AhciError};
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[repr(u8)]
+3 -3
View File
@@ -7,9 +7,9 @@ use libk_util::{ConstAssert, IsTrue};
use static_assertions::const_assert_eq;
use crate::{
MAX_PRD_SIZE,
command::{AtaCommand, AtaIdentify, AtaIdentifyResponse},
error::AhciError,
MAX_PRD_SIZE,
};
pub const COMMAND_LIST_LENGTH: usize = 32;
@@ -20,7 +20,7 @@ const AHCI_FIS_REG_H2D: u8 = 0x27;
#[repr(C)]
pub struct AtaString<const N: usize>
where
ConstAssert<{ N % 2 == 0 }>: IsTrue,
ConstAssert<{ N.is_multiple_of(2) }>: IsTrue,
{
data: [u8; N],
}
@@ -243,7 +243,7 @@ impl AtaIdentifyResponse {
impl<const N: usize> AtaString<N>
where
ConstAssert<{ N % 2 == 0 }>: IsTrue,
ConstAssert<{ N.is_multiple_of(2) }>: IsTrue,
{
#[allow(clippy::inherent_to_string)]
pub fn to_string(&self) -> String {
+3 -3
View File
@@ -15,18 +15,18 @@ use device_api::{
use error::AhciError;
use libk::{device::manager::probe_partitions, dma::DmaBuffer, fs::devfs, task::runtime};
use libk_mm::device::DeviceMemoryIo;
use libk_util::{sync::IrqSafeSpinlock, OneTimeInit};
use libk_util::{OneTimeInit, sync::IrqSafeSpinlock};
use port::AhciPort;
use regs::{PortRegs, Regs};
use tock_registers::interfaces::{ReadWriteable, Readable, Writeable};
use ygg_driver_pci::{
PciCommandRegister, PciConfigurationSpace,
device::{PciDeviceInfo, PreferredInterruptMode},
macros::pci_driver,
PciCommandRegister, PciConfigurationSpace,
};
use yggdrasil_abi::{error::Error, io::FileMode};
use crate::regs::{Version, CAP, GHC, SSTS};
use crate::regs::{CAP, GHC, SSTS, Version};
mod command;
mod data;
+9 -9
View File
@@ -16,18 +16,18 @@ use libk::{
error::Error,
};
use libk_mm::{
address::PhysicalAddress, device::DeviceMemoryIo, table::MapAttributes, OnDemandPage,
PageProvider, VirtualPage,
OnDemandPage, PageProvider, VirtualPage, address::PhysicalAddress, device::DeviceMemoryIo,
table::MapAttributes,
};
use libk_util::{sync::IrqSafeSpinlock, waker::QueueWaker, OneTimeInit};
use libk_util::{OneTimeInit, sync::IrqSafeSpinlock, waker::QueueWaker};
use tock_registers::interfaces::{Readable, Writeable};
use crate::{
command::{AtaCommand, AtaIdentify, AtaReadDmaEx},
data::{CommandListEntry, CommandTable, ReceivedFis, COMMAND_LIST_LENGTH},
error::AhciError,
regs::{PortRegs, CMD_PENDING, CMD_READY, IE, TFD},
AhciController, MAX_COMMANDS, MAX_PRD_SIZE, SECTOR_SIZE,
command::{AtaCommand, AtaIdentify, AtaReadDmaEx},
data::{COMMAND_LIST_LENGTH, CommandListEntry, CommandTable, ReceivedFis},
error::AhciError,
regs::{CMD_PENDING, CMD_READY, IE, PortRegs, TFD},
};
#[derive(Clone, Copy, PartialEq, Debug)]
@@ -319,11 +319,11 @@ impl BlockDevice for AhciPort {
position: u64,
buffer: DmaSliceMut<'_, MaybeUninit<u8>>,
) -> Result<(), Error> {
if buffer.len() % SECTOR_SIZE != 0 {
if !buffer.len().is_multiple_of(SECTOR_SIZE) {
log::warn!("ahci: misaligned buffer size: {}", buffer.len());
return Err(Error::InvalidOperation);
}
if position % SECTOR_SIZE as u64 != 0 {
if !position.is_multiple_of(SECTOR_SIZE as u64) {
log::warn!("ahci: misaligned read");
return Err(Error::InvalidOperation);
}
+1 -1
View File
@@ -1,7 +1,7 @@
[package]
name = "ygg_driver_nvme"
version = "0.1.0"
edition = "2021"
edition = "2024"
authors = ["Mark Poliakov <mark@alnyan.me>"]
[dependencies]
+1 -1
View File
@@ -3,7 +3,7 @@
use core::fmt::{self, Write};
use libk::dma::BusAddress;
use tock_registers::{interfaces::Readable, register_structs, registers::ReadOnly, UIntLike};
use tock_registers::{UIntLike, interfaces::Readable, register_structs, registers::ReadOnly};
use crate::queue::PhysicalRegionPage;
+7 -7
View File
@@ -9,14 +9,14 @@ use libk::{
error::Error,
};
use libk_mm::{
OnDemandPage, PageProvider, PageSlice, VirtualPage,
address::{AsPhysicalAddress, PhysicalAddress},
table::MapAttributes,
OnDemandPage, PageProvider, PageSlice, VirtualPage,
};
use crate::{command::IdentifyNamespaceRequest, register_nvme_namespace, IoDirection};
use crate::{IoDirection, command::IdentifyNamespaceRequest, register_nvme_namespace};
use super::{error::NvmeError, NvmeController};
use super::{NvmeController, error::NvmeError};
#[allow(unused)]
pub struct NvmeNamespace {
@@ -92,10 +92,10 @@ impl BlockDevice for NvmeNamespace {
position: u64,
buffer: DmaSliceMut<'_, MaybeUninit<u8>>,
) -> Result<(), Error> {
if position % self.block_size() as u64 != 0 {
if !position.is_multiple_of(self.block_size() as u64) {
return Err(Error::InvalidOperation);
}
if buffer.len() % self.block_size() != 0 || buffer.is_empty() {
if !buffer.len().is_multiple_of(self.block_size()) || buffer.is_empty() {
return Err(Error::InvalidOperation);
}
let lba = position / self.block_size() as u64;
@@ -115,10 +115,10 @@ impl BlockDevice for NvmeNamespace {
}
async fn write_aligned(&self, position: u64, buffer: DmaSlice<'_, u8>) -> Result<(), Error> {
if position % self.block_size() as u64 != 0 {
if !position.is_multiple_of(self.block_size() as u64) {
return Err(Error::InvalidOperation);
}
if buffer.len() % self.block_size() != 0 || buffer.is_empty() {
if !buffer.len().is_multiple_of(self.block_size()) || buffer.is_empty() {
return Err(Error::InvalidOperation);
}
let lba = position / self.block_size() as u64;
+7 -7
View File
@@ -1,4 +1,4 @@
#![feature(const_trait_impl, let_chains, if_let_guard, maybe_uninit_slice)]
#![feature(const_trait_impl, if_let_guard)]
#![allow(missing_docs)]
#![no_std]
// TODO
@@ -7,7 +7,7 @@
extern crate alloc;
use core::{
mem::{size_of, MaybeUninit},
mem::{MaybeUninit, size_of},
sync::atomic::{AtomicUsize, Ordering},
time::Duration,
};
@@ -27,10 +27,10 @@ use libk::{
fs::devfs,
task::{cpu_count, cpu_index, runtime},
};
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIo, L3_PAGE_SIZE};
use libk_mm::{L3_PAGE_SIZE, address::PhysicalAddress, device::DeviceMemoryIo};
use libk_util::{
sync::{IrqGuard, IrqSafeSpinlock},
OneTimeInit,
sync::{IrqGuard, IrqSafeSpinlock},
};
use queue::PrpList;
use regs::{CAP, CC};
@@ -40,9 +40,9 @@ use tock_registers::{
registers::{ReadOnly, ReadWrite, WriteOnly},
};
use ygg_driver_pci::{
PciCommandRegister, PciConfigurationSpace,
device::{PciDeviceInfo, PreferredInterruptMode},
macros::pci_driver,
PciCommandRegister, PciConfigurationSpace,
};
use yggdrasil_abi::{error::Error, io::FileMode};
@@ -284,8 +284,8 @@ impl NvmeController {
unsafe fn doorbell_pair(&self, idx: usize) -> (*mut u32, *mut u32) {
let regs = self.regs.lock();
let sq_ptr = regs.doorbell_ptr(self.doorbell_shift, false, idx);
let cq_ptr = regs.doorbell_ptr(self.doorbell_shift, true, idx);
let sq_ptr = unsafe { regs.doorbell_ptr(self.doorbell_shift, false, idx) };
let cq_ptr = unsafe { regs.doorbell_ptr(self.doorbell_shift, true, idx) };
(sq_ptr, cq_ptr)
}
}
+1 -1
View File
@@ -112,7 +112,7 @@ impl PrpList {
size: usize,
) -> Result<Self, NvmeError> {
// TODO hardcoded page size
if base.into_u64() % 0x1000 != 0 {
if !base.into_u64().is_multiple_of(0x1000) {
todo!();
}
+13 -12
View File
@@ -1,4 +1,4 @@
#![feature(generic_const_exprs, maybe_uninit_slice)]
#![feature(generic_const_exprs)]
#![allow(incomplete_features)]
#![no_std]
@@ -23,11 +23,11 @@ use libk::{
task::{runtime, sync::AsyncMutex},
};
use libk_mm::{
address::PhysicalAddress, table::MapAttributes, OnDemandPage, PageProvider, VirtualPage,
OnDemandPage, PageProvider, VirtualPage, address::PhysicalAddress, table::MapAttributes,
};
use libk_util::{
sync::{spin_rwlock::IrqSafeRwLock, IrqSafeSpinlock},
OneTimeInit,
sync::{IrqSafeSpinlock, spin_rwlock::IrqSafeRwLock},
};
use transport::{ScsiTransport, ScsiTransportWrapper};
use yggdrasil_abi::io::FileMode;
@@ -40,7 +40,7 @@ pub mod transport;
pub struct ScsiEnclosure {
transport: AsyncMutex<ScsiTransportWrapper>,
units: Vec<IrqSafeRwLock<Option<Arc<ScsiUnit>>>>,
units: Vec<AsyncMutex<Option<Arc<ScsiUnit>>>>,
index: OneTimeInit<u32>,
shutdown: AtomicBool,
}
@@ -60,7 +60,7 @@ impl ScsiEnclosure {
lun_count: usize,
) -> Result<Arc<Self>, Error> {
let transport = AsyncMutex::new(ScsiTransportWrapper::new(transport));
let units = (0..lun_count).map(|_| IrqSafeRwLock::new(None)).collect();
let units = (0..lun_count).map(|_| AsyncMutex::new(None)).collect();
let this = Arc::new(Self {
transport,
units,
@@ -74,7 +74,7 @@ impl ScsiEnclosure {
if this.probe_lun(i as u8).await
&& let Ok(unit) = ScsiUnit::setup(this.clone(), i as u8).await
{
*this.units[i].write() = Some(unit);
*this.units[i].lock().await = Some(unit);
}
}
@@ -123,7 +123,7 @@ impl ScsiEnclosure {
async fn poll(self: &Arc<Self>) {
let index = *self.index.get();
for lun in 0..self.units.len() {
let mut slot = self.units[lun].write();
let mut slot = self.units[lun].lock().await;
let present = self.probe_lun(lun as u8).await;
if let Some(unit) = slot.as_ref() {
@@ -143,12 +143,12 @@ impl ScsiEnclosure {
}
}
pub fn detach(&self) {
pub async fn detach(&self) {
self.shutdown.store(true, Ordering::Release);
let index = self.index.try_get().copied();
for unit in self.units.iter() {
if let Some(unit) = unit.write().take() {
if let Some(unit) = unit.lock().await.take() {
unit.detach();
}
}
@@ -214,11 +214,11 @@ impl BlockDevice for ScsiUnit {
position: u64,
buffer: DmaSliceMut<'_, MaybeUninit<u8>>,
) -> Result<(), Error> {
if position % self.lba_size as u64 != 0 {
if !position.is_multiple_of(self.lba_size as u64) {
log::warn!("scsi: misaligned read");
return Err(Error::InvalidArgument);
}
if buffer.len() % self.lba_size != 0 {
if !buffer.len().is_multiple_of(self.lba_size) {
log::warn!("scsi: misaligned buffer size");
return Err(Error::InvalidArgument);
}
@@ -368,10 +368,11 @@ fn register_unit(enclosure_index: u32, lun: u8, unit: Arc<ScsiUnit>) {
}
fn remove_enclosure(index: u32) {
log::info!("scsi: enclosure {index} detached");
let mut devices = SCSI_ENCLOSURES.lock();
let mut bitmap = SCSI_BITMAP.lock();
*bitmap &= !(1 << index);
devices.remove(&index);
log::info!("scsi: enclosure {index} detached");
}
+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,23 +1,26 @@
//! ARM PL011 driver
use abi::{error::Error, io::TerminalOptions};
// TODO baud rate configuration
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 device_tree::driver::{Node, ProbeContext, device_tree_driver};
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};
use libk_util::{sync::IrqSafeSpinlock, OneTimeInit};
use libk_util::{OneTimeInit, sync::IrqSafeSpinlock};
use tock_registers::{
interfaces::{ReadWriteable, Readable, Writeable},
register_bitfields, register_structs,
registers::{ReadOnly, ReadWrite, WriteOnly},
};
use yggdrasil_abi::{
error::Error,
io::{TerminalOptions, TerminalOutputOptions},
};
register_bitfields! {
u32,
@@ -69,7 +72,7 @@ struct Pl011Inner {
pub struct Pl011 {
inner: OneTimeInit<Arc<Terminal<Pl011Inner>>>,
base: PhysicalAddress,
irq: FullIrq,
irq: IrqHandle,
}
impl Io {
@@ -90,14 +93,25 @@ impl Io {
}
impl TerminalOutput for Pl011Inner {
fn write(&self, byte: u8) -> Result<(), Error> {
self.io.lock().send(byte);
fn write(&self, byte: u8, options: &TerminalOutputOptions) -> Result<(), Error> {
let mut lock = self.io.lock();
if byte == b'\n' && options.contains(TerminalOutputOptions::NL_TO_CRNL) {
lock.send(b'\r');
}
lock.send(b'\n');
Ok(())
}
fn write_multiple(&self, bytes: &[u8]) -> Result<usize, Error> {
fn write_multiple(
&self,
bytes: &[u8],
options: &TerminalOutputOptions,
) -> Result<usize, Error> {
let mut lock = self.io.lock();
for &byte in bytes {
if byte == b'\n' && options.contains(TerminalOutputOptions::NL_TO_CRNL) {
lock.send(b'\r');
}
lock.send(byte);
}
Ok(bytes.len())
@@ -118,6 +132,11 @@ impl DebugSink for Pl011 {
self.inner.get().putc_to_output(byte)
}
fn puts(&self, s: &str) -> Result<(), Error> {
self.inner.get().write_to_output(s.as_bytes())?;
Ok(())
}
fn supports_control_sequences(&self) -> bool {
true
}
@@ -165,14 +184,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::{Node, ProbeContext, device_tree_driver};
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::{
DeviceTreePropertyRead, TProp,
driver::{
DeviceTreeGpioPins, DeviceTreePinController, Node, ProbeContext, device_tree_driver,
util::generic_gpio_config,
},
};
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)
}
}
}
+19
View File
@@ -0,0 +1,19 @@
[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
async-trait.workspace = true
@@ -1,21 +1,20 @@
//! BCM283x AUX peripheral
use aarch64_cpu::registers::ReadWriteable;
use abi::error::Error;
use alloc::sync::Arc;
use device_api::{
clock::{ClockController, ClockHandle},
clock::{ClockController, ClockHandle, Hertz, IntoHertz},
device::{Device, DeviceInitContext},
};
use device_tree::{
driver::{device_tree_driver, DeviceTreeClockController, Node, ProbeContext},
DeviceTreePropertyRead, TProp,
driver::{DeviceTreeClockController, Node, ProbeContext, device_tree_driver},
};
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIo};
use libk_util::{sync::IrqSafeSpinlock, OneTimeInit};
use libk_util::{OneTimeInit, sync::IrqSafeSpinlock};
use tock_registers::{
interfaces::ReadWriteable,
register_bitfields, register_structs,
registers::{ReadOnly, ReadWrite},
};
use yggdrasil_abi::error::Error;
register_bitfields! {
u32,
@@ -55,10 +54,10 @@ impl ClockController for Bcm2835Aux {
let regs = regs.lock();
match clock {
Some(0) => {
// TODO CPRMAN driver
regs.AUX_ENABLES.modify(AUX_ENABLES::MU_ENABLE::SET);
Ok(())
}
None => todo!(),
_ => Err(Error::InvalidArgument),
}
}
@@ -66,6 +65,16 @@ impl ClockController for Bcm2835Aux {
fn disable_clock(&self, _clock: Option<u32>) -> Result<(), Error> {
Err(Error::NotImplemented)
}
fn clock_rate(&self, clock: Option<u32>) -> Result<Hertz, Error> {
match clock {
Some(0) => {
// TODO CPRMAN driver
Ok(54u64.mhz())
}
_ => Err(Error::InvalidArgument),
}
}
}
impl DeviceTreeClockController for Bcm2835Aux {
+93
View File
@@ -0,0 +1,93 @@
use alloc::sync::Arc;
use device_api::{
clock::{ClockController, ClockHandle, Hertz},
device::{Device, DeviceInitContext},
};
use device_tree::{
DeviceTreePropertyRead, TProp,
driver::{DeviceTreeClockController, Node, ProbeContext, device_tree_driver},
};
use libk::error::Error;
use crate::mbox::{Bcm2835Mbox, Bcm2835VcClock};
const CLK_VPU: u32 = 0x14;
struct Cprman {
clk_osc: ClockHandle,
mbox: Arc<Bcm2835Mbox>,
}
impl Device for Cprman {
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
let _ = &self.clk_osc;
Ok(())
}
fn display_name(&self) -> &str {
"bcm2711-cprman"
}
}
impl ClockController for Cprman {
fn clock_rate(&self, clock: Option<u32>) -> Result<Hertz, Error> {
let vc_clock = match clock {
Some(CLK_VPU) => Bcm2835VcClock::Core,
Some(n) => todo!("clock_rate({n:#02x})"),
None => return Err(Error::InvalidArgument),
};
let rate = self.mbox.vc_clock_rate(vc_clock)?;
Ok(rate)
}
fn set_clock_rate(&self, clock: Option<u32>, rate: Hertz) -> Result<Hertz, Error> {
let _ = clock;
let _ = rate;
todo!()
}
fn enable_clock(&self, clock: Option<u32>) -> Result<(), Error> {
let _ = clock;
todo!()
}
fn disable_clock(&self, clock: Option<u32>) -> Result<(), Error> {
let _ = clock;
todo!()
}
}
impl DeviceTreeClockController for Cprman {
fn map_clock(self: Arc<Self>, property: &TProp, offset: usize) -> Option<(ClockHandle, usize)> {
let clock = property.read_cell(offset, 1)? as u32;
Some((
ClockHandle {
parent: self.clone(),
clock: Some(clock),
},
1,
))
}
}
device_tree_driver! {
compatible: ["brcm,bcm2711-cprman"],
driver: {
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
let _base = node.map_base(context, 0)?;
let mbox = Node::by_compatible("brcm,bcm2835-mbox", true).unwrap().device().unwrap();
let mbox = Arc::downcast::<Bcm2835Mbox>(mbox.as_any()).unwrap();
let clk_osc = node.clock(0)?;
let cprman = Arc::new(Cprman {
clk_osc,
mbox,
});
node.make_clock_controller(cprman.clone());
Some(cprman)
}
}
}
+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::{
DeviceTreePropertyRead, TProp,
driver::{
DeviceTreeGpioPins, DeviceTreePinController, Node, ProbeContext, device_tree_driver,
util::generic_gpio_config,
},
};
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)
}
}
}
+290
View File
@@ -0,0 +1,290 @@
use alloc::sync::Arc;
use device_api::{
clock::{ClockHandle, Hertz},
device::{Device, DeviceInitContext},
i2c::{I2CAddress, I2CController},
interrupt::{InterruptHandler, IrqHandle, IrqVector},
};
use device_tree::driver::{Node, ProbeContext, device_tree_driver};
use libk::{device::manager::DEVICE_REGISTRY, error::Error};
use libk_mm::device::DeviceMemoryIo;
use libk_util::{OneTimeInit, sync::spin_rwlock::IrqSafeRwLock};
use tock_registers::{
interfaces::{ReadWriteable, Readable, Writeable},
register_bitfields, register_structs,
registers::ReadWrite,
};
use yggdrasil_abi::io::device::i2c::I2CMasterInfo;
register_bitfields! {
u32,
C [
I2CEN OFFSET(15) NUMBITS(1) [],
INTR OFFSET(10) NUMBITS(1) [],
INTT OFFSET(9) NUMBITS(1) [],
INTD OFFSET(8) NUMBITS(1) [],
ST OFFSET(7) NUMBITS(1) [],
CLEAR OFFSET(4) NUMBITS(2) [],
READ OFFSET(0) NUMBITS(1) [],
],
S [
CLKT OFFSET(9) NUMBITS(1) [],
ERR OFFSET(8) NUMBITS(1) [],
RXF OFFSET(7) NUMBITS(1) [],
TXE OFFSET(6) NUMBITS(1) [],
RXD OFFSET(5) NUMBITS(1) [],
TXD OFFSET(4) NUMBITS(1) [],
RXR OFFSET(3) NUMBITS(1) [],
TXW OFFSET(2) NUMBITS(1) [],
DONE OFFSET(1) NUMBITS(1) [],
TA OFFSET(0) NUMBITS(1) [],
],
}
register_structs! {
#[allow(non_snake_case)]
Regs {
(0x00 => C: ReadWrite<u32, C::Register>),
(0x04 => S: ReadWrite<u32, S::Register>),
(0x08 => DLEN: ReadWrite<u32>),
(0x0C => A: ReadWrite<u32>),
(0x10 => FIFO: ReadWrite<u32>),
(0x14 => DIV: ReadWrite<u32>),
(0x18 => DEL: ReadWrite<u32>),
(0x1C => CLKT: ReadWrite<u32>),
(0x20 => @END),
}
}
struct I2C {
name: &'static str,
clock_frequency: Option<Hertz>,
irq: IrqHandle,
clock: Option<ClockHandle>,
regs: IrqSafeRwLock<DeviceMemoryIo<'static, Regs>>,
index: OneTimeInit<u32>,
}
impl Regs {
fn start_transfer(&self, name: &str, buflen: u16, address: I2CAddress, read: bool) {
log::debug!(
"{}: start address={}, read={}, len={}",
name,
address,
read,
buflen
);
let address = address.as_8_bit().unwrap();
let read = if read { C::READ::SET } else { C::READ::CLEAR };
self.S.write(S::ERR::SET + S::DONE::SET);
self.DLEN.set(buflen as u32);
self.C.modify(C::ST::CLEAR + C::I2CEN::CLEAR);
self.A.set(address as u32);
self.C.modify(read + C::ST::SET + C::I2CEN::SET);
}
fn finish_transfer(&self, name: &str) -> Result<(), Error> {
log::debug!("{}: finish transfer", name);
let status = self.S.extract();
self.C.set(0);
if status.matches_all(S::ERR::SET) {
self.S.write(S::DONE::SET + S::ERR::SET);
return Err(Error::HostUnreachable);
}
if status.matches_all(S::DONE::SET) {
self.S.write(S::DONE::SET);
}
Ok(())
}
fn write_byte(&self, byte: u8) -> Result<bool, Error> {
loop {
let status = self.S.extract();
if status.matches_all(S::ERR::SET) {
self.C.write(C::CLEAR.val(1));
self.S.write(S::ERR::SET + S::DONE::SET);
// TODO better code
return Err(Error::HostUnreachable);
}
if status.matches_all(S::DONE::SET) {
self.C.set(0);
self.S.write(S::DONE::SET);
return Ok(false);
}
if status.matches_all(S::TXD::SET) {
self.FIFO.set(byte as u32);
return Ok(true);
}
core::hint::spin_loop();
}
}
fn read_byte(&self) -> Result<Option<u8>, Error> {
loop {
let status = self.S.extract();
if status.matches_all(S::ERR::SET) {
self.C.write(C::CLEAR.val(1));
self.S.write(S::ERR::SET + S::DONE::SET);
// TODO better code
return Err(Error::HostUnreachable);
}
if status.matches_all(S::DONE::SET) {
self.C.set(0);
self.S.write(S::DONE::SET);
return Ok(None);
}
if status.matches_all(S::RXD::SET) {
let val = self.FIFO.get() as u8;
return Ok(Some(val));
}
core::hint::spin_loop();
}
}
}
impl Device for I2C {
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
// TODO
let _ = &self.clock;
log::info!("{}: initialize", self.name);
let regs = self.regs.write();
regs.C.set(0);
let index = DEVICE_REGISTRY.i2c.register_bus(self.clone())?;
self.index.init(index);
Ok(())
}
unsafe fn init_irq(self: Arc<Self>) -> Result<(), Error> {
// TODO
let _ = &self.irq;
// self.irq.register(self.clone())?;
// self.irq.enable()?;
// let regs = self.regs.write();
// regs.C.modify(C::INTD::SET);
Ok(())
}
fn display_name(&self) -> &str {
self.name
}
}
impl InterruptHandler for I2C {
fn handle_irq(self: Arc<Self>, _vector: IrqVector) -> bool {
todo!()
}
}
impl I2CController for I2C {
fn bus_number(&self) -> u32 {
*self.index.get()
}
fn child_number(&self) -> Option<u32> {
None
}
fn set_speed(&self, speed: Hertz) -> Result<Hertz, Error> {
if let Some(frequency) = self.clock_frequency {
// Fixed frequency
if speed == frequency {
Ok(speed)
} else {
Err(Error::InvalidArgument)
}
} else {
todo!("Set i2c speed = {speed}")
}
}
fn capabilities(&self) -> I2CMasterInfo {
let max_speed_hz = if let Some(frequency) = self.clock_frequency {
frequency
} else {
todo!()
}
.0 as u32;
I2CMasterInfo {
max_speed_hz,
supports_10bit_addresses: true,
}
}
fn i2c_write(&self, address: I2CAddress, buffer: &[u8]) -> Result<usize, Error> {
let buflen: u16 = buffer
.len()
.try_into()
.map_err(|_| Error::InvalidArgument)?;
let regs = self.regs.write();
// TODO DMA/interrupts
regs.start_transfer(self.name, buflen, address, false);
let mut bytes_written = 0;
// let mut done = false;
for &byte in buffer {
if regs.write_byte(byte)? {
bytes_written += 1;
} else {
break;
}
}
regs.finish_transfer(self.name)?;
Ok(bytes_written)
}
fn i2c_read(&self, address: I2CAddress, buffer: &mut [u8]) -> Result<usize, Error> {
let buflen: u16 = buffer
.len()
.try_into()
.map_err(|_| Error::InvalidArgument)?;
let regs = self.regs.write();
// TODO DMA/interrupts
regs.start_transfer(self.name, buflen, address, true);
let mut bytes_read = 0;
// let mut done = false;
for byte in buffer {
if let Some(val) = regs.read_byte()? {
*byte = val;
bytes_read += 1;
} else {
break;
}
}
regs.finish_transfer(self.name)?;
Ok(bytes_read)
}
}
device_tree_driver! {
compatible: ["brcm,bcm2711-i2c", "brcm,bcm2835-i2c"],
driver: {
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
let clock_frequency = node.prop_usize("clock-frequency").map(|p| Hertz(p as u64));
let base = node.map_base(context, 0)?;
let name = node.name().unwrap_or("bcm2835-i2c");
let irq = node.interrupt(0)?;
let clock = node.clock(0);
let regs = unsafe { DeviceMemoryIo::map(base, Default::default()) }.ok()?;
let i2c = Arc::new(I2C {
name,
clock_frequency,
irq,
clock,
index: OneTimeInit::new(),
regs: IrqSafeRwLock::new(regs)
});
node.make_i2c_controller(i2c.clone());
Some(i2c)
}
}
}
+11
View File
@@ -0,0 +1,11 @@
#![no_std]
extern crate alloc;
mod aux;
// mod aux_uart;
mod cprman;
mod gpio;
mod i2c;
mod mbox;
mod spi;
@@ -1,11 +1,11 @@
#![allow(missing_docs)]
use core::{any::Any, cell::UnsafeCell, mem::MaybeUninit};
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};
use device_api::{
clock::Hertz,
device::{Device, DeviceInitContext},
};
use device_tree::driver::{Node, ProbeContext, device_tree_driver};
use kernel_arch_aarch64::mem::table::L3;
use libk::device::{
display::{
@@ -14,10 +14,10 @@ use libk::device::{
manager::DEVICE_REGISTRY,
};
use libk_mm::{
OnDemandPage, PageBox, PageProvider, VirtualPage,
address::{AsPhysicalAddress, PhysicalAddress},
device::DeviceMemoryIo,
table::{EntryLevel, MapAttributes},
OnDemandPage, PageBox, PageProvider, VirtualPage,
};
use libk_util::sync::IrqSafeSpinlock;
use tock_registers::{
@@ -25,6 +25,7 @@ use tock_registers::{
register_bitfields, register_structs,
registers::{ReadOnly, ReadWrite, WriteOnly},
};
use yggdrasil_abi::error::Error;
register_bitfields! {
u32,
@@ -63,7 +64,13 @@ register_structs! {
}
}
struct Bcm2835Mbox {
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[repr(u32)]
pub(crate) enum Bcm2835VcClock {
Core = 4,
}
pub(crate) struct Bcm2835Mbox {
regs: IrqSafeSpinlock<DeviceMemoryIo<'static, Regs>>,
}
@@ -87,13 +94,20 @@ impl Device for Bcm2835Mbox {
fn display_name(&self) -> &str {
"bcm2835-mbox"
}
fn as_any(self: Arc<Self>) -> Arc<dyn Any + Send + Sync> {
self
}
}
impl Bcm2835Mbox {
const RAM_TO_GPU_OFFSET: u32 = 0x40000000;
const CHANNEL_FRAMEBUFFER: u32 = 1;
const CHANNEL_ARM2VC: u32 = 8;
pub unsafe fn call_raw(&self, buffer: PhysicalAddress, channel: u32) -> Result<(), Error> {
log::info!("bcm2835-mbox: mbox call buffer={buffer:#x}, channel={channel}");
log::debug!("bcm2835-mbox: mbox call buffer={buffer:#x}, channel={channel}");
let address = buffer
.try_into_u32()
@@ -127,10 +141,31 @@ impl Bcm2835Mbox {
}
}
pub fn configure_framebuffer(
&self,
mode_info: &DisplayMode,
) -> Result<FramebufferResponse, Error> {
pub fn vc_clock_rate(&self, clock: Bcm2835VcClock) -> Result<Hertz, Error> {
let mut buffer = PageBox::new_slice(0u32, 4096 / 4)?;
buffer[0] = 9 * 4; // buffer size
buffer[1] = 0; // process request
// Tag 1 (Response)
buffer[2] = 0x00030002; // tag
buffer[3] = 0; // value buffer size
buffer[4] = 0; // request
buffer[5] = clock as u32; // value buffer[0]
buffer[6] = 0; // value buffer[1]
buffer[7] = 0;
// End tag
buffer[8] = 0;
unsafe { self.call_raw(buffer.as_physical_address(), Self::CHANNEL_ARM2VC) }?;
if buffer[4] == 0 || buffer[1] == 0 {
todo!();
}
let rate = Hertz::from(buffer[6]);
Ok(rate)
}
fn configure_framebuffer(&self, mode_info: &DisplayMode) -> Result<FramebufferResponse, Error> {
#[derive(Debug, Clone, Copy)]
#[repr(C)]
struct FramebufferRequest {
@@ -160,7 +195,7 @@ impl Bcm2835Mbox {
}))?;
// TODO flush cache for buffer
unsafe { self.call_raw(buffer.as_physical_address(), 0x01) }?;
unsafe { self.call_raw(buffer.as_physical_address(), Self::CHANNEL_FRAMEBUFFER) }?;
let buffer = buffer.get_mut();
+202
View File
@@ -0,0 +1,202 @@
use core::iter;
use alloc::{boxed::Box, sync::Arc};
use async_trait::async_trait;
use device_api::{
clock::{ClockHandle, Hertz},
device::{Device, DeviceInitContext},
interrupt::IrqHandle,
spi::SpiController,
};
use device_tree::driver::{Node, ProbeContext, device_tree_driver};
use libk::{device::manager::DEVICE_REGISTRY, error::Error};
use libk_mm::device::DeviceMemoryIo;
use libk_util::sync::IrqSafeSpinlock;
use tock_registers::{
interfaces::{Readable, Writeable},
register_bitfields, register_structs,
registers::ReadWrite,
};
use yggdrasil_abi::io::device::spi::{SpiClockPhase, SpiClockPolarity, SpiConfig};
register_bitfields! {
u32,
CS [
LEN_LONG OFFSET(25) NUMBITS(1) [],
DMA_LEN OFFSET(24) NUMBITS(1) [],
CSPOL2 OFFSET(23) NUMBITS(1) [],
CSPOL1 OFFSET(22) NUMBITS(1) [],
CSPOL0 OFFSET(21) NUMBITS(1) [],
RXF OFFSET(20) NUMBITS(1) [],
RXR OFFSET(19) NUMBITS(1) [],
TXD OFFSET(18) NUMBITS(1) [],
RXD OFFSET(17) NUMBITS(1) [],
DONE OFFSET(16) NUMBITS(1) [],
LEN OFFSET(13) NUMBITS(1) [],
REN OFFSET(12) NUMBITS(1) [],
ADCS OFFSET(11) NUMBITS(1) [],
INTR OFFSET(10) NUMBITS(1) [],
INTD OFFSET(9) NUMBITS(1) [],
DMAEN OFFSET(8) NUMBITS(1) [],
TA OFFSET(7) NUMBITS(1) [],
CSPOL OFFSET(6) NUMBITS(1) [],
CLEAR OFFSET(4) NUMBITS(2) [],
CPOL OFFSET(3) NUMBITS(1) [
IdleLow = 0,
IdleHigh = 1
],
CPHA OFFSET(2) NUMBITS(1) [
CaptureOnFirstTransition = 0,
CaptureOnSecondTransition = 1
],
CS OFFSET(0) NUMBITS(2) [],
]
}
register_structs! {
#[allow(non_snake_case)]
Regs {
(0x00 => CS: ReadWrite<u32, CS::Register>),
(0x04 => FIFO: ReadWrite<u32>),
(0x08 => CLK: ReadWrite<u32>),
(0x0C => DLEN: ReadWrite<u32>),
(0x10 => LTOH: ReadWrite<u32>),
(0x14 => DC: ReadWrite<u32>),
(0x18 => @END),
}
}
pub struct Spi {
name: &'static str,
clock: ClockHandle,
irq: IrqHandle,
regs: IrqSafeSpinlock<DeviceMemoryIo<'static, Regs>>,
}
impl Device for Spi {
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
let _ = &self.irq;
let regs = self.regs.lock();
regs.CS.set(0);
DEVICE_REGISTRY.spi.register_bus(self.clone()).ok();
Ok(())
}
fn display_name(&self) -> &str {
self.name
}
}
impl Regs {
fn start_transfer(&self, core_clock: Hertz, config: &SpiConfig) -> Result<(), Error> {
let want_clock = Hertz::from(config.frequency.unwrap_or(100000));
let divider = Hertz::divider(core_clock, want_clock).ok_or(Error::InvalidArgument)?;
let divider: u16 = divider.try_into().map_err(|_| Error::InvalidArgument)?;
let divider = divider.wrapping_add(1) & !1;
let mut cs = CS::REN::SET + CS::TA::SET;
if let Some(cs_index) = config.chip_select_index
&& cs_index < 3
{
cs += CS::CS.val(cs_index);
}
if let Some(cpol) = config.clock_polarity {
match cpol {
SpiClockPolarity::IdleLow => cs += CS::CPOL::IdleLow,
SpiClockPolarity::IdleHigh => cs += CS::CPOL::IdleHigh,
}
}
if let Some(cpha) = config.clock_phase {
match cpha {
SpiClockPhase::CaptureOnFirstTransition => cs += CS::CPHA::CaptureOnFirstTransition,
SpiClockPhase::CaptureOnSecondTransition => {
cs += CS::CPHA::CaptureOnSecondTransition
}
}
}
self.CLK.set(divider as u32);
self.CS.write(cs);
Ok(())
}
fn tx_rx_byte(&self, tx: u8) -> Result<Option<u8>, Error> {
// Wait for tx
loop {
let status = self.CS.extract();
if status.matches_all(CS::TXD::SET) {
break;
}
if status.matches_all(CS::DONE::SET) {
return Ok(None);
}
}
self.FIFO.set(tx as u32);
// Wait for rx
loop {
let status = self.CS.extract();
if status.matches_all(CS::RXD::SET) {
break;
}
if status.matches_all(CS::DONE::SET) {
return Ok(None);
}
}
Ok(Some(self.FIFO.get() as u8))
}
}
#[async_trait]
impl SpiController for Spi {
async fn spi_transfer(
&self,
tx_buffer: &[u8],
rx_buffer: &mut [u8],
config: &SpiConfig,
) -> Result<usize, Error> {
// TODO interrupt/DMA
log::debug!("{}: spi_transfer {} bytes", self.name, rx_buffer.len());
let core_clock = self.clock.rate()?;
let regs = self.regs.lock();
regs.start_transfer(core_clock, config)?;
let mut len = 0;
for (&tx, rx) in iter::zip(tx_buffer, rx_buffer) {
match regs.tx_rx_byte(tx)? {
Some(b) => {
*rx = b;
len += 1;
}
None => {
break;
}
}
}
Ok(len)
}
}
device_tree_driver! {
compatible: ["brcm,bcm2835-spi"],
driver: {
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
let name = node.name().unwrap_or("bcm2835-spi");
let base = node.map_base(context, 0)?;
let irq = node.interrupt(0)?;
let clock = node.clock(0)?;
let regs = unsafe { DeviceMemoryIo::map(base, Default::default()) }.ok()?;
let spi = Arc::new(Spi {
regs: IrqSafeSpinlock::new(regs),
name,
clock,
irq
});
Some(spi)
}
}
}
+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,22 +1,20 @@
//! 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},
device::{Device, DeviceInitContext},
};
use device_tree::{
driver::{
device_tree_driver, lookup_phandle, DeviceTreeClockController, DeviceTreeResetController,
DeviceTreeSyscon, Node, ProbeContext,
},
DeviceTreePropertyRead, TProp,
driver::{
DeviceTreeClockController, DeviceTreeResetController, DeviceTreeSyscon, Node, ProbeContext,
device_tree_driver, lookup_phandle,
},
};
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIoMut};
use libk_util::sync::IrqSafeSpinlock;
use yggdrasil_abi::error::Error;
// SYSCRG clocks
@@ -59,6 +57,16 @@ const SYSCRG_GMAC0_GTXC: usize = 0x1BC / 4;
// uart
const SYSCRG_UART0_APB: usize = 0x244 / 4;
const SYSCRG_UART0_CORE: usize = 0x248 / 4;
const SYSCRG_UART1_APB: usize = 0x24C / 4;
const SYSCRG_UART1_CORE: usize = 0x250 / 4;
const SYSCRG_UART2_APB: usize = 0x254 / 4;
const SYSCRG_UART2_CORE: usize = 0x258 / 4;
const SYSCRG_UART3_APB: usize = 0x25C / 4;
const SYSCRG_UART3_CORE: usize = 0x260 / 4;
const SYSCRG_UART4_APB: usize = 0x264 / 4;
const SYSCRG_UART4_CORE: usize = 0x268 / 4;
const SYSCRG_UART5_APB: usize = 0x26C / 4;
const SYSCRG_UART5_CORE: usize = 0x270 / 4;
// jtag
const SYSCRG_JTAG_CERTIFICATION_TRNG: usize = 0x2F4 / 4;
const SYSCRG_CLOCK_COUNT: usize = SYSCRG_JTAG_CERTIFICATION_TRNG + 1;
@@ -173,7 +181,7 @@ trait ClockDefinition {
const CLOCKS: &'static [Option<(&'static str, ClockDef)>];
}
const SYSCRG_CLOCKS: &'static [Option<(&'static str, ClockDef)>] = &const {
const SYSCRG_CLOCKS: &[Option<(&'static str, ClockDef)>] = &const {
use ClockDef::*;
let mut t = [const { None }; SYSCRG_CLOCK_COUNT];
@@ -228,11 +236,21 @@ const SYSCRG_CLOCKS: &'static [Option<(&'static str, ClockDef)>] = &const {
// uart
t[SYSCRG_UART0_APB] = Some(("clk_uart0_apb", Gate(SYSCRG_APB0)));
t[SYSCRG_UART0_CORE] = Some(("clk_uart0_core", Gate(SYSCRG_OSC)));
t[SYSCRG_UART1_APB] = Some(("clk_uart1_apb", Gate(SYSCRG_APB0)));
t[SYSCRG_UART1_CORE] = Some(("clk_uart1_core", Gate(SYSCRG_OSC)));
t[SYSCRG_UART2_APB] = Some(("clk_uart2_apb", Gate(SYSCRG_APB0)));
t[SYSCRG_UART2_CORE] = Some(("clk_uart2_core", Gate(SYSCRG_OSC)));
t[SYSCRG_UART3_APB] = Some(("clk_uart3_apb", Gate(SYSCRG_APB0)));
t[SYSCRG_UART3_CORE] = Some(("clk_uart3_core", GateDiv(10, SYSCRG_PERH_ROOT)));
t[SYSCRG_UART4_APB] = Some(("clk_uart4_apb", Gate(SYSCRG_APB0)));
t[SYSCRG_UART4_CORE] = Some(("clk_uart4_core", GateDiv(10, SYSCRG_PERH_ROOT)));
t[SYSCRG_UART5_APB] = Some(("clk_uart5_apb", Gate(SYSCRG_APB0)));
t[SYSCRG_UART5_CORE] = Some(("clk_uart5_core", GateDiv(10, SYSCRG_PERH_ROOT)));
t
};
const AONCRG_CLOCKS: &'static [Option<(&'static str, ClockDef)>] = &const {
const AONCRG_CLOCKS: &[Option<(&'static str, ClockDef)>] = &const {
use ClockDef::*;
let mut t = [const { None }; AONCRG_CLOCK_COUNT];
@@ -264,7 +282,7 @@ const AONCRG_CLOCKS: &'static [Option<(&'static str, ClockDef)>] = &const {
t
};
const STGCRG_CLOCKS: &'static [Option<(&'static str, ClockDef)>] = &const {
const STGCRG_CLOCKS: &[Option<(&'static str, ClockDef)>] = &const {
use ClockDef::*;
let mut t = [const { None }; STGCRG_CLOCK_COUNT];
+7
View File
@@ -0,0 +1,7 @@
#![allow(unsafe_op_in_unsafe_fn, clippy::eq_op, clippy::erasing_op)]
#![no_std]
extern crate alloc;
mod clocks;
mod pinctrl;
+444
View File
@@ -0,0 +1,444 @@
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::{
DeviceTreePropertyRead, TProp,
driver::{
DeviceTreeGpioPins, DeviceTreePinController, Node, ProbeContext, device_tree_driver,
util::{GenericPinctrlBiasConfig, GenericPinctrlConfig, generic_gpio_config},
},
};
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIoMut};
use libk_util::{OneTimeInit, bit::BitField, sync::IrqSafeSpinlock};
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::{Node, ProbeContext, device_tree_driver};
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,30 +1,29 @@
//! 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, InterruptHandler, Irq, IrqHandle, IrqOptions, IrqVector,
},
};
use device_tree::{
driver::{
device_tree_driver, lookup_phandle, DeviceTreeInterruptController, Node, ProbeContext,
},
DeviceTreePropertyRead, TProp,
driver::{
DeviceTreeInterruptController, Node, ProbeContext, device_tree_driver, lookup_phandle,
},
};
use kernel_arch_riscv64::boot_hart_id;
use libk::{
arch::Cpu,
device::{interrupt::FixedInterruptTable, register_external_interrupt_controller},
};
use libk::{arch::Cpu, device::register_external_interrupt_controller};
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIo};
use libk_util::{sync::spin_rwlock::IrqSafeRwLock, OneTimeInit};
use libk_util::{OneTimeInit, sync::spin_rwlock::IrqSafeRwLock};
use tock_registers::{
interfaces::{Readable, Writeable},
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;
@@ -80,8 +79,7 @@ register_structs! {
struct Context {
enable: IrqSafeRwLock<DeviceMemoryIo<'static, ContextEnableRegs>>,
control: IrqSafeRwLock<DeviceMemoryIo<'static, ContextControlRegs>>,
// TODO scale the table depending on effective MAX_IRQS value
table: IrqSafeRwLock<FixedInterruptTable<128>>,
table: FixedInterruptTable,
}
struct Inner {
@@ -166,13 +164,13 @@ impl ExternalInterruptController for Plic {
.inspect_err(|_| log::error!("plic: no context for hart {bsp_hart_id}"))?
.context
.get();
let mut table = context.table.write();
// let mut table = context.table.write();
log::info!(
"Bind irq #{irq} -> hart {bsp_hart_id}, {:?}",
handler.display_name()
);
table.insert(irq as usize, handler)?;
context.table.insert(irq as usize, handler);
Ok(())
}
@@ -186,7 +184,7 @@ impl ExternalInterruptController for Plic {
let context = context.context.get();
let control = context.control.write();
let table = context.table.read();
// let table = context.table.read();
loop {
let irq = control.CLAIM.get();
@@ -195,9 +193,7 @@ impl ExternalInterruptController for Plic {
}
let vector = IrqVector::Irq(Irq::External(irq));
if let Some(handler) = table.handler(irq as usize) {
handler.clone().handle_irq(vector);
} else {
if !context.table.handle_irq_line(irq as usize, vector) {
log::warn!("plic: no handler for IRQ #{irq}");
}
@@ -245,7 +241,7 @@ impl Device for Plic {
context.context.init(Context {
enable: IrqSafeRwLock::new(enable),
control: IrqSafeRwLock::new(control),
table: IrqSafeRwLock::new(FixedInterruptTable::new()),
table: FixedInterruptTable::new(MAX_IRQS), // table: IrqSafeRwLock::new(FixedInterruptTable::new()),
});
}
@@ -264,11 +260,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(),
})
}
+1 -1
View File
@@ -1,7 +1,7 @@
[package]
name = "ygg_driver_pci"
version = "0.1.0"
edition = "2021"
edition = "2024"
authors = ["Mark Poliakov <mark@alnyan.me>"]
[dependencies]
+1 -1
View File
@@ -498,7 +498,7 @@ impl MsiXVectorTableAccess<'_> {
impl MsiXVectorTable<'_> {
unsafe fn memory_from_raw_parts(base: PhysicalAddress, len: usize) -> Result<Self, Error> {
let vectors = DeviceMemoryIoMut::map_slice(base, len, Default::default())?;
let vectors = unsafe { DeviceMemoryIoMut::map_slice(base, len, Default::default()) }?;
Ok(Self {
access: MsiXVectorTableAccess::Memory(vectors),
len,
+15 -16
View File
@@ -9,13 +9,13 @@ use device_api::{
},
};
use libk::device::external_interrupt_controller;
use libk_util::{sync::spin_rwlock::IrqSafeRwLock, OneTimeInit};
use libk_util::{OneTimeInit, sync::spin_rwlock::IrqSafeRwLock};
use yggdrasil_abi::error::Error;
use crate::{
PciAddress, PciCommandRegister, PciConfigSpace, PciConfigurationSpace, PciSegmentInfo,
capability::{MsiCapability, MsiXCapability, MsiXVectorTable},
driver::PciDriver,
PciAddress, PciCommandRegister, PciConfigSpace, PciConfigurationSpace, PciSegmentInfo,
};
/// Describes a PCI device
@@ -152,22 +152,21 @@ impl PciDeviceInfo {
let mut result = None;
if want_msix
&& let Some(mut msix) = self.config_space.capability::<MsiXCapability>()
&& let Ok(mut vt) = msix.vector_table()
{
if let Ok(mut vt) = msix.vector_table() {
if let Some(mut msi) = self.config_space.capability::<MsiCapability>() {
msi.set_enabled(false);
}
vt.mask_all();
msix.set_function_mask(false);
msix.set_enabled(true);
result = Some(ConfiguredInterruptMode::MsiX(
msi_route.controller.clone(),
vt,
));
if let Some(mut msi) = self.config_space.capability::<MsiCapability>() {
msi.set_enabled(false);
}
vt.mask_all();
msix.set_function_mask(false);
msix.set_enabled(true);
result = Some(ConfiguredInterruptMode::MsiX(
msi_route.controller.clone(),
vt,
));
}
// Fall back to MSI if MSI-x is not available or not requested
+1 -1
View File
@@ -5,8 +5,8 @@ use device_api::interrupt::MessageInterruptController;
use libk::error::Error;
use crate::{
device::{PciInterrupt, PciInterruptRoute, PciMsiRoute},
PciAddress,
device::{PciInterrupt, PciInterruptRoute, PciMsiRoute},
};
#[derive(Debug)]
+3 -3
View File
@@ -1,6 +1,6 @@
//! PCI/PCIe bus interfaces
#![no_std]
#![feature(let_chains, decl_macro)]
#![feature(decl_macro)]
#![allow(clippy::missing_transmute_annotations, clippy::identity_op)]
extern crate alloc;
@@ -20,7 +20,7 @@ use libk::{
fs::sysfs::{self, object::KObject},
};
use libk_mm::address::PhysicalAddress;
use libk_util::{sync::IrqSafeSpinlock, OneTimeInit};
use libk_util::{OneTimeInit, sync::IrqSafeSpinlock};
use space::legacy;
use yggdrasil_abi::{error::Error, primitive_enum};
@@ -36,9 +36,9 @@ mod nodes;
mod space;
pub use space::{
PciConfigSpace, PciConfigurationSpace,
ecam::PciEcam,
legacy::{LegacyPciAccess, PciLegacyConfigurationSpace},
PciConfigSpace, PciConfigurationSpace,
};
bitflags! {
+1 -1
View File
@@ -17,7 +17,7 @@ pub macro pci_driver(
matches: [$($kind:ident $match:tt),+ $(,)?],
driver: $driver:tt
) {
#[link_section = ".init_array"]
#[unsafe(link_section = ".init_array")]
#[used]
static __REGISTER_FN: extern "C" fn() = __register_fn;
+1 -1
View File
@@ -8,7 +8,7 @@ use libk::{
};
use libk_util::sync::IrqSafeSpinlock;
use crate::{device::PciBusDevice, PciBaseAddress, PciCapabilityId, PciConfigurationSpace};
use crate::{PciBaseAddress, PciCapabilityId, PciConfigurationSpace, device::PciBusDevice};
pub(crate) fn make_sysfs_object(
device: PciBusDevice,
+2 -2
View File
@@ -32,7 +32,7 @@ impl PciEcam {
/// regions. The address must be aligned to a 4KiB boundary and be valid for accesses within a
/// 4KiB-sized range.
pub unsafe fn map(phys_addr: PhysicalAddress) -> Result<Self, Error> {
let mapping = DeviceMemoryMapping::map(phys_addr, 0x1000, Default::default())?;
let mapping = unsafe { DeviceMemoryMapping::map(phys_addr, 0x1000, Default::default()) }?;
Ok(Self { mapping })
}
@@ -53,7 +53,7 @@ impl PciEcam {
+ address.function as usize)
* 0x1000,
);
let this = Self::map(phys_addr)?;
let this = unsafe { Self::map(phys_addr) }?;
Ok(if this.is_valid() { Some(this) } else { None })
}
+6 -12
View File
@@ -2,7 +2,7 @@ use alloc::sync::Arc;
use legacy::PciLegacyConfigurationSpace;
use super::{PciAddress, PciBaseAddress, PciCapability, PciCapabilityId, PciEcam};
use crate::{device::PciInterruptPin, PciCommandRegister, PciStatusRegister};
use crate::{PciCommandRegister, PciStatusRegister, device::PciInterruptPin};
pub(super) mod ecam;
pub(super) mod legacy;
@@ -26,9 +26,7 @@ macro_rules! pci_config_field_setter {
$self.write_u32($offset, $value)
};
($self:ident, u16, $offset:expr, $value:expr) => {{
$self.write_u16($offset, $value)
}};
($self:ident, u16, $offset:expr, $value:expr) => {{ $self.write_u16($offset, $value) }};
($self:ident, u8, $offset:expr, $value:expr) => {
$self.write_u8($offset, $value)
@@ -222,11 +220,7 @@ pub trait PciConfigurationSpace {
fn interrupt_line(&self) -> Option<u8> {
let value = self.read_u8(0x3C);
if value < 16 {
Some(value)
} else {
None
}
if value < 16 { Some(value) } else { None }
}
/// # Safety
@@ -248,7 +242,7 @@ pub trait PciConfigurationSpace {
PciBaseAddress::Memory32(_) => PciBaseAddress::Memory32(0xFFFFFFF0),
PciBaseAddress::Memory64(_) => PciBaseAddress::Memory64(0xFFFFFFFFFFFFFFF0),
};
self.set_bar(index, mask_value);
unsafe { self.set_bar(index, mask_value) };
let new_value = self.bar(index).unwrap();
let size = match new_value {
@@ -258,7 +252,7 @@ pub trait PciConfigurationSpace {
_ => 0,
};
self.set_bar(index, orig_value);
unsafe { self.set_bar(index, orig_value) };
self.set_command(cmd);
size
@@ -302,7 +296,7 @@ pub trait PciConfigurationSpace {
fn bar(&self, index: usize) -> Option<PciBaseAddress> {
assert!(index < 6);
if index % 2 == 0 {
if index.is_multiple_of(2) {
let w0 = self.read_u32(0x10 + index * 4);
match w0 & 1 {
+13 -8
View File
@@ -6,13 +6,12 @@ use libk_util::{queue::UnboundedMpmcQueue, sync::spin_rwlock::IrqSafeRwLock};
use crate::{
class_driver,
device::{UsbBusAddress, UsbDeviceAccess},
UsbHostController,
host::UsbHostController,
};
pub struct UsbBusManager {
busses: IrqSafeRwLock<BTreeMap<u16, Arc<dyn UsbHostController>>>,
devices: IrqSafeRwLock<BTreeMap<UsbBusAddress, Arc<UsbDeviceAccess>>>,
last_bus_address: AtomicU16,
}
@@ -33,9 +32,9 @@ impl UsbBusManager {
QUEUE.push_back(device);
}
pub fn detach_device(address: UsbBusAddress) {
pub async fn detach_device(address: UsbBusAddress) {
if let Some(device) = BUS_MANAGER.devices.write().remove(&address) {
device.handle_detach();
device.handle_detach().await;
}
}
}
@@ -45,20 +44,26 @@ pub async fn bus_handler() {
loop {
let new_device = QUEUE.pop_front().await;
let id_vendor = new_device.device_descriptor.id_vendor;
let id_product = new_device.device_descriptor.id_product;
log::info!(
"New {:?}-speed USB device connected: {}",
"{} ({}): {:?}-speed USB device connected: {:?} {:?} ({:04x}:{:04x})",
new_device.bus_address(),
new_device.port_string(),
new_device.speed(),
new_device.bus_address()
new_device.vendor_str,
new_device.product_str,
id_vendor,
id_product
);
class_driver::spawn_driver(new_device).await.ok();
class_driver::spawn_driver(new_device).await;
}
}
static BUS_MANAGER: UsbBusManager = UsbBusManager {
busses: IrqSafeRwLock::new(BTreeMap::new()),
devices: IrqSafeRwLock::new(BTreeMap::new()),
last_bus_address: AtomicU16::new(0),
};
static QUEUE: UnboundedMpmcQueue<Arc<UsbDeviceAccess>> = UnboundedMpmcQueue::new();
@@ -2,13 +2,12 @@ use core::mem::MaybeUninit;
use alloc::{boxed::Box, sync::Arc};
use async_trait::async_trait;
use yggdrasil_abi::io::{KeyboardKey, KeyboardKeyEvent};
use yggdrasil_abi::io::{KeyboardKey, KeyboardKeyEvent, MouseEvent};
use crate::{device::UsbDeviceAccess, error::UsbError, info::UsbDeviceClass};
use super::{UsbClassInfo, UsbDriver};
use crate::{class_driver::UsbInterfaceDriver, device::UsbInterfaceAccess, error::UsbError};
pub struct UsbHidKeyboardDriver;
pub struct UsbHidMouseDriver;
const MODIFIER_MAP: &[KeyboardKey] = &[
KeyboardKey::LControl,
@@ -54,11 +53,20 @@ impl KeyboardState {
54 => KeyboardKey::Char(b','),
55 => KeyboardKey::Char(b'.'),
56 => KeyboardKey::Char(b'/'),
58..=69 => KeyboardKey::F(k - 58),
73 => KeyboardKey::Insert,
74 => KeyboardKey::Home,
75 => KeyboardKey::PageUp,
76 => KeyboardKey::Delete,
77 => KeyboardKey::End,
78 => KeyboardKey::PageDown,
79 => KeyboardKey::Right,
80 => KeyboardKey::Left,
81 => KeyboardKey::Down,
82 => KeyboardKey::Up,
_ => {
log::debug!("Unknown key: {}", k);
log::warn!("Unknown key: {}", k);
KeyboardKey::Unknown
}
}
@@ -125,14 +133,25 @@ impl KeyboardState {
}
#[async_trait]
impl UsbDriver for UsbHidKeyboardDriver {
async fn run(self: Arc<Self>, device: Arc<UsbDeviceAccess>) -> Result<(), UsbError> {
// TODO not sure whether to use boot protocol (easy) or GetReport
let config = device.select_configuration(|_| true).await?.unwrap();
impl UsbInterfaceDriver for UsbHidKeyboardDriver {
async fn run(self: Arc<Self>, interface: UsbInterfaceAccess) -> Result<(), UsbError> {
log::info!("{}: HID keyboard", interface.address());
log::info!("Setup HID keyboard");
let pipe = device
.open_interrupt_in_pipe(1, config.endpoints[0].max_packet_size as u16)
let endpoint_infos = interface.endpoints();
if endpoint_infos.is_empty() {
log::warn!(
"{}: no available endpoints in interface description",
interface.address()
);
return Err(UsbError::InvalidConfiguration);
}
let pipe = interface
.device()
.open_interrupt_in_pipe(
endpoint_infos[0].number,
endpoint_infos[0].max_packet_size as _,
)
.await?;
let mut buffer = [0; 8];
@@ -156,7 +175,7 @@ impl UsbDriver for UsbHidKeyboardDriver {
for &event in events {
log::trace!("Generic Keyboard: {:?}", event);
ygg_driver_input::send_event(event);
ygg_driver_input::send_keyboard_event(event);
}
}
}
@@ -165,12 +184,61 @@ impl UsbDriver for UsbHidKeyboardDriver {
"USB HID Keyboard"
}
fn probe(&self, class: &UsbClassInfo, _device: &UsbDeviceAccess) -> bool {
log::info!(
"class = {:?}, subclass = {:02x}",
class.class,
class.subclass
);
class.class == UsbDeviceClass::Hid && (class.subclass == 0x00 || class.subclass == 0x01)
fn probe(&self, class: u8, subclass: u8, protocol: u8) -> bool {
class == 0x03 && subclass == 0x01 && protocol == 0x01
}
}
#[async_trait]
impl UsbInterfaceDriver for UsbHidMouseDriver {
async fn run(self: Arc<Self>, interface: UsbInterfaceAccess) -> Result<(), UsbError> {
log::info!("{}: HID mouse", interface.address());
let endpoint_infos = interface.endpoints();
if endpoint_infos.is_empty() {
log::warn!(
"{}: no available endpoints in interface description",
interface.address()
);
return Err(UsbError::InvalidConfiguration);
}
let pipe = interface
.device()
.open_interrupt_in_pipe(
endpoint_infos[0].number,
endpoint_infos[0].max_packet_size as _,
)
.await?;
let mut buffer = [0; 8];
loop {
let len = pipe.read(&mut buffer).await?;
if len < 4 {
continue;
}
let data = &buffer[..len];
let buttons = data[0];
let dx = data[1] as i8;
let dy = data[2] as i8;
log::trace!("mouse {dx:+},{dy:+} {buttons:08b}");
ygg_driver_input::send_mouse_event(MouseEvent {
dx,
dy,
buttons,
unused: 0,
});
}
}
fn name(&self) -> &'static str {
"USB HID Mouse"
}
fn probe(&self, class: u8, subclass: u8, protocol: u8) -> bool {
class == 0x03 && subclass == 0x01 && protocol == 0x02
}
}
@@ -0,0 +1,384 @@
use core::{mem::MaybeUninit, num::NonZeroU8, time::Duration};
use alloc::{boxed::Box, sync::Arc};
use async_trait::async_trait;
use libk::{task::runtime, time::monotonic_time};
use yggdrasil_abi::bitflags;
use crate::{
class_driver::UsbDeviceDriver,
descriptor::UsbHubDescriptorHeader,
device::{UsbDeviceAccess, UsbSpeed},
error::UsbError,
pipe::control::ControlTransferSetup,
};
pub struct UsbHubDriver;
const BM_REQUEST_TYPE_GET_HUB_DESCRIPTOR: u8 = 0b10100000;
const BM_REQUEST_TYPE_GET_PORT_STATUS: u8 = 0b10100011;
const BM_REQUEST_TYPE_PORT_FEATURE: u8 = 0b00100011;
const B_REQUEST_GET_STATUS: u8 = 0x00;
const B_REQUEST_CLEAR_FEATURE: u8 = 0x01;
const B_REQUEST_SET_FEATURE: u8 = 0x03;
const B_REQUEST_GET_DESCRIPTOR: u8 = 0x06;
#[allow(unused)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u16)]
pub enum PortFeatureSelector {
PortConnection = 0,
PortEnable = 1,
PortSuspend = 2,
PortOverCurrent = 3,
PortReset = 4,
PortPower = 8,
PortLowSpeed = 9,
CPortConnection = 16,
CPortEnable = 17,
CPortSuspend = 18,
CPortOverCurrent = 19,
CPortReset = 20,
PortTest = 21,
PortIndicator = 22,
}
bitflags! {
pub struct UsbHubPortStatus: u16 {
const PORT_CONNECTION: bit 0;
const PORT_ENABLE: bit 1;
const PORT_SUSPEND: bit 2;
const PORT_OVER_CURRENT: bit 3;
const PORT_RESET: bit 4;
const PORT_POWER: bit 8;
const PORT_LOW_SPEED: bit 9;
const PORT_HIGH_SPEED: bit 10;
const PORT_TEST: bit 11;
const PORT_INDICATOR: bit 12;
}
}
bitflags! {
pub struct UsbHubPortChange: u16 {
const C_PORT_CONNECTION: bit 0;
const C_PORT_ENABLE: bit 1;
const C_PORT_SUSPEND: bit 2;
const C_PORT_OVER_CURRENT: bit 3;
const C_PORT_RESET: bit 4;
}
}
struct UsbHub {
device: Arc<UsbDeviceAccess>,
hub_descriptor: UsbHubDescriptorHeader,
children: u64,
// TODO extra info
}
impl UsbHub {
pub async fn setup(device: Arc<UsbDeviceAccess>) -> Result<Self, UsbError> {
let control_pipe = device.control_pipe();
let mut hub_descriptor = MaybeUninit::uninit();
let len = control_pipe
.control_transfer_in(
ControlTransferSetup {
bm_request_type: BM_REQUEST_TYPE_GET_HUB_DESCRIPTOR,
b_request: B_REQUEST_GET_DESCRIPTOR,
w_value: 0,
w_index: 0,
w_length: size_of::<UsbHubDescriptorHeader>() as _,
},
hub_descriptor.as_bytes_mut(),
)
.await?;
if len != size_of::<UsbHubDescriptorHeader>() {
return Err(UsbError::TruncatedDescriptor(
size_of::<UsbHubDescriptorHeader>(),
len,
));
}
let hub_descriptor: UsbHubDescriptorHeader = unsafe { hub_descriptor.assume_init() };
if hub_descriptor.b_nr_ports < 1 {
log::warn!(
"{}: ignoring hub with zero downstream ports",
device.device.bus_address()
);
return Err(UsbError::InvalidDescriptorField);
}
log::info!(
"{}: setting up {:?}-speed hub, {} downstream ports",
device.device.bus_address(),
device.device.speed(),
hub_descriptor.b_nr_ports
);
device.configure_hub(&hub_descriptor).await?;
// TODO power on the ports?
Ok(Self {
device,
hub_descriptor,
children: 0,
})
}
async fn set_port_feature(
&mut self,
port: NonZeroU8,
feature: PortFeatureSelector,
) -> Result<(), UsbError> {
self.device
.control_pipe()
.control_transfer(ControlTransferSetup {
bm_request_type: BM_REQUEST_TYPE_PORT_FEATURE,
b_request: B_REQUEST_SET_FEATURE,
w_value: feature as _,
w_index: port.get() as _,
w_length: 0,
})
.await
}
async fn clear_port_feature(
&mut self,
port: NonZeroU8,
feature: PortFeatureSelector,
) -> Result<(), UsbError> {
self.device
.control_pipe()
.control_transfer(ControlTransferSetup {
bm_request_type: BM_REQUEST_TYPE_PORT_FEATURE,
b_request: B_REQUEST_CLEAR_FEATURE,
w_value: feature as _,
w_index: port.get() as _,
w_length: 0,
})
.await
}
async fn clear_port_changes(
&mut self,
port: NonZeroU8,
change: UsbHubPortChange,
) -> Result<(), UsbError> {
if change.contains(UsbHubPortChange::C_PORT_RESET) {
self.clear_port_feature(port, PortFeatureSelector::CPortReset)
.await?;
}
if change.contains(UsbHubPortChange::C_PORT_CONNECTION) {
self.clear_port_feature(port, PortFeatureSelector::CPortConnection)
.await?;
}
if change.contains(UsbHubPortChange::C_PORT_ENABLE) {
self.clear_port_feature(port, PortFeatureSelector::CPortEnable)
.await?;
}
Ok(())
}
async fn get_port_status(
&mut self,
port: NonZeroU8,
) -> Result<(UsbHubPortStatus, UsbHubPortChange), UsbError> {
let mut buffer = [MaybeUninit::uninit(); 2];
let len = self
.device
.control_pipe()
.control_transfer_in(
ControlTransferSetup {
bm_request_type: BM_REQUEST_TYPE_GET_PORT_STATUS,
b_request: B_REQUEST_GET_STATUS,
w_value: 0,
w_index: port.get() as _,
w_length: 4,
},
buffer.as_bytes_mut(),
)
.await?;
if len != 4 {
return Err(UsbError::TruncatedDescriptor(4, len));
}
let [w0, w1] = unsafe { MaybeUninit::array_assume_init(buffer) };
let status = UsbHubPortStatus(w0);
let change = UsbHubPortChange(w1);
Ok((status, change))
}
async fn reset_port(&mut self, port: NonZeroU8) -> Result<UsbHubPortStatus, UsbError> {
self.set_port_feature(port, PortFeatureSelector::PortReset)
.await?;
// Wait for reset to be asserted
let deadline = monotonic_time() + Duration::from_secs(3);
let mut status;
let mut change;
loop {
(status, change) = self.get_port_status(port).await?;
self.clear_port_changes(port, change).await?;
if status.contains(UsbHubPortStatus::PORT_RESET)
|| status.contains(UsbHubPortStatus::PORT_ENABLE)
|| !status.contains(UsbHubPortStatus::PORT_CONNECTION)
{
// Port reset got asserted or port got enabled
break;
}
if monotonic_time() >= deadline {
log::warn!("Port reset did not assert in 3 sec");
return Ok(status);
}
}
if status.contains(UsbHubPortStatus::PORT_ENABLE)
|| !status.contains(UsbHubPortStatus::PORT_CONNECTION)
{
return Ok(status);
}
todo!()
}
async fn setup_connected_port(&mut self, port: NonZeroU8) -> Result<(), UsbError> {
let status = self.reset_port(port).await?;
if !status.contains(UsbHubPortStatus::PORT_CONNECTION) {
log::warn!(
"{}: port {} disconnected during reset",
self.device.bus_address(),
port
);
return Ok(());
}
if !status.contains(UsbHubPortStatus::PORT_ENABLE) {
log::warn!("{}: port {} did not reset", self.device.bus_address(), port);
return Ok(());
}
let usb_speed = match (
status.contains(UsbHubPortStatus::PORT_LOW_SPEED),
status.contains(UsbHubPortStatus::PORT_HIGH_SPEED),
) {
(false, false) => UsbSpeed::Full,
(true, false) => UsbSpeed::Low,
(false, true) => UsbSpeed::High,
(true, true) => todo!(),
};
log::info!(
"{}: hub port {}: {:?}-speed device connected",
self.device.bus_address(),
port,
usb_speed
);
let port_string = self.device.port_string().append(port);
if let Err(error) = self
.device
.host_controller()
.setup_hub_device(self.device.clone(), port_string, usb_speed)
.await
{
log::error!(
"{}: hub port {} ({}) setup failed: {:?}",
self.device.bus_address(),
port,
port_string,
error
);
return Ok(());
}
self.children |= 1 << (port.get() - 1);
Ok(())
}
async fn disconnect_port(&mut self, port: NonZeroU8) -> Result<(), UsbError> {
let port_string = self.device.port_string().append(port);
self.children &= !(1 << (port.get() - 1));
self.device
.host_controller()
.disconnect_device(port_string)
.await
}
async fn poll_port(&mut self, port: NonZeroU8) -> Result<(), UsbError> {
let (status, change) = self.get_port_status(port).await?;
if change.contains(UsbHubPortChange::C_PORT_CONNECTION) {
// Clear feature: C_PORT_CONNECTION
self.clear_port_feature(port, PortFeatureSelector::CPortConnection)
.await?;
if status.contains(UsbHubPortStatus::PORT_CONNECTION) {
self.setup_connected_port(port).await?;
} else if let Err(error) = self.disconnect_port(port).await {
log::warn!(
"{}: hub port {} did not disconnect cleanly: {:?}",
self.device.bus_address(),
port,
error
);
}
}
Ok(())
}
async fn poll(&mut self) -> bool {
for i in 0..self.hub_descriptor.b_nr_ports {
let port = NonZeroU8::new(i + 1).unwrap();
if let Err(error) = self.poll_port(port).await {
log::error!(
"{}: hub port {} poll error: {:?}",
self.device.bus_address(),
port,
error
);
return false;
}
}
true
}
async fn cleanup(&mut self) {
for i in 0..self.hub_descriptor.b_nr_ports {
let port = NonZeroU8::new(i + 1).unwrap();
if self.children & (1 << i) != 0 {
if let Err(error) = self.disconnect_port(port).await {
log::warn!(
"{}: downstream {} disconnect error: {:?}",
self.device.bus_address(),
port,
error
);
}
}
}
}
async fn run(mut self) -> Result<(), UsbError> {
loop {
if !self.poll().await {
self.cleanup().await;
return Ok(());
}
runtime::sleep(Duration::from_millis(100)).await;
}
}
}
#[async_trait]
impl UsbDeviceDriver for UsbHubDriver {
async fn run(self: Arc<Self>, device: Arc<UsbDeviceAccess>) -> Result<(), UsbError> {
let hub = UsbHub::setup(device).await?;
hub.run().await
}
fn name(&self) -> &'static str {
"USB Hub"
}
fn probe(&self, class: u8, subclass: u8, protocol: u8, vid: u16, pid: u16) -> bool {
let _ = (protocol, vid, pid);
class == 0x09 && subclass == 0x00
}
}
@@ -7,20 +7,25 @@ use libk::{
dma::{DmaBuffer, DmaSliceMut},
error::Error,
};
use ygg_driver_scsi::{transport::ScsiTransport, ScsiEnclosure};
use ygg_driver_scsi::{ScsiEnclosure, transport::ScsiTransport};
use crate::{
class_driver::UsbInterfaceDriver,
communication::UsbDirection,
device::{UsbDeviceAccess, UsbDeviceDetachHandler},
device::{UsbDeviceDetachHandler, UsbInterfaceAccess},
error::UsbError,
info::{UsbDeviceClass, UsbEndpointType},
info::UsbEndpointType,
pipe::{
control::{ControlTransferSetup, UsbClassSpecificRequest},
control::ControlTransferSetup,
normal::{UsbBulkInPipeAccess, UsbBulkOutPipeAccess},
},
};
use super::{UsbClassInfo, UsbDriver};
const BM_REQUEST_TYPE_BULK_ONLY_MASS_STORAGE_RESET: u8 = 0b00100001;
const BM_REQUEST_TYPE_GET_MAX_LUN: u8 = 0b10100001;
const B_REQUEST_BULK_ONLY_MASS_STORAGE_RESET: u8 = 0b11111111;
const B_REQUEST_GET_MAX_LUN: u8 = 0b11111110;
pub struct UsbMassStorageDriverBulkOnly;
@@ -50,7 +55,7 @@ struct Csw {
struct Bbb {
#[allow(unused)]
device: Arc<UsbDeviceAccess>,
interface: UsbInterfaceAccess,
in_pipe: UsbBulkInPipeAccess,
out_pipe: UsbBulkOutPipeAccess,
last_tag: u32,
@@ -60,12 +65,12 @@ struct DetachHandler(Arc<ScsiEnclosure>);
impl Bbb {
pub fn new(
device: Arc<UsbDeviceAccess>,
interface: UsbInterfaceAccess,
in_pipe: UsbBulkInPipeAccess,
out_pipe: UsbBulkOutPipeAccess,
) -> Result<Self, UsbError> {
Ok(Self {
device,
interface,
in_pipe,
out_pipe,
last_tag: 0,
@@ -172,60 +177,49 @@ impl ScsiTransport for Bbb {
}
}
#[async_trait]
impl UsbDeviceDetachHandler for DetachHandler {
fn handle_device_detach(&self) {
log::info!("Mass storage detached");
self.0.detach();
async fn handle_device_detach(&self) {
self.0.detach().await;
}
}
#[derive(Debug, Pod, Zeroable, Clone, Copy)]
#[repr(C)]
pub struct BulkOnlyMassStorageReset;
#[derive(Debug, Pod, Zeroable, Clone, Copy)]
#[repr(C)]
pub struct GetMaxLun;
impl UsbClassSpecificRequest for BulkOnlyMassStorageReset {
const BM_REQUEST_TYPE: u8 = 0b00100001;
const B_REQUEST: u8 = 0b11111111;
}
impl UsbClassSpecificRequest for GetMaxLun {
const BM_REQUEST_TYPE: u8 = 0b10100001;
const B_REQUEST: u8 = 0b11111110;
}
#[async_trait]
impl UsbDriver for UsbMassStorageDriverBulkOnly {
async fn run(self: Arc<Self>, device: Arc<UsbDeviceAccess>) -> Result<(), UsbError> {
// TODO filter to only accept BBB config
let config = device.select_configuration(|_| true).await?.unwrap();
// Bulk-in, bulk-out
assert_eq!(config.endpoints.len(), 2);
let control_pipe = device.control_pipe();
let (in_index, in_info) = config
.find_endpoint(|ep| ep.is(UsbEndpointType::Bulk, UsbDirection::In))
.ok_or(UsbError::InvalidConfiguration)?;
let (out_index, out_info) = config
.find_endpoint(|ep| ep.is(UsbEndpointType::Bulk, UsbDirection::Out))
.ok_or(UsbError::InvalidConfiguration)?;
let in_pipe = device
.open_bulk_in_pipe(in_index, in_info.max_packet_size as u16)
impl UsbInterfaceDriver for UsbMassStorageDriverBulkOnly {
async fn run(self: Arc<Self>, interface: UsbInterfaceAccess) -> Result<(), UsbError> {
let endpoints = interface.endpoints();
let bulk_in = endpoints
.iter()
.find(|ep| ep.ty == UsbEndpointType::Bulk && ep.direction == UsbDirection::In);
let bulk_out = endpoints
.iter()
.find(|ep| ep.ty == UsbEndpointType::Bulk && ep.direction == UsbDirection::Out);
let (Some(bulk_in), Some(bulk_out)) = (bulk_in, bulk_out) else {
log::warn!(
"{}: BBB mass storage needs at least 2 bulk endpoints",
interface.address()
);
return Err(UsbError::InvalidConfiguration);
};
let control_pipe = interface.device().control_pipe();
let in_pipe = interface
.device()
.open_bulk_in_pipe(bulk_in.number, bulk_in.max_packet_size as _)
.await?;
let out_pipe = device
.open_bulk_out_pipe(out_index, out_info.max_packet_size as u16)
let out_pipe = interface
.device()
.open_bulk_out_pipe(bulk_out.number, bulk_out.max_packet_size as _)
.await?;
// Perform a Bulk-Only Mass Storage Reset
// TODO interface id?
control_pipe
.control_transfer(ControlTransferSetup {
bm_request_type: BulkOnlyMassStorageReset::BM_REQUEST_TYPE,
b_request: BulkOnlyMassStorageReset::B_REQUEST,
bm_request_type: BM_REQUEST_TYPE_BULK_ONLY_MASS_STORAGE_RESET,
b_request: B_REQUEST_BULK_ONLY_MASS_STORAGE_RESET,
w_value: 0,
w_index: 0,
w_index: interface.number() as _,
w_length: 0,
})
.await?;
@@ -236,10 +230,10 @@ impl UsbDriver for UsbMassStorageDriverBulkOnly {
let len = control_pipe
.control_transfer_in(
ControlTransferSetup {
bm_request_type: GetMaxLun::BM_REQUEST_TYPE,
b_request: GetMaxLun::B_REQUEST,
bm_request_type: BM_REQUEST_TYPE_GET_MAX_LUN,
b_request: B_REQUEST_GET_MAX_LUN,
w_value: 0,
w_index: 0,
w_index: interface.number() as _,
w_length: 1,
},
&mut buffer,
@@ -251,13 +245,13 @@ impl UsbDriver for UsbMassStorageDriverBulkOnly {
unsafe { buffer[0].assume_init() }
};
let bbb = Bbb::new(device.clone(), in_pipe, out_pipe)?;
let bbb = Bbb::new(interface.clone(), in_pipe, out_pipe)?;
let scsi = ScsiEnclosure::setup(Box::new(bbb), max_lun as usize + 1)
.await
.inspect_err(|error| log::error!("msc: scsi error {error:?}"))
.map_err(|_| UsbError::DriverError)?;
let detach = DetachHandler(scsi.clone());
device.set_detach_handler(Arc::new(detach));
interface.device().set_detach_handler(Arc::new(detach));
Ok(())
}
@@ -266,8 +260,9 @@ impl UsbDriver for UsbMassStorageDriverBulkOnly {
"USB Mass Storage"
}
fn probe(&self, class: &UsbClassInfo, _device: &UsbDeviceAccess) -> bool {
fn probe(&self, class: u8, subclass: u8, protocol: u8) -> bool {
// TODO support other protocols
class.class == UsbDeviceClass::MassStorage && class.interface_protocol_number == 0x50
let _ = subclass;
class == 0x08 && protocol == 0x50
}
}
+109 -81
View File
@@ -1,117 +1,145 @@
// TODO Split drivers into device and interface drivers
use alloc::{boxed::Box, sync::Arc, vec::Vec};
use async_trait::async_trait;
use libk::task::runtime;
use libk_util::sync::spin_rwlock::IrqSafeRwLock;
use crate::{
device::UsbDeviceAccess,
device::{UsbDeviceAccess, UsbInterfaceAccess},
error::UsbError,
info::{UsbDeviceClass, UsbDeviceProtocol},
};
pub mod hid_keyboard;
pub mod mass_storage;
#[derive(Debug)]
pub struct UsbClassInfo {
pub class: UsbDeviceClass,
pub subclass: u8,
pub protocol: UsbDeviceProtocol,
pub device_protocol_number: u8,
pub interface_protocol_number: u8,
}
mod hid;
mod hub;
mod mass_storage;
mod serial;
#[async_trait]
pub trait UsbDriver: Send + Sync {
pub trait UsbDeviceDriver: Send + Sync {
async fn run(self: Arc<Self>, device: Arc<UsbDeviceAccess>) -> Result<(), UsbError>;
fn name(&self) -> &'static str;
fn probe(&self, class: &UsbClassInfo, device: &UsbDeviceAccess) -> bool;
fn probe(&self, class: u8, subclass: u8, protocol: u8, vid: u16, pid: u16) -> bool;
}
async fn extract_class_info(device: &UsbDeviceAccess) -> Result<Option<UsbClassInfo>, UsbError> {
if device.info.num_configurations != 1 {
return Ok(None);
}
let device_info = &device.info;
let config_info = device.query_configuration_info(0).await?;
#[async_trait]
pub trait UsbInterfaceDriver: Send + Sync {
async fn run(self: Arc<Self>, interface: UsbInterfaceAccess) -> Result<(), UsbError>;
if !config_info.interfaces.is_empty() {
let if_info = &config_info.interfaces[0];
let class = if device_info.device_class == UsbDeviceClass::FromInterface {
if_info.interface_class
} else {
device_info.device_class
};
let subclass = if device_info.device_subclass == 0 {
if_info.interface_subclass
} else {
device_info.device_subclass
};
let protocol = if device_info.device_protocol == UsbDeviceProtocol::FromInterface {
if_info.interface_protocol
} else {
device_info.device_protocol
};
Ok(Some(UsbClassInfo {
class,
subclass,
protocol,
interface_protocol_number: if_info.interface_protocol_number,
device_protocol_number: device_info.device_protocol_number,
}))
} else {
Ok(None)
}
fn name(&self) -> &'static str;
fn probe(&self, class: u8, subclass: u8, protocol: u8) -> bool;
}
async fn pick_driver(
device: &UsbDeviceAccess,
) -> Result<Option<Arc<dyn UsbDriver + 'static>>, UsbError> {
let Some(class) = extract_class_info(device).await? else {
return Ok(None);
async fn spawn_device_driver(device: Arc<UsbDeviceAccess>) -> Result<bool, UsbError> {
let class = device.device_descriptor.device_class;
let subclass = device.device_descriptor.device_subclass;
let protocol = device.device_descriptor.device_protocol;
let vid = device.device_descriptor.id_vendor;
let pid = device.device_descriptor.id_product;
let Some(driver) = USB_DEVICE_DRIVERS.read().iter().find_map(|driver| {
driver
.probe(class, subclass, protocol, vid, pid)
.then(|| driver.clone())
}) else {
return Ok(false);
};
for driver in USB_DEVICE_DRIVERS.read().iter() {
if driver.probe(&class, device) {
return Ok(Some(driver.clone()));
// if let Some(driver) = pick_driver(&device)? {
runtime::spawn(async move {
let name = driver.name();
match driver.run(device).await {
e @ Err(UsbError::DeviceDisconnected) => {
log::warn!(
"Driver {:?} did not exit cleanly: device disconnected",
name,
);
e
}
e => e,
}
}
Ok(None)
})
.map_err(UsbError::SystemError)?;
Ok(true)
}
pub async fn spawn_driver(device: Arc<UsbDeviceAccess>) -> Result<(), UsbError> {
if let Some(driver) = pick_driver(&device).await? {
runtime::spawn(async move {
let name = driver.name();
match driver.run(device).await {
e @ Err(UsbError::DeviceDisconnected) => {
log::warn!(
"Driver {:?} did not exit cleanly: device disconnected",
name,
);
async fn spawn_interface_driver(interface: UsbInterfaceAccess) -> Result<(), UsbError> {
let (class, subclass, protocol) = interface.class();
let Some(driver) = USB_INTERFACE_DRIVERS.read().iter().find_map(|driver| {
driver
.probe(class, subclass, protocol)
.then(|| driver.clone())
}) else {
return Ok(());
};
e
}
e => e,
runtime::spawn(async move {
let name = driver.name();
match driver.run(interface).await {
e @ Err(UsbError::DeviceDisconnected) => {
log::warn!(
"Driver {:?} did not exit cleanly: device disconnected",
name,
);
e
}
})
.map_err(UsbError::SystemError)?;
}
e => e,
}
})
.map_err(UsbError::SystemError)?;
Ok(())
}
pub fn register_driver(driver: Arc<dyn UsbDriver + 'static>) {
pub async fn spawn_driver(device: Arc<UsbDeviceAccess>) {
match spawn_device_driver(device.clone()).await {
Ok(true) => return,
Ok(false) => (),
Err(error) => {
log::error!(
"{}: device driver probe failed: {:?}",
device.bus_address(),
error
);
}
}
// Enumerate interfaces
for index in 0..device.configuration0_info.interfaces.len() as u8 {
let access = device.interface(index);
if let Err(error) = spawn_interface_driver(access).await {
log::error!(
"{}: interface {} driver probe failed: {:?}",
device.bus_address(),
index,
error
);
}
}
}
pub fn register_device_driver(driver: Arc<dyn UsbDeviceDriver + 'static>) {
// TODO check for duplicates
USB_DEVICE_DRIVERS.write().push(driver);
}
pub fn register_default_class_drivers() {
register_driver(Arc::new(hid_keyboard::UsbHidKeyboardDriver));
register_driver(Arc::new(mass_storage::UsbMassStorageDriverBulkOnly));
pub fn register_interface_driver(driver: Arc<dyn UsbInterfaceDriver + 'static>) {
USB_INTERFACE_DRIVERS.write().push(driver);
}
static USB_DEVICE_DRIVERS: IrqSafeRwLock<Vec<Arc<dyn UsbDriver + 'static>>> =
pub fn register_default_class_drivers() {
register_device_driver(Arc::new(hub::UsbHubDriver));
register_device_driver(Arc::new(serial::FT232Driver));
register_interface_driver(Arc::new(hid::UsbHidKeyboardDriver));
register_interface_driver(Arc::new(hid::UsbHidMouseDriver));
register_interface_driver(Arc::new(mass_storage::UsbMassStorageDriverBulkOnly));
// register_driver(Arc::new(hub::UsbHubDriver));
// register_driver(Arc::new(hid_keyboard::UsbHidKeyboardDriver));
// register_driver(Arc::new(mass_storage::UsbMassStorageDriverBulkOnly));
}
static USB_DEVICE_DRIVERS: IrqSafeRwLock<Vec<Arc<dyn UsbDeviceDriver + 'static>>> =
IrqSafeRwLock::new(Vec::new());
static USB_INTERFACE_DRIVERS: IrqSafeRwLock<Vec<Arc<dyn UsbInterfaceDriver + 'static>>> =
IrqSafeRwLock::new(Vec::new());
@@ -0,0 +1,311 @@
use core::sync::atomic::{AtomicBool, Ordering};
use alloc::{boxed::Box, collections::btree_map::BTreeMap, sync::Arc};
use async_trait::async_trait;
use libk::{
block,
error::Error,
fs::devfs,
vfs::{Terminal, TerminalInput, TerminalOutput},
};
use libk_util::sync::{IrqSafeSpinlock, spin_rwlock::IrqSafeRwLock};
use yggdrasil_abi::{
io::{FileMode, TerminalOptions, TerminalOutputOptions},
process::ProcessId,
};
use crate::{
class_driver::UsbDeviceDriver,
communication::UsbDirection,
device::{UsbDeviceAccess, UsbDeviceDetachHandler},
error::UsbError,
info::UsbEndpointType,
pipe::{control::ControlTransferSetup, normal::UsbBulkOutPipeAccess},
};
pub struct FT232Driver;
struct FT232Device {
device: Arc<UsbDeviceAccess>,
bulk_out: UsbBulkOutPipeAccess,
baud_rate: IrqSafeRwLock<u32>,
}
pub struct UsbSerialDeviceWrapper {
device: Arc<dyn UsbSerialDevice>,
index: u32,
disconnected: AtomicBool,
}
#[async_trait]
pub trait UsbSerialDevice: Send + Sync + 'static {
async fn setup(&self) -> Result<(), UsbError>;
async fn set_baud_rate(&self, baud: u32) -> Result<(), UsbError>;
fn baud_rate(&self) -> u32;
async fn write(&self, buffer: &[u8], options: TerminalOutputOptions)
-> Result<usize, UsbError>;
fn display_name(&self) -> &str;
fn device(&self) -> &Arc<UsbDeviceAccess>;
}
#[async_trait]
impl UsbDeviceDetachHandler for UsbSerialDeviceWrapper {
async fn handle_device_detach(&self) {
self.disconnected.store(true, Ordering::Release);
log::info!("USB serial #{} disconnected", self.index);
remove_usb_serial(self.index);
}
}
impl TerminalOutput for UsbSerialDeviceWrapper {
fn write(&self, byte: u8, options: &TerminalOutputOptions) -> Result<(), Error> {
self.write_multiple(&[byte], options)?;
Ok(())
}
fn write_multiple(
&self,
bytes: &[u8],
options: &TerminalOutputOptions,
) -> Result<usize, Error> {
if self.disconnected.load(Ordering::Acquire) {
return Err(Error::InvalidOperation);
}
let result = block!(self.device.write(bytes, *options).await)?;
match result {
Ok(len) => Ok(len),
Err(UsbError::MemoryError(err) | UsbError::SystemError(err)) => Err(err),
Err(error) => {
log::warn!("{}: write error: {:?}", self.device.display_name(), error);
Err(Error::InvalidOperation)
}
}
}
fn baud_rate(&self) -> u32 {
self.device.baud_rate()
}
fn set_baud_rate(&self, baud: u32) -> Result<(), Error> {
if self.disconnected.load(Ordering::Acquire) {
return Err(Error::InvalidOperation);
}
let result = block!(self.device.set_baud_rate(baud).await)?;
match result {
Ok(()) => Ok(()),
Err(UsbError::MemoryError(err) | UsbError::SystemError(err)) => Err(err),
Err(error) => {
log::warn!(
"{}: baud rate set error: {:?}",
self.device.display_name(),
error
);
Err(Error::InvalidOperation)
}
}
}
fn open(&self, pid: ProcessId) -> Result<(), Error> {
if self.disconnected.load(Ordering::Acquire) {
return Err(Error::InvalidOperation);
}
let _ = pid;
let result = block! { self.device.setup().await }?;
match result {
Ok(()) => Ok(()),
Err(UsbError::MemoryError(err) | UsbError::SystemError(err)) => Err(err),
Err(error) => {
log::warn!("{}: setup error: {:?}", self.device.display_name(), error);
Err(Error::InvalidOperation)
}
}
}
}
impl FT232Device {
const FTDI_RESET: u8 = 0;
const FTDI_SET_BAUD_RATE: u8 = 3;
async fn ftdi_reset(&self, port: u16) -> Result<(), UsbError> {
self.device
.control_pipe()
.control_transfer(ControlTransferSetup {
bm_request_type: 0b01000000,
b_request: Self::FTDI_RESET,
w_value: 0,
w_index: port,
w_length: 0,
})
.await
}
async fn ftdi_set_baud_rate(&self, port: u16, baud: u32) -> Result<(), UsbError> {
let w_value = match baud {
300 => 0x2710,
600 => 0x1388,
1200 => 0x09C4,
2400 => 0x04E2,
4800 => 0x0271,
9600 => 0x4138,
19200 => 0x809C,
38400 => 0xC04E,
57600 => 0x0034,
115200 => 0x001A,
230400 => 0x000D,
460800 => 0x4006,
921600 => 0x8003,
_ => {
log::warn!("ft232: unsupported baud rate {baud}");
return Err(UsbError::InvalidConfiguration);
}
};
self.device
.control_pipe()
.control_transfer(ControlTransferSetup {
bm_request_type: 0b01000000,
b_request: Self::FTDI_SET_BAUD_RATE,
w_value,
w_index: port,
w_length: 0,
})
.await
}
}
#[async_trait]
impl UsbSerialDevice for FT232Device {
async fn setup(&self) -> Result<(), UsbError> {
log::info!("ft232: setup");
Ok(())
}
async fn set_baud_rate(&self, baud: u32) -> Result<(), UsbError> {
self.ftdi_set_baud_rate(0, baud).await?;
// *self.baud_rate.write() = baud;
log::info!("ft232: set baud rate {baud}");
Ok(())
}
fn baud_rate(&self) -> u32 {
*self.baud_rate.read()
}
async fn write(
&self,
buffer: &[u8],
options: TerminalOutputOptions,
) -> Result<usize, UsbError> {
if options.contains(TerminalOutputOptions::NL_TO_CRNL) {
for &byte in buffer {
if byte == b'\n' {
self.bulk_out.write(b"\r").await?;
}
self.bulk_out.write(&[byte]).await?;
}
Ok(buffer.len())
} else {
self.bulk_out.write(buffer).await
}
}
fn display_name(&self) -> &str {
"FT232 Serial Converter"
}
fn device(&self) -> &Arc<UsbDeviceAccess> {
&self.device
}
}
#[async_trait]
impl UsbDeviceDriver for FT232Driver {
async fn run(self: Arc<Self>, device: Arc<UsbDeviceAccess>) -> Result<(), UsbError> {
let interface = device.interface(0);
let endpoints = interface.endpoints();
let bulk_in = endpoints
.iter()
.find(|ep| ep.ty == UsbEndpointType::Bulk && ep.direction == UsbDirection::In)
.ok_or(UsbError::InvalidConfiguration)?;
let bulk_out = endpoints
.iter()
.find(|ep| ep.ty == UsbEndpointType::Bulk && ep.direction == UsbDirection::Out)
.ok_or(UsbError::InvalidConfiguration)?;
let bulk_in = device
.open_bulk_in_pipe(bulk_in.number, bulk_in.max_packet_size as _)
.await?;
let bulk_out = device
.open_bulk_out_pipe(bulk_out.number, bulk_out.max_packet_size as _)
.await?;
let ft232 = Arc::new(FT232Device {
device,
bulk_out,
baud_rate: IrqSafeRwLock::new(115200),
});
ft232.ftdi_reset(0).await?;
let terminal = register_usb_serial(ft232)?;
let mut buffer = [0; 3];
loop {
let len = bulk_in.read(&mut buffer).await?;
if len < 2 {
continue;
}
for &byte in &buffer[2..len] {
terminal.write_to_input(byte);
}
}
}
fn name(&self) -> &'static str {
"FT232 Serial Converter"
}
fn probe(&self, class: u8, subclass: u8, protocol: u8, vid: u16, pid: u16) -> bool {
let _ = (class, subclass, protocol);
vid == 0x0403 && pid == 0x6001
}
}
static USB_SERIALS: IrqSafeSpinlock<BTreeMap<u32, Arc<Terminal<Arc<UsbSerialDeviceWrapper>>>>> =
IrqSafeSpinlock::new(BTreeMap::new());
fn register_usb_serial(
serial: Arc<dyn UsbSerialDevice>,
) -> Result<Arc<Terminal<Arc<UsbSerialDeviceWrapper>>>, UsbError> {
let mut serials = USB_SERIALS.lock();
for i in 0..64 {
if serials.contains_key(&i) {
continue;
}
let wrapper = Arc::new(UsbSerialDeviceWrapper {
device: serial,
index: i,
disconnected: AtomicBool::new(false),
});
wrapper.device.device().set_detach_handler(wrapper.clone());
let input = TerminalInput::with_capacity(64).expect("Couldn't allocate input buffer");
let terminal = Arc::new(Terminal::from_parts(
TerminalOptions::const_default(),
input,
wrapper,
));
serials.insert(i, terminal.clone());
let name = alloc::format!("ttyUSB{i}");
devfs::add_named_char_device(terminal.clone(), name.clone(), FileMode::new(0o600)).ok();
return Ok(terminal);
}
Err(UsbError::DriverError)
}
fn remove_usb_serial(index: u32) {
let serial = USB_SERIALS.lock().remove(&index);
if serial.is_none() {
log::warn!("usb-serial #{index} doesn't exist in the table");
}
let name = alloc::format!("ttyUSB{index}");
devfs::remove_node(name).ok();
}
+42 -18
View File
@@ -4,9 +4,22 @@ use crate::{
communication::UsbDirection,
device::UsbSpeed,
error::UsbError,
info::{UsbDeviceClass, UsbDeviceProtocol, UsbEndpointType, UsbVersion},
info::{UsbEndpointType, UsbVersion},
// info::{UsbDeviceClass, UsbDeviceProtocol, UsbEndpointType, UsbVersion},
};
#[derive(Clone, Copy, Debug, Default, Pod, Zeroable)]
#[repr(C, packed)]
pub struct UsbDeviceDescriptor0 {
pub length: u8,
pub ty: u8,
pub bcd_usb: u16,
pub device_class: u8,
pub device_subclass: u8,
pub device_protocol: u8,
pub max_packet_size_0: u8,
}
#[derive(Clone, Copy, Debug, Default, Pod, Zeroable)]
#[repr(C, packed)]
pub struct UsbDeviceDescriptor {
@@ -91,16 +104,27 @@ pub struct UsbOtherSpeedConfiguration {
pub max_power: u8,
}
impl UsbInterfaceDescriptor {
pub fn class(&self) -> UsbDeviceClass {
UsbDeviceClass::try_from(self.interface_class).unwrap_or(UsbDeviceClass::Unknown)
}
pub fn protocol(&self) -> UsbDeviceProtocol {
UsbDeviceProtocol::try_from(self.interface_protocol).unwrap_or(UsbDeviceProtocol::Unknown)
}
#[derive(Clone, Copy, Debug, Default, Pod, Zeroable)]
#[repr(C, packed)]
pub struct UsbHubDescriptorHeader {
pub b_desc_length: u8,
pub b_descriptor_type: u8,
pub b_nr_ports: u8,
pub w_hub_characteristics: u16,
pub b_pwr_on_2_pwr_good: u8,
pub b_hub_contr_current: u8,
}
// impl UsbInterfaceDescriptor {
// pub fn class(&self) -> UsbDeviceClass {
// UsbDeviceClass::try_from(self.interface_class).unwrap_or(UsbDeviceClass::Unknown)
// }
//
// pub fn protocol(&self) -> UsbDeviceProtocol {
// UsbDeviceProtocol::try_from(self.interface_protocol).unwrap_or(UsbDeviceProtocol::Unknown)
// }
// }
//
impl UsbEndpointDescriptor {
pub fn direction(&self) -> UsbDirection {
match self.endpoint_address >> 7 {
@@ -125,16 +149,16 @@ impl UsbEndpointDescriptor {
}
}
}
//
impl UsbDeviceDescriptor {
pub fn class(&self) -> UsbDeviceClass {
UsbDeviceClass::try_from(self.device_class).unwrap_or(UsbDeviceClass::Unknown)
}
pub fn protocol(&self) -> UsbDeviceProtocol {
UsbDeviceProtocol::try_from(self.device_protocol).unwrap_or(UsbDeviceProtocol::Unknown)
}
// pub fn class(&self) -> UsbDeviceClass {
// UsbDeviceClass::try_from(self.device_class).unwrap_or(UsbDeviceClass::Unknown)
// }
//
// pub fn protocol(&self) -> UsbDeviceProtocol {
// UsbDeviceProtocol::try_from(self.device_protocol).unwrap_or(UsbDeviceProtocol::Unknown)
// }
//
pub fn max_packet_size(&self, version: UsbVersion, speed: UsbSpeed) -> Result<usize, UsbError> {
match (version.is_version_3(), speed, self.max_packet_size_0) {
(true, UsbSpeed::Super, 9) => Ok(1 << 9),
+282 -127
View File
@@ -1,15 +1,27 @@
use core::{fmt, ops::Deref};
use core::{any::Any, fmt, ops::Deref};
use alloc::{boxed::Box, sync::Arc, vec::Vec};
use alloc::{
boxed::Box,
string::String,
sync::{Arc, Weak},
vec::Vec,
};
use async_trait::async_trait;
use libk_util::sync::spin_rwlock::{IrqSafeRwLock, IrqSafeRwLockReadGuard};
use libk::{
error::Error,
fs::sysfs::{
self,
attribute::{StringAttribute, StringAttributeOps},
object::KObject,
},
};
use libk_util::OneTimeInit;
use crate::{
descriptor::{UsbDeviceDescriptor, UsbHubDescriptorHeader},
error::UsbError,
info::{
UsbConfigurationInfo, UsbDeviceInfo, UsbEndpointInfo, UsbEndpointType, UsbInterfaceInfo,
UsbVersion,
},
host::UsbHostController,
info::{PortString, UsbConfigurationInfo, UsbEndpointInfo, UsbEndpointType, UsbInterfaceInfo},
pipe::{
control::{ConfigurationDescriptorEntry, UsbControlPipeAccess},
normal::{
@@ -17,10 +29,10 @@ use crate::{
UsbNormalPipeOut,
},
},
UsbHostController,
};
// High-level structures for info provided through descriptors
type UsbDeviceKObject = KObject<Weak<UsbDeviceAccess>>;
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub struct UsbBusAddress {
@@ -28,10 +40,28 @@ pub struct UsbBusAddress {
pub device: u8,
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub struct UsbInterfaceAddress {
pub bus: UsbBusAddress,
pub interface: u8,
}
pub struct UsbDeviceAccess {
pub device: Arc<dyn UsbDevice>,
pub info: UsbDeviceInfo,
pub current_configuration: IrqSafeRwLock<Option<UsbConfigurationInfo>>,
pub device_descriptor: UsbDeviceDescriptor,
pub product_str: Option<String>,
pub vendor_str: Option<String>,
pub configuration0_info: UsbConfigurationInfo,
kobject: OneTimeInit<Arc<UsbDeviceKObject>>,
}
// USB device, limited in scope by one interface
#[derive(Clone)]
pub struct UsbInterfaceAccess {
device: Arc<UsbDeviceAccess>,
interface_index: u8,
endpoint_start_index: u8,
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
@@ -42,8 +72,9 @@ pub enum UsbSpeed {
Super,
}
#[async_trait]
pub trait UsbDeviceDetachHandler: Send + Sync {
fn handle_device_detach(&self);
async fn handle_device_detach(&self);
}
#[async_trait]
@@ -66,66 +97,231 @@ pub trait UsbDevice: Send + Sync {
ty: UsbEndpointType,
) -> Result<Box<dyn UsbNormalPipeOut>, UsbError>;
fn port_number(&self) -> u8;
async fn configure_hub(&self, hub_descriptor: &UsbHubDescriptorHeader) -> Result<(), UsbError>;
// fn port_number(&self) -> u8;
fn port_string(&self) -> &PortString;
fn bus_address(&self) -> UsbBusAddress;
fn speed(&self) -> UsbSpeed;
fn controller_ref(&self) -> &dyn UsbHostController;
fn host_controller(&self) -> Arc<dyn UsbHostController>;
fn as_any(self: Arc<Self>) -> Arc<dyn Any + Send + Sync>;
fn set_detach_handler(&self, handler: Arc<dyn UsbDeviceDetachHandler>);
fn handle_detach(&self);
async fn handle_detach(&self);
fn debug(&self) {}
}
impl UsbDeviceAccess {
fn usb_kobject() -> Option<&'static Arc<KObject<()>>> {
static USB_KOBJECT: OneTimeInit<Arc<KObject<()>>> = OneTimeInit::new();
USB_KOBJECT.or_init_with_opt(|| {
let bus_kobject = sysfs::bus()?;
let usb_kobject = KObject::new(());
bus_kobject.add_object("usb", usb_kobject.clone()).ok()?;
Some(usb_kobject)
})
}
fn setup_kobject(self: &Arc<Self>) -> Result<(), Error> {
struct Id;
struct DeviceClass;
struct VendorString;
struct ProductString;
impl StringAttributeOps for Id {
type Data = Weak<UsbDeviceAccess>;
const NAME: &'static str = "id";
fn read(state: &Self::Data) -> Result<String, Error> {
let state = state.upgrade().ok_or(Error::InvalidOperation)?;
let id_product = state.device_descriptor.id_product;
let id_vendor = state.device_descriptor.id_vendor;
Ok(alloc::format!("{id_vendor:04x}:{id_product:04x}",))
}
}
impl StringAttributeOps for DeviceClass {
type Data = Weak<UsbDeviceAccess>;
const NAME: &'static str = "device_class";
fn read(state: &Self::Data) -> Result<String, Error> {
let state = state.upgrade().ok_or(Error::InvalidOperation)?;
Ok(alloc::format!(
"{:02x}:{:02x}:{:02x}",
state.device_descriptor.device_class,
state.device_descriptor.device_subclass,
state.device_descriptor.device_protocol
))
}
}
impl StringAttributeOps for VendorString {
type Data = Weak<UsbDeviceAccess>;
const NAME: &'static str = "vendor_str";
fn read(state: &Self::Data) -> Result<String, Error> {
let state = state.upgrade().ok_or(Error::InvalidOperation)?;
if let Some(vendor_str) = state.vendor_str.as_ref() {
Ok(vendor_str.clone())
} else {
Ok(String::new())
}
}
}
impl StringAttributeOps for ProductString {
type Data = Weak<UsbDeviceAccess>;
const NAME: &'static str = "product_str";
fn read(state: &Self::Data) -> Result<String, Error> {
let state = state.upgrade().ok_or(Error::InvalidOperation)?;
if let Some(product_str) = state.product_str.as_ref() {
Ok(product_str.clone())
} else {
Ok(String::new())
}
}
}
let usb_kobject = Self::usb_kobject().ok_or(Error::DoesNotExist)?;
let dev_kobject = KObject::new(Arc::downgrade(self));
dev_kobject.add_attribute(StringAttribute::from(Id)).ok();
dev_kobject
.add_attribute(StringAttribute::from(DeviceClass))
.ok();
dev_kobject
.add_attribute(StringAttribute::from(ProductString))
.ok();
dev_kobject
.add_attribute(StringAttribute::from(VendorString))
.ok();
let name = alloc::format!("{}", self.device.bus_address());
usb_kobject.add_object(name, dev_kobject.clone())?;
self.kobject.init(dev_kobject);
Ok(())
}
/// Expected device state:
///
/// * Link-layer stuff has been reset and established properly by the HCD
/// * Device is not yet configured
/// * Control pipe for the device has been properly set up
/// * Device has been assigned a bus address
pub async fn setup(raw: Arc<dyn UsbDevice>) -> Result<Self, UsbError> {
let control = raw.control_pipe();
pub async fn setup(raw: Arc<dyn UsbDevice>) -> Result<Arc<Self>, UsbError> {
let control_pipe = raw.control_pipe();
let device_descriptor = control_pipe.query_device_descriptor().await?;
if device_descriptor.num_configurations < 1 {
return Err(UsbError::InvalidDescriptorField);
}
let config_descriptor = control_pipe.query_configuration_descriptor(0).await?;
let device_desc = control.query_device_descriptor().await?;
// Use configuration 0
control_pipe
.set_configuration(config_descriptor.configuration().config_val as _)
.await?;
let bcd_usb = device_desc.bcd_usb;
let usb_version = UsbVersion::from_bcd_usb(device_desc.bcd_usb)
.ok_or(UsbError::InvalidDescriptorField)
.inspect_err(|_| {
log::error!(
"{}: unsupported/invalid USB version: {:#x}",
let vendor_str = control_pipe
.query_string(device_descriptor.manufacturer_str)
.await
.inspect_err(|e| {
log::warn!(
"{}: manufacturer string query error: {:?}",
raw.bus_address(),
bcd_usb
e
)
})?;
})
.ok();
let product_str = control_pipe
.query_string(device_descriptor.product_str)
.await
.inspect_err(|e| {
log::warn!("{}: product string query error: {:?}", raw.bus_address(), e)
})
.ok();
let manufacturer = control.query_string(device_desc.manufacturer_str).await?;
let product = control.query_string(device_desc.product_str).await?;
// Extract configuration 0 information
// let query = control_pipe.query_configuration_descriptor(index).await?;
let info = UsbDeviceInfo {
manufacturer,
product,
usb_version,
let configuration_name = control_pipe
.query_string(config_descriptor.configuration().config_str)
.await
.ok();
id_vendor: device_desc.id_vendor,
id_product: device_desc.id_product,
let mut endpoints = Vec::new();
let mut interfaces = Vec::new();
device_class: device_desc.class(),
device_subclass: device_desc.device_subclass,
device_protocol: device_desc.protocol(),
device_protocol_number: device_desc.device_protocol,
for desc in config_descriptor.descriptors() {
match desc {
ConfigurationDescriptorEntry::Interface(iface) => {
let name = control_pipe.query_string(iface.interface_str).await.ok();
interfaces.push(UsbInterfaceInfo {
name,
number: iface.interface_number,
num_endpoints: iface.num_endpoints,
num_configurations: device_desc.num_configurations,
interface_class: iface.interface_class,
interface_subclass: iface.interface_subclass,
interface_protocol: iface.interface_protocol,
});
}
ConfigurationDescriptorEntry::Endpoint(ep) => {
endpoints.push(UsbEndpointInfo {
number: ep.number(),
direction: ep.direction(),
max_packet_size: ep.max_packet_size as _,
ty: ep.transfer_type(),
});
}
_ => (),
}
}
max_packet_size: device_desc.max_packet_size(usb_version, raw.speed())?,
interfaces.sort_by_key(|r| r.number);
endpoints.sort_by_key(|r| r.number);
let configuration0_info = UsbConfigurationInfo {
name: configuration_name,
config_value: config_descriptor.configuration().config_val,
interfaces,
endpoints,
};
Ok(Self {
let device = Arc::new(Self {
device: raw,
info,
current_configuration: IrqSafeRwLock::new(None),
})
device_descriptor,
product_str,
vendor_str,
configuration0_info,
kobject: OneTimeInit::new(),
});
if let Err(error) = device.setup_kobject() {
log::error!(
"{} kobject setup error: {:?}",
device.device.bus_address(),
error
);
}
Ok(device)
}
pub fn interface(self: &Arc<Self>, interface_index: u8) -> UsbInterfaceAccess {
let endpoint_start_index = self
.configuration0_info
.interfaces
.iter()
.take(interface_index as usize)
.map(|r| r.num_endpoints)
.sum();
UsbInterfaceAccess {
device: self.clone(),
interface_index,
endpoint_start_index,
}
}
pub async fn open_interrupt_in_pipe(
@@ -164,90 +360,6 @@ impl UsbDeviceAccess {
Ok(UsbBulkOutPipeAccess(pipe))
}
pub fn read_current_configuration(
&self,
) -> IrqSafeRwLockReadGuard<'_, Option<UsbConfigurationInfo>> {
self.current_configuration.read()
}
pub async fn select_configuration<F: Fn(&UsbConfigurationInfo) -> bool>(
&self,
predicate: F,
) -> Result<Option<UsbConfigurationInfo>, UsbError> {
let mut current_config = self.current_configuration.write();
let control_pipe = self.control_pipe();
for i in 0..self.info.num_configurations {
let info = self.query_configuration_info(i).await?;
if predicate(&info) {
log::debug!("Selected configuration: {:#?}", info);
let config = current_config.insert(info);
control_pipe
.set_configuration(config.config_value as _)
.await?;
return Ok(Some(config.clone()));
}
}
Ok(None)
}
pub async fn query_configuration_info(
&self,
index: u8,
) -> Result<UsbConfigurationInfo, UsbError> {
if index >= self.info.num_configurations {
return Err(UsbError::InvalidConfiguration);
}
let control_pipe = self.control_pipe();
let query = control_pipe.query_configuration_descriptor(index).await?;
let configuration_name = control_pipe
.query_string(query.configuration().config_str)
.await?;
let mut endpoints = Vec::new();
let mut interfaces = Vec::new();
for desc in query.descriptors() {
match desc {
ConfigurationDescriptorEntry::Endpoint(ep) => {
endpoints.push(UsbEndpointInfo {
number: ep.number(),
direction: ep.direction(),
max_packet_size: ep.max_packet_size as _,
ty: ep.transfer_type(),
});
}
ConfigurationDescriptorEntry::Interface(iface) => {
let name = control_pipe.query_string(iface.interface_str).await?;
interfaces.push(UsbInterfaceInfo {
name,
number: iface.interface_number,
interface_class: iface.class(),
interface_subclass: iface.interface_subclass,
interface_protocol: iface.protocol(),
interface_protocol_number: iface.interface_protocol,
});
}
_ => (),
}
}
let info = UsbConfigurationInfo {
name: configuration_name,
config_value: query.configuration().config_val,
interfaces,
endpoints,
};
Ok(info)
}
pub fn set_detach_handler(&self, handler: Arc<dyn UsbDeviceDetachHandler>) {
self.device.set_detach_handler(handler);
}
@@ -266,3 +378,46 @@ impl fmt::Display for UsbBusAddress {
write!(f, "{}:{}", self.bus, self.device)
}
}
impl fmt::Display for UsbInterfaceAddress {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}.{}", self.bus, self.interface)
}
}
impl UsbInterfaceAccess {
fn interface_info(&self) -> &UsbInterfaceInfo {
&self.device.configuration0_info.interfaces[self.interface_index as usize]
}
pub fn number(&self) -> u8 {
self.interface_index
}
pub fn endpoints(&self) -> &[UsbEndpointInfo] {
let info = self.interface_info();
let start = self.endpoint_start_index as usize;
let end = start + info.num_endpoints as usize;
&self.device.configuration0_info.endpoints[start..end]
}
pub fn device(&self) -> &Arc<UsbDeviceAccess> {
&self.device
}
pub fn address(&self) -> UsbInterfaceAddress {
UsbInterfaceAddress {
bus: self.device.bus_address(),
interface: self.interface_index,
}
}
pub fn class(&self) -> (u8, u8, u8) {
let i = self.interface_info();
(
i.interface_class,
i.interface_subclass,
i.interface_protocol,
)
}
}
+6 -1
View File
@@ -7,6 +7,7 @@ pub enum TransferError {
BufferError,
UsbTransactionError,
Stall,
Shutdown,
Other(u8),
}
@@ -29,13 +30,17 @@ pub enum UsbError {
DeviceBusy,
DeviceDisconnected,
TransferFailed(TransferError),
TruncatedDescriptor(usize, usize),
// Driver errors
DriverError,
}
impl From<TransferError> for UsbError {
fn from(value: TransferError) -> Self {
Self::TransferFailed(value)
match value {
TransferError::Shutdown => Self::DeviceDisconnected,
value => Self::TransferFailed(value),
}
}
}
+20
View File
@@ -0,0 +1,20 @@
use alloc::{boxed::Box, sync::Arc};
use async_trait::async_trait;
use device_api::device::Device;
use crate::{
device::{UsbDeviceAccess, UsbSpeed},
error::UsbError,
info::PortString,
};
#[async_trait]
pub trait UsbHostController: Device + Sync + Send {
async fn setup_hub_device(
self: Arc<Self>,
hub: Arc<UsbDeviceAccess>,
port_string: PortString,
usb_speed: UsbSpeed,
) -> Result<(), UsbError>;
async fn disconnect_device(self: Arc<Self>, port_string: PortString) -> Result<(), UsbError>;
}

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