Compare commits
35 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2a49c655c2 | |||
| d108494314 | |||
| 195c19e225 | |||
| 21a8361eec | |||
| fd0a3f50ea | |||
| c72c8f88d7 | |||
| e44b616998 | |||
| 7abb950a8f | |||
| 57143f9d8d | |||
| 3491e1a227 | |||
| 61644bdef5 | |||
| e1b905c65a | |||
| 9855142c25 | |||
| bb4e805733 | |||
| ecf1c18240 | |||
| a87c8a7ee2 | |||
| 312458b8f0 | |||
| 33474c10d3 | |||
| e934b4d696 | |||
| 6d8d97d492 | |||
| ca01f57873 | |||
| 9be467d5d5 | |||
| 0c2ebbf7b3 | |||
| cb4c0bc4b0 | |||
| c1b62aef1d | |||
| 6469914be1 | |||
| 131e6adc3d | |||
| 322cb0a958 | |||
| 6552fa8059 | |||
| 0ff48fd520 | |||
| 9c32c11b0b | |||
| 3be32b7b8f | |||
| 919d6d62ba | |||
| 062db06473 | |||
| 06a6e11dab |
Generated
+112
-11
@@ -499,7 +499,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 +1199,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 +1353,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 +1793,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 +2023,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 +2406,7 @@ checksum = "b4ee928febd44d98f2f459a4a79bd4d928591333a494a10a868418ac1b39cf1f"
|
||||
dependencies = [
|
||||
"either",
|
||||
"home",
|
||||
"rustix",
|
||||
"rustix 0.38.44",
|
||||
"winsafe",
|
||||
]
|
||||
|
||||
@@ -2557,8 +2575,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 +2607,7 @@ dependencies = [
|
||||
"semver 1.0.25",
|
||||
"serde",
|
||||
"tar",
|
||||
"tempfile",
|
||||
"thiserror",
|
||||
"toml",
|
||||
"walkdir",
|
||||
@@ -2638,6 +2657,70 @@ dependencies = [
|
||||
"yggdrasil-abi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ygg_driver_bsp_arm"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"device-api",
|
||||
"device-tree",
|
||||
"libk",
|
||||
"libk-mm",
|
||||
"libk-util",
|
||||
"log",
|
||||
"tock-registers",
|
||||
"yggdrasil-abi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ygg_driver_bsp_bcm283x"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"device-api",
|
||||
"device-tree",
|
||||
"futures-util",
|
||||
"kernel-arch-aarch64",
|
||||
"libk",
|
||||
"libk-mm",
|
||||
"libk-util",
|
||||
"log",
|
||||
"tock-registers",
|
||||
"yggdrasil-abi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ygg_driver_bsp_jh7110"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"device-api",
|
||||
"device-tree",
|
||||
"futures-util",
|
||||
"libk",
|
||||
"libk-mm",
|
||||
"libk-util",
|
||||
"log",
|
||||
"tock-registers",
|
||||
"yggdrasil-abi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ygg_driver_bsp_riscv"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"device-api",
|
||||
"device-tree",
|
||||
"kernel-arch-riscv64",
|
||||
"libk",
|
||||
"libk-mm",
|
||||
"libk-util",
|
||||
"log",
|
||||
"tock-registers",
|
||||
"yggdrasil-abi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ygg_driver_fat32"
|
||||
version = "0.1.0"
|
||||
@@ -2791,6 +2874,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 +3047,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 +3060,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",
|
||||
|
||||
@@ -7,6 +7,7 @@ exclude = [
|
||||
"toolchain",
|
||||
"userspace/dynload-program",
|
||||
"userspace/lib/ygglibc",
|
||||
"userspace",
|
||||
"toolchain-c"
|
||||
]
|
||||
members = [
|
||||
|
||||
@@ -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...
|
||||
|
||||
|
||||
@@ -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
@@ -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")
|
||||
|
||||
@@ -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>;
|
||||
@@ -0,0 +1,492 @@
|
||||
/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;
|
||||
|
||||
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;
|
||||
};
|
||||
};
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
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>;
|
||||
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>;
|
||||
};
|
||||
};
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -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";
|
||||
};
|
||||
@@ -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,
|
||||
|
||||
@@ -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
@@ -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,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,7 +1,7 @@
|
||||
[package]
|
||||
name = "kernel-arch-aarch64"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
yggdrasil-abi.workspace = true
|
||||
|
||||
@@ -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 ()) -> !;
|
||||
|
||||
@@ -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 () {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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>()))
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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!(
|
||||
|
||||
@@ -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!()
|
||||
}
|
||||
|
||||
@@ -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))]
|
||||
|
||||
@@ -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>,
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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) };
|
||||
|
||||
@@ -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!()
|
||||
}
|
||||
|
||||
@@ -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,4 +1,4 @@
|
||||
#![feature(iter_chain, new_zeroed_alloc, box_as_ptr)]
|
||||
#![feature(box_as_ptr)]
|
||||
#![allow(clippy::new_without_default)]
|
||||
#![no_std]
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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() {
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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));
|
||||
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,7 +1,7 @@
|
||||
[package]
|
||||
name = "ygg_driver_nvme"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
authors = ["Mark Poliakov <mark@alnyan.me>"]
|
||||
|
||||
[dependencies]
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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!();
|
||||
}
|
||||
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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(())
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
[package]
|
||||
name = "ygg_driver_bsp_bcm283x"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
device-tree.workspace = true
|
||||
device-api.workspace = true
|
||||
yggdrasil-abi.workspace = true
|
||||
libk-mm.workspace = true
|
||||
libk-util.workspace = true
|
||||
libk.workspace = true
|
||||
kernel-arch-aarch64.workspace = true
|
||||
|
||||
tock-registers.workspace = true
|
||||
log.workspace = true
|
||||
bytemuck.workspace = true
|
||||
futures-util.workspace = true
|
||||
@@ -1,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 {
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
#![no_std]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
mod aux;
|
||||
// mod aux_uart;
|
||||
mod gpio;
|
||||
mod mbox;
|
||||
@@ -1,11 +1,8 @@
|
||||
#![allow(missing_docs)]
|
||||
|
||||
use core::{cell::UnsafeCell, mem::MaybeUninit};
|
||||
|
||||
use abi::error::Error;
|
||||
use alloc::sync::Arc;
|
||||
use device_api::device::{Device, DeviceInitContext};
|
||||
use device_tree::driver::{device_tree_driver, Node, ProbeContext};
|
||||
use device_tree::driver::{Node, ProbeContext, device_tree_driver};
|
||||
use kernel_arch_aarch64::mem::table::L3;
|
||||
use libk::device::{
|
||||
display::{
|
||||
@@ -14,10 +11,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 +22,7 @@ use tock_registers::{
|
||||
register_bitfields, register_structs,
|
||||
registers::{ReadOnly, ReadWrite, WriteOnly},
|
||||
};
|
||||
use yggdrasil_abi::error::Error;
|
||||
|
||||
register_bitfields! {
|
||||
u32,
|
||||
@@ -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];
|
||||
@@ -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;
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
#![allow(unsafe_op_in_unsafe_fn)]
|
||||
#![no_std]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
mod goldfish_rtc;
|
||||
mod plic;
|
||||
@@ -1,30 +1,27 @@
|
||||
//! RISC-V PLIC driver
|
||||
|
||||
use abi::{error::Error, primitive_enum};
|
||||
use alloc::{sync::Arc, vec::Vec};
|
||||
use device_api::{
|
||||
device::{Device, DeviceInitContext},
|
||||
interrupt::{
|
||||
ExternalInterruptController, FixedInterruptTable, FullIrq, InterruptHandler,
|
||||
InterruptTable, Irq, IrqOptions, IrqVector,
|
||||
ExternalInterruptController, FixedInterruptTable, InterruptHandler, InterruptTable, Irq,
|
||||
IrqHandle, IrqOptions, IrqVector,
|
||||
},
|
||||
};
|
||||
use device_tree::{
|
||||
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::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;
|
||||
|
||||
@@ -264,11 +261,12 @@ impl Device for Plic {
|
||||
}
|
||||
|
||||
impl DeviceTreeInterruptController for Plic {
|
||||
fn map_interrupt(&self, property: &TProp, offset: usize) -> Option<FullIrq> {
|
||||
fn map_interrupt(self: Arc<Self>, property: &TProp, offset: usize) -> Option<IrqHandle> {
|
||||
let num = property.read_cell(offset, 1)?;
|
||||
Some(FullIrq {
|
||||
Some(IrqHandle {
|
||||
irq: Irq::External(num as _),
|
||||
options: IrqOptions::default(),
|
||||
intc: self.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "ygg_driver_pci"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
authors = ["Mark Poliakov <mark@alnyan.me>"]
|
||||
|
||||
[dependencies]
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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! {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 })
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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();
|
||||
|
||||
+89
-21
@@ -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) -> bool {
|
||||
let _ = protocol;
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,117 +1,141 @@
|
||||
// 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;
|
||||
|
||||
#[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) -> 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 Some(driver) = USB_DEVICE_DRIVERS.read().iter().find_map(|driver| {
|
||||
driver
|
||||
.probe(class, subclass, protocol)
|
||||
.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_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());
|
||||
|
||||
@@ -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
@@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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>;
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
use core::fmt;
|
||||
use core::{fmt, num::NonZeroU8};
|
||||
|
||||
use alloc::{string::String, vec::Vec};
|
||||
use yggdrasil_abi::primitive_enum;
|
||||
|
||||
use crate::communication::UsbDirection;
|
||||
|
||||
@@ -39,31 +38,21 @@ pub enum UsbVersion {
|
||||
Usb32,
|
||||
}
|
||||
|
||||
primitive_enum! {
|
||||
pub enum UsbDeviceClass: u8 {
|
||||
FromInterface = 0x00,
|
||||
Hid = 0x03,
|
||||
MassStorage = 0x08,
|
||||
Unknown = 0xFF,
|
||||
}
|
||||
}
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct PortString(u64);
|
||||
|
||||
primitive_enum! {
|
||||
pub enum UsbDeviceProtocol: u8 {
|
||||
FromInterface = 0x00,
|
||||
Unknown = 0xFF,
|
||||
}
|
||||
}
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct PortStringIter(u64);
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct UsbInterfaceInfo {
|
||||
pub name: String,
|
||||
pub name: Option<String>,
|
||||
pub number: u8,
|
||||
pub num_endpoints: u8,
|
||||
|
||||
pub interface_class: UsbDeviceClass,
|
||||
pub interface_class: u8,
|
||||
pub interface_subclass: u8,
|
||||
pub interface_protocol: UsbDeviceProtocol,
|
||||
pub interface_protocol_number: u8,
|
||||
pub interface_protocol: u8,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -76,33 +65,12 @@ pub struct UsbEndpointInfo {
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct UsbConfigurationInfo {
|
||||
pub name: String,
|
||||
pub name: Option<String>,
|
||||
pub config_value: u8,
|
||||
pub interfaces: Vec<UsbInterfaceInfo>,
|
||||
pub endpoints: Vec<UsbEndpointInfo>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct UsbDeviceInfo {
|
||||
pub manufacturer: String,
|
||||
pub product: String,
|
||||
|
||||
pub usb_version: UsbVersion,
|
||||
|
||||
pub id_vendor: u16,
|
||||
pub id_product: u16,
|
||||
|
||||
pub device_class: UsbDeviceClass,
|
||||
pub device_subclass: u8,
|
||||
pub device_protocol: UsbDeviceProtocol,
|
||||
pub device_protocol_number: u8,
|
||||
|
||||
/// Max packet size for endpoint zero
|
||||
pub max_packet_size: usize,
|
||||
|
||||
pub num_configurations: u8,
|
||||
}
|
||||
|
||||
impl UsbVersion {
|
||||
pub fn is_version_3(&self) -> bool {
|
||||
matches!(self, Self::Usb30 | Self::Usb31 | Self::Usb32)
|
||||
@@ -151,3 +119,59 @@ impl UsbConfigurationInfo {
|
||||
Some((index as u8 + 1, info))
|
||||
}
|
||||
}
|
||||
|
||||
impl PortString {
|
||||
pub const fn new_root_port(root_hub_port_number: NonZeroU8) -> Self {
|
||||
Self(root_hub_port_number.get() as u64)
|
||||
}
|
||||
|
||||
pub const fn root_hub_port_number(&self) -> NonZeroU8 {
|
||||
unsafe { NonZeroU8::new_unchecked((self.0 & 0xF) as u8) }
|
||||
}
|
||||
|
||||
pub fn append(mut self, port: NonZeroU8) -> Self {
|
||||
for i in 0..16 {
|
||||
let pos = i * 4;
|
||||
if (self.0 >> pos) & 0xF == 0 {
|
||||
self.0 |= (port.get() as u64) << pos;
|
||||
return self;
|
||||
}
|
||||
}
|
||||
panic!("Port route string too long");
|
||||
}
|
||||
|
||||
pub fn parent_hub_port_number(&self) -> Option<NonZeroU8> {
|
||||
self.iter().last()
|
||||
}
|
||||
|
||||
pub const fn raw(&self) -> u64 {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> PortStringIter {
|
||||
PortStringIter(self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for PortString {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "<port ")?;
|
||||
for (i, port) in self.iter().enumerate() {
|
||||
if i != 0 {
|
||||
write!(f, ".")?;
|
||||
}
|
||||
write!(f, "{port}")?;
|
||||
}
|
||||
write!(f, ">")
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for PortStringIter {
|
||||
type Item = NonZeroU8;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let port = NonZeroU8::new((self.0 & 0xF) as u8)?;
|
||||
self.0 >>= 4;
|
||||
Some(port)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
#![no_std]
|
||||
#![allow(clippy::new_without_default)]
|
||||
#![allow(clippy::new_without_default, incomplete_features)]
|
||||
#![feature(
|
||||
generic_const_exprs,
|
||||
iter_array_chunks,
|
||||
maybe_uninit_slice,
|
||||
maybe_uninit_as_bytes,
|
||||
maybe_uninit_write_slice,
|
||||
maybe_uninit_fill
|
||||
maybe_uninit_fill,
|
||||
maybe_uninit_array_assume_init
|
||||
)]
|
||||
|
||||
extern crate alloc;
|
||||
@@ -15,6 +15,7 @@ pub mod communication;
|
||||
pub mod descriptor;
|
||||
pub mod device;
|
||||
pub mod error;
|
||||
pub mod host;
|
||||
pub mod info;
|
||||
pub mod pipe;
|
||||
pub mod util;
|
||||
@@ -24,5 +25,3 @@ pub mod class_driver;
|
||||
// pub use communication::{UsbControlTransfer, UsbDirection, UsbTransferStatus, UsbTransferToken};
|
||||
|
||||
pub trait UsbEndpoint: Sync {}
|
||||
|
||||
pub trait UsbHostController: Sync + Send {}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use core::{
|
||||
mem::{size_of, MaybeUninit},
|
||||
mem::{MaybeUninit, size_of},
|
||||
ops::Deref,
|
||||
};
|
||||
|
||||
@@ -10,8 +10,8 @@ use libk_mm::PageBox;
|
||||
|
||||
use crate::{
|
||||
descriptor::{
|
||||
UsbConfigurationDescriptor, UsbDeviceDescriptor, UsbDeviceQualifier, UsbEndpointDescriptor,
|
||||
UsbInterfaceDescriptor, UsbOtherSpeedConfiguration,
|
||||
UsbConfigurationDescriptor, UsbDeviceDescriptor, UsbDeviceDescriptor0, UsbDeviceQualifier,
|
||||
UsbEndpointDescriptor, UsbInterfaceDescriptor, UsbOtherSpeedConfiguration,
|
||||
},
|
||||
error::UsbError,
|
||||
};
|
||||
@@ -34,11 +34,6 @@ pub trait UsbDeviceRequest: Sized + Pod {
|
||||
const B_REQUEST: u8;
|
||||
}
|
||||
|
||||
pub trait UsbClassSpecificRequest: Sized + Pod {
|
||||
const BM_REQUEST_TYPE: u8;
|
||||
const B_REQUEST: u8;
|
||||
}
|
||||
|
||||
pub trait UsbDescriptorRequest: UsbDeviceRequest {
|
||||
const DESCRIPTOR_TYPE: u8;
|
||||
}
|
||||
@@ -66,7 +61,7 @@ impl<U: UsbDescriptorRequest> UsbDeviceRequest for U {
|
||||
}
|
||||
|
||||
fn decode_usb_string(bytes: &[u8]) -> Result<String, UsbError> {
|
||||
if bytes.len() % 2 != 0 {
|
||||
if !bytes.len().is_multiple_of(2) {
|
||||
return Err(UsbError::InvalidDescriptorField);
|
||||
}
|
||||
|
||||
@@ -177,6 +172,25 @@ impl ConfigurationDescriptorQuery {
|
||||
}
|
||||
|
||||
impl UsbControlPipeAccess {
|
||||
pub async fn query_device_descriptor_0(&self) -> Result<UsbDeviceDescriptor0, UsbError> {
|
||||
assert_eq!(size_of::<UsbDeviceDescriptor0>(), 8);
|
||||
let mut buffer = MaybeUninit::uninit();
|
||||
|
||||
self.control_transfer_in(
|
||||
ControlTransferSetup {
|
||||
bm_request_type: 0b10000000,
|
||||
b_request: 0x06,
|
||||
w_value: 0x100,
|
||||
w_index: 0,
|
||||
w_length: size_of::<UsbDeviceDescriptor0>() as _,
|
||||
},
|
||||
buffer.as_bytes_mut(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(unsafe { buffer.assume_init() })
|
||||
}
|
||||
|
||||
pub async fn query_device_descriptor(&self) -> Result<UsbDeviceDescriptor, UsbError> {
|
||||
let mut buffer = MaybeUninit::uninit();
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ use libk::{
|
||||
use walk::{DirentIter, DirentIterMut};
|
||||
use yggdrasil_abi::io::{DirectoryEntry, FileType};
|
||||
|
||||
use crate::{file::RegularNode, inode::InodeAccess, symlink::SymlinkNode, Dirent, Ext2Fs, Inode};
|
||||
use crate::{Dirent, Ext2Fs, Inode, file::RegularNode, inode::InodeAccess, symlink::SymlinkNode};
|
||||
|
||||
mod walk;
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ use yggdrasil_abi::{
|
||||
util::FixedString,
|
||||
};
|
||||
|
||||
use crate::{access::InodeBlock, data::FsRequiredFeatures, Dirent, Ext2Fs};
|
||||
use crate::{Dirent, Ext2Fs, access::InodeBlock, data::FsRequiredFeatures};
|
||||
|
||||
use super::DirentName;
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user