Compare commits

39 Commits

Author SHA1 Message Date
alnyan d5f70c6a7c red: move more logic into lysp 2026-06-04 17:35:55 +03:00
alnyan 1736582613 lysp: more string prelude, fallible set_global_value 2026-06-04 17:35:30 +03:00
alnyan 1f670a66a4 red: rework command and key handling 2026-06-04 13:15:54 +03:00
alnyan 60f3572fec lysp: fix (explain ...) for macros 2026-06-04 13:15:25 +03:00
alnyan f5d3809f37 lysp: rework and/or, add prelude functions 2026-06-04 12:47:21 +03:00
alnyan 1261c037f8 lysp: rework upvalue handling 2026-06-03 11:24:32 +03:00
alnyan fd8e1df696 lysp: add hashtable support 2026-06-02 17:21:47 +03:00
alnyan befdf63c7c lysp: add dotted pair notation 2026-06-02 09:42:45 +03:00
alnyan 679ac51602 red: add .gitignore 2026-05-30 17:27:17 +03:00
alnyan 7909fa3808 red: add eval/set/unset commands 2026-05-28 17:50:07 +03:00
alnyan 4b98ec1ce2 red: add syntax highlighting and lysp support 2026-05-28 15:51:32 +03:00
alnyan 37ad3702d0 userspace: add lysp interpreter/compiler 2026-05-28 14:37:49 +03:00
alnyan 505a57abda pl011: fix incorrect byte written in write() 2026-05-28 14:37:25 +03:00
alnyan 677ec96c08 sysutils/top: better ui 2026-03-31 17:23:41 +03:00
alnyan 7064a21d8d libterm: fix incorrect bg index decode 2026-03-31 17:12:03 +03:00
alnyan 6d31142258 sysutils: add cpu/memory information to top 2026-03-30 12:23:25 +03:00
alnyan d7df44b1d9 lib/term: move from tui to ratatui 2026-03-28 20:45:38 +02:00
alnyan 1552bb27f4 toolchain: migrate to 1.94.0 toolchain 2026-03-28 01:59:17 +02:00
alnyan 7f256cf3a6 i2c: improve i2c architecture, add sifive i2c driver 2026-03-25 19:07:59 +02:00
alnyan f416414b93 char: add pwm subsystem, add sifive pwm driver 2026-03-23 14:02:02 +02:00
alnyan 0386e3916d maint: fix irq priority addition for aarch64/x86_64 2026-03-20 16:01:26 +02:00
alnyan 31176fb5aa riscv: initial support for hifive unmatched 2026-03-20 15:24:55 +02:00
alnyan 1411f1eed0 maint: update rustc to 1.95.0-nightly 2026-03-19 10:51:42 +02:00
alnyan 4c5309fa15 rsh: add rcp, remote copy program 2026-02-08 14:57:12 +02:00
alnyan 1012abceec cross: add unix i2c/spi stubs 2026-02-07 16:11:15 +02:00
alnyan a6a6dba155 net/stmmac: use GenericQueue 2026-02-07 15:47:40 +02:00
alnyan 7f46da9ebd net: unify netdev tx/rx queue interface 2026-02-07 14:13:09 +02:00
alnyan 1a87bc3666 sifive: support for cadence gemgxl ethernet 2026-02-05 17:10:14 +02:00
alnyan 4f648142c8 sifive: initial support for hifive unleashed a00 2026-02-04 17:34:37 +02:00
alnyan 0e979a9e09 spi: initial spi device support 2026-02-04 14:46:57 +02:00
alnyan ed9d7a7145 usb: add ft232 driver 2026-02-03 17:28:15 +02:00
alnyan 6b5dd9f673 irq: more flexible interrupt tables 2026-02-03 13:29:49 +02:00
alnyan 58dbaddf11 i2c: implement initial support for i2c devices 2026-02-03 12:08:15 +02:00
alnyan 218e391505 aarch64: better page fault info 2026-02-03 12:07:52 +02:00
alnyan 2a49c655c2 8250: fix reg-io-width/reg-shift for raspi4b 2026-01-19 11:36:01 +02:00
alnyan d108494314 8250: merge bcm aux uart into 8250 driver 2026-01-19 11:06:05 +02:00
alnyan 195c19e225 serial: unify 8250 drivers, better dts support 2026-01-16 23:18:46 +02:00
alnyan 21a8361eec usb: more keys for hid keyboard 2026-01-14 09:11:00 +02:00
alnyan fd0a3f50ea maint: update README.md 2026-01-14 09:06:03 +02:00
489 changed files with 32117 additions and 8745 deletions
Generated
+68 -26
View File
@@ -25,7 +25,6 @@ dependencies = [
name = "abi-lib"
version = "0.1.0"
dependencies = [
"compiler_builtins",
"rustc-std-workspace-core",
]
@@ -33,7 +32,6 @@ dependencies = [
name = "abi-serde"
version = "0.1.0"
dependencies = [
"compiler_builtins",
"rustc-std-workspace-alloc",
"rustc-std-workspace-core",
]
@@ -407,15 +405,6 @@ version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
[[package]]
name = "compiler_builtins"
version = "0.1.146"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a97117b1434b79833f39a5fabdf82f890bd98c1988334dea1cb67f7e627fa311"
dependencies = [
"rustc-std-workspace-core",
]
[[package]]
name = "console"
version = "0.15.10"
@@ -469,6 +458,7 @@ dependencies = [
name = "device-api"
version = "0.1.0"
dependencies = [
"async-trait",
"device-api-macros",
"yggdrasil-abi",
]
@@ -1199,9 +1189,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",
]
@@ -1290,9 +1280,8 @@ dependencies = [
[[package]]
name = "libm"
version = "0.2.8"
source = "git+https://git.alnyan.me/yggdrasil/libm.git#ace5825d9683d2bf4a71c8f18f2c854660c297b2"
source = "git+https://git.alnyan.me/yggdrasil/libm.git#78b62c33fc6a56b6c063c19bbffc5224616b7028"
dependencies = [
"compiler_builtins",
"rustc-std-workspace-core",
]
@@ -1329,7 +1318,6 @@ version = "0.1.0"
name = "libyalloc"
version = "0.1.0"
dependencies = [
"compiler_builtins",
"libc",
"rustc-std-workspace-core",
"yggdrasil-rt",
@@ -1353,6 +1341,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 +1781,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 +2011,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 +2394,7 @@ checksum = "b4ee928febd44d98f2f459a4a79bd4d928591333a494a10a868418ac1b39cf1f"
dependencies = [
"either",
"home",
"rustix",
"rustix 0.38.44",
"winsafe",
]
@@ -2557,8 +2563,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 +2595,7 @@ dependencies = [
"semver 1.0.25",
"serde",
"tar",
"tempfile",
"thiserror",
"toml",
"walkdir",
@@ -2657,6 +2664,7 @@ dependencies = [
name = "ygg_driver_bsp_bcm283x"
version = "0.1.0"
dependencies = [
"async-trait",
"bytemuck",
"device-api",
"device-tree",
@@ -2702,6 +2710,25 @@ dependencies = [
"yggdrasil-abi",
]
[[package]]
name = "ygg_driver_bsp_sifive"
version = "0.1.0"
dependencies = [
"async-trait",
"bytemuck",
"device-api",
"device-tree",
"futures-util",
"libk",
"libk-mm",
"libk-util",
"log",
"static_assertions",
"tock-registers",
"ygg_driver_net_core",
"yggdrasil-abi",
]
[[package]]
name = "ygg_driver_fat32"
version = "0.1.0"
@@ -2733,6 +2760,7 @@ version = "0.1.0"
dependencies = [
"async-trait",
"bytemuck",
"device-api",
"kernel-fs",
"libk",
"libk-mm",
@@ -2749,6 +2777,7 @@ name = "ygg_driver_net_igbe"
version = "0.1.0"
dependencies = [
"device-api",
"futures-util",
"libk",
"libk-mm",
"libk-util",
@@ -2855,6 +2884,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"
@@ -2968,7 +3010,6 @@ dependencies = [
"abi-lib",
"abi-serde",
"bytemuck",
"compiler_builtins",
"prettyplease",
"rustc-std-workspace-alloc",
"rustc-std-workspace-core",
@@ -3019,6 +3060,7 @@ dependencies = [
"ygg_driver_bsp_bcm283x",
"ygg_driver_bsp_jh7110",
"ygg_driver_bsp_riscv",
"ygg_driver_bsp_sifive",
"ygg_driver_fat32",
"ygg_driver_input",
"ygg_driver_net_core",
@@ -3028,6 +3070,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",
@@ -3044,7 +3087,6 @@ dependencies = [
"abi-lib",
"abi-serde",
"cc",
"compiler_builtins",
"libm",
"prettyplease",
"rustc-std-workspace-alloc",
+1
View File
@@ -89,6 +89,7 @@ features = ["no_std_stream"]
[workspace.lints.rust]
unexpected_cfgs = { level = "allow", check-cfg = ['cfg(rust_analyzer)'] }
unsafe_op_in_unsafe_fn.level = "deny"
[workspace.lints.clippy]
derivable_impls = { level = "allow" }
+18 -14
View File
@@ -9,9 +9,9 @@ Main features
-------------
* Architecture support:
* [aarch64](kernel/src/arch/aarch64)
* [x86_64](kernel/src/arch/x86_64)
* [i686](kernel/src/arch/i686) (Pentium Pro and later)
* [aarch64](kernel/arch/aarch64)
* [x86_64](kernel/arch/x86_64)
* [riscv64](kernel/arch/riscv64)
* Core features:
* Kernel/userspace preemptive multithreading
* Kernel-space multitasking with `async`/`await` runtime
@@ -24,6 +24,7 @@ Main features
* sysfs
* devfs
* ext2
* fat32 (read-only)
* Userspace features:
* [Kernel-user ABI](lib/abi-def/yggdrasil.abi) generated from a rust-like description language
* Sanitized system calls better suited for use in Rust
@@ -33,33 +34,37 @@ Main features
* Synchronization primitives through futex-like interface
* Unix-like signals and exceptions
* [Dynamic loader](userspace/dyn-loader) for linking with shared libraries
* Runs DOOM
* Hardware features:
* PCI Express devices
* NVMe drive support (read/write, currently x86_64 only, due to lack of MSI-X support on aarch64/i686).
* AHCI SATA drive support (read/write)
* NVMe drive support
* AHCI SATA drive support
* xHCI USB host controller
* VirtIO Network + GPU framebuffer support
* USB HID keyboards
* USB device support
* Hub driver
* HID keyboards and mice
* Mass storage (BBB)
* Partial hardware support for aarch64/riscv64 SBCs like StarFive VisionFive 2 and Raspberry Pi 4
aarch64-specific:
* PSCI for SMP start-up and power control
* PL011 serial port
* PL061 GPIO controller
* PL031 RTC
* ARM generic timer as system/monotonic timer
* GICv2 IRQ controller
* GICv2 IRQ controller + GICv2m MSI interrupts
x86-specific:
* Boot options:
* x86_64: UEFI [yboot](https://git.alnyan.me/yggdrasil/yboot)
* i686: multiboot/grub
* Boot via UEFI [yboot](https://git.alnyan.me/yggdrasil/yboot)
* I/O and Local APIC IRQ controllers
* PS/2 keyboard
* HPET for x86_64
* i8253-based timer for i686 or as a fallback timer
* i8253 as a fallback timer
* COM ports
* ACPI, [work in progress](https://github.com/rust-osdev/acpi), mostly broken
on real hardware, so currently disabled
* ACPI, [work in progress](https://github.com/rust-osdev/acpi)
* ACPI shutdown
* PCI IRQ pin routing
* Events like power button, etc.
@@ -122,7 +127,6 @@ General plans (in no particular order)
2. Get a full LLVM build to work
3. Get rustc to work
4. Get self-hosted
5. Run doom (?)
In addition to eternal code cleanup, I've been doing quite a lazy job at that lately...
+35
View File
@@ -0,0 +1,35 @@
Booting Yggdrasil OS on SiFive HiFive Unmatched Rev B RISC-V board:
* TODO: proper format for initrd image
Prerequisites:
* OpenSBI + u-boot (can use the u-boot from the Freedom U SDK that comes with the board)
* yggdrasil-kernel.bin
* initrd.img
Steps:
1. Copy yggdrasil-kernel.bin and initrd.img into some directory and start a TFTP server there
2. Connect to HFU's serial port, ethernet and enter u-boot
3. Run the following commands in u-boot:
### If using DHCP
=> setenv autoload no
=> setenv initrd_addr_r 0x90000000
=> dhcp
=> tftpboot ${initrd_addr_r} <SERVER IP>:initrd.img
=> tftpboot ${loadaddr} <SERVER IP>:yggdrasil-kernel.bin
=> tftpboot ${fdt_addr_r} <SERVER IP>:hifive-unmatched-a00.dtb
=> fdt resize
=> booti ${loadaddr} ${initrd_addr_r}:60000000 ${fdt_addr_r}
Two-liner (server is 13.0.0.1, board is 13.0.0.2):
setenv ipaddr 13.0.0.2;setenv initrd_addr_r 0x90000000;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:hifive-unmatched-a00.dtb;fdt resize
booti ${loadaddr} ${initrd_addr_r}:60000000 ${fdt_addr_r}
One-liner without initrd
setenv ipaddr 13.0.0.2;tftpboot ${loadaddr} 13.0.0.1:yggdrasil-kernel.bin;tftpboot ${fdt_addr_r} 13.0.0.1:hifive-unmatched-a00.dtb;fdt resize;booti ${loadaddr} - ${fdt_addr_r}
+3 -2
View File
@@ -33,10 +33,11 @@ $ 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:
+1
View File
@@ -2,6 +2,7 @@
"arch": "aarch64",
"os": "none",
"abi": "softfloat",
"rustc-abi": "softfloat",
"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,
Binary file not shown.
@@ -264,16 +264,15 @@
compatible = "gpio-keys";
poweroff {
gpios = <0x8007 0x03 0x00>;
gpios = <&gpio 0x03 0x00>;
linux,code = <0x74>;
label = "GPIO Key Poweroff";
};
};
pl061@9030000 {
phandle = <0x8007>;
gpio: pl061@9030000 {
clock-names = "apb_pclk";
clocks = <0x8000>;
clocks = <&clk_24mhz>;
interrupts = <0x00 0x07 0x04>;
gpio-controller;
#gpio-cells = <0x02>;
@@ -299,15 +298,15 @@
pl031@9010000 {
clock-names = "apb_pclk";
clocks = <0x8000>;
clocks = <&clk_24mhz>;
interrupts = <0x00 0x02 0x04>;
reg = <0x00 0x9010000 0x00 0x1000>;
compatible = "arm,pl031", "arm,primecell";
};
pl011@9000000 {
uart0: pl011@9000000 {
clock-names = "uartclk", "apb_pclk";
clocks = <0x8000 0x8000>;
clocks = <&clk_24mhz &clk_24mhz>;
interrupts = <0x00 0x01 0x04>;
reg = <0x00 0x9000000 0x00 0x1000>;
compatible = "arm,pl011", "arm,primecell";
@@ -411,8 +410,7 @@
compatible = "arm,armv8-timer", "arm,armv7-timer";
};
apb-pclk {
phandle = <0x8000>;
clk_24mhz: apb-pclk {
clock-output-names = "clk24mhz";
clock-frequency = <0x16e3600>;
#clock-cells = <0x00>;
+669
View File
@@ -0,0 +1,669 @@
/dts-v1/;
#include <aarch64/gicv2.h>
/memreserve/ 0x0000000000000000 0x0000000000001000;
/ {
compatible = "raspberrypi,4-model-b", "brcm,bcm2711";
model = "Raspberry Pi 4 Model B";
#address-cells = <0x02>;
#size-cells = <0x01>;
interrupt-parent = <&gicv2>;
aliases {
serial0 = "/soc/serial@7e201000";
serial1 = "/soc/serial@7e215040";
blconfig = "/reserved-memory/nvram@0";
};
chosen {
stdout-path = "serial1:115200n8";
};
reserved-memory {
#address-cells = <0x02>;
#size-cells = <0x01>;
ranges;
linux,cma {
compatible = "shared-dma-pool";
size = <0x4000000>;
reusable;
linux,cma-default;
alloc-ranges = <0x00 0x00 0x40000000>;
};
nvram@0 {
compatible = "raspberrypi,bootloader-config", "nvmem-rmem";
#address-cells = <0x01>;
#size-cells = <0x01>;
reg = <0x00 0x00 0x00>;
no-map;
status = "disabled";
};
};
soc {
compatible = "simple-bus";
#address-cells = <0x01>;
#size-cells = <0x01>;
ranges = <0x7e000000 0x00 0xfe000000 0x01800000>,
<0x7c000000 0x00 0xfc000000 0x02000000>,
<0x40000000 0x00 0xff800000 0x00800000>;
dma-ranges = <0xc0000000 0x00 0x00 0x40000000>;
timer@7e003000 {
compatible = "brcm,bcm2835-system-timer";
reg = <0x7e003000 0x1000>;
interrupts = <GIC_SPI 64 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 65 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 66 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 67 IRQ_TYPE_LEVEL_HIGH>;
clock-frequency = <0xf4240>;
};
txp@7e004000 {
compatible = "brcm,bcm2835-txp";
reg = <0x7e004000 0x20>;
interrupts = <GIC_SPI 75 IRQ_TYPE_LEVEL_HIGH>;
};
dsi0: dsi@7e209000 {
compatible = "brcm,bcm2835-dsi0";
reg = <0x7e209000 0x78>;
interrupts = <GIC_SPI 100 IRQ_TYPE_LEVEL_HIGH>;
#address-cells = <0x01>;
#size-cells = <0x00>;
#clock-cells = <0x01>;
clocks = <&cprman 0x20>,
<&cprman 0x2f>,
<&cprman 0x31>;
clock-names = "phy", "escape", "pixel";
clock-output-names = "dsi0_byte", "dsi0_ddr2", "dsi0_ddr";
status = "disabled";
power-domains = <&power 0x11>;
};
dsi1: dsi@7e700000 {
compatible = "brcm,bcm2711-dsi1";
reg = <0x7e700000 0x8c>;
interrupts = <GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH>;
#address-cells = <0x01>;
#size-cells = <0x00>;
#clock-cells = <0x01>;
clocks = <&cprman 0x23>,
<&cprman 0x30>,
<&cprman 0x32>;
clock-names = "phy", "escape", "pixel";
clock-output-names = "dsi1_byte", "dsi1_ddr2", "dsi1_ddr";
status = "disabled";
power-domains = <&power 0x12>;
};
cprman: cprman@7e101000 {
compatible = "brcm,bcm2711-cprman";
#clock-cells = <0x01>;
reg = <0x7e101000 0x2000>;
clocks = <&clk_osc>,
<&dsi0 0x00>,
<&dsi0 0x01>,
<&dsi0 0x02>,
<&dsi1 0x00>,
<&dsi1 0x01>,
<&dsi1 0x02>;
};
mbox: mailbox@7e00b880 {
compatible = "brcm,bcm2835-mbox";
reg = <0x7e00b880 0x40>;
interrupts = <GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>;
#mbox-cells = <0x00>;
};
gpio: gpio@7e200000 {
compatible = "brcm,bcm2711-gpio";
reg = <0x7e200000 0xb4>;
interrupts = <GIC_SPI 113 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 114 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 115 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 116 IRQ_TYPE_LEVEL_HIGH>;
gpio-controller;
#gpio-cells = <0x02>;
interrupt-controller;
#interrupt-cells = <0x02>;
pinctrl-names = "default";
bootph-all;
// UART
uart0_ctsrts_gpio30: uart0_ctsrts_gpio30 {
brcm,pins = <30>, <31>;
brcm,pull = <2>, <0>;
brcm,function = <7>;
};
uart0_gpio32: uart0_gpio32 {
brcm,pins = <32>, <33>;
brcm,pull = <0>, <2>;
brcm,function = <8>;
};
uart1_gpio14: uart1_gpio14 {
brcm,pins = <14>, <15>;
brcm,function = <2>;
bootph-all;
};
uart2_gpio: uart2_pins {
brcm,pins = <0>, <1>;
brcm,function = <3>;
brcm,pull = <0>, <2>;
};
// I²C
i2c0if_gpio0: i2c0if-gpio0 {
brcm,pins = <0>, <1>;
brcm,function = <4>;
};
i2c0if_gpio44: i2c0if-gpio44 {
brcm,pins = <44>, <45>;
brcm,function = <5>;
};
i2c1_gpio: i2c1 {
brcm,pins = <2>, <3>;
brcm,function = <4>;
brcm,pull = <2>;
};
i2c3_gpio: i2c3 {
brcm,pins = <4>, <5>;
brcm,function = <2>;
brcm,pull = <2>;
};
i2c4_gpio: i2c4 {
brcm,pins = <8>, <9>;
brcm,function = <2>;
brcm,pull = <2>;
};
i2c5_gpio: i2c5 {
brcm,pins = <12>, <13>;
brcm,function = <2>;
brcm,pull = <2>;
};
i2c6_gpio: i2c6 {
brcm,pins = <22>, <23>;
brcm,function = <2>;
brcm,pull = <2>;
};
// SPI
spi0_gpio: spi0_pins {
brcm,pins = <9>, <10>, <11>;
brcm,function = <4>;
};
spi0_cs_gpio: spi0_cs_pins {
brcm,pins = <8>, <7>;
brcm,function = <1>;
};
};
uart0: serial@7e201000 {
compatible = "arm,pl011", "arm,primecell";
reg = <0x7e201000 0x200>;
interrupts = <GIC_SPI 121 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cprman 0x13>,
<&cprman 0x14>;
clock-names = "uartclk",
"apb_pclk";
arm,primecell-periphid = <0x241011>;
pinctrl-names = "default";
pinctrl-0 = <&uart0_ctsrts_gpio30>, <&uart0_gpio32>;
uart-has-rtscts;
status = "okay";
skip-init;
bootph-all;
};
aux: aux@7e215000 {
compatible = "brcm,bcm2835-aux";
#clock-cells = <0x01>;
reg = <0x7e215000 0x08>;
clocks = <&cprman 0x14>;
};
uart1: serial@7e215040 {
compatible = "brcm,bcm2835-aux-uart";
reg = <0x7e215040 0x40>;
interrupts = <GIC_SPI 93 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&aux 0x00>;
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&uart1_gpio14>;
skip-init;
bootph-all;
};
i2c0if: i2c@7e205000 {
compatible = "brcm,bcm2711-i2c", "brcm,bcm2835-i2c";
reg = <0x7e205000 0x200>;
interrupts = <GIC_SPI 117 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cprman 0x14>;
#address-cells = <0x01>;
#size-cells = <0x00>;
status = "disabled";
clock-frequency = <0x186a0>;
};
i2c0mux: i2c0mux {
compatible = "i2c-mux-pinctrl";
#address-cells = <0x01>;
#size-cells = <0x00>;
i2c-parent = <&i2c0if>;
status = "disabled";
pinctrl-names = "i2c0", "i2c_csi_dsi";
pinctrl-0 = <&i2c0if_gpio0>;
pinctrl-1 = <&i2c0if_gpio44>;
i2c0: i2c@0 {
reg = <0x00>;
#address-cells = <0x01>;
#size-cells = <0x00>;
};
i2c_csi_dsi: i2c@1 {
reg = <0x01>;
#address-cells = <0x01>;
#size-cells = <0x00>;
};
};
i2c1: i2c@7e804000 {
compatible = "brcm,bcm2711-i2c", "brcm,bcm2835-i2c";
reg = <0x7e804000 0x1000>;
interrupts = <GIC_SPI 117 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cprman 0x14>;
#address-cells = <0x01>;
#size-cells = <0x00>;
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&i2c1_gpio>;
clock-frequency = <0x186a0>;
};
// TODO: memory access crashes on qemu (not implemented?)
i2c3: i2c@7e205600 {
compatible = "brcm,bcm2711-i2c", "brcm,bcm2835-i2c";
reg = <0x7e205600 0x200>;
interrupts = <GIC_SPI 117 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cprman 0x14>;
#address-cells = <0x01>;
#size-cells = <0x00>;
status = "disabled";
pinctrl-0 = <&i2c3_gpio>;
pinctrl-names = "default";
};
// TODO: memory access crashes on qemu (not implemented?)
i2c4: i2c@7e205800 {
compatible = "brcm,bcm2711-i2c", "brcm,bcm2835-i2c";
reg = <0x7e205800 0x200>;
interrupts = <GIC_SPI 117 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cprman 0x14>;
#address-cells = <0x01>;
#size-cells = <0x00>;
status = "disabled";
pinctrl-0 = <&i2c4_gpio>;
pinctrl-names = "default";
};
// TODO: memory access crashes on qemu (not implemented?)
i2c5: i2c@7e205a00 {
compatible = "brcm,bcm2711-i2c", "brcm,bcm2835-i2c";
reg = <0x7e205a00 0x200>;
interrupts = <GIC_SPI 117 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cprman 0x14>;
#address-cells = <0x01>;
#size-cells = <0x00>;
status = "disabled";
pinctrl-0 = <&i2c5_gpio>;
pinctrl-names = "default";
};
// TODO: memory access crashes on qemu (not implemented?)
i2c6: i2c@7e205c00 {
compatible = "brcm,bcm2711-i2c", "brcm,bcm2835-i2c";
reg = <0x7e205c00 0x200>;
interrupts = <GIC_SPI 117 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cprman 0x14>;
#address-cells = <0x01>;
#size-cells = <0x00>;
status = "disabled";
pinctrl-0 = <&i2c6_gpio>;
pinctrl-names = "default";
};
spi0: spi@7e204000 {
compatible = "brcm,bcm2835-spi";
reg = <0x7e204000 0x200>;
interrupts = <GIC_SPI 118 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cprman 0x14>;
#address-cells = <0x01>;
#size-cells = <0x00>;
status = "okay";
dmas = <&dma 0x06>,
<&dma 0x07>;
dma-names = "tx", "rx";
pinctrl-names = "default";
pinctrl-0 = <&spi0_gpio>, <&spi0_cs_gpio>;
cs-gpios = <&gpio 8 1>, <&gpio 7 1>;
spi0_0: spidev@0 {
compatible = "spidev";
reg = <0x00>;
#address-cells = <0x01>;
#size-cells = <0x00>;
spi-max-frequency = <125000000>;
};
spi0_1: spidev@1 {
compatible = "spidev";
reg = <0x01>;
#address-cells = <0x01>;
#size-cells = <0x00>;
spi-max-frequency = <125000000>;
};
};
l1_intc: local_intc@40000000 {
compatible = "brcm,bcm2836-l1-intc";
reg = <0x40000000 0x100>;
};
gicv2: interrupt-controller@40041000 {
interrupt-controller;
#interrupt-cells = <0x03>;
compatible = "arm,gic-400";
reg = <0x40041000 0x1000>,
<0x40042000 0x2000>,
<0x40044000 0x2000>,
<0x40046000 0x2000>;
interrupts = <GIC_PPI 9 (IRQ_TYPE_LEVEL_HIGH | GIC_CPU_MASK_SIMPLE(4))>;
};
avs_monitor: avs-monitor@7d5d2000 {
compatible = "brcm,bcm2711-avs-monitor", "syscon", "simple-mfd";
reg = <0x7d5d2000 0xf00>;
};
dma: dma@7e007000 {
compatible = "brcm,bcm2835-dma";
reg = <0x7e007000 0xb00>;
interrupts = <GIC_SPI 80 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 81 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 82 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 83 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 84 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 85 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 86 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 87 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 87 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 88 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 88 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "dma0",
"dma1",
"dma2",
"dma3",
"dma4",
"dma5",
"dma6",
"dma7",
"dma8",
"dma9",
"dma10";
#dma-cells = <0x01>;
brcm,dma-channel-mask = <0x7f5>;
};
pm_wdt: watchdog@7e100000 {
compatible = "brcm,bcm2835-pm", "brcm,bcm2835-pm-wdt";
#power-domain-cells = <0x01>;
#reset-cells = <0x01>;
reg = <0x7e100000 0x114>,
<0x7e00a000 0x24>,
<0x7ec11000 0x20>;
clocks = <&cprman 0x15>,
<&cprman 0x1d>,
<&cprman 0x17>,
<&cprman 0x16>;
clock-names = "v3d", "peri_image", "h264", "isp";
system-power-controller;
};
uart2: serial@7e201400 {
compatible = "arm,pl011", "arm,primecell";
reg = <0x7e201400 0x200>;
interrupts = <GIC_SPI 121 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cprman 0x13>,
<&cprman 0x14>;
clock-names = "uartclk",
"apb_pclk";
arm,primecell-periphid = <0x241011>;
pinctrl-0 = <&uart2_gpio>;
pinctrl-names = "default";
status = "disabled";
};
uart3: serial@7e201600 {
compatible = "arm,pl011", "arm,primecell";
reg = <0x7e201600 0x200>;
interrupts = <GIC_SPI 121 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cprman 0x13>,
<&cprman 0x14>;
clock-names = "uartclk", "apb_pclk";
arm,primecell-periphid = <0x241011>;
status = "disabled";
};
uart4: serial@7e201800 {
compatible = "arm,pl011", "arm,primecell";
reg = <0x7e201800 0x200>;
interrupts = <GIC_SPI 121 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cprman 0x13>,
<&cprman 0x14>;
clock-names = "uartclk", "apb_pclk";
arm,primecell-periphid = <0x241011>;
status = "disabled";
};
uart5: serial@7e201a00 {
compatible = "arm,pl011", "arm,primecell";
reg = <0x7e201a00 0x200>;
interrupts = <GIC_SPI 121 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cprman 0x13>,
<&cprman 0x14>;
clock-names = "uartclk", "apb_pclk";
arm,primecell-periphid = <0x241011>;
status = "disabled";
};
clk_dvp: clock@7ef00000 {
compatible = "brcm,brcm2711-dvp";
reg = <0x7ef00000 0x10>;
clocks = <&clk_108m>;
#clock-cells = <0x01>;
#reset-cells = <0x01>;
};
l2_intc: interrupt-controller@7ef00100 {
compatible = "brcm,bcm2711-l2-intc", "brcm,l2-intc";
reg = <0x7ef00100 0x30>;
interrupts = <GIC_SPI 96 IRQ_TYPE_LEVEL_HIGH>;
interrupt-controller;
#interrupt-cells = <0x01>;
};
firmware: firmware {
compatible = "raspberrypi,bcm2835-firmware", "simple-mfd";
#address-cells = <0x01>;
#size-cells = <0x01>;
mboxes = <&mbox>;
dma-ranges;
clk_firmware: clocks {
compatible = "raspberrypi,firmware-clocks";
#clock-cells = <0x01>;
};
gpio_firmware: gpio {
compatible = "raspberrypi,firmware-gpio";
gpio-controller;
#gpio-cells = <0x02>;
status = "okay";
gpio-line-names = "BT_ON", "WL_ON", "PWR_LED_OFF", "GLOBAL_RESET", "VDD_SD_IO_SEL", "CAM_GPIO", "SD_PWR_ON", "";
};
firmware_reset: reset {
compatible = "raspberrypi,firmware-reset";
#reset-cells = <0x01>;
};
};
power: power {
compatible = "raspberrypi,bcm2835-power";
firmware = <&firmware>;
#power-domain-cells = <0x01>;
};
vchiq: mailbox@7e00b840 {
compatible = "brcm,bcm2835-vchiq";
reg = <0x7e00b840 0x3c>;
interrupts = <GIC_SPI 34 IRQ_TYPE_LEVEL_HIGH>;
};
};
clocks {
clk_osc: clk-osc {
compatible = "fixed-clock";
#clock-cells = <0x00>;
clock-output-names = "osc";
clock-frequency = <54000000>;
};
clk_usb: clk-usb {
compatible = "fixed-clock";
#clock-cells = <0x00>;
clock-output-names = "otg";
clock-frequency = <480000000>;
};
};
clk_27m: clk-27M {
#clock-cells = <0x00>;
compatible = "fixed-clock";
clock-frequency = <27000000>;
clock-output-names = "27MHz-clock";
};
clk_108m: clk-108M {
#clock-cells = <0x00>;
compatible = "fixed-clock";
clock-frequency = <108000000>;
clock-output-names = "108MHz-clock";
};
pmu: arm-pmu {
compatible = "arm,cortex-a72-pmu", "arm,armv8-pmuv3";
interrupts = <GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 17 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 18 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 19 IRQ_TYPE_LEVEL_HIGH>;
interrupt-affinity = <0x21>,
<0x22>,
<0x23>,
<0x24>;
};
arm_timer: timer {
compatible = "arm,armv8-timer";
interrupts = <GIC_PPI 13 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>,
<GIC_PPI 14 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>,
<GIC_PPI 15 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>,
<GIC_PPI 16 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>;
arm,cpu-registers-not-fw-configured;
};
cpus {
#address-cells = <0x01>;
#size-cells = <0x00>;
enable-method = "brcm,bcm2836-smp";
cpu0: cpu@0 {
device_type = "cpu";
compatible = "arm,cortex-a72";
reg = <0x00>;
enable-method = "spin-table";
cpu-release-addr = <0x00 0xd8>;
};
cpu1: cpu@1 {
device_type = "cpu";
compatible = "arm,cortex-a72";
reg = <0x01>;
enable-method = "spin-table";
cpu-release-addr = <0x00 0xe0>;
};
cpu2: cpu@2 {
device_type = "cpu";
compatible = "arm,cortex-a72";
reg = <0x02>;
enable-method = "spin-table";
cpu-release-addr = <0x00 0xe8>;
};
cpu3: cpu@3 {
device_type = "cpu";
compatible = "arm,cortex-a72";
reg = <0x03>;
enable-method = "spin-table";
cpu-release-addr = <0x00 0xf0>;
};
};
leds {
compatible = "gpio-leds";
led_act: led-act {
label = "ACT";
default-state = "keep";
linux,default-trigger = "heartbeat";
gpios = <&gpio 0x2a 0x00>;
};
led_pwr: led-pwr {
label = "PWR";
gpios = <&gpio_firmware 0x02 0x01>;
default-state = "keep";
linux,default-trigger = "default-on";
};
};
memory@0 {
device_type = "memory";
reg = <0x00 0x00 0x00>;
};
sd_io_1v8_reg {
compatible = "regulator-gpio";
regulator-name = "vdd-sd-io";
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <3300000>;
regulator-boot-on;
regulator-always-on;
regulator-settling-time-us = <0x1388>;
gpios = <&gpio_firmware 0x04 0x00>;
states = <3300000 0x01>,
<1800000 0x00>;
status = "okay";
};
sd_vcc_reg {
compatible = "regulator-fixed";
regulator-name = "vcc-sd";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
regulator-boot-on;
enable-active-high;
gpio = <&gpio_firmware 0x06 0x00>;
};
};
+14
View File
@@ -0,0 +1,14 @@
#pragma once
#define GIC_SPI 0
#define GIC_PPI 1
#define GIC_CPU_MASK_RAW(x) ((x) << 8)
#define GIC_CPU_MASK_SIMPLE(num) GIC_CPU_MASK_RAW((1 << (num)) - 1)
#define IRQ_TYPE_NONE 0
#define IRQ_TYPE_EDGE_RISING 1
#define IRQ_TYPE_EDGE_FALLING 2
#define IRQ_TYPE_EDGE_BOTH (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)
#define IRQ_TYPE_LEVEL_HIGH 4
#define IRQ_TYPE_LEVEL_LOW 8
Binary file not shown.
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
Binary file not shown.
+319
View File
@@ -0,0 +1,319 @@
/dts-v1/;
#define CLK_COREPLL 0
#define CLK_DDRPLL 1
#define CLK_GEMGXLPLL 2
#define CLK_DVFSCOREPLL 3
#define CLK_HFPCLKPLL 4
#define CLK_CLTXPLL 5
#define CLK_TLCLK 6
#define CLK_PCLK 7
#define CLK_PCIE_AUX 8
/ {
#address-cells = <2>;
#size-cells = <2>;
compatible = "sifive,hifive-unmatched-a00", "sifive,fu740-c000", "sifive,fu740";
model = "SiFive HiFive Unmatched";
aliases {
serial0 = "/soc/serial@10010000";
serial1 = "/soc/serial@10011000";
};
chosen {
stdout-path = "serial0";
};
cpus {
#address-cells = <1>;
#size-cells = <0>;
timebase-frequency = <1000000>;
cpu0: cpu@0 {
compatible = "sifive,bullet0", "riscv";
device_type = "cpu";
i-cache-block-size = <64>;
i-cache-sets = <128>;
i-cache-size = <16384>;
next-level-cache = <&ccache>;
reg = <0>;
riscv,isa = "rv64imac";
status = "disabled";
cpu0_intc: interrupt-controller {
#interrupt-cells = <1>;
compatible = "riscv,cpu-intc";
interrupt-controller;
};
};
cpu1: cpu@1 {
compatible = "sifive,u74-mc", "sifive,bullet0", "riscv";
device_type = "cpu";
d-cache-block-size = <64>;
d-cache-sets = <64>;
d-cache-size = <32768>;
i-cache-block-size = <64>;
i-cache-sets = <128>;
i-cache-size = <32768>;
i-tlb-sets = <1>;
i-tlb-size = <40>;
d-tlb-sets = <1>;
d-tlb-size = <40>;
mmu-type = "riscv,sv39";
next-level-cache = <&ccache>;
reg = <1>;
riscv,isa = "rv64imafdc";
tlb-split;
cpu1_intc: interrupt-controller {
#interrupt-cells = <1>;
compatible = "riscv,cpu-intc";
interrupt-controller;
};
};
cpu2: cpu@2 {
compatible = "sifive,u74-mc", "sifive,bullet0", "riscv";
device_type = "cpu";
d-cache-block-size = <64>;
d-cache-sets = <64>;
d-cache-size = <32768>;
i-cache-block-size = <64>;
i-cache-sets = <128>;
i-cache-size = <32768>;
d-tlb-sets = <1>;
d-tlb-size = <40>;
i-tlb-sets = <1>;
i-tlb-size = <40>;
mmu-type = "riscv,sv39";
next-level-cache = <&ccache>;
reg = <2>;
riscv,isa = "rv64imafdc";
tlb-split;
cpu2_intc: interrupt-controller {
#interrupt-cells = <1>;
compatible = "riscv,cpu-intc";
interrupt-controller;
};
};
cpu3: cpu@3 {
compatible = "sifive,u74-mc", "sifive,bullet0", "riscv";
device_type = "cpu";
d-cache-block-size = <64>;
d-cache-sets = <64>;
d-cache-size = <32768>;
i-cache-block-size = <64>;
i-cache-sets = <128>;
i-cache-size = <32768>;
d-tlb-sets = <1>;
d-tlb-size = <40>;
i-tlb-sets = <1>;
i-tlb-size = <40>;
mmu-type = "riscv,sv39";
next-level-cache = <&ccache>;
reg = <3>;
riscv,isa = "rv64imafdc";
tlb-split;
cpu3_intc: interrupt-controller {
#interrupt-cells = <1>;
compatible = "riscv,cpu-intc";
interrupt-controller;
};
};
cpu4: cpu@4 {
compatible = "sifive,u74-mc", "sifive,bullet0", "riscv";
device_type = "cpu";
d-cache-block-size = <64>;
d-cache-sets = <64>;
d-cache-size = <32768>;
i-cache-block-size = <64>;
i-cache-sets = <128>;
i-cache-size = <32768>;
d-tlb-sets = <1>;
d-tlb-size = <40>;
i-tlb-sets = <1>;
i-tlb-size = <40>;
mmu-type = "riscv,sv39";
next-level-cache = <&ccache>;
reg = <4>;
riscv,isa = "rv64imafdc";
tlb-split;
cpu4_intc: interrupt-controller {
#interrupt-cells = <1>;
compatible = "riscv,cpu-intc";
interrupt-controller;
};
};
};
soc {
#address-cells = <2>;
#size-cells = <2>;
compatible = "simple-bus";
ranges;
plic: interrupt-controller@c000000 {
#interrupt-cells = <1>;
#address-cells = <0>;
compatible = "sifive,fu540-c000-plic", "sifive,plic-1.0.0";
reg = <0x00 0xc000000 0x00 0x4000000>;
riscv,ndev = <69>;
interrupt-controller;
interrupts-extended = <&cpu0_intc 0xffffffff>,
<&cpu1_intc 0xffffffff>,
<&cpu1_intc 0x09>,
<&cpu2_intc 0xffffffff>,
<&cpu2_intc 0x09>,
<&cpu3_intc 0xffffffff>,
<&cpu3_intc 0x09>,
<&cpu4_intc 0xffffffff>,
<&cpu4_intc 0x09>;
};
prci: clock-controller@10000000 {
compatible = "sifive,fu740-c000-prci";
reg = <0x00 0x10000000 0x00 0x1000>;
clocks = <&clk_hfclk>, <&clk_rtcclk>;
#clock-cells = <1>;
#reset-cells = <1>;
};
uart0: serial@10010000 {
compatible = "sifive,fu740-c000-uart", "sifive,uart0";
reg = <0x00 0x10010000 0x00 0x1000>;
interrupt-parent = <&plic>;
interrupts = <39>;
clocks = <&prci CLK_PCLK>;
status = "okay";
};
uart1: serial@10011000 {
compatible = "sifive,fu740-c000-uart", "sifive,uart0";
reg = <0x00 0x10011000 0x00 0x1000>;
interrupt-parent = <&plic>;
interrupts = <40>;
clocks = <&prci CLK_PCLK>;
status = "disabled";
};
ccache: cache-controller@2010000 {
compatible = "sifive,fu740-c000-ccache", "cache";
cache-block-size = <64>;
cache-level = <2>;
cache-sets = <2048>;
cache-size = <0x200000>;
cache-unified;
interrupt-parent = <&plic>;
interrupts = <0x13>, <0x15>, <0x16>, <0x14>;
reg = <0x00 0x2010000 0x00 0x1000>;
};
pwm0: pwm@10020000 {
compatible = "sifive,fu740-c000-pwm", "sifive,pwm0";
reg = <0x00 0x10020000 0x00 0x1000>;
interrupt-parent = <&plic>;
interrupts = <44>, <45>, <46>, <47>;
clocks = <&prci CLK_PCLK>;
#pwm-cells = <3>;
status = "okay";
};
pwm1: pwm@10021000 {
compatible = "sifive,fu740-c000-pwm", "sifive,pwm0";
reg = <0x00 0x10021000 0x00 0x1000>;
interrupt-parent = <&plic>;
interrupts = <48>, <49>, <50>, <51>;
clocks = <&prci CLK_PCLK>;
#pwm-cells = <3>;
status = "disabled";
};
i2c0: i2c@10030000 {
compatible = "sifive,fu740-c000-i2c", "sifive,i2c0";
reg = <0x00 0x10030000 0x00 0x1000>;
interrupt-parent = <&plic>;
interrupts = <52>;
clocks = <&prci CLK_PCLK>;
reg-shift = <2>;
reg-io-width = <1>;
#address-cells = <1>;
#size-cells = <0>;
status = "okay";
};
i2c1: i2c@10031000 {
compatible = "sifive,fu740-c000-i2c", "sifive,i2c0";
reg = <0x00 0x10031000 0x00 0x1000>;
interrupt-parent = <&plic>;
interrupts = <53>;
clocks = <&prci CLK_PCLK>;
reg-shift = <2>;
reg-io-width = <1>;
#address-cells = <1>;
#size-cells = <0>;
status = "disabled";
};
};
memory@80000000 {
device_type = "memory";
reg = <0x00 0x80000000 0x04 0x00>;
};
leds: pwmleds {
compatible = "pwm-leds";
green-d12 {
label = "green:d12";
pwms = <&pwm0 0 7812500 0x01>;
active-low = <1>;
max-brightness = <255>;
linux,default-trigger = "none";
};
green-d2 {
label = "green:d2";
pwms = <&pwm0 1 7812500 0x01>;
active-low = <1>;
max-brightness = <255>;
linux,default-trigger = "none";
};
red-d2 {
label = "red:d2";
pwms = <&pwm0 2 7812500 0x01>;
active-low = <0x01>;
max-brightness = <0xff>;
linux,default-trigger = "none";
};
blue-d2 {
label = "blue:d2";
pwms = <&pwm0 3 7812500 0x01>;
active-low = <1>;
max-brightness = <255>;
linux,default-trigger = "none";
};
};
clk_hfclk: hfclk {
#clock-cells = <0>;
compatible = "fixed-clock";
clock-frequency = <26000000>;
clock-output-names = "hfclk";
};
clk_rtcclk: rtcclk {
#clock-cells = <0>;
compatible = "fixed-clock";
clock-frequency = <1000000>;
clock-output-names = "rtcclk";
};
};
+308
View File
@@ -0,0 +1,308 @@
/* SPDX-License-Identifier: GPL-2.0 OR MIT */
/*
* Copyright (C) 2022 Emil Renner Berthing <kernel@esmil.dk>
* Copyright (C) 2022 StarFive Technology Co., Ltd.
*/
#ifndef __JH7110_PINFUNC_H__
#define __JH7110_PINFUNC_H__
/*
* mux bits:
* | 31 - 24 | 23 - 16 | 15 - 10 | 9 - 8 | 7 - 0 |
* | din | dout | doen | function | gpio nr |
*
* dout: output signal
* doen: output enable signal
* din: optional input signal, 0xff = none
* function: function selector
* gpio nr: gpio number, 0 - 63
*/
#define GPIOMUX(n, dout, doen, din) ( \
(((din) & 0xff) << 24) | \
(((dout) & 0xff) << 16) | \
(((doen) & 0x3f) << 10) | \
((n) & 0x3f))
#define PINMUX(n, func) ((1 << 10) | (((func) & 0x3) << 8) | ((n) & 0xff))
/* sys_iomux dout */
#define GPOUT_LOW 0
#define GPOUT_HIGH 1
#define GPOUT_SYS_WAVE511_UART_TX 2
#define GPOUT_SYS_CAN0_STBY 3
#define GPOUT_SYS_CAN0_TST_NEXT_BIT 4
#define GPOUT_SYS_CAN0_TST_SAMPLE_POINT 5
#define GPOUT_SYS_CAN0_TXD 6
#define GPOUT_SYS_USB_DRIVE_VBUS 7
#define GPOUT_SYS_QSPI_CS1 8
#define GPOUT_SYS_SPDIF 9
#define GPOUT_SYS_HDMI_CEC_SDA 10
#define GPOUT_SYS_HDMI_DDC_SCL 11
#define GPOUT_SYS_HDMI_DDC_SDA 12
#define GPOUT_SYS_WATCHDOG 13
#define GPOUT_SYS_I2C0_CLK 14
#define GPOUT_SYS_I2C0_DATA 15
#define GPOUT_SYS_SDIO0_BACK_END_POWER 16
#define GPOUT_SYS_SDIO0_CARD_POWER_EN 17
#define GPOUT_SYS_SDIO0_CCMD_OD_PULLUP_EN 18
#define GPOUT_SYS_SDIO0_RST 19
#define GPOUT_SYS_UART0_TX 20
#define GPOUT_SYS_HIFI4_JTAG_TDO 21
#define GPOUT_SYS_JTAG_TDO 22
#define GPOUT_SYS_PDM_MCLK 23
#define GPOUT_SYS_PWM_CHANNEL0 24
#define GPOUT_SYS_PWM_CHANNEL1 25
#define GPOUT_SYS_PWM_CHANNEL2 26
#define GPOUT_SYS_PWM_CHANNEL3 27
#define GPOUT_SYS_PWMDAC_LEFT 28
#define GPOUT_SYS_PWMDAC_RIGHT 29
#define GPOUT_SYS_SPI0_CLK 30
#define GPOUT_SYS_SPI0_FSS 31
#define GPOUT_SYS_SPI0_TXD 32
#define GPOUT_SYS_GMAC_PHYCLK 33
#define GPOUT_SYS_I2SRX_BCLK 34
#define GPOUT_SYS_I2SRX_LRCK 35
#define GPOUT_SYS_I2STX0_BCLK 36
#define GPOUT_SYS_I2STX0_LRCK 37
#define GPOUT_SYS_MCLK 38
#define GPOUT_SYS_TDM_CLK 39
#define GPOUT_SYS_TDM_SYNC 40
#define GPOUT_SYS_TDM_TXD 41
#define GPOUT_SYS_TRACE_DATA0 42
#define GPOUT_SYS_TRACE_DATA1 43
#define GPOUT_SYS_TRACE_DATA2 44
#define GPOUT_SYS_TRACE_DATA3 45
#define GPOUT_SYS_TRACE_REF 46
#define GPOUT_SYS_CAN1_STBY 47
#define GPOUT_SYS_CAN1_TST_NEXT_BIT 48
#define GPOUT_SYS_CAN1_TST_SAMPLE_POINT 49
#define GPOUT_SYS_CAN1_TXD 50
#define GPOUT_SYS_I2C1_CLK 51
#define GPOUT_SYS_I2C1_DATA 52
#define GPOUT_SYS_SDIO1_BACK_END_POWER 53
#define GPOUT_SYS_SDIO1_CARD_POWER_EN 54
#define GPOUT_SYS_SDIO1_CLK 55
#define GPOUT_SYS_SDIO1_CMD_OD_PULLUP_EN 56
#define GPOUT_SYS_SDIO1_CMD 57
#define GPOUT_SYS_SDIO1_DATA0 58
#define GPOUT_SYS_SDIO1_DATA1 59
#define GPOUT_SYS_SDIO1_DATA2 60
#define GPOUT_SYS_SDIO1_DATA3 61
#define GPOUT_SYS_SDIO1_DATA4 62
#define GPOUT_SYS_SDIO1_DATA5 63
#define GPOUT_SYS_SDIO1_DATA6 64
#define GPOUT_SYS_SDIO1_DATA7 65
#define GPOUT_SYS_SDIO1_RST 66
#define GPOUT_SYS_UART1_RTS 67
#define GPOUT_SYS_UART1_TX 68
#define GPOUT_SYS_I2STX1_SDO0 69
#define GPOUT_SYS_I2STX1_SDO1 70
#define GPOUT_SYS_I2STX1_SDO2 71
#define GPOUT_SYS_I2STX1_SDO3 72
#define GPOUT_SYS_SPI1_CLK 73
#define GPOUT_SYS_SPI1_FSS 74
#define GPOUT_SYS_SPI1_TXD 75
#define GPOUT_SYS_I2C2_CLK 76
#define GPOUT_SYS_I2C2_DATA 77
#define GPOUT_SYS_UART2_RTS 78
#define GPOUT_SYS_UART2_TX 79
#define GPOUT_SYS_SPI2_CLK 80
#define GPOUT_SYS_SPI2_FSS 81
#define GPOUT_SYS_SPI2_TXD 82
#define GPOUT_SYS_I2C3_CLK 83
#define GPOUT_SYS_I2C3_DATA 84
#define GPOUT_SYS_UART3_TX 85
#define GPOUT_SYS_SPI3_CLK 86
#define GPOUT_SYS_SPI3_FSS 87
#define GPOUT_SYS_SPI3_TXD 88
#define GPOUT_SYS_I2C4_CLK 89
#define GPOUT_SYS_I2C4_DATA 90
#define GPOUT_SYS_UART4_RTS 91
#define GPOUT_SYS_UART4_TX 92
#define GPOUT_SYS_SPI4_CLK 93
#define GPOUT_SYS_SPI4_FSS 94
#define GPOUT_SYS_SPI4_TXD 95
#define GPOUT_SYS_I2C5_CLK 96
#define GPOUT_SYS_I2C5_DATA 97
#define GPOUT_SYS_UART5_RTS 98
#define GPOUT_SYS_UART5_TX 99
#define GPOUT_SYS_SPI5_CLK 100
#define GPOUT_SYS_SPI5_FSS 101
#define GPOUT_SYS_SPI5_TXD 102
#define GPOUT_SYS_I2C6_CLK 103
#define GPOUT_SYS_I2C6_DATA 104
#define GPOUT_SYS_SPI6_CLK 105
#define GPOUT_SYS_SPI6_FSS 106
#define GPOUT_SYS_SPI6_TXD 107
/* aon_iomux dout */
#define GPOUT_AON_CLK_32K_OUT 2
#define GPOUT_AON_PTC0_PWM4 3
#define GPOUT_AON_PTC0_PWM5 4
#define GPOUT_AON_PTC0_PWM6 5
#define GPOUT_AON_PTC0_PWM7 6
#define GPOUT_AON_CLK_GCLK0 7
#define GPOUT_AON_CLK_GCLK1 8
#define GPOUT_AON_CLK_GCLK2 9
/* sys_iomux doen */
#define GPOEN_ENABLE 0
#define GPOEN_DISABLE 1
#define GPOEN_SYS_HDMI_CEC_SDA 2
#define GPOEN_SYS_HDMI_DDC_SCL 3
#define GPOEN_SYS_HDMI_DDC_SDA 4
#define GPOEN_SYS_I2C0_CLK 5
#define GPOEN_SYS_I2C0_DATA 6
#define GPOEN_SYS_HIFI4_JTAG_TDO 7
#define GPOEN_SYS_JTAG_TDO 8
#define GPOEN_SYS_PWM0_CHANNEL0 9
#define GPOEN_SYS_PWM0_CHANNEL1 10
#define GPOEN_SYS_PWM0_CHANNEL2 11
#define GPOEN_SYS_PWM0_CHANNEL3 12
#define GPOEN_SYS_SPI0_NSSPCTL 13
#define GPOEN_SYS_SPI0_NSSP 14
#define GPOEN_SYS_TDM_SYNC 15
#define GPOEN_SYS_TDM_TXD 16
#define GPOEN_SYS_I2C1_CLK 17
#define GPOEN_SYS_I2C1_DATA 18
#define GPOEN_SYS_SDIO1_CMD 19
#define GPOEN_SYS_SDIO1_DATA0 20
#define GPOEN_SYS_SDIO1_DATA1 21
#define GPOEN_SYS_SDIO1_DATA2 22
#define GPOEN_SYS_SDIO1_DATA3 23
#define GPOEN_SYS_SDIO1_DATA4 24
#define GPOEN_SYS_SDIO1_DATA5 25
#define GPOEN_SYS_SDIO1_DATA6 26
#define GPOEN_SYS_SDIO1_DATA7 27
#define GPOEN_SYS_SPI1_NSSPCTL 28
#define GPOEN_SYS_SPI1_NSSP 29
#define GPOEN_SYS_I2C2_CLK 30
#define GPOEN_SYS_I2C2_DATA 31
#define GPOEN_SYS_SPI2_NSSPCTL 32
#define GPOEN_SYS_SPI2_NSSP 33
#define GPOEN_SYS_I2C3_CLK 34
#define GPOEN_SYS_I2C3_DATA 35
#define GPOEN_SYS_SPI3_NSSPCTL 36
#define GPOEN_SYS_SPI3_NSSP 37
#define GPOEN_SYS_I2C4_CLK 38
#define GPOEN_SYS_I2C4_DATA 39
#define GPOEN_SYS_SPI4_NSSPCTL 40
#define GPOEN_SYS_SPI4_NSSP 41
#define GPOEN_SYS_I2C5_CLK 42
#define GPOEN_SYS_I2C5_DATA 43
#define GPOEN_SYS_SPI5_NSSPCTL 44
#define GPOEN_SYS_SPI5_NSSP 45
#define GPOEN_SYS_I2C6_CLK 46
#define GPOEN_SYS_I2C6_DATA 47
#define GPOEN_SYS_SPI6_NSSPCTL 48
#define GPOEN_SYS_SPI6_NSSP 49
/* aon_iomux doen */
#define GPOEN_AON_PTC0_OE_N_4 2
#define GPOEN_AON_PTC0_OE_N_5 3
#define GPOEN_AON_PTC0_OE_N_6 4
#define GPOEN_AON_PTC0_OE_N_7 5
/* sys_iomux gin */
#define GPI_NONE 255
#define GPI_SYS_WAVE511_UART_RX 0
#define GPI_SYS_CAN0_RXD 1
#define GPI_SYS_USB_OVERCURRENT 2
#define GPI_SYS_SPDIF 3
#define GPI_SYS_JTAG_RST 4
#define GPI_SYS_HDMI_CEC_SDA 5
#define GPI_SYS_HDMI_DDC_SCL 6
#define GPI_SYS_HDMI_DDC_SDA 7
#define GPI_SYS_HDMI_HPD 8
#define GPI_SYS_I2C0_CLK 9
#define GPI_SYS_I2C0_DATA 10
#define GPI_SYS_SDIO0_CD 11
#define GPI_SYS_SDIO0_INT 12
#define GPI_SYS_SDIO0_WP 13
#define GPI_SYS_UART0_RX 14
#define GPI_SYS_HIFI4_JTAG_TCK 15
#define GPI_SYS_HIFI4_JTAG_TDI 16
#define GPI_SYS_HIFI4_JTAG_TMS 17
#define GPI_SYS_HIFI4_JTAG_RST 18
#define GPI_SYS_JTAG_TDI 19
#define GPI_SYS_JTAG_TMS 20
#define GPI_SYS_PDM_DMIC0 21
#define GPI_SYS_PDM_DMIC1 22
#define GPI_SYS_I2SRX_SDIN0 23
#define GPI_SYS_I2SRX_SDIN1 24
#define GPI_SYS_I2SRX_SDIN2 25
#define GPI_SYS_SPI0_CLK 26
#define GPI_SYS_SPI0_FSS 27
#define GPI_SYS_SPI0_RXD 28
#define GPI_SYS_JTAG_TCK 29
#define GPI_SYS_MCLK_EXT 30
#define GPI_SYS_I2SRX_BCLK 31
#define GPI_SYS_I2SRX_LRCK 32
#define GPI_SYS_I2STX1_BCLK 33
#define GPI_SYS_I2STX1_LRCK 34
#define GPI_SYS_TDM_CLK 35
#define GPI_SYS_TDM_RXD 36
#define GPI_SYS_TDM_SYNC 37
#define GPI_SYS_CAN1_RXD 38
#define GPI_SYS_I2C1_CLK 39
#define GPI_SYS_I2C1_DATA 40
#define GPI_SYS_SDIO1_CD 41
#define GPI_SYS_SDIO1_INT 42
#define GPI_SYS_SDIO1_WP 43
#define GPI_SYS_SDIO1_CMD 44
#define GPI_SYS_SDIO1_DATA0 45
#define GPI_SYS_SDIO1_DATA1 46
#define GPI_SYS_SDIO1_DATA2 47
#define GPI_SYS_SDIO1_DATA3 48
#define GPI_SYS_SDIO1_DATA4 49
#define GPI_SYS_SDIO1_DATA5 50
#define GPI_SYS_SDIO1_DATA6 51
#define GPI_SYS_SDIO1_DATA7 52
#define GPI_SYS_SDIO1_STRB 53
#define GPI_SYS_UART1_CTS 54
#define GPI_SYS_UART1_RX 55
#define GPI_SYS_SPI1_CLK 56
#define GPI_SYS_SPI1_FSS 57
#define GPI_SYS_SPI1_RXD 58
#define GPI_SYS_I2C2_CLK 59
#define GPI_SYS_I2C2_DATA 60
#define GPI_SYS_UART2_CTS 61
#define GPI_SYS_UART2_RX 62
#define GPI_SYS_SPI2_CLK 63
#define GPI_SYS_SPI2_FSS 64
#define GPI_SYS_SPI2_RXD 65
#define GPI_SYS_I2C3_CLK 66
#define GPI_SYS_I2C3_DATA 67
#define GPI_SYS_UART3_RX 68
#define GPI_SYS_SPI3_CLK 69
#define GPI_SYS_SPI3_FSS 70
#define GPI_SYS_SPI3_RXD 71
#define GPI_SYS_I2C4_CLK 72
#define GPI_SYS_I2C4_DATA 73
#define GPI_SYS_UART4_CTS 74
#define GPI_SYS_UART4_RX 75
#define GPI_SYS_SPI4_CLK 76
#define GPI_SYS_SPI4_FSS 77
#define GPI_SYS_SPI4_RXD 78
#define GPI_SYS_I2C5_CLK 79
#define GPI_SYS_I2C5_DATA 80
#define GPI_SYS_UART5_CTS 81
#define GPI_SYS_UART5_RX 82
#define GPI_SYS_SPI5_CLK 83
#define GPI_SYS_SPI5_FSS 84
#define GPI_SYS_SPI5_RXD 85
#define GPI_SYS_I2C6_CLK 86
#define GPI_SYS_I2C6_DATA 87
#define GPI_SYS_SPI6_CLK 88
#define GPI_SYS_SPI6_FSS 89
#define GPI_SYS_SPI6_RXD 90
/* aon_iomux gin */
#define GPI_AON_PMU_GPIO_WAKEUP_0 0
#define GPI_AON_PMU_GPIO_WAKEUP_1 1
#define GPI_AON_PMU_GPIO_WAKEUP_2 2
#define GPI_AON_PMU_GPIO_WAKEUP_3 3
#endif
File diff suppressed because it is too large Load Diff
@@ -38,7 +38,7 @@
#size-cells = <0x00>;
timebase-frequency = <0x989680>;
cpu@0 {
cpu0: cpu@0 {
phandle = <0x01>;
device_type = "cpu";
reg = <0x00>;
@@ -52,7 +52,7 @@
riscv,isa = "rv64imafdch_zic64b_zicbom_zicbop_zicboz_ziccamoa_ziccif_zicclsm_ziccrse_zicntr_zicsr_zifencei_zihintntl_zihintpause_zihpm_zmmul_za64rs_zaamo_zalrsc_zawrs_zfa_zca_zcd_zba_zbb_zbc_zbs_shcounterenw_shgatpa_shtvala_shvsatpa_shvstvala_shvstvecd_ssccptr_sscounterenw_sstc_sstvala_sstvecd_ssu64xl_svadu_svvptc";
mmu-type = "riscv,sv57";
interrupt-controller {
cpu0_intc: interrupt-controller {
#interrupt-cells = <0x01>;
interrupt-controller;
compatible = "riscv,cpu-intc";
@@ -61,11 +61,9 @@
};
cpu-map {
cluster0 {
core0 {
cpu = <0x01>;
cpu = <&cpu0>;
};
};
};
@@ -186,7 +184,8 @@
phandle = <0x03>;
riscv,ndev = <0x5f>;
reg = <0x00 0xc000000 0x00 0x600000>;
interrupts-extended = <0x02 0x0b 0x02 0x09>;
interrupts-extended = <&cpu0_intc 0x0b>,
<&cpu0_intc 0x09>;
interrupt-controller;
compatible = "sifive,plic-1.0.0", "riscv,plic0";
#address-cells = <0x00>;
@@ -194,7 +193,8 @@
};
clint@2000000 {
interrupts-extended = <0x02 0x03 0x02 0x07>;
interrupts-extended = <&cpu0_intc 0x03>,
<&cpu0_intc 0x07>;
reg = <0x00 0x2000000 0x00 0x10000>;
compatible = "sifive,clint0", "riscv,clint0";
};
@@ -0,0 +1,252 @@
/dts-v1/;
/ {
#address-cells = <0x02>;
#size-cells = <0x02>;
compatible = "sifive,hifive-unleashed-a00";
model = "SiFive HiFive Unleashed A00";
chosen {
linux,initrd-end = <0x00 0x87688400>;
linux,initrd-start = <0x00 0x84200000>;
stdout-path = "/soc/serial@10010000";
};
aliases {
serial0 = "/soc/serial@10010000";
serial1 = "/soc/serial@10011000";
ethernet0 = "/soc/ethernet@10090000";
};
gpio-restart {
compatible = "gpio-restart";
gpios = <&gpio 0x0a 0x01>;
};
cpus {
#address-cells = <0x01>;
#size-cells = <0x00>;
timebase-frequency = <0xf4240>;
cpu0: cpu@0 {
device_type = "cpu";
reg = <0x00>;
status = "okay";
compatible = "riscv";
riscv,isa-extensions = "i", "m", "a", "c", "zicntr", "zicsr", "zifencei", "zihpm", "sdtrig";
riscv,isa-base = "rv64i";
riscv,isa = "rv64imac_zicntr_zicsr_zifencei_zihpm_sdtrig";
cpu0_intc: interrupt-controller {
#interrupt-cells = <0x01>;
interrupt-controller;
compatible = "riscv,cpu-intc";
phandle = <0x04>;
};
};
cpu1: cpu@1 {
device_type = "cpu";
reg = <0x01>;
status = "okay";
compatible = "riscv";
riscv,isa-extensions = "i", "m", "a", "f", "d", "c", "zicntr", "zicsr", "zifencei", "zihpm", "sdtrig";
riscv,isa-base = "rv64i";
riscv,isa = "rv64imafdc_zicntr_zicsr_zifencei_zihpm_sdtrig";
mmu-type = "riscv,sv39";
cpu1_intc: interrupt-controller {
#interrupt-cells = <0x01>;
interrupt-controller;
compatible = "riscv,cpu-intc";
phandle = <0x03>;
};
};
};
memory@80000000 {
device_type = "memory";
reg = <0x00 0x80000000 0x00 0x8000000>;
};
rtcclk: rtcclk {
#clock-cells = <0x00>;
compatible = "fixed-clock";
clock-frequency = <0xf4240>;
clock-output-names = "rtcclk";
phandle = <0x02>;
};
hfclk: hfclk {
#clock-cells = <0x00>;
compatible = "fixed-clock";
clock-frequency = <0x1fca055>;
clock-output-names = "hfclk";
phandle = <0x01>;
};
soc {
#address-cells = <0x02>;
#size-cells = <0x02>;
compatible = "simple-bus";
ranges;
uart0: serial@10010000 {
interrupts = <4>;
interrupt-parent = <&plic>;
clocks = <&prci 0x03>;
reg = <0x00 0x10010000 0x00 0x1000>;
compatible = "sifive,uart0";
};
uart1: serial@10011000 {
interrupts = <5>;
interrupt-parent = <&plic>;
clocks = <&prci 0x03>;
reg = <0x00 0x10011000 0x00 0x1000>;
compatible = "sifive,uart0";
};
pwm0: pwm@10020000 {
#pwm-cells = <0>;
clocks = <&prci 0x03>;
interrupts = <42>, <43>, <44>, <45>;
interrupt-parent = <&plic>;
reg = <0x00 0x10020000 0x00 0x1000>;
compatible = "sifive,pwm0";
};
pwm1: pwm@10021000 {
#pwm-cells = <0>;
clocks = <&prci 0x03>;
interrupts = <46>, <47>, <48>, <49>;
interrupt-parent = <&plic>;
reg = <0x00 0x10021000 0x00 0x1000>;
compatible = "sifive,pwm0";
status = "disabled";
};
gmac0: ethernet@10090000 {
#size-cells = <0x00>;
#address-cells = <0x01>;
local-mac-address = [52 54 00 12 34 56];
clock-names = "pclk", "hclk";
clocks = <&prci 0x02>, <&prci 0x02>;
interrupts = <0x35>;
interrupt-parent = <&plic>;
phy-handle = <&gmac0_phy>;
phy-mode = "gmii";
reg-names = "control";
reg = <0x00 0x10090000 0x00 0x2000>,
<0x00 0x100a0000 0x00 0x1000>;
compatible = "sifive,fu540-c000-gem";
gmac0_phy: ethernet-phy@0 {
reg = <0x00>;
};
};
// spi@10040000 {
// compatible = "sifive,spi0";
// reg = <0x00 0x10040000 0x00 0x1000>;
// interrupt-parent = <0x06>;
// interrupts = <0x33>;
// clocks = <0x05 0x03>;
// #address-cells = <0x01>;
// #size-cells = <0x00>;
// flash@0 {
// compatible = "jedec,spi-nor";
// reg = <0x00>;
// spi-max-frequency = <0x2faf080>;
// m25p,fast-read;
// spi-tx-bus-width = <0x04>;
// spi-rx-bus-width = <0x04>;
// };
// };
// spi@10050000 {
// compatible = "sifive,spi0";
// reg = <0x00 0x10050000 0x00 0x1000>;
// interrupt-parent = <0x06>;
// interrupts = <0x06>;
// clocks = <0x05 0x03>;
// #address-cells = <0x01>;
// #size-cells = <0x00>;
// mmc@0 {
// compatible = "mmc-spi-slot";
// reg = <0x00>;
// spi-max-frequency = <0x1312d00>;
// voltage-ranges = <0xce4 0xce4>;
// disable-wp;
// };
// };
// cache-controller@2010000 {
// compatible = "sifive,fu540-c000-ccache";
// cache-block-size = <0x40>;
// cache-level = <0x02>;
// cache-sets = <0x400>;
// cache-size = <0x200000>;
// cache-unified;
// interrupt-parent = <0x06>;
// interrupts = <0x01 0x02 0x03>;
// reg = <0x00 0x2010000 0x00 0x1000>;
// };
// dma@3000000 {
// compatible = "sifive,fu540-c000-pdma";
// reg = <0x00 0x3000000 0x00 0x100000>;
// interrupt-parent = <0x06>;
// interrupts = <0x17 0x18 0x19 0x1a 0x1b 0x1c 0x1d 0x1e>;
// #dma-cells = <0x01>;
// };
gpio: gpio@10060000 {
compatible = "sifive,gpio0";
interrupt-parent = <&plic>;
interrupts = <0x07>, <0x08>, <0x09>, <0x0a>,
<0x0b>, <0x0c>, <0x0d>, <0x0e>,
<0x0f>, <0x10>, <0x11>, <0x12>,
<0x13>, <0x14>, <0x15>, <0x16>;
reg = <0x00 0x10060000 0x00 0x1000>;
gpio-controller;
#gpio-cells = <0x02>;
interrupt-controller;
#interrupt-cells = <0x02>;
clocks = <&prci 0x03>;
phandle = <0x07>;
};
plic: interrupt-controller@c000000 {
phandle = <0x06>;
riscv,ndev = <0x35>;
reg = <0x00 0xc000000 0x00 0x4000000>;
interrupts-extended = <&cpu0_intc 0x0b>, <&cpu1_intc 0x0b>, <&cpu1_intc 0x09>;
interrupt-controller;
compatible = "sifive,plic-1.0.0", "riscv,plic0";
#interrupt-cells = <0x01>;
};
prci: clock-controller@10000000 {
compatible = "sifive,fu540-c000-prci";
reg = <0x00 0x10000000 0x00 0x1000>;
clocks = <&hfclk>, <&rtcclk>;
#clock-cells = <0x01>;
phandle = <0x05>;
};
// otp@10070000 {
// compatible = "sifive,fu540-c000-otp";
// reg = <0x00 0x10070000 0x00 0x1000>;
// fuse-count = <0x1000>;
// };
clint: clint@2000000 {
interrupts-extended = <&cpu0_intc 0x03>, <&cpu0_intc 0x07>, <&cpu1_intc 0x03>, <&cpu1_intc 0x07>;
reg = <0x00 0x2000000 0x00 0x10000>;
compatible = "sifive,clint0", "riscv,clint0";
};
};
};
Binary file not shown.
File diff suppressed because it is too large Load Diff
Binary file not shown.
+577
View File
@@ -0,0 +1,577 @@
/dts-v1/;
/ {
#address-cells = <0x02>;
#size-cells = <0x02>;
compatible = "sifive,hifive-unmatched-a00", "sifive,fu740-c000", "sifive,fu740";
model = "SiFive HiFive Unmatched";
aliases {
serial0 = "/soc/serial@10010000";
serial1 = "/soc/serial@10011000";
ethernet0 = "/soc/ethernet@10090000";
};
chosen {
stdout-path = "serial0";
};
cpus {
#address-cells = <0x01>;
#size-cells = <0x00>;
timebase-frequency = <0xf4240>;
cpu@0 {
compatible = "sifive,bullet0", "riscv";
device_type = "cpu";
i-cache-block-size = <0x40>;
i-cache-sets = <0x80>;
i-cache-size = <0x4000>;
next-level-cache = <0x01>;
reg = <0x00>;
riscv,isa = "rv64imac";
status = "disabled";
interrupt-controller {
#interrupt-cells = <0x01>;
compatible = "riscv,cpu-intc";
interrupt-controller;
phandle = <0x02>;
};
};
cpu@1 {
compatible = "sifive,u74-mc", "sifive,bullet0", "riscv";
d-cache-block-size = <0x40>;
d-cache-sets = <0x40>;
d-cache-size = <0x8000>;
d-tlb-sets = <0x01>;
d-tlb-size = <0x28>;
device_type = "cpu";
i-cache-block-size = <0x40>;
i-cache-sets = <0x80>;
i-cache-size = <0x8000>;
i-tlb-sets = <0x01>;
i-tlb-size = <0x28>;
mmu-type = "riscv,sv39";
next-level-cache = <0x01>;
reg = <0x01>;
riscv,isa = "rv64imafdc";
tlb-split;
interrupt-controller {
#interrupt-cells = <0x01>;
compatible = "riscv,cpu-intc";
interrupt-controller;
phandle = <0x03>;
};
};
cpu@2 {
compatible = "sifive,u74-mc", "sifive,bullet0", "riscv";
d-cache-block-size = <0x40>;
d-cache-sets = <0x40>;
d-cache-size = <0x8000>;
d-tlb-sets = <0x01>;
d-tlb-size = <0x28>;
device_type = "cpu";
i-cache-block-size = <0x40>;
i-cache-sets = <0x80>;
i-cache-size = <0x8000>;
i-tlb-sets = <0x01>;
i-tlb-size = <0x28>;
mmu-type = "riscv,sv39";
next-level-cache = <0x01>;
reg = <0x02>;
riscv,isa = "rv64imafdc";
tlb-split;
interrupt-controller {
#interrupt-cells = <0x01>;
compatible = "riscv,cpu-intc";
interrupt-controller;
phandle = <0x04>;
};
};
cpu@3 {
compatible = "sifive,u74-mc", "sifive,bullet0", "riscv";
d-cache-block-size = <0x40>;
d-cache-sets = <0x40>;
d-cache-size = <0x8000>;
d-tlb-sets = <0x01>;
d-tlb-size = <0x28>;
device_type = "cpu";
i-cache-block-size = <0x40>;
i-cache-sets = <0x80>;
i-cache-size = <0x8000>;
i-tlb-sets = <0x01>;
i-tlb-size = <0x28>;
mmu-type = "riscv,sv39";
next-level-cache = <0x01>;
reg = <0x03>;
riscv,isa = "rv64imafdc";
tlb-split;
interrupt-controller {
#interrupt-cells = <0x01>;
compatible = "riscv,cpu-intc";
interrupt-controller;
phandle = <0x05>;
};
};
cpu@4 {
compatible = "sifive,u74-mc", "sifive,bullet0", "riscv";
d-cache-block-size = <0x40>;
d-cache-sets = <0x40>;
d-cache-size = <0x8000>;
d-tlb-sets = <0x01>;
d-tlb-size = <0x28>;
device_type = "cpu";
i-cache-block-size = <0x40>;
i-cache-sets = <0x80>;
i-cache-size = <0x8000>;
i-tlb-sets = <0x01>;
i-tlb-size = <0x28>;
mmu-type = "riscv,sv39";
next-level-cache = <0x01>;
reg = <0x04>;
riscv,isa = "rv64imafdc";
tlb-split;
interrupt-controller {
#interrupt-cells = <0x01>;
compatible = "riscv,cpu-intc";
interrupt-controller;
phandle = <0x06>;
};
};
};
soc {
#address-cells = <0x02>;
#size-cells = <0x02>;
compatible = "simple-bus";
ranges;
interrupt-controller@c000000 {
#interrupt-cells = <0x01>;
#address-cells = <0x00>;
compatible = "sifive,fu540-c000-plic", "sifive,plic-1.0.0";
reg = <0x00 0xc000000 0x00 0x4000000>;
riscv,ndev = <0x45>;
interrupt-controller;
interrupts-extended = <0x02 0xffffffff 0x03 0xffffffff 0x03 0x09 0x04 0xffffffff 0x04 0x09 0x05 0xffffffff 0x05 0x09 0x06 0xffffffff 0x06 0x09>;
phandle = <0x09>;
};
prci: clock-controller@10000000 {
compatible = "sifive,fu740-c000-prci";
reg = <0x00 0x10000000 0x00 0x1000>;
clocks = <&clk_hfclk>, <&clk_rtcclk>;
#clock-cells = <0x01>;
#reset-cells = <0x01>;
};
serial@10010000 {
compatible = "sifive,fu740-c000-uart", "sifive,uart0";
reg = <0x00 0x10010000 0x00 0x1000>;
interrupt-parent = <0x09>;
interrupts = <0x27>;
clocks = <&prci 0x07>;
status = "okay";
};
serial@10011000 {
compatible = "sifive,fu740-c000-uart", "sifive,uart0";
reg = <0x00 0x10011000 0x00 0x1000>;
interrupt-parent = <0x09>;
interrupts = <0x28>;
clocks = <&prci 0x07>;
status = "okay";
};
i2c@10030000 {
compatible = "sifive,fu740-c000-i2c", "sifive,i2c0";
reg = <0x00 0x10030000 0x00 0x1000>;
interrupt-parent = <0x09>;
interrupts = <0x34>;
clocks = <&prci 0x07>;
reg-shift = <0x02>;
reg-io-width = <0x01>;
#address-cells = <0x01>;
#size-cells = <0x00>;
status = "okay";
temperature-sensor@4c {
compatible = "ti,tmp451";
reg = <0x4c>;
interrupt-parent = <0x0b>;
interrupts = <0x06 0x08>;
};
pmic@58 {
compatible = "dlg,da9063";
reg = <0x58>;
interrupt-parent = <0x0b>;
interrupts = <0x01 0x08>;
interrupt-controller;
regulators {
bcore1 {
regulator-min-microvolt = <0x100590>;
regulator-max-microvolt = <0x100590>;
regulator-min-microamp = <0x4c4b40>;
regulator-max-microamp = <0x4c4b40>;
regulator-always-on;
};
bcore2 {
regulator-min-microvolt = <0x100590>;
regulator-max-microvolt = <0x100590>;
regulator-min-microamp = <0x4c4b40>;
regulator-max-microamp = <0x4c4b40>;
regulator-always-on;
};
bpro {
regulator-min-microvolt = <0x1b7740>;
regulator-max-microvolt = <0x1b7740>;
regulator-min-microamp = <0x2625a0>;
regulator-max-microamp = <0x2625a0>;
regulator-always-on;
};
bperi {
regulator-min-microvolt = <0x100590>;
regulator-max-microvolt = <0x100590>;
regulator-min-microamp = <0x16e360>;
regulator-max-microamp = <0x16e360>;
regulator-always-on;
};
bmem {
regulator-min-microvolt = <0x124f80>;
regulator-max-microvolt = <0x124f80>;
regulator-min-microamp = <0x2dc6c0>;
regulator-max-microamp = <0x2dc6c0>;
regulator-always-on;
};
bio {
regulator-min-microvolt = <0x124f80>;
regulator-max-microvolt = <0x124f80>;
regulator-min-microamp = <0x2dc6c0>;
regulator-max-microamp = <0x2dc6c0>;
regulator-always-on;
};
ldo1 {
regulator-min-microvolt = <0x1b7740>;
regulator-max-microvolt = <0x1b7740>;
regulator-min-microamp = <0x186a0>;
regulator-max-microamp = <0x186a0>;
regulator-always-on;
};
ldo2 {
regulator-min-microvolt = <0x1b7740>;
regulator-max-microvolt = <0x1b7740>;
regulator-min-microamp = <0x30d40>;
regulator-max-microamp = <0x30d40>;
regulator-always-on;
};
ldo3 {
regulator-min-microvolt = <0x325aa0>;
regulator-max-microvolt = <0x325aa0>;
regulator-min-microamp = <0x30d40>;
regulator-max-microamp = <0x30d40>;
regulator-always-on;
};
ldo4 {
regulator-min-microvolt = <0x2625a0>;
regulator-max-microvolt = <0x2625a0>;
regulator-min-microamp = <0x30d40>;
regulator-max-microamp = <0x30d40>;
regulator-always-on;
};
ldo5 {
regulator-min-microvolt = <0x325aa0>;
regulator-max-microvolt = <0x325aa0>;
regulator-min-microamp = <0x186a0>;
regulator-max-microamp = <0x186a0>;
regulator-always-on;
};
ldo6 {
regulator-min-microvolt = <0x1b7740>;
regulator-max-microvolt = <0x1b7740>;
regulator-min-microamp = <0x30d40>;
regulator-max-microamp = <0x30d40>;
regulator-always-on;
};
ldo7 {
regulator-min-microvolt = <0x325aa0>;
regulator-max-microvolt = <0x325aa0>;
regulator-min-microamp = <0x30d40>;
regulator-max-microamp = <0x30d40>;
regulator-always-on;
};
ldo8 {
regulator-min-microvolt = <0x325aa0>;
regulator-max-microvolt = <0x325aa0>;
regulator-min-microamp = <0x30d40>;
regulator-max-microamp = <0x30d40>;
regulator-always-on;
};
ldo9 {
regulator-min-microvolt = <0x100590>;
regulator-max-microvolt = <0x100590>;
regulator-min-microamp = <0x30d40>;
regulator-max-microamp = <0x30d40>;
};
ldo10 {
regulator-min-microvolt = <0xf4240>;
regulator-max-microvolt = <0xf4240>;
regulator-min-microamp = <0x493e0>;
regulator-max-microamp = <0x493e0>;
};
ldo11 {
regulator-min-microvolt = <0x2625a0>;
regulator-max-microvolt = <0x2625a0>;
regulator-min-microamp = <0x493e0>;
regulator-max-microamp = <0x493e0>;
regulator-always-on;
};
};
};
};
i2c@10031000 {
compatible = "sifive,fu740-c000-i2c", "sifive,i2c0";
reg = <0x00 0x10031000 0x00 0x1000>;
interrupt-parent = <0x09>;
interrupts = <0x35>;
clocks = <&prci 0x07>;
reg-shift = <0x02>;
reg-io-width = <0x01>;
#address-cells = <0x01>;
#size-cells = <0x00>;
status = "disabled";
};
spi@10040000 {
compatible = "sifive,fu740-c000-spi", "sifive,spi0";
reg = <0x00 0x10040000 0x00 0x1000 0x00 0x20000000 0x00 0x10000000>;
interrupt-parent = <0x09>;
interrupts = <0x29>;
clocks = <&prci 0x07>;
#address-cells = <0x01>;
#size-cells = <0x00>;
status = "okay";
flash@0 {
compatible = "issi,is25wp256", "jedec,spi-nor";
reg = <0x00>;
spi-max-frequency = <0x2faf080>;
m25p,fast-read;
spi-tx-bus-width = <0x04>;
spi-rx-bus-width = <0x04>;
};
};
spi@10041000 {
compatible = "sifive,fu740-c000-spi", "sifive,spi0";
reg = <0x00 0x10041000 0x00 0x1000 0x00 0x30000000 0x00 0x10000000>;
interrupt-parent = <0x09>;
interrupts = <0x2a>;
clocks = <&prci 0x07>;
#address-cells = <0x01>;
#size-cells = <0x00>;
status = "disabled";
};
spi@10050000 {
compatible = "sifive,fu740-c000-spi", "sifive,spi0";
reg = <0x00 0x10050000 0x00 0x1000>;
interrupt-parent = <0x09>;
interrupts = <0x2b>;
clocks = <&prci 0x07>;
#address-cells = <0x01>;
#size-cells = <0x00>;
status = "okay";
mmc@0 {
compatible = "mmc-spi-slot";
reg = <0x00>;
spi-max-frequency = <0x1312d00>;
voltage-ranges = <0xce4 0xce4>;
disable-wp;
};
};
macb: ethernet@10090000 {
compatible = "sifive,fu540-c000-gem";
interrupt-parent = <0x09>;
interrupts = <0x37>;
reg = <0x00 0x10090000 0x00 0x2000 0x00 0x100a0000 0x00 0x1000>;
local-mac-address = [00 00 00 00 00 00];
clock-names = "pclk", "hclk";
clocks = <&prci 0x02 &prci 0x02>;
#address-cells = <0x01>;
#size-cells = <0x00>;
status = "okay";
phy-mode = "gmii";
phy-handle = <0x0c>;
ethernet-phy@0 {
reg = <0x00>;
phandle = <0x0c>;
};
};
pwm@10020000 {
compatible = "sifive,fu740-c000-pwm", "sifive,pwm0";
reg = <0x00 0x10020000 0x00 0x1000>;
interrupt-parent = <0x09>;
interrupts = <0x2c 0x2d 0x2e 0x2f>;
clocks = <&prci 0x07>;
#pwm-cells = <0x03>;
status = "okay";
phandle = <0x0d>;
};
pwm@10021000 {
compatible = "sifive,fu740-c000-pwm", "sifive,pwm0";
reg = <0x00 0x10021000 0x00 0x1000>;
interrupt-parent = <0x09>;
interrupts = <0x30 0x31 0x32 0x33>;
clocks = <&prci 0x07>;
#pwm-cells = <0x03>;
status = "okay";
};
cache-controller@2010000 {
compatible = "sifive,fu740-c000-ccache", "cache";
cache-block-size = <0x40>;
cache-level = <0x02>;
cache-sets = <0x800>;
cache-size = <0x200000>;
cache-unified;
interrupt-parent = <0x09>;
interrupts = <0x13 0x15 0x16 0x14>;
reg = <0x00 0x2010000 0x00 0x1000>;
phandle = <0x01>;
};
gpio@10060000 {
compatible = "sifive,fu740-c000-gpio", "sifive,gpio0";
interrupt-parent = <0x09>;
interrupts = <0x17 0x18 0x19 0x1a 0x1b 0x1c 0x1d 0x1e 0x1f 0x20 0x21 0x22 0x23 0x24 0x25 0x26>;
reg = <0x00 0x10060000 0x00 0x1000>;
gpio-controller;
#gpio-cells = <0x02>;
interrupt-controller;
#interrupt-cells = <0x02>;
clocks = <&prci 0x07>;
status = "okay";
phandle = <0x0b>;
};
pcie@e00000000 {
#address-cells = <0x03>;
#interrupt-cells = <0x01>;
#num-lanes = <0x08>;
#size-cells = <0x02>;
compatible = "sifive,fu740-pcie";
reg = <0x0e 0x00 0x01 0x00 0x0d 0xf0000000 0x00 0x10000000 0x00 0x100d0000 0x00 0x1000>;
reg-names = "dbi", "config", "mgmt";
device_type = "pci";
dma-coherent;
bus-range = <0x00 0xff>;
ranges = <0x81000000 0x00 0x60080000 0x00 0x60080000 0x00 0x10000 0x82000000 0x00 0x60090000 0x00 0x60090000 0x00 0xff70000 0x82000000 0x00 0x70000000 0x00 0x70000000 0x00 0x1000000 0xc3000000 0x20 0x00 0x20 0x00 0x20 0x00>;
num-lanes = <0x08>;
interrupts = <0x38 0x39 0x3a 0x3b 0x3c 0x3d 0x3e 0x3f 0x40>;
interrupt-names = "msi", "inta", "intb", "intc", "intd";
interrupt-parent = <0x09>;
interrupt-map-mask = <0x00 0x00 0x00 0x07>;
interrupt-map = <0x00 0x00 0x00 0x01 0x09 0x39 0x00 0x00 0x00 0x02 0x09 0x3a 0x00 0x00 0x00 0x03 0x09 0x3b 0x00 0x00 0x00 0x04 0x09 0x3c>;
clock-names = "pcie_aux";
clocks = <&prci 0x08>;
pwren-gpios = <0x0b 0x05 0x00>;
perstn-gpios = <0x0b 0x08 0x00>;
resets = <&prci 0x04>;
status = "okay";
};
};
memory@80000000 {
device_type = "memory";
reg = <0x00 0x80000000 0x04 0x00>;
};
pwmleds {
compatible = "pwm-leds";
green-d12 {
label = "green:d12";
pwms = <0x0d 0x00 0x773594 0x01>;
active-low = <0x01>;
max-brightness = <0xff>;
linux,default-trigger = "none";
};
green-d2 {
label = "green:d2";
pwms = <0x0d 0x01 0x773594 0x01>;
active-low = <0x01>;
max-brightness = <0xff>;
linux,default-trigger = "none";
};
red-d2 {
label = "red:d2";
pwms = <0x0d 0x02 0x773594 0x01>;
active-low = <0x01>;
max-brightness = <0xff>;
linux,default-trigger = "none";
};
blue-d2 {
label = "blue:d2";
pwms = <0x0d 0x03 0x773594 0x01>;
active-low = <0x01>;
max-brightness = <0xff>;
linux,default-trigger = "none";
};
};
clk_hfclk: hfclk {
#clock-cells = <0x00>;
compatible = "fixed-clock";
clock-frequency = <26000000>;
clock-output-names = "hfclk";
};
clk_rtcclk: rtcclk {
#clock-cells = <0x00>;
compatible = "fixed-clock";
clock-frequency = <1000000>;
clock-output-names = "rtcclk";
};
gpio-poweroff {
compatible = "gpio-poweroff";
gpios = <0x0b 0x02 0x01>;
};
};
Binary file not shown.
+248
View File
@@ -0,0 +1,248 @@
/dts-v1/;
/ {
#address-cells = <0x02>;
#size-cells = <0x02>;
compatible = "sifive,hifive-unleashed-a00";
model = "SiFive HiFive Unleashed A00";
chosen {
linux,initrd-end = <0x00 0x87688400>;
linux,initrd-start = <0x00 0x84200000>;
stdout-path = "/soc/serial@10010000";
};
aliases {
serial0 = "/soc/serial@10010000";
serial1 = "/soc/serial@10011000";
ethernet0 = "/soc/ethernet@10090000";
};
gpio-restart {
compatible = "gpio-restart";
gpios = <0x07 0x0a 0x01>;
};
cpus {
#address-cells = <0x01>;
#size-cells = <0x00>;
timebase-frequency = <0xf4240>;
cpu@0 {
device_type = "cpu";
reg = <0x00>;
status = "okay";
compatible = "riscv";
riscv,isa-extensions = "i", "m", "a", "c", "zicntr", "zicsr", "zifencei", "zihpm", "sdtrig";
riscv,isa-base = "rv64i";
riscv,isa = "rv64imac_zicntr_zicsr_zifencei_zihpm_sdtrig";
interrupt-controller {
#interrupt-cells = <0x01>;
interrupt-controller;
compatible = "riscv,cpu-intc";
phandle = <0x04>;
};
};
cpu@1 {
device_type = "cpu";
reg = <0x01>;
status = "okay";
compatible = "riscv";
riscv,isa-extensions = "i", "m", "a", "f", "d", "c", "zicntr", "zicsr", "zifencei", "zihpm", "sdtrig";
riscv,isa-base = "rv64i";
riscv,isa = "rv64imafdc_zicntr_zicsr_zifencei_zihpm_sdtrig";
mmu-type = "riscv,sv39";
interrupt-controller {
#interrupt-cells = <0x01>;
interrupt-controller;
compatible = "riscv,cpu-intc";
phandle = <0x03>;
};
};
};
memory@80000000 {
device_type = "memory";
reg = <0x00 0x80000000 0x00 0x8000000>;
};
rtcclk {
#clock-cells = <0x00>;
compatible = "fixed-clock";
clock-frequency = <0xf4240>;
clock-output-names = "rtcclk";
phandle = <0x02>;
};
hfclk {
#clock-cells = <0x00>;
compatible = "fixed-clock";
clock-frequency = <0x1fca055>;
clock-output-names = "hfclk";
phandle = <0x01>;
};
soc {
#address-cells = <0x02>;
#size-cells = <0x02>;
compatible = "simple-bus";
ranges;
serial@10010000 {
interrupts = <0x04>;
interrupt-parent = <0x06>;
clocks = <0x05 0x03>;
reg = <0x00 0x10010000 0x00 0x1000>;
compatible = "sifive,uart0";
};
serial@10011000 {
interrupts = <0x05>;
interrupt-parent = <0x06>;
clocks = <0x05 0x03>;
reg = <0x00 0x10011000 0x00 0x1000>;
compatible = "sifive,uart0";
};
pwm@10021000 {
#pwm-cells = <0x00>;
clocks = <0x05 0x03>;
interrupts = <0x2e 0x2f 0x30 0x31>;
interrupt-parent = <0x06>;
reg = <0x00 0x10021000 0x00 0x1000>;
compatible = "sifive,pwm0";
};
pwm@10020000 {
#pwm-cells = <0x00>;
clocks = <0x05 0x03>;
interrupts = <0x2a 0x2b 0x2c 0x2d>;
interrupt-parent = <0x06>;
reg = <0x00 0x10020000 0x00 0x1000>;
compatible = "sifive,pwm0";
};
ethernet@10090000 {
#size-cells = <0x00>;
#address-cells = <0x01>;
local-mac-address = [52 54 00 12 34 56];
clock-names = "pclk", "hclk";
clocks = <0x05 0x02 0x05 0x02>;
interrupts = <0x35>;
interrupt-parent = <0x06>;
phy-handle = <0x08>;
phy-mode = "gmii";
reg-names = "control";
reg = <0x00 0x10090000 0x00 0x2000 0x00 0x100a0000 0x00 0x1000>;
compatible = "sifive,fu540-c000-gem";
ethernet-phy@0 {
reg = <0x00>;
phandle = <0x08>;
};
};
spi@10040000 {
compatible = "sifive,spi0";
reg = <0x00 0x10040000 0x00 0x1000>;
interrupt-parent = <0x06>;
interrupts = <0x33>;
clocks = <0x05 0x03>;
#address-cells = <0x01>;
#size-cells = <0x00>;
flash@0 {
compatible = "jedec,spi-nor";
reg = <0x00>;
spi-max-frequency = <0x2faf080>;
m25p,fast-read;
spi-tx-bus-width = <0x04>;
spi-rx-bus-width = <0x04>;
};
};
spi@10050000 {
compatible = "sifive,spi0";
reg = <0x00 0x10050000 0x00 0x1000>;
interrupt-parent = <0x06>;
interrupts = <0x06>;
clocks = <0x05 0x03>;
#address-cells = <0x01>;
#size-cells = <0x00>;
mmc@0 {
compatible = "mmc-spi-slot";
reg = <0x00>;
spi-max-frequency = <0x1312d00>;
voltage-ranges = <0xce4 0xce4>;
disable-wp;
};
};
cache-controller@2010000 {
compatible = "sifive,fu540-c000-ccache";
cache-block-size = <0x40>;
cache-level = <0x02>;
cache-sets = <0x400>;
cache-size = <0x200000>;
cache-unified;
interrupt-parent = <0x06>;
interrupts = <0x01 0x02 0x03>;
reg = <0x00 0x2010000 0x00 0x1000>;
};
dma@3000000 {
compatible = "sifive,fu540-c000-pdma";
reg = <0x00 0x3000000 0x00 0x100000>;
interrupt-parent = <0x06>;
interrupts = <0x17 0x18 0x19 0x1a 0x1b 0x1c 0x1d 0x1e>;
#dma-cells = <0x01>;
};
gpio@10060000 {
compatible = "sifive,gpio0";
interrupt-parent = <0x06>;
interrupts = <0x07 0x08 0x09 0x0a 0x0b 0x0c 0x0d 0x0e 0x0f 0x10 0x11 0x12 0x13 0x14 0x15 0x16>;
reg = <0x00 0x10060000 0x00 0x1000>;
gpio-controller;
#gpio-cells = <0x02>;
interrupt-controller;
#interrupt-cells = <0x02>;
clocks = <0x05 0x03>;
phandle = <0x07>;
};
interrupt-controller@c000000 {
phandle = <0x06>;
riscv,ndev = <0x35>;
reg = <0x00 0xc000000 0x00 0x4000000>;
interrupts-extended = <0x04 0x0b 0x03 0x0b 0x03 0x09>;
interrupt-controller;
compatible = "sifive,plic-1.0.0", "riscv,plic0";
#interrupt-cells = <0x01>;
};
clock-controller@10000000 {
compatible = "sifive,fu540-c000-prci";
reg = <0x00 0x10000000 0x00 0x1000>;
clocks = <0x01 0x02>;
#clock-cells = <0x01>;
phandle = <0x05>;
};
otp@10070000 {
compatible = "sifive,fu540-c000-otp";
reg = <0x00 0x10070000 0x00 0x1000>;
fuse-count = <0x1000>;
};
clint@2000000 {
interrupts-extended = <0x04 0x03 0x04 0x07 0x03 0x03 0x03 0x07>;
reg = <0x00 0x2000000 0x00 0x10000>;
compatible = "sifive,clint0", "riscv,clint0";
};
};
};
+1 -2
View File
@@ -1,8 +1,8 @@
{
"arch": "riscv64",
"os": "none",
"abi": "softfloat",
"cpu": "generic-rv64",
"llvm-abiname": "lp64",
"llvm-target": "riscv64",
"data-layout": "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128",
"max-atomic-width": 64,
@@ -20,7 +20,6 @@
"crt-objects-fallback": "false",
"emit-debug-gdb-scripts": false,
"llvm-abiname": "lp64",
"linker": "rust-lld",
"linker-flavor": "ld.lld"
+2
View File
@@ -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" }
@@ -66,6 +67,7 @@ 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"
ygg_driver_bsp_sifive.path = "driver/bsp/sifive"
[target.'cfg(target_arch = "x86_64")'.dependencies]
yboot-proto.workspace = true
-1
View File
@@ -1,5 +1,4 @@
#![no_std]
#![feature(decl_macro)]
#![allow(clippy::new_without_default)]
extern crate alloc;
+30 -7
View File
@@ -22,7 +22,7 @@ use crate::KernelTableManagerImpl;
use super::dc_cvac;
bitflags! {
#[derive(Clone, Copy, PartialEq, Eq)]
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct PageAttributes: u64 {
const PRESENT = 1 << 0;
@@ -71,8 +71,8 @@ pub struct L3;
#[derive(Debug, Clone, Copy)]
pub enum EntryType {
Table(PhysicalAddress),
Page(PhysicalAddress),
Table(PageAttributes, PhysicalAddress),
Page(PageAttributes, PhysicalAddress),
Invalid,
}
@@ -311,9 +311,14 @@ impl<L: NonTerminalEntryLevel> PageEntry<L> {
if !self.is_present() {
EntryType::Invalid
} else if let Some(table) = self.as_table() {
EntryType::Table(table)
let attributes = self.attributes();
EntryType::Table(attributes, table)
} else {
EntryType::Page(PhysicalAddress::from_u64(self.0 & !Self::ATTR_MASK))
let attributes = self.attributes();
EntryType::Page(
attributes,
PhysicalAddress::from_u64(self.0 & !Self::ATTR_MASK),
)
}
}
}
@@ -430,8 +435,26 @@ impl From<PageAttributes> for MapAttributes {
impl fmt::Display for EntryType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Table(address) => write!(f, "table @ {address:#x}"),
Self::Page(address) => write!(f, "page @ {address:#x}"),
&Self::Table(attrs, address) => {
let mask = match attrs & PageAttributes::AP_ACCESS_MASK {
PageAttributes::AP_BOTH_READONLY => "r- r-",
PageAttributes::AP_BOTH_READWRITE => "rw rw",
PageAttributes::AP_KERNEL_READONLY => "r- --",
PageAttributes::AP_KERNEL_READWRITE => "rw --",
_ => unreachable!(),
};
write!(f, "table @ {address:#010x} {mask}")
}
&Self::Page(attrs, address) => {
let mask = match attrs & PageAttributes::AP_ACCESS_MASK {
PageAttributes::AP_BOTH_READONLY => "r- r-",
PageAttributes::AP_BOTH_READWRITE => "rw rw",
PageAttributes::AP_KERNEL_READONLY => "r- --",
PageAttributes::AP_KERNEL_READWRITE => "rw --",
_ => unreachable!(),
};
write!(f, "page @ {address:#010x} {mask}")
}
Self::Invalid => f.write_str("<invalid>"),
}
}
+1 -1
View File
@@ -1,7 +1,7 @@
[package]
name = "kernel-arch-hosted"
version = "0.1.0"
edition = "2021"
edition = "2024"
[dependencies]
kernel-arch-interface.workspace = true
+9 -1
View File
@@ -16,7 +16,7 @@ use kernel_arch_interface::{
};
use libk_mm_interface::{
address::PhysicalAddress,
process::ProcessAddressSpaceManager,
process::{PageAttributeUpdate, ProcessAddressSpaceManager},
table::{MapAttributes, TableAllocator},
};
use yggdrasil_abi::{
@@ -164,6 +164,14 @@ impl<TA: TableAllocator> ProcessAddressSpaceManager<TA> for ProcessAddressSpaceI
unimplemented!()
}
unsafe fn update_page_attributes(
&mut self,
_address: usize,
_update: &PageAttributeUpdate,
) -> Result<(), Error> {
unimplemented!()
}
fn translate(&self, _address: usize) -> Result<(PhysicalAddress, MapAttributes), Error> {
unimplemented!()
}
+1 -1
View File
@@ -1,7 +1,7 @@
[package]
name = "kernel-arch-interface"
version = "0.1.0"
edition = "2021"
edition = "2024"
[dependencies]
yggdrasil-abi.workspace = true
+1 -1
View File
@@ -94,7 +94,7 @@ impl<A: Architecture, S: Scheduler + 'static> CpuImpl<A, S> {
///
/// See [Architecture::set_local_cpu].
pub unsafe fn set_local(&'static mut self) {
A::set_local_cpu(self as *mut _ as *mut _)
unsafe { A::set_local_cpu(self as *mut _ as *mut _) }
}
pub fn try_local<'a>() -> Option<LocalCpuImpl<'a, A, S>> {
+1 -1
View File
@@ -1,5 +1,5 @@
#![no_std]
#![feature(step_trait, const_trait_impl, never_type, decl_macro)]
#![feature(never_type)]
#![allow(clippy::new_without_default)]
use core::ops::Range;
+1 -1
View File
@@ -86,7 +86,7 @@ impl<A: KernelTableManager> RawDeviceMemoryMapping<A> {
size: usize,
attrs: DeviceMemoryAttributes,
) -> Result<Self, Error> {
A::map_device_pages(base, size, attrs)
unsafe { A::map_device_pages(base, size, attrs) }
}
/// Consumes the device mapping, leaking its address without deallocating the translation
+21
View File
@@ -1,3 +1,4 @@
use device_api::{ResetDevice, device::Device};
use yggdrasil_abi::{error::Error, primitive_enum};
const EXT_HSM: u64 = 0x48534D;
@@ -6,6 +7,7 @@ const EXT_DBCN: u64 = 0x4442434E;
const EXT_SPI: u64 = 0x735049;
const EXT_SYSTEM_SHUTDOWN: u64 = 0x53525354;
const EXT_SYSTEM_SHUTDOWN_LEGACY: u64 = 0x08;
const EXT_SYSTEM_RESET: u64 = 0x53525354;
primitive_enum! {
pub enum Status: i64 {
@@ -52,6 +54,20 @@ impl From<i64> for SbiError {
}
}
pub struct SbiResetMethod;
impl Device for SbiResetMethod {
fn display_name(&self) -> &str {
"SBI reset"
}
}
impl ResetDevice for SbiResetMethod {
unsafe fn reset(&self) -> ! {
sbi_system_reset()
}
}
#[allow(clippy::too_many_arguments)]
#[inline(always)]
unsafe fn sbi_do_call(
@@ -107,6 +123,11 @@ 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_reset() -> ! {
unsafe { sbi_do_call(EXT_SYSTEM_RESET, 0x00, 0x01, 0x00, 0, 0, 0, 0) }.ok();
unreachable!()
}
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();
+1 -1
View File
@@ -1,7 +1,7 @@
[package]
name = "kernel-arch-x86"
version = "0.1.0"
edition = "2021"
edition = "2024"
[dependencies]
kernel-arch-interface.workspace = true
+20 -16
View File
@@ -170,36 +170,40 @@ impl CpuFeatureSet for CpuFeatures {
#[cfg(any(target_arch = "x86_64", rust_analyzer))]
unsafe fn raw_cpuid(eax: u32, result: &mut [u32]) {
core::arch::asm!(
r#"
unsafe {
core::arch::asm!(
r#"
push %rbx
cpuid
mov %ebx, {0:e}
pop %rbx
"#,
out(reg) result[0],
out("edx") result[1],
out("ecx") result[2],
in("eax") eax,
options(att_syntax)
);
out(reg) result[0],
out("edx") result[1],
out("ecx") result[2],
in("eax") eax,
options(att_syntax)
);
}
}
#[cfg(any(target_arch = "x86", rust_analyzer))]
unsafe fn raw_cpuid(eax: u32, result: &mut [u32]) {
core::arch::asm!(
r#"
unsafe {
core::arch::asm!(
r#"
push %ebx
cpuid
mov %ebx, {0:e}
pop %ebx
"#,
out(reg) result[0],
out("edx") result[1],
out("ecx") result[2],
in("eax") eax,
options(att_syntax)
);
out(reg) result[0],
out("edx") result[1],
out("ecx") result[2],
in("eax") eax,
options(att_syntax)
);
}
}
fn cpuid_features() -> (EcxFeatures, EdxFeatures, ExtEdxFeatures) {
+34 -32
View File
@@ -225,42 +225,44 @@ mod imp {
offset: gdt_addr,
};
core::arch::asm!(
r#"
wbinvd
lgdt ({0})
unsafe {
core::arch::asm!(
r#"
wbinvd
lgdt ({0})
// Have to use iretq here
mov %rsp, %rcx
leaq 1f(%rip), %rax
// Have to use iretq here
mov %rsp, %rcx
leaq 1f(%rip), %rax
// SS:RSP
pushq $0x10
pushq %rcx
// SS:RSP
pushq $0x10
pushq %rcx
// RFLAGS
pushfq
// RFLAGS
pushfq
// CS:RIP
pushq $0x08
pushq %rax
iretq
1:
mov $0x10, %ax
mov %ax, %ds
mov %ax, %es
mov %ax, %fs
mov %ax, %gs
mov %ax, %ss
// CS:RIP
pushq $0x08
pushq %rax
iretq
1:
mov $0x10, %ax
mov %ax, %ds
mov %ax, %es
mov %ax, %fs
mov %ax, %gs
mov %ax, %ss
mov $0x28, %ax
ltr %ax
"#,
in(reg) &gdtr,
out("rax") _,
out("rcx") _,
options(att_syntax)
);
mov $0x28, %ax
ltr %ax
"#,
in(reg) &gdtr,
out("rax") _,
out("rcx") _,
options(att_syntax)
);
}
}
/// Initializes and loads the GDT data structure for the current CPU.
@@ -270,7 +272,7 @@ mod imp {
/// Intended to be called once per each CPU during their early initialization.
pub unsafe fn init() -> usize {
let (gdt, tss) = create_gdt();
load_gdt(gdt);
unsafe { load_gdt(gdt) };
(tss as *const Tss).addr()
}
}
+18 -6
View File
@@ -76,7 +76,9 @@ impl IoPortAccess<u32> for IoPort<u32> {
#[inline]
pub unsafe fn inb(port: u16) -> u8 {
let value: u8;
core::arch::asm!("inb %dx, %al", in("dx") port, out("al") value, options(att_syntax));
unsafe {
core::arch::asm!("inb %dx, %al", in("dx") port, out("al") value, options(att_syntax))
};
value
}
@@ -88,7 +90,9 @@ pub unsafe fn inb(port: u16) -> u8 {
#[inline]
pub unsafe fn inw(port: u16) -> u16 {
let value: u16;
core::arch::asm!("inw %dx, %ax", in("dx") port, out("ax") value, options(att_syntax));
unsafe {
core::arch::asm!("inw %dx, %ax", in("dx") port, out("ax") value, options(att_syntax))
};
value
}
@@ -100,7 +104,9 @@ pub unsafe fn inw(port: u16) -> u16 {
#[inline]
pub unsafe fn inl(port: u16) -> u32 {
let value: u32;
core::arch::asm!("inl %dx, %eax", in("dx") port, out("eax") value, options(att_syntax));
unsafe {
core::arch::asm!("inl %dx, %eax", in("dx") port, out("eax") value, options(att_syntax))
};
value
}
@@ -111,7 +117,9 @@ pub unsafe fn inl(port: u16) -> u32 {
/// Provides direct access to port I/O, unsafe.
#[inline]
pub unsafe fn outb(port: u16, value: u8) {
core::arch::asm!("outb %al, %dx", in("dx") port, in("al") value, options(att_syntax));
unsafe {
core::arch::asm!("outb %al, %dx", in("dx") port, in("al") value, options(att_syntax))
};
}
/// Writes a 16-bit value to the I/O port.
@@ -121,7 +129,9 @@ pub unsafe fn outb(port: u16, value: u8) {
/// Provides direct access to port I/O, unsafe.
#[inline]
pub unsafe fn outw(port: u16, value: u16) {
core::arch::asm!("outw %ax, %dx", in("dx") port, in("ax") value, options(att_syntax));
unsafe {
core::arch::asm!("outw %ax, %dx", in("dx") port, in("ax") value, options(att_syntax))
};
}
/// Writes a 32-bit value to the I/O port.
@@ -131,7 +141,9 @@ pub unsafe fn outw(port: u16, value: u16) {
/// Provides direct access to port I/O, unsafe.
#[inline]
pub unsafe fn outl(port: u16, value: u32) {
core::arch::asm!("outl %eax, %dx", in("dx") port, in("eax") value, options(att_syntax));
unsafe {
core::arch::asm!("outl %eax, %dx", in("dx") port, in("eax") value, options(att_syntax))
};
}
#[inline]
+1 -1
View File
@@ -1,7 +1,7 @@
[package]
name = "kernel-arch-x86_64"
version = "0.1.0"
edition = "2021"
edition = "2024"
[dependencies]
yggdrasil-abi.workspace = true
+24 -16
View File
@@ -255,7 +255,7 @@ impl<K: KernelTableManager, PA: PhysicalMemoryAllocator<Address = PhysicalAddres
type Context = TaskContextImpl<K, PA>;
unsafe fn fork(&self, address_space: u64) -> Result<TaskContextImpl<K, PA>, Error> {
TaskContextImpl::from_syscall_frame(self, address_space)
unsafe { TaskContextImpl::from_syscall_frame(self, address_space) }
}
fn set_return_value(&mut self, value: u64) {
@@ -405,18 +405,20 @@ impl<K: KernelTableManager, PA: PhysicalMemoryAllocator<Address = PhysicalAddres
}
unsafe fn store_state(&self) {
FpuContext::store(self.fpu_context.get());
unsafe { FpuContext::store(self.fpu_context.get()) };
// No need to save TSS/%cr3/%fs base back into the TCB, only the kernel
// can make changes to those
}
unsafe fn load_state(&self) {
FpuContext::restore(self.fpu_context.get());
// When the task is interrupted from Ring 3, make the CPU load
// the top of its kernel stack
ArchitectureImpl::set_local_tss_sp0(self.tss_rsp0);
MSR_IA32_FS_BASE.set((*self.inner.get()).fs_base as u64);
CR3.set_address(self.cr3);
unsafe {
FpuContext::restore(self.fpu_context.get());
// When the task is interrupted from Ring 3, make the CPU load
// the top of its kernel stack
ArchitectureImpl::set_local_tss_sp0(self.tss_rsp0);
MSR_IA32_FS_BASE.set((*self.inner.get()).fs_base as u64);
CR3.set_address(self.cr3);
}
}
}
@@ -506,8 +508,10 @@ impl<K: KernelTableManager, PA: PhysicalMemoryAllocator<Address = PhysicalAddres
}
unsafe fn enter(&self) -> ! {
self.load_state();
__x86_64_enter_task(self.inner.get())
unsafe {
self.load_state();
__x86_64_enter_task(self.inner.get())
}
}
unsafe fn switch(&self, from: &Self) {
@@ -515,14 +519,18 @@ impl<K: KernelTableManager, PA: PhysicalMemoryAllocator<Address = PhysicalAddres
return;
}
from.store_state();
self.load_state();
__x86_64_switch_task(self.inner.get(), from.inner.get())
unsafe {
from.store_state();
self.load_state();
__x86_64_switch_task(self.inner.get(), from.inner.get())
}
}
unsafe fn switch_and_drop(&self, thread: *const ()) {
self.load_state();
__x86_64_switch_and_drop(self.inner.get(), thread)
unsafe {
self.load_state();
__x86_64_switch_and_drop(self.inner.get(), thread)
}
}
fn set_thread_pointer(&self, tp: usize) {
@@ -561,7 +569,7 @@ fn setup_common_context(builder: &mut StackBuilder, entry: usize) {
builder.push(0); // %rbx
}
extern "C" {
unsafe extern "C" {
fn __x86_64_task_enter_kernel();
fn __x86_64_task_enter_user();
fn __x86_64_task_enter_from_fork();
+8 -6
View File
@@ -103,7 +103,7 @@ impl Architecture for ArchitectureImpl {
unsafe fn set_local_cpu(cpu: *mut ()) {
MSR_IA32_KERNEL_GS_BASE.set(cpu as u64);
core::arch::asm!("wbinvd; swapgs");
unsafe { core::arch::asm!("wbinvd; swapgs") };
}
fn local_cpu() -> *mut () {
@@ -127,7 +127,7 @@ impl Architecture for ArchitectureImpl {
)));
cpu.this = cpu.deref_mut();
cpu.set_local();
unsafe { cpu.set_local() };
}
fn idle_task() -> extern "C" fn(usize) -> ! {
@@ -153,10 +153,12 @@ impl Architecture for ArchitectureImpl {
unsafe fn set_interrupt_mask(mask: bool) -> bool {
let old = Self::interrupt_mask();
if mask {
core::arch::asm!("cli");
} else {
core::arch::asm!("sti");
unsafe {
if mask {
core::arch::asm!("cli");
} else {
core::arch::asm!("sti");
}
}
old
}
+22 -18
View File
@@ -101,29 +101,33 @@ impl DevicePageTableLevel for L3DeviceMemory {
}
pub(super) unsafe fn setup(have_1gib_pages: bool) {
let phys = PhysicalAddress::from_usize(auto_lower_address(&raw const KERNEL_PDPT));
KERNEL_PML4[KERNEL_L0I] = PageEntry::table(phys, PageAttributes::WRITABLE);
unsafe {
let phys = PhysicalAddress::from_usize(auto_lower_address(&raw const KERNEL_PDPT));
KERNEL_PML4[KERNEL_L0I] = PageEntry::table(phys, PageAttributes::WRITABLE);
if have_1gib_pages {
for i in 0..IDENTITY_SIZE_L1 {
let phys = PhysicalAddress::from_usize(i * L1::SIZE);
KERNEL_PDPT[i] = PageEntry::<L1>::block(phys, PageAttributes::WRITABLE);
if have_1gib_pages {
for i in 0..IDENTITY_SIZE_L1 {
let phys = PhysicalAddress::from_usize(i * L1::SIZE);
KERNEL_PDPT[i] = PageEntry::<L1>::block(phys, PageAttributes::WRITABLE);
}
} else {
// TODO
ArchitectureImpl::halt();
}
} else {
// TODO
ArchitectureImpl::halt();
}
// 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::WRITABLE);
}
let phys =
PhysicalAddress::from_usize(auto_lower_address(&raw const DEVICE_MEMORY.normal.0[i]));
DEVICE_MEMORY.large.0[i] = PageEntry::table(phys, PageAttributes::WRITABLE);
PhysicalAddress::from_usize(auto_lower_address(&raw const DEVICE_MEMORY.large.0));
KERNEL_PDPT[DEVICE_L1] = PageEntry::table(phys, PageAttributes::WRITABLE);
}
let phys = PhysicalAddress::from_usize(auto_lower_address(&raw const DEVICE_MEMORY.large.0));
KERNEL_PDPT[DEVICE_L1] = PageEntry::table(phys, PageAttributes::WRITABLE);
}
pub(super) unsafe fn load() {
+12 -6
View File
@@ -41,13 +41,17 @@ impl KernelTableManager for KernelTableManagerImpl {
) -> Result<RawDeviceMemoryMapping<Self>, Error> {
let _lock = fixed::LOCK.lock();
#[allow(static_mut_refs)]
fixed::DEVICE_MEMORY.map_device_pages(PhysicalAddress::from_u64(base), count, attrs)
unsafe {
fixed::DEVICE_MEMORY.map_device_pages(PhysicalAddress::from_u64(base), count, attrs)
}
}
unsafe fn unmap_device_pages(mapping: &RawDeviceMemoryMapping<Self>) {
let _lock = fixed::LOCK.lock();
#[allow(static_mut_refs)]
fixed::DEVICE_MEMORY.unmap_device_pages(mapping);
unsafe {
fixed::DEVICE_MEMORY.unmap_device_pages(mapping)
};
}
}
@@ -84,9 +88,11 @@ pub fn auto_lower_address<T>(pointer: *const T) -> usize {
/// Unsafe, must only be called by BSP during its early init, must already be in "higher-half"
#[inline(never)]
pub unsafe fn init_fixed_tables(have_1gib_pages: bool, bsp: bool) {
fixed::setup(have_1gib_pages);
if bsp {
fixed::load();
unsafe {
fixed::setup(have_1gib_pages);
if bsp {
fixed::load();
}
}
}
@@ -95,5 +101,5 @@ pub unsafe fn init_fixed_tables(have_1gib_pages: bool, bsp: bool) {
/// `address` must be page-aligned.
#[inline]
pub unsafe fn flush_tlb_entry(address: usize) {
core::arch::asm!("invlpg ({0})", in(reg) address, options(att_syntax));
unsafe { core::arch::asm!("invlpg ({0})", in(reg) address, options(att_syntax)) };
}
+4 -2
View File
@@ -85,8 +85,10 @@ impl<TA: TableAllocator> ProcessAddressSpaceManager<TA> for ProcessAddressSpaceI
}
unsafe fn clear(&mut self) {
self.l0
.drop_range::<TA>(0..((Self::UPPER_LIMIT_PFN * L3::SIZE).page_index::<L1>()));
unsafe {
self.l0
.drop_range::<TA>(0..((Self::UPPER_LIMIT_PFN * L3::SIZE).page_index::<L1>()));
}
}
}
+24 -18
View File
@@ -220,7 +220,7 @@ impl<L: EntryLevel> PageTable<L> {
/// Unsafe: the caller must ensure the provided reference is properly aligned and contains sane
/// data.
pub unsafe fn from_raw_slice_mut(data: &mut [PageEntry<L>; 512]) -> &mut Self {
core::mem::transmute(data)
unsafe { core::mem::transmute(data) }
}
/// Allocates a new page table, filling it with non-preset entries
@@ -243,8 +243,10 @@ impl<L: EntryLevel> PageTable<L> {
///
/// The caller must ensure the table is no longer in use and is not referenced anymore.
pub unsafe fn free<TA: TableAllocator>(this: PhysicalRefMut<Self, KernelTableManagerImpl>) {
let physical = this.as_physical_address();
TA::free_page_table(physical);
unsafe {
let physical = this.as_physical_address();
TA::free_page_table(physical);
}
}
// /// Returns the physical address of this table
@@ -303,25 +305,29 @@ where
const FULL_RANGE: Range<usize> = 0..512;
unsafe fn drop_range<TA: TableAllocator>(&mut self, range: Range<usize>) {
for index in range {
let entry = self[index];
unsafe {
for index in range {
let entry = self[index];
if let Some(table) = entry.as_table() {
let mut table_ref: PhysicalRefMut<PageTable<L::NextLevel>, KernelTableManagerImpl> =
PhysicalRefMut::map(table);
if let Some(table) = entry.as_table() {
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);
} else if entry.is_present() {
// Memory must've been cleared beforehand, so no non-table entries must be present
panic!(
"Expected a table containing only tables, got table[{}] = {:#x?}",
index, entry.0
);
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!(
"Expected a table containing only tables, got table[{}] = {:#x?}",
index, entry.0
);
}
self[index] = PageEntry::INVALID;
}
self[index] = PageEntry::INVALID;
}
}
}
+1 -1
View File
@@ -1,7 +1,7 @@
[package]
name = "ygg_driver_ahci"
version = "0.1.0"
edition = "2021"
edition = "2024"
authors = ["Mark Poliakov <mark@alnyan.me>"]
[dependencies]
+1 -1
View File
@@ -115,7 +115,7 @@ impl AtaCommand for AtaIdentify {
}
unsafe fn into_response(self) -> Self::Response {
DmaBuffer::assume_init(self.buffer)
unsafe { DmaBuffer::assume_init(self.buffer) }
}
}
+1 -1
View File
@@ -1,4 +1,4 @@
#![feature(const_trait_impl, if_let_guard)]
#![feature(const_trait_impl)]
#![allow(missing_docs)]
#![no_std]
// TODO
+24 -4
View File
@@ -1,3 +1,4 @@
// TODO baud rate configuration
use alloc::sync::Arc;
use device_api::{
device::{Device, DeviceInitContext},
@@ -16,7 +17,10 @@ use tock_registers::{
register_bitfields, register_structs,
registers::{ReadOnly, ReadWrite, WriteOnly},
};
use yggdrasil_abi::{error::Error, io::TerminalOptions};
use yggdrasil_abi::{
error::Error,
io::{TerminalOptions, TerminalOutputOptions},
};
register_bitfields! {
u32,
@@ -89,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(byte);
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())
@@ -117,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
}
+1
View File
@@ -16,3 +16,4 @@ tock-registers.workspace = true
log.workspace = true
bytemuck.workspace = true
futures-util.workspace = true
async-trait.workspace = true
+12 -2
View File
@@ -1,6 +1,6 @@
use alloc::sync::Arc;
use device_api::{
clock::{ClockController, ClockHandle},
clock::{ClockController, ClockHandle, Hertz, IntoHertz},
device::{Device, DeviceInitContext},
};
use device_tree::{
@@ -54,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),
}
}
@@ -65,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 {
-208
View File
@@ -1,208 +0,0 @@
use alloc::sync::Arc;
use device_api::{
clock::ClockHandle,
device::{Device, DeviceInitContext},
interrupt::{InterruptHandler, IrqHandle, IrqVector},
};
use device_tree::driver::{Node, ProbeContext, device_tree_driver};
use libk::{
debug::DebugSink,
device::manager::DEVICE_REGISTRY,
vfs::{Terminal, TerminalInput, TerminalOutput},
};
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIo};
use libk_util::{OneTimeInit, sync::IrqSafeSpinlock};
use tock_registers::{
interfaces::{ReadWriteable, Readable, Writeable},
register_bitfields, register_structs,
registers::{ReadOnly, ReadWrite},
};
use yggdrasil_abi::{
error::Error,
io::{TerminalOptions, TerminalOutputOptions},
};
register_bitfields! {
u32,
AUX_MU_IER_REG [
RX_IRQ OFFSET(0) NUMBITS(1) [],
TX_IRQ OFFSET(1) NUMBITS(1) [],
],
AUX_MU_IIR_REG [
IID OFFSET(1) NUMBITS(2) [
None = 0,
TxEmpty = 1,
RxNotEmpty = 2,
],
PENDING OFFSET(0) NUMBITS(1) [],
],
AUX_MU_LSR_REG [
TX_EMPTY OFFSET(5) NUMBITS(1) [],
]
}
register_structs! {
#[allow(non_snake_case)]
Regs {
(0x00 => AUX_MU_IO_REG: ReadWrite<u32>),
(0x04 => AUX_MU_IER_REG: ReadWrite<u32, AUX_MU_IER_REG::Register>),
(0x08 => AUX_MU_IIR_REG: ReadWrite<u32, AUX_MU_IIR_REG::Register>),
(0x0C => _0),
(0x14 => AUX_MU_LSR_REG: ReadOnly<u32, AUX_MU_LSR_REG::Register>),
(0x18 => _1),
(0x30 => @END),
}
}
struct Inner {
regs: IrqSafeSpinlock<DeviceMemoryIo<'static, Regs>>,
}
/// Broadcom 283x mini-UART driver
pub struct Bcm2835AuxUart {
base: PhysicalAddress,
irq: IrqHandle,
clock: ClockHandle,
inner: OneTimeInit<Arc<Terminal<Inner>>>,
}
impl Regs {
fn write_byte(&self, byte: u8) -> Result<(), Error> {
if byte == b'\n' {
self.write_byte(b'\r').ok();
}
while !self
.AUX_MU_LSR_REG
.matches_all(AUX_MU_LSR_REG::TX_EMPTY::SET)
{
core::hint::spin_loop();
}
self.AUX_MU_IO_REG.set(byte as u32);
Ok(())
}
fn write_bytes(&self, bytes: &[u8]) -> Result<(), Error> {
for &byte in bytes {
self.write_byte(byte)?;
}
Ok(())
}
}
impl TerminalOutput for Inner {
fn write(&self, byte: u8) -> Result<(), Error> {
self.regs.lock().write_byte(byte)
}
fn write_multiple(&self, bytes: &[u8]) -> Result<usize, Error> {
self.regs.lock().write_bytes(bytes)?;
Ok(bytes.len())
}
}
impl DebugSink for Bcm2835AuxUart {
fn putc(&self, c: u8) -> Result<(), Error> {
self.inner.get().putc_to_output(c)
}
fn puts(&self, s: &str) -> Result<(), Error> {
self.inner.get().write_to_output(s.as_bytes())?;
Ok(())
}
fn supports_control_sequences(&self) -> bool {
true
}
}
impl InterruptHandler for Bcm2835AuxUart {
fn handle_irq(self: Arc<Self>, _vector: IrqVector) -> bool {
let inner = self.inner.get();
let (status, byte) = {
let regs = inner.output().regs.lock();
// Reset IRQ
regs.AUX_MU_IIR_REG.modify(AUX_MU_IIR_REG::IID::SET);
let byte = regs.AUX_MU_IO_REG.get() as u8;
let status = regs
.AUX_MU_IIR_REG
.matches_all(AUX_MU_IIR_REG::PENDING::SET);
(status, byte)
};
if status {
inner.write_to_input(byte);
}
status
}
}
impl Device for Bcm2835AuxUart {
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
// TODO initialize pinctrl
self.clock.enable()?;
let regs = unsafe { DeviceMemoryIo::map(self.base, Default::default()) }?;
let config = TerminalOptions {
output: TerminalOutputOptions::NL_TO_CRNL,
..Default::default()
};
let output = Inner {
regs: IrqSafeSpinlock::new(regs),
};
let input = TerminalInput::with_capacity(64)?;
let inner = self
.inner
.init(Arc::new(Terminal::from_parts(config, input, output)));
DEVICE_REGISTRY
.serial_terminal
.register(inner.clone(), Some(self.clone()))
.ok();
Ok(())
}
unsafe fn init_irq(self: Arc<Self>) -> Result<(), Error> {
self.irq.register(self.clone())?;
self.irq.enable()?;
let inner = self.inner.get().output();
let regs = inner.regs.lock();
regs.AUX_MU_IER_REG
.modify(AUX_MU_IER_REG::RX_IRQ::SET + AUX_MU_IER_REG::TX_IRQ::CLEAR);
Ok(())
}
fn display_name(&self) -> &str {
"bcm2835 mini-UART"
}
}
// TODO handle pinctrl
device_tree_driver! {
compatible: ["brcm,bcm2835-aux-uart"],
driver: {
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
let base = node.map_base(context, 0)?;
let irq = node.interrupt(0)?;
let clock = node.clock(0)?;
Some(Arc::new(Bcm2835AuxUart {
base,
irq,
clock,
inner: OneTimeInit::new(),
}))
}
}
}
+93
View File
@@ -0,0 +1,93 @@
use alloc::sync::Arc;
use device_api::{
clock::{ClockController, ClockHandle, Hertz},
device::{Device, DeviceInitContext},
};
use device_tree::{
DeviceTreePropertyRead, TProp,
driver::{DeviceTreeClockController, Node, ProbeContext, device_tree_driver},
};
use libk::error::Error;
use crate::mbox::{Bcm2835Mbox, Bcm2835VcClock};
const CLK_VPU: u32 = 0x14;
struct Cprman {
clk_osc: ClockHandle,
mbox: Arc<Bcm2835Mbox>,
}
impl Device for Cprman {
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
let _ = &self.clk_osc;
Ok(())
}
fn display_name(&self) -> &str {
"bcm2711-cprman"
}
}
impl ClockController for Cprman {
fn clock_rate(&self, clock: Option<u32>) -> Result<Hertz, Error> {
let vc_clock = match clock {
Some(CLK_VPU) => Bcm2835VcClock::Core,
Some(n) => todo!("clock_rate({n:#02x})"),
None => return Err(Error::InvalidArgument),
};
let rate = self.mbox.vc_clock_rate(vc_clock)?;
Ok(rate)
}
fn set_clock_rate(&self, clock: Option<u32>, rate: Hertz) -> Result<Hertz, Error> {
let _ = clock;
let _ = rate;
todo!()
}
fn enable_clock(&self, clock: Option<u32>) -> Result<(), Error> {
let _ = clock;
todo!()
}
fn disable_clock(&self, clock: Option<u32>) -> Result<(), Error> {
let _ = clock;
todo!()
}
}
impl DeviceTreeClockController for Cprman {
fn map_clock(self: Arc<Self>, property: &TProp, offset: usize) -> Option<(ClockHandle, usize)> {
let clock = property.read_cell(offset, 1)? as u32;
Some((
ClockHandle {
parent: self.clone(),
clock: Some(clock),
},
1,
))
}
}
device_tree_driver! {
compatible: ["brcm,bcm2711-cprman"],
driver: {
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
let _base = node.map_base(context, 0)?;
let mbox = Node::by_compatible("brcm,bcm2835-mbox", true).unwrap().device().unwrap();
let mbox = Arc::downcast::<Bcm2835Mbox>(mbox.as_any()).unwrap();
let clk_osc = node.clock(0)?;
let cprman = Arc::new(Cprman {
clk_osc,
mbox,
});
node.make_clock_controller(cprman.clone());
Some(cprman)
}
}
}
+298
View File
@@ -0,0 +1,298 @@
use alloc::{boxed::Box, sync::Arc};
use async_trait::async_trait;
use device_api::{
clock::{ClockHandle, Hertz},
device::{Device, DeviceInitContext},
i2c::{I2CAddress, I2CController, I2CMessage, I2CTransfer},
interrupt::{InterruptHandler, IrqHandle, IrqVector},
};
use device_tree::driver::{Node, ProbeContext, device_tree_driver};
use futures_util::task::AtomicWaker;
use libk::{device::manager::DEVICE_REGISTRY, error::Error};
use libk_mm::device::DeviceMemoryIo;
use libk_util::{OneTimeInit, event::BitmapEvent, sync::spin_rwlock::IrqSafeRwLock};
use tock_registers::{
LocalRegisterCopy,
fields::FieldValue,
interfaces::{ReadWriteable, Readable, Writeable},
register_bitfields, register_structs,
registers::ReadWrite,
};
use yggdrasil_abi::io::device::i2c::I2CMasterInfo;
register_bitfields! {
u32,
C [
I2CEN OFFSET(15) NUMBITS(1) [],
INTR OFFSET(10) NUMBITS(1) [],
INTT OFFSET(9) NUMBITS(1) [],
INTD OFFSET(8) NUMBITS(1) [],
ST OFFSET(7) NUMBITS(1) [],
CLEAR OFFSET(4) NUMBITS(2) [],
READ OFFSET(0) NUMBITS(1) [],
],
S [
CLKT OFFSET(9) NUMBITS(1) [],
ERR OFFSET(8) NUMBITS(1) [],
RXF OFFSET(7) NUMBITS(1) [],
TXE OFFSET(6) NUMBITS(1) [],
RXD OFFSET(5) NUMBITS(1) [],
TXD OFFSET(4) NUMBITS(1) [],
RXR OFFSET(3) NUMBITS(1) [],
TXW OFFSET(2) NUMBITS(1) [],
DONE OFFSET(1) NUMBITS(1) [],
TA OFFSET(0) NUMBITS(1) [],
],
}
register_structs! {
#[allow(non_snake_case)]
Regs {
(0x00 => C: ReadWrite<u32, C::Register>),
(0x04 => S: ReadWrite<u32, S::Register>),
(0x08 => DLEN: ReadWrite<u32>),
(0x0C => A: ReadWrite<u32>),
(0x10 => FIFO: ReadWrite<u32>),
(0x14 => DIV: ReadWrite<u32>),
(0x18 => DEL: ReadWrite<u32>),
(0x1C => CLKT: ReadWrite<u32>),
(0x20 => @END),
}
}
struct I2CState {
status: BitmapEvent<AtomicWaker>,
}
struct I2C {
name: &'static str,
clock_frequency: Option<Hertz>,
irq: IrqHandle,
clock: Option<ClockHandle>,
regs: IrqSafeRwLock<DeviceMemoryIo<'static, Regs>>,
index: OneTimeInit<u32>,
state: I2CState,
}
impl I2CState {
fn new() -> Self {
Self {
status: BitmapEvent::new(AtomicWaker::new()),
}
}
}
// All of these are thread-unsafe, but I2C subsystem locks the peripheral by process
impl I2C {
async fn wait_for_event(&self) -> LocalRegisterCopy<u32, S::Register> {
let event = self.state.status.wait().await;
LocalRegisterCopy::new(event as u32)
}
}
impl Device for I2C {
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
// TODO
let _ = &self.clock;
log::info!("{}: initialize", self.name);
let regs = self.regs.write();
regs.C.set(0);
let index = DEVICE_REGISTRY.i2c.register_bus(self.clone())?;
self.index.init(index);
Ok(())
}
unsafe fn init_irq(self: Arc<Self>) -> Result<(), Error> {
self.irq.register(self.clone())?;
self.irq.enable()?;
Ok(())
}
fn display_name(&self) -> &str {
self.name
}
}
impl InterruptHandler for I2C {
fn handle_irq(self: Arc<Self>, _vector: IrqVector) -> bool {
let regs = self.regs.write();
let event = regs.S.extract();
// log::info!("I2C irq {:#x}", event.get());
let mut clear = FieldValue::none();
if event.matches_all(S::RXR::SET) {
clear += C::INTR::CLEAR;
}
if event.matches_all(S::TXW::SET) {
clear += C::INTT::CLEAR;
}
if event.matches_all(S::DONE::SET) {
clear += C::INTD::CLEAR;
}
regs.C.modify(clear);
self.state.status.signal(event.get() as u64);
true
}
}
#[async_trait]
impl I2CController for I2C {
fn bus_number(&self) -> u32 {
*self.index.get()
}
fn child_number(&self) -> Option<u32> {
None
}
fn set_speed(&self, speed: Hertz) -> Result<Hertz, Error> {
if let Some(frequency) = self.clock_frequency {
// Fixed frequency
if speed == frequency {
Ok(speed)
} else {
Err(Error::InvalidArgument)
}
} else {
todo!("Set i2c speed = {speed}")
}
}
fn capabilities(&self) -> I2CMasterInfo {
let max_speed_hz = if let Some(frequency) = self.clock_frequency {
frequency
} else {
todo!()
}
.0 as u32;
I2CMasterInfo {
max_speed_hz,
supports_10bit_addresses: true,
}
}
async fn i2c_transfer(
&self,
address: I2CAddress,
transfer: &mut I2CTransfer,
) -> Result<usize, Error> {
// TODO validate transfer
let address = address.as_8_bit().ok_or(Error::NotImplemented)?;
let repeat_start = transfer.repeat_start;
let mut nbytes = 0;
let mut error = None;
for (i, message) in transfer.messages.iter_mut().enumerate() {
if i == 0 || repeat_start {
let (read, len) = match message {
I2CMessage::Read(buffer) => (true, buffer.len()),
I2CMessage::Write(buffer) => (false, buffer.len()),
};
// log::info!("{}: START {:#x} read={}", self.name, address, read);
let read = if read { C::READ::SET } else { C::READ::CLEAR };
let regs = self.regs.write();
regs.S.write(S::ERR::SET + S::DONE::SET);
regs.DLEN.set(len as u32);
regs.C.modify(C::ST::CLEAR);
regs.A.set(address as u32);
regs.C.modify(
read + C::ST::SET + C::I2CEN::SET + C::INTD::SET + C::INTR::SET + C::INTT::SET,
);
}
let mut pos = 0;
let status = loop {
let s = self.wait_for_event().await;
let regs = self.regs.write();
if s.matches_all(S::ERR::SET) || s.matches_all(S::DONE::SET) {
break s;
}
match message {
I2CMessage::Write(buffer) => {
if s.matches_all(S::TXW::SET) {
while regs.S.matches_all(S::TXD::SET) && pos < buffer.len() {
let byte = buffer[pos];
// log::info!("{}: tx {:#x}", self.name, byte);
regs.FIFO.set(byte as u32);
pos += 1;
nbytes += 1;
}
}
regs.C.modify(C::INTT::SET);
}
I2CMessage::Read(buffer) => {
if s.matches_all(S::RXR::SET) {
while regs.S.matches_all(S::RXD::SET) && pos < buffer.len() {
let byte = regs.FIFO.get() as u8;
// log::info!("{}: rx {:#x}", self.name, byte);
buffer[pos] = byte;
pos += 1;
nbytes += 1;
}
}
regs.C.modify(C::INTR::SET);
}
}
};
// Finalize message
let regs = self.regs.write();
if status.matches_all(S::ERR::SET) {
regs.S.write(S::DONE::SET + S::ERR::SET);
error = Some(Error::HostUnreachable);
break;
} else if status.matches_all(S::DONE::SET) {
regs.S.write(S::DONE::SET);
} else {
log::error!("{}: transfer finished without DONE or ERR set", self.name);
log::error!("{}: S = {:#x}", self.name, status.get());
todo!();
}
}
// Finish transfer
// log::info!(
// "{}: finish xfer, error={:?}, nbytes={}",
// self.name,
// error,
// nbytes
// );
let regs = self.regs.write();
regs.C.set(0);
match error {
Some(error) => Err(error),
None => Ok(nbytes),
}
}
}
device_tree_driver! {
compatible: ["brcm,bcm2711-i2c", "brcm,bcm2835-i2c"],
driver: {
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
let clock_frequency = node.prop_usize("clock-frequency").map(|p| Hertz(p as u64));
let base = node.map_base(context, 0)?;
let name = node.name().unwrap_or("bcm2835-i2c");
let irq = node.interrupt(0)?;
let clock = node.clock(0);
let regs = unsafe { DeviceMemoryIo::map(base, Default::default()) }.ok()?;
let i2c = Arc::new(I2C {
name,
clock_frequency,
irq,
clock,
index: OneTimeInit::new(),
regs: IrqSafeRwLock::new(regs),
state: I2CState::new()
});
node.make_i2c_controller(i2c.clone());
Some(i2c)
}
}
}
+4 -1
View File
@@ -3,6 +3,9 @@
extern crate alloc;
mod aux;
mod aux_uart;
// mod aux_uart;
mod cprman;
mod gpio;
mod i2c;
mod mbox;
mod spi;
+46 -9
View File
@@ -1,7 +1,10 @@
use core::{cell::UnsafeCell, mem::MaybeUninit};
use core::{any::Any, cell::UnsafeCell, mem::MaybeUninit};
use alloc::sync::Arc;
use device_api::device::{Device, DeviceInitContext};
use device_api::{
clock::Hertz,
device::{Device, DeviceInitContext},
};
use device_tree::driver::{Node, ProbeContext, device_tree_driver};
use kernel_arch_aarch64::mem::table::L3;
use libk::device::{
@@ -61,7 +64,13 @@ register_structs! {
}
}
struct Bcm2835Mbox {
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[repr(u32)]
pub(crate) enum Bcm2835VcClock {
Core = 4,
}
pub(crate) struct Bcm2835Mbox {
regs: IrqSafeSpinlock<DeviceMemoryIo<'static, Regs>>,
}
@@ -85,13 +94,20 @@ impl Device for Bcm2835Mbox {
fn display_name(&self) -> &str {
"bcm2835-mbox"
}
fn as_any(self: Arc<Self>) -> Arc<dyn Any + Send + Sync> {
self
}
}
impl Bcm2835Mbox {
const RAM_TO_GPU_OFFSET: u32 = 0x40000000;
const CHANNEL_FRAMEBUFFER: u32 = 1;
const CHANNEL_ARM2VC: u32 = 8;
pub unsafe fn call_raw(&self, buffer: PhysicalAddress, channel: u32) -> Result<(), Error> {
log::info!("bcm2835-mbox: mbox call buffer={buffer:#x}, channel={channel}");
log::debug!("bcm2835-mbox: mbox call buffer={buffer:#x}, channel={channel}");
let address = buffer
.try_into_u32()
@@ -125,10 +141,31 @@ impl Bcm2835Mbox {
}
}
pub fn configure_framebuffer(
&self,
mode_info: &DisplayMode,
) -> Result<FramebufferResponse, Error> {
pub fn vc_clock_rate(&self, clock: Bcm2835VcClock) -> Result<Hertz, Error> {
let mut buffer = PageBox::new_slice(0u32, 4096 / 4)?;
buffer[0] = 9 * 4; // buffer size
buffer[1] = 0; // process request
// Tag 1 (Response)
buffer[2] = 0x00030002; // tag
buffer[3] = 0; // value buffer size
buffer[4] = 0; // request
buffer[5] = clock as u32; // value buffer[0]
buffer[6] = 0; // value buffer[1]
buffer[7] = 0;
// End tag
buffer[8] = 0;
unsafe { self.call_raw(buffer.as_physical_address(), Self::CHANNEL_ARM2VC) }?;
if buffer[4] == 0 || buffer[1] == 0 {
todo!();
}
let rate = Hertz::from(buffer[6]);
Ok(rate)
}
fn configure_framebuffer(&self, mode_info: &DisplayMode) -> Result<FramebufferResponse, Error> {
#[derive(Debug, Clone, Copy)]
#[repr(C)]
struct FramebufferRequest {
@@ -158,7 +195,7 @@ impl Bcm2835Mbox {
}))?;
// TODO flush cache for buffer
unsafe { self.call_raw(buffer.as_physical_address(), 0x01) }?;
unsafe { self.call_raw(buffer.as_physical_address(), Self::CHANNEL_FRAMEBUFFER) }?;
let buffer = buffer.get_mut();
+202
View File
@@ -0,0 +1,202 @@
use core::iter;
use alloc::{boxed::Box, sync::Arc};
use async_trait::async_trait;
use device_api::{
clock::{ClockHandle, Hertz},
device::{Device, DeviceInitContext},
interrupt::IrqHandle,
spi::SpiController,
};
use device_tree::driver::{Node, ProbeContext, device_tree_driver};
use libk::{device::manager::DEVICE_REGISTRY, error::Error};
use libk_mm::device::DeviceMemoryIo;
use libk_util::sync::IrqSafeSpinlock;
use tock_registers::{
interfaces::{Readable, Writeable},
register_bitfields, register_structs,
registers::ReadWrite,
};
use yggdrasil_abi::io::device::spi::{SpiClockPhase, SpiClockPolarity, SpiConfig};
register_bitfields! {
u32,
CS [
LEN_LONG OFFSET(25) NUMBITS(1) [],
DMA_LEN OFFSET(24) NUMBITS(1) [],
CSPOL2 OFFSET(23) NUMBITS(1) [],
CSPOL1 OFFSET(22) NUMBITS(1) [],
CSPOL0 OFFSET(21) NUMBITS(1) [],
RXF OFFSET(20) NUMBITS(1) [],
RXR OFFSET(19) NUMBITS(1) [],
TXD OFFSET(18) NUMBITS(1) [],
RXD OFFSET(17) NUMBITS(1) [],
DONE OFFSET(16) NUMBITS(1) [],
LEN OFFSET(13) NUMBITS(1) [],
REN OFFSET(12) NUMBITS(1) [],
ADCS OFFSET(11) NUMBITS(1) [],
INTR OFFSET(10) NUMBITS(1) [],
INTD OFFSET(9) NUMBITS(1) [],
DMAEN OFFSET(8) NUMBITS(1) [],
TA OFFSET(7) NUMBITS(1) [],
CSPOL OFFSET(6) NUMBITS(1) [],
CLEAR OFFSET(4) NUMBITS(2) [],
CPOL OFFSET(3) NUMBITS(1) [
IdleLow = 0,
IdleHigh = 1
],
CPHA OFFSET(2) NUMBITS(1) [
CaptureOnFirstTransition = 0,
CaptureOnSecondTransition = 1
],
CS OFFSET(0) NUMBITS(2) [],
]
}
register_structs! {
#[allow(non_snake_case)]
Regs {
(0x00 => CS: ReadWrite<u32, CS::Register>),
(0x04 => FIFO: ReadWrite<u32>),
(0x08 => CLK: ReadWrite<u32>),
(0x0C => DLEN: ReadWrite<u32>),
(0x10 => LTOH: ReadWrite<u32>),
(0x14 => DC: ReadWrite<u32>),
(0x18 => @END),
}
}
pub struct Spi {
name: &'static str,
clock: ClockHandle,
irq: IrqHandle,
regs: IrqSafeSpinlock<DeviceMemoryIo<'static, Regs>>,
}
impl Device for Spi {
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
let _ = &self.irq;
let regs = self.regs.lock();
regs.CS.set(0);
DEVICE_REGISTRY.spi.register_bus(self.clone()).ok();
Ok(())
}
fn display_name(&self) -> &str {
self.name
}
}
impl Regs {
fn start_transfer(&self, core_clock: Hertz, config: &SpiConfig) -> Result<(), Error> {
let want_clock = Hertz::from(config.frequency.unwrap_or(100000));
let divider = Hertz::divider(core_clock, want_clock).ok_or(Error::InvalidArgument)?;
let divider: u16 = divider.try_into().map_err(|_| Error::InvalidArgument)?;
let divider = divider.wrapping_add(1) & !1;
let mut cs = CS::REN::SET + CS::TA::SET;
if let Some(cs_index) = config.chip_select_index
&& cs_index < 3
{
cs += CS::CS.val(cs_index);
}
if let Some(cpol) = config.clock_polarity {
match cpol {
SpiClockPolarity::IdleLow => cs += CS::CPOL::IdleLow,
SpiClockPolarity::IdleHigh => cs += CS::CPOL::IdleHigh,
}
}
if let Some(cpha) = config.clock_phase {
match cpha {
SpiClockPhase::CaptureOnFirstTransition => cs += CS::CPHA::CaptureOnFirstTransition,
SpiClockPhase::CaptureOnSecondTransition => {
cs += CS::CPHA::CaptureOnSecondTransition
}
}
}
self.CLK.set(divider as u32);
self.CS.write(cs);
Ok(())
}
fn tx_rx_byte(&self, tx: u8) -> Result<Option<u8>, Error> {
// Wait for tx
loop {
let status = self.CS.extract();
if status.matches_all(CS::TXD::SET) {
break;
}
if status.matches_all(CS::DONE::SET) {
return Ok(None);
}
}
self.FIFO.set(tx as u32);
// Wait for rx
loop {
let status = self.CS.extract();
if status.matches_all(CS::RXD::SET) {
break;
}
if status.matches_all(CS::DONE::SET) {
return Ok(None);
}
}
Ok(Some(self.FIFO.get() as u8))
}
}
#[async_trait]
impl SpiController for Spi {
async fn spi_transfer(
&self,
tx_buffer: &[u8],
rx_buffer: &mut [u8],
config: &SpiConfig,
) -> Result<usize, Error> {
// TODO interrupt/DMA
log::debug!("{}: spi_transfer {} bytes", self.name, rx_buffer.len());
let core_clock = self.clock.rate()?;
let regs = self.regs.lock();
regs.start_transfer(core_clock, config)?;
let mut len = 0;
for (&tx, rx) in iter::zip(tx_buffer, rx_buffer) {
match regs.tx_rx_byte(tx)? {
Some(b) => {
*rx = b;
len += 1;
}
None => {
break;
}
}
}
Ok(len)
}
}
device_tree_driver! {
compatible: ["brcm,bcm2835-spi"],
driver: {
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
let name = node.name().unwrap_or("bcm2835-spi");
let base = node.map_base(context, 0)?;
let irq = node.interrupt(0)?;
let clock = node.clock(0)?;
let regs = unsafe { DeviceMemoryIo::map(base, Default::default()) }.ok()?;
let spi = Arc::new(Spi {
regs: IrqSafeSpinlock::new(regs),
name,
clock,
irq
});
Some(spi)
}
}
}
+20
View File
@@ -57,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;
@@ -226,6 +236,16 @@ const SYSCRG_CLOCKS: &[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
};
-1
View File
@@ -1,4 +1,3 @@
#![allow(unsafe_op_in_unsafe_fn)]
#![no_std]
extern crate alloc;
+97 -73
View File
@@ -2,8 +2,8 @@ use alloc::{sync::Arc, vec::Vec};
use device_api::{
device::{Device, DeviceInitContext},
interrupt::{
ExternalInterruptController, FixedInterruptTable, InterruptHandler, InterruptTable, Irq,
IrqHandle, IrqOptions, IrqVector,
ExternalInterruptController, InterruptHandler, Irq, IrqHandle, IrqOptions, IrqPriority,
IrqVector,
},
};
use device_tree::{
@@ -13,7 +13,10 @@ use device_tree::{
},
};
use kernel_arch_riscv64::boot_hart_id;
use libk::{arch::Cpu, device::register_external_interrupt_controller};
use libk::{
arch::Cpu,
device::{interrupt::FixedInterruptTable, register_external_interrupt_controller},
};
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIo};
use libk_util::{OneTimeInit, sync::spin_rwlock::IrqSafeRwLock};
use tock_registers::{
@@ -77,8 +80,7 @@ register_structs! {
struct Context {
enable: IrqSafeRwLock<DeviceMemoryIo<'static, ContextEnableRegs>>,
control: IrqSafeRwLock<DeviceMemoryIo<'static, ContextControlRegs>>,
// TODO scale the table depending on effective MAX_IRQS value
table: IrqSafeRwLock<FixedInterruptTable<128>>,
table: FixedInterruptTable,
}
struct Inner {
@@ -95,13 +97,71 @@ struct HartContext {
/// RISC-V Platform-Level Interrupt Controller (PLIC) device
pub struct Plic {
base: PhysicalAddress,
max_irqs: usize,
max_irq: usize,
// hart id -> context map
context_map: Vec<HartContext>,
inner: OneTimeInit<Inner>,
}
impl Plic {
fn ensure_init(&self) -> Result<(), Error> {
self.inner.or_try_init_with(|| {
log::info!("Initialize RISC-V PLIC");
let common =
unsafe { DeviceMemoryIo::<CommonRegs>::map(self.base, Default::default())? };
for i in 0..self.max_irq {
common.PRIORITY[i].set(0);
}
for context in self.context_map.iter() {
let enable_offset = ENABLE_BASE + context.index * ENABLE_STRIDE;
let control_offset = CONTROL_BASE + context.index * CONTROL_STRIDE;
log::info!(
"* HART {}: context {}, enable={:#x}, control={:#x}",
context.hart,
context.index,
enable_offset,
control_offset
);
let enable = unsafe {
DeviceMemoryIo::<ContextEnableRegs>::map(
self.base.add(enable_offset),
Default::default(),
)?
};
let control = unsafe {
DeviceMemoryIo::<ContextControlRegs>::map(
self.base.add(control_offset),
Default::default(),
)?
};
for i in 0..self.max_irq.div_ceil(32) {
enable.ENABLE[i].set(0);
}
control.THRESHOLD.set(0);
context.context.init(Context {
enable: IrqSafeRwLock::new(enable),
control: IrqSafeRwLock::new(control),
table: FixedInterruptTable::new(MAX_IRQS), // table: IrqSafeRwLock::new(FixedInterruptTable::new()),
});
}
// self.inner.init(Inner {
// common: IrqSafeRwLock::new(common),
// });
Ok(Inner {
common: IrqSafeRwLock::new(common),
})
})?;
Ok(())
}
fn hart_context(&self, hart: u32) -> Option<&HartContext> {
self.context_map.iter().find(|c| c.hart == hart)
}
@@ -115,8 +175,8 @@ impl Plic {
log::error!("plic: irq cannot be zero");
return Err(Error::InvalidArgument);
}
if irq as usize >= self.max_irqs {
log::error!("plic: irq ({}) >= max_irqs ({})", irq, self.max_irqs);
if irq as usize > self.max_irq {
log::error!("plic: irq ({}) > max_irq ({})", irq, self.max_irq);
return Err(Error::InvalidArgument);
}
Ok(irq)
@@ -152,24 +212,33 @@ impl ExternalInterruptController for Plic {
fn register_irq(
&self,
irq: Irq,
_options: IrqOptions,
options: IrqOptions,
handler: Arc<dyn InterruptHandler>,
) -> Result<(), Error> {
self.ensure_init()?;
let bsp_hart_id = boot_hart_id() as u32;
let irq = self.validate_irq(irq)?;
let prioval = match options.priority {
IrqPriority::Low => 3,
IrqPriority::Normal => 5,
IrqPriority::High => 7,
};
let inner = self.inner.get();
let common = inner.common.write();
common.PRIORITY[irq as usize - 1].set(prioval);
let context = self
.hart_context(bsp_hart_id)
.ok_or(Error::InvalidArgument)
.inspect_err(|_| log::error!("plic: no context for hart {bsp_hart_id}"))?
.context
.get();
let mut table = context.table.write();
// let mut table = context.table.write();
log::info!(
"Bind irq #{irq} -> hart {bsp_hart_id}, {:?}",
handler.display_name()
);
table.insert(irq as usize, handler)?;
context.table.insert(irq as usize, handler);
Ok(())
}
@@ -183,72 +252,26 @@ impl ExternalInterruptController for Plic {
let context = context.context.get();
let control = context.control.write();
let table = context.table.read();
// let table = context.table.read();
loop {
let irq = control.CLAIM.get();
if irq == 0 {
break;
}
let vector = IrqVector::Irq(Irq::External(irq));
if let Some(handler) = table.handler(irq as usize) {
handler.clone().handle_irq(vector);
} else {
log::warn!("plic: no handler for IRQ #{irq}");
}
// Done servicing
control.CLAIM.set(irq);
let irq = control.CLAIM.get();
if irq == 0 {
return;
}
let vector = IrqVector::Irq(Irq::External(irq));
if !context.table.handle_irq_line(irq as usize, vector) {
log::warn!("plic: no handler for IRQ #{irq}");
}
// Done servicing
control.CLAIM.set(irq);
}
}
impl Device for Plic {
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
log::info!("Initialize RISC-V PLIC");
let common = DeviceMemoryIo::<CommonRegs>::map(self.base, Default::default())?;
for i in 0..self.max_irqs - 1 {
common.PRIORITY[i].set(3);
}
for context in self.context_map.iter() {
let enable_offset = ENABLE_BASE + context.index * ENABLE_STRIDE;
let control_offset = CONTROL_BASE + context.index * CONTROL_STRIDE;
log::info!(
"* HART {}: context {}, enable={:#x}, control={:#x}",
context.hart,
context.index,
enable_offset,
control_offset
);
let enable = DeviceMemoryIo::<ContextEnableRegs>::map(
self.base.add(enable_offset),
Default::default(),
)?;
let control = DeviceMemoryIo::<ContextControlRegs>::map(
self.base.add(control_offset),
Default::default(),
)?;
for i in 0..self.max_irqs.div_ceil(32) {
enable.ENABLE[i].set(0);
}
control.THRESHOLD.set(0);
context.context.init(Context {
enable: IrqSafeRwLock::new(enable),
control: IrqSafeRwLock::new(control),
table: IrqSafeRwLock::new(FixedInterruptTable::new()),
});
}
self.inner.init(Inner {
common: IrqSafeRwLock::new(common),
});
self.ensure_init()?;
register_external_interrupt_controller(self);
@@ -290,7 +313,7 @@ device_tree_driver! {
let ndev = node.prop_usize("riscv,ndev")?;
let iext = node.property("interrupts-extended")?;
let max_irqs = MAX_IRQS.min(ndev);
let max_irq = MAX_IRQS.min(ndev);
// Parse the context -> hart mapping, only select S-mode external interrupts
let mut context_map = Vec::new();
@@ -302,6 +325,7 @@ device_tree_driver! {
) else {
continue;
};
log::info!("Context #{context}: mode={mode:?}, hart={hart_id}");
if mode != ContextMode::ExternalS {
continue;
}
@@ -323,7 +347,7 @@ device_tree_driver! {
let intc = Arc::new(Plic {
base,
max_irqs,
max_irq,
context_map,
inner: OneTimeInit::new(),
});
+20
View File
@@ -0,0 +1,20 @@
[package]
name = "ygg_driver_bsp_sifive"
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
ygg_driver_net_core.path = "../../net/core"
static_assertions.workspace = true
tock-registers.workspace = true
log.workspace = true
bytemuck.workspace = true
futures-util.workspace = true
async-trait.workspace = true
+190
View File
@@ -0,0 +1,190 @@
use alloc::sync::Arc;
use device_api::{
clock::{ClockController, ClockHandle, Hertz},
device::{Device, DeviceInitContext},
};
use device_tree::{
DeviceTreePropertyRead, TProp,
driver::{DeviceTreeClockController, Node, ProbeContext, device_tree_driver},
};
use libk::error::Error;
use libk_mm::device::DeviceMemoryIo;
use libk_util::{OneTimeInit, sync::IrqSafeSpinlock};
use tock_registers::{
interfaces::{Readable, Writeable},
register_structs,
registers::ReadWrite,
};
use crate::pll::{PllCfg, WrpllData};
const CLK_COREPLL: u32 = 0;
const CLK_DDRPLL: u32 = 1;
const CLK_GEMGXLPLL: u32 = 2;
const CLK_TLCLK: u32 = 3;
register_structs! {
Regs {
(0x000 => hfxosccfg: ReadWrite<u32>),
(0x004 => corepllcfg0: ReadWrite<u32, PllCfg::Register>),
(0x008 => _0),
(0x00C => ddrpllcfg0: ReadWrite<u32>),
(0x010 => ddrpllcfg1: ReadWrite<u32>),
(0x014 => _1),
(0x01C => gemgxlpllcfg0: ReadWrite<u32>),
(0x020 => gemgxlpllcfg1: ReadWrite<u32>),
(0x024 => coreclksel: ReadWrite<u32>),
(0x028 => deviceresetreg: ReadWrite<u32>),
(0x02C => _2),
(0x100 => @END),
}
}
struct Prci {
clk_osc: ClockHandle,
clk_rtc: ClockHandle,
regs: IrqSafeSpinlock<DeviceMemoryIo<'static, Regs>>,
init: OneTimeInit<()>,
}
impl Regs {
fn read_core_pll(&self) -> WrpllData {
let pll = self.corepllcfg0.extract();
let divr = pll.read(PllCfg::PLLR);
let divf = pll.read(PllCfg::PLLF);
let divq = pll.read(PllCfg::PLLQ);
WrpllData {
divq,
divr,
divf,
int_feedback: true,
}
}
}
impl Prci {
fn ensure_init(&self) {
// U-Boot should configure coreclk to 1GHz, but when booting
// in QEMU, there is not U-Boot stage, and some peripherals
// like PWM expect the clock to be configured this way. So
// this needs to be done manually here
self.init.or_init_with(|| {
let regs = self.regs.lock();
if regs.coreclksel.get() != 0 {
// coreclk configured to hfclk
// configure corepll to output 1GHz and switch to it
// divf = 150
// divr = 4
// divq = 1
regs.corepllcfg0.write(
PllCfg::PLLF.val(150)
+ PllCfg::PLLR.val(4)
+ PllCfg::PLLQ.val(1)
+ PllCfg::PLLFSEBYPASS::SET,
);
while !regs.corepllcfg0.matches_all(PllCfg::PLLLOCK::SET) {
core::hint::spin_loop();
}
regs.coreclksel.set(0);
}
});
}
}
impl Device for Prci {
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
self.ensure_init();
if let Ok(coreclk) = self.clock_rate(Some(CLK_COREPLL)) {
log::info!("coreclk = {coreclk}");
}
Ok(())
}
fn display_name(&self) -> &str {
"sifive,fu540-c000-prci"
}
}
impl ClockController for Prci {
fn clock_rate(&self, clock: Option<u32>) -> Result<Hertz, Error> {
self.ensure_init();
let _ = &self.clk_rtc;
match clock {
Some(CLK_COREPLL) => {
let regs = self.regs.lock();
let hfclk = self.clk_osc.rate()?;
// == 0 -> use corepll
// == 1 -> use hfclk
let coreclksel = regs.coreclksel.get() != 0;
let coreclk = match coreclksel {
true => hfclk,
false => {
let pll = regs.read_core_pll();
pll.output_rate(hfclk)
}
};
Ok(coreclk)
}
Some(CLK_TLCLK) => {
let coreclk = self.clock_rate(Some(CLK_COREPLL))?;
Ok(coreclk / 2)
}
Some(_) => todo!(),
None => Err(Error::InvalidArgument),
}
}
fn set_clock_rate(&self, _clock: Option<u32>, _rate: Hertz) -> Result<Hertz, Error> {
Err(Error::NotImplemented)
}
fn enable_clock(&self, clock: Option<u32>) -> Result<(), Error> {
self.ensure_init();
match clock {
Some(CLK_GEMGXLPLL) | Some(CLK_DDRPLL) | Some(CLK_COREPLL) => todo!(),
Some(CLK_TLCLK) => Ok(()),
_ => Err(Error::InvalidArgument),
}
}
fn disable_clock(&self, _clock: Option<u32>) -> Result<(), Error> {
Err(Error::NotImplemented)
}
}
impl DeviceTreeClockController for Prci {
fn map_clock(self: Arc<Self>, property: &TProp, offset: usize) -> Option<(ClockHandle, usize)> {
let clock = property.read_cell(offset, 1)? as u32;
Some((
ClockHandle {
parent: self.clone(),
clock: Some(clock),
},
1,
))
}
}
device_tree_driver! {
compatible: ["sifive,fu540-c000-prci"],
driver: {
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
let base = node.map_base(context, 0)?;
let clk_osc = node.clock(0)?;
let clk_rtc = node.clock(1)?;
let regs = unsafe { DeviceMemoryIo::map(base, Default::default()) }.ok()?;
let prci = Arc::new(Prci {
regs: IrqSafeSpinlock::new(regs),
clk_osc,
clk_rtc,
init: OneTimeInit::new()
});
node.make_clock_controller(prci.clone());
Some(prci)
}
}
}
+171
View File
@@ -0,0 +1,171 @@
use alloc::sync::Arc;
use device_api::{
clock::{ClockController, ClockHandle, Hertz},
device::Device,
};
use device_tree::{
DeviceTreePropertyRead, TProp,
driver::{DeviceTreeClockController, Node, ProbeContext, device_tree_driver},
};
use libk::error::Error;
use libk_mm::device::DeviceMemoryIo;
use libk_util::sync::IrqSafeSpinlock;
use tock_registers::{
interfaces::Readable,
register_structs,
registers::{ReadOnly, ReadWrite},
};
use crate::pll::{PllCfg, WrpllData};
// const CLK_COREPLL: u32 = 0;
// const CLK_DDRPLL: u32 = 1;
// const CLK_GEMGXLPLL: u32 = 2;
// const CLK_DVFSCOREPLL: u32 = 3;
// const CLK_HFPCLKPLL: u32 = 4;
// const CLK_CLTXPLL: u32 = 5;
// const CLK_TLCLK: u32 = 6;
const CLK_PCLK: u32 = 7;
// const CLK_PCIE_AUX: u32 = 8;
register_structs! {
Regs {
(0x00 => hfxosccfg: ReadWrite<u32>),
(0x04 => core_pllcfg: ReadWrite<u32>),
(0x08 => core_plloutdiv: ReadWrite<u32>),
(0x0C => ddr_pllcfg: ReadWrite<u32>),
(0x10 => ddr_plloutdiv: ReadWrite<u32>),
(0x14 => _0),
(0x1C => gemgxl_pllcfg: ReadWrite<u32, PllCfg::Register>),
(0x20 => gemgxl_plloutdiv: ReadWrite<u32>),
(0x24 => core_clk_sel_reg: ReadWrite<u32>),
(0x28 => devices_reset_n: ReadWrite<u32>),
(0x2C => clk_mux_status: ReadOnly<u32>),
(0x30 => _1),
(0x38 => dvfs_core_pllcfg: ReadWrite<u32>),
(0x3C => dvfs_core_plloutdiv: ReadWrite<u32>),
(0x40 => corepllsel: ReadWrite<u32>),
(0x44 => _2),
(0x50 => hfpclk_pllcfg: ReadWrite<u32, PllCfg::Register>),
(0x54 => hfpclk_plloutdiv: ReadWrite<u32>),
(0x58 => hfpclkpllsel: ReadWrite<u32>),
(0x5C => hfpclk_div_reg: ReadWrite<u32>),
(0x60 => _3),
(0xE0 => prci_plls: ReadOnly<u32>),
(0xE4 => _4),
(0x100 => @END),
}
}
struct Prci {
clk_hfclk: ClockHandle,
clk_rtcclk: ClockHandle,
regs: IrqSafeSpinlock<DeviceMemoryIo<'static, Regs>>,
}
impl Regs {
fn read_hfpclk_pll(&self) -> WrpllData {
let reg = self.hfpclk_pllcfg.extract();
let divr = reg.read(PllCfg::PLLR);
let divq = reg.read(PllCfg::PLLQ);
let divf = reg.read(PllCfg::PLLF);
WrpllData {
divq,
divr,
divf,
int_feedback: true,
}
}
}
impl Device for Prci {
fn display_name(&self) -> &str {
"sifive,fu740-c000-prci"
}
}
impl ClockController for Prci {
fn clock_rate(&self, clock: Option<u32>) -> Result<Hertz, Error> {
// hart frequency: coreclk, supplied either by corepll or dvfscorepll, selected by
// corepllsel
// hfpclkpll: clock for SPI, UART, GPIO, I2C, PWM
//
let _ = &self.clk_rtcclk;
let regs = self.regs.lock();
match clock {
Some(CLK_PCLK) => {
// PCLK calculation:
// pclk <- X / hfpclkdiv
// case [hfpclkpllsel]:
// 1: X <- (hfclk)
// 0: X <- hfpclkpllcfg/out & hfpclkpll
// hfpclkdiv <- [hfpclk_div_reg] + 2
let hfpclkpllsel = regs.hfpclkpllsel.get() & 1 != 0;
let hfpclk_div_reg = regs.hfpclk_div_reg.get() + 2;
let x = match hfpclkpllsel {
true => self.clk_hfclk.rate()?,
false => {
let pclk_pll = regs.read_hfpclk_pll();
pclk_pll.output_rate(self.clk_hfclk.rate()?)
}
};
Ok(x / hfpclk_div_reg)
}
Some(_) => {
todo!();
}
None => {
todo!();
}
}
}
fn set_clock_rate(&self, _clock: Option<u32>, _rate: Hertz) -> Result<Hertz, Error> {
Err(Error::NotImplemented)
}
fn enable_clock(&self, _clock: Option<u32>) -> Result<(), Error> {
Ok(())
}
fn disable_clock(&self, _clock: Option<u32>) -> Result<(), Error> {
Err(Error::NotImplemented)
}
}
impl DeviceTreeClockController for Prci {
fn map_clock(self: Arc<Self>, property: &TProp, offset: usize) -> Option<(ClockHandle, usize)> {
let clock = property.read_cell(offset, 1)? as u32;
Some((
ClockHandle {
parent: self.clone(),
clock: Some(clock),
},
1,
))
}
}
device_tree_driver! {
compatible: ["sifive,fu740-c000-prci"],
driver: {
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
let base = node.map_base(context, 0)?;
let clk_hfclk = node.clock(0)?;
let clk_rtcclk = node.clock(1)?;
let regs = unsafe { DeviceMemoryIo::map(base, Default::default()) }.ok()?;
let prci = Arc::new(Prci {
regs: IrqSafeSpinlock::new(regs),
clk_hfclk,
clk_rtcclk
});
node.make_clock_controller(prci.clone());
Some(prci)
}
}
}
@@ -0,0 +1,278 @@
use core::{mem::MaybeUninit, time::Duration};
use alloc::sync::Arc;
use device_api::{
clock::ClockHandle,
device::{Device, DeviceInitContext},
dma::DmaAllocator,
interrupt::{InterruptHandler, IrqHandle, IrqVector},
};
use device_tree::driver::{
InitSequence, Node, ProbeContext, device_tree_driver, util::read_mac_address,
};
use futures_util::task::AtomicWaker;
use libk::{
dma::DmaBuffer,
error::Error,
task::runtime::{self, with_timeout},
};
use libk_mm::device::DeviceMemoryIo;
use libk_util::{OneTimeInit, event::BitmapEvent, sync::IrqSafeSpinlock};
use tock_registers::{
LocalRegisterCopy,
interfaces::{ReadWriteable, Readable, Writeable},
};
use ygg_driver_net_core::{
RxPacket,
ephy::PhyAccess,
interface::{NetworkDevice, NetworkInterfaceType},
util::GenericQueue,
};
use yggdrasil_abi::net::{MacAddress, link::LinkState};
use crate::ethernet::{
queue::Descriptor,
regs::{ControlRegs, Regs},
};
const RX_BUF_SIZE_MUL: usize = 64;
mod queue;
mod regs;
// TODO multiqueue
struct Ethernet {
// Device tree info
name: &'static str,
irq: IrqHandle,
clk_pclk: ClockHandle,
clk_hclk: ClockHandle,
local_mac_address: Option<MacAddress>,
// Memory/HW
// caps: Capabilities,
dma: OneTimeInit<Arc<dyn DmaAllocator>>,
control_regs: IrqSafeSpinlock<DeviceMemoryIo<'static, ControlRegs>>,
regs: IrqSafeSpinlock<DeviceMemoryIo<'static, Regs>>,
// Operation
queue: OneTimeInit<GenericQueue<Descriptor, Descriptor>>,
softirq: BitmapEvent<AtomicWaker>,
iface_id: OneTimeInit<u32>,
}
// bitflags! {
// pub struct CapFlags: u32 {
// const MACB_IS_GEM: bit 0;
// }
// }
// struct Capabilities {
// flags: CapFlags,
// queue_mask: u32,
// queue_count: usize,
// }
impl Ethernet {
fn probe(node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
let name = node.name().unwrap_or("gemgxl");
let irq = node.interrupt(0)?;
let clk_pclk = node.named_clock("pclk")?;
let clk_hclk = node.named_clock("hclk")?;
let base = node.map_base(context, 0)?;
let base_control = node.map_base(context, 1)?;
let local_mac_address = read_mac_address(node);
let regs = unsafe { DeviceMemoryIo::<Regs>::map(base, Default::default()) }.ok()?;
let control_regs = unsafe { DeviceMemoryIo::map(base_control, Default::default()) }.ok()?;
// let caps = regs.probe_capabilities();
let gem = Arc::new(Ethernet {
name,
clk_pclk,
clk_hclk,
irq,
local_mac_address,
// caps,
dma: OneTimeInit::new(),
regs: IrqSafeSpinlock::new(regs),
control_regs: IrqSafeSpinlock::new(control_regs),
queue: OneTimeInit::new(),
softirq: BitmapEvent::new(AtomicWaker::new()),
iface_id: OneTimeInit::new(),
});
context.sequence = Some(InitSequence::Late);
Some(gem)
}
async fn softirq(self: Arc<Self>) {
let iface = *self.iface_id.get();
let queue = self.queue.get();
loop {
let event_fut = self.softirq.wait();
let bits = match with_timeout(event_fut, Duration::from_millis(100)).await {
Ok(bits) => bits as u32,
Err(_) => {
queue.consume_tx().ok();
continue;
}
};
let status = LocalRegisterCopy::<_, regs::Interrupt::Register>::new(bits);
if status.matches_all(regs::Interrupt::RCOMP::SET) {
queue
.consume_rx(&**self.dma.get(), None, |packet, _len| {
let packet = RxPacket::new(packet, 0, iface);
ygg_driver_net_core::receive_packet(packet).ok();
})
.ok();
let regs = self.regs.lock();
regs.interrupt_enable.write(regs::Interrupt::RCOMP::SET);
}
if status.matches_all(regs::Interrupt::TCOMP::SET) {
queue.consume_tx().ok();
let regs = self.regs.lock();
regs.interrupt_enable.write(regs::Interrupt::RCOMP::SET);
}
if status.matches_all(regs::Interrupt::LINK::SET) {
log::info!("Link status changed");
let regs = self.regs.lock();
regs.interrupt_enable.write(regs::Interrupt::LINK::SET);
}
}
}
fn start_xmit(&self, frame: DmaBuffer<[u8]>) -> Result<(), Error> {
let queue = self.queue.get();
queue.try_push_xmit(frame)?;
let regs = self.regs.lock();
regs.network_control
.modify(regs::NetworkControl::TSTART::SET);
Ok(())
}
}
impl Device for Ethernet {
unsafe fn init(self: Arc<Self>, cx: DeviceInitContext) -> Result<(), Error> {
let _ = (&self.clk_pclk, &self.clk_hclk, &self.control_regs);
log::info!("{}: hw address {:?}", self.name, self.local_mac_address);
// TODO enable clocks
let dma = self.dma.init(cx.dma_allocator);
let regs = self.regs.lock();
let queue = self.queue.init(GenericQueue::with_capacity(
&**dma,
256,
256,
queue::RX_BUFFER_SIZE,
)?);
let rx_queue_base = queue.rx_buffer_base().try_into_u32().unwrap();
let tx_queue_base = queue.tx_buffer_base().try_into_u32().unwrap();
regs.interrupt_disable.set(0xFFFFFFFF);
regs.rx_buffer_queue_base.set(rx_queue_base);
regs.tx_buffer_queue_base.set(tx_queue_base);
regs.dma_config.write(
regs::DmaConfig::RX_BUF_SIZE.val((queue::RX_BUFFER_SIZE / RX_BUF_SIZE_MUL) as _),
);
regs.network_control.modify(regs::NetworkControl::MPE::SET);
{
let phy = PhyAccess::new(&**regs, 0);
let phy_id = phy.id()?;
log::info!("{}: PHY {:04x}:{:04x}", self.name, phy_id.0, phy_id.1);
phy.setup_link(false, None)?;
}
regs.network_config.write(regs::NetworkConfig::PROMISC::SET);
regs.network_control
.modify(regs::NetworkControl::TE::SET + regs::NetworkControl::RE::SET);
// if (GEM_BFEXT(DAW64, gem_readl(...)))
// hw_dma_cap |= HW_DMA_CAP_64B
regs.interrupt_enable.write(
regs::Interrupt::LINK::SET + regs::Interrupt::RCOMP::SET + regs::Interrupt::TCOMP::SET,
);
let iface =
ygg_driver_net_core::register_interface(NetworkInterfaceType::Ethernet, self.clone());
self.iface_id.init(iface.id());
runtime::spawn(self.clone().softirq())?;
Ok(())
}
unsafe fn init_irq(self: Arc<Self>) -> Result<(), Error> {
self.irq.register(self.clone())?;
self.irq.enable()?;
Ok(())
}
fn display_name(&self) -> &str {
self.name
}
}
impl NetworkDevice for Ethernet {
fn allocate_transmit_buffer(&self, len: usize) -> Result<DmaBuffer<[MaybeUninit<u8>]>, Error> {
DmaBuffer::new_uninit_slice(&**self.dma.get(), len)
}
fn transmit_buffer(&self, buffer: DmaBuffer<[u8]>) -> Result<(), Error> {
self.start_xmit(buffer)
}
fn packet_prefix_size(&self) -> usize {
0
}
fn read_hardware_address(&self) -> MacAddress {
// TODO from registers
self.local_mac_address.unwrap()
}
fn link_state(&self) -> LinkState {
self.regs.lock().link_state()
}
}
impl InterruptHandler for Ethernet {
fn handle_irq(self: Arc<Self>, _vector: IrqVector) -> bool {
const MASK: u32 = regs::Interrupt::RCOMP::SET.value
| regs::Interrupt::TCOMP::SET.value
| regs::Interrupt::LINK::SET.value;
let regs = self.regs.lock();
let status = regs.interrupt_status.get();
// Mask the IRQ and send it to softirq handler
regs.interrupt_disable.set(MASK & status);
self.softirq.signal(status as u64);
status != 0
}
}
device_tree_driver! {
compatible: ["sifive,fu540-c000-gem"],
driver: {
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
Ethernet::probe(node, context)
}
}
}
@@ -0,0 +1,87 @@
use libk::{dma::BusAddress, error::Error};
use ygg_driver_net_core::util::{GenericRxDescriptor, GenericTxDescriptor};
pub const RX_BUFFER_SIZE: usize = 4096;
static_assertions::const_assert_eq!(RX_BUFFER_SIZE % super::RX_BUF_SIZE_MUL, 0);
static_assertions::const_assert_ne!(RX_BUFFER_SIZE / super::RX_BUF_SIZE_MUL, 0);
#[derive(Clone, Copy, Debug)]
#[repr(C)]
pub(crate) struct Descriptor {
address: u32,
control: u32,
}
impl GenericTxDescriptor for Descriptor {
// USED = 0: owned by hardware
// USED = 1: owned by software
const EMPTY: Self = Self {
address: 0,
control: Self::DESC_1_USED,
};
const EMPTY_LAST: Self = Self {
address: 0,
control: Self::DESC_1_USED | Self::DESC_1_TX_WRAP,
};
fn consume(&mut self) -> Option<Result<(), Error>> {
if self.control & Self::DESC_1_USED != 0 && self.address != 0 {
Some(Ok(()))
} else {
None
}
}
fn setup_tx(
&mut self,
buffer_address: BusAddress,
size: usize,
index: usize,
capacity: usize,
) -> Result<(), Error> {
let buffer_address = buffer_address.try_into_u32()?;
if !(8..8192).contains(&size) {
return Err(Error::InvalidArgument);
}
let mut control = size as u32 | Self::DESC_1_TX_LAST;
if index == capacity - 1 {
control |= Self::DESC_1_TX_WRAP;
}
self.address = buffer_address;
self.control = control;
Ok(())
}
}
impl GenericRxDescriptor for Descriptor {
fn consume(&mut self) -> Option<Result<usize, Error>> {
if self.address & Self::DESC_0_RX_OWNERSHIP != 0 {
Some(Ok((self.control & Self::LENGTH) as usize))
} else {
None
}
}
fn setup_rx(buffer_address: BusAddress, _size: usize, last: bool) -> Result<Self, Error> {
let mut address = buffer_address.try_into_u32()?;
if last {
address |= Self::DESC_0_RX_WRAP;
}
Ok(Self {
control: 0,
address,
})
}
}
impl Descriptor {
const DESC_1_USED: u32 = 1 << 31;
const LENGTH: u32 = 0x1FFF;
const DESC_1_TX_WRAP: u32 = 1 << 30;
const DESC_1_TX_LAST: u32 = 1 << 15;
const DESC_0_RX_OWNERSHIP: u32 = 1 << 0;
const DESC_0_RX_WRAP: u32 = 1 << 1;
}
@@ -0,0 +1,197 @@
use core::time::Duration;
use libk::{error::Error, task::runtime::pwait};
use tock_registers::{
interfaces::{Readable, Writeable},
register_bitfields, register_structs,
registers::{ReadOnly, ReadWrite, WriteOnly},
};
use ygg_driver_net_core::ephy::MdioBus;
use yggdrasil_abi::net::link::LinkState;
// use crate::ethernet::{CapFlags, Capabilities};
register_structs! {
pub ControlRegs {
(0x00 => pub tx_clk_sel: ReadWrite<u32>),
(0x04 => _0),
(0x20 => pub control_status_speed_mode: ReadWrite<u32>),
(0x24 => _1),
(0x40 => @END),
}
}
register_bitfields! {
u32,
pub NetworkControl [
// Rx enable
RE OFFSET(2) NUMBITS(1) [],
// Tx enable
TE OFFSET(3) NUMBITS(1) [],
// MDIO enable
MPE OFFSET(4) NUMBITS(1) [],
// Start Tx
TSTART OFFSET(9) NUMBITS(1) [],
// Halt Tx
THALT OFFSET(10) NUMBITS(1) [],
],
pub NetworkConfig [
// Speed
SPD OFFSET(0) NUMBITS(1) [],
// Full duplex
FD OFFSET(1) NUMBITS(1) [],
PROMISC OFFSET(4) NUMBITS(1) [],
// Gigabit mode enable
GBE OFFSET(10) NUMBITS(1) [],
],
pub NetworkStatus [
LINK OFFSET(0) NUMBITS(1) [],
MDIO_IDLE OFFSET(2) NUMBITS(1) [],
],
pub DmaConfig [
RX_BUF_SIZE OFFSET(16) NUMBITS(8) [],
],
pub Interrupt [
// Rx complete
RCOMP OFFSET(1) NUMBITS(1) [],
// Tx complete
TCOMP OFFSET(7) NUMBITS(1) [],
// Link change
LINK OFFSET(8) NUMBITS(1) [],
],
pub PhyMaintenance [
DATA OFFSET(0) NUMBITS(16) [],
CODE OFFSET(16) NUMBITS(2) [],
REGA OFFSET(18) NUMBITS(5) [],
PHYA OFFSET(23) NUMBITS(5) [],
RW OFFSET(28) NUMBITS(2) [
Read = 0b10,
Write = 0b01,
],
SOF OFFSET(30) NUMBITS(1) [],
],
pub ModuleId [
IDNUM OFFSET(16) NUMBITS(12) [],
REV OFFSET(0) NUMBITS(16) [],
]
}
register_structs! {
pub Regs {
(0x000 => pub network_control: ReadWrite<u32, NetworkControl::Register>),
(0x004 => pub network_config: ReadWrite<u32, NetworkConfig::Register>),
(0x008 => pub network_status: ReadOnly<u32, NetworkStatus::Register>),
(0x00C => pub user_io: ReadWrite<u32>),
(0x010 => pub dma_config: ReadWrite<u32, DmaConfig::Register>),
(0x014 => pub transmit_status: ReadWrite<u32>),
(0x018 => pub rx_buffer_queue_base: ReadWrite<u32>),
(0x01C => pub tx_buffer_queue_base: ReadWrite<u32>),
(0x020 => pub rx_status: ReadWrite<u32>),
(0x024 => pub interrupt_status: ReadWrite<u32, Interrupt::Register>),
(0x028 => pub interrupt_enable: WriteOnly<u32, Interrupt::Register>),
(0x02C => pub interrupt_disable: WriteOnly<u32, Interrupt::Register>),
(0x030 => pub interrupt_mask: ReadOnly<u32, Interrupt::Register>),
(0x034 => pub phy_maintenance: ReadWrite<u32, PhyMaintenance::Register>),
(0x038 => pub rx_pause_quantum: ReadOnly<u32>),
(0x03C => pub tx_pause_quantum: ReadWrite<u32>),
(0x040 => pub tx_partial_store_fwd: ReadWrite<u32>),
(0x044 => pub rx_partial_store_fwd: ReadWrite<u32>),
(0x048 => _0),
(0x080 => pub hash_register_bottom: ReadWrite<u32>),
(0x084 => pub hash_register_top: ReadWrite<u32>),
(0x088 => pub specific_address_x: [ReadWrite<u32>; 8]),
(0x0A8 => pub type_id_match: [ReadWrite<u32>; 4]),
(0x0B8 => pub wake_on_lan: ReadWrite<u32>),
(0x0BC => pub ipg_stretch: ReadWrite<u32>),
(0x0C0 => pub stacked_vlan: ReadWrite<u32>),
(0x0C4 => pub tx_pfc_pause: ReadWrite<u32>),
(0x0C8 => pub specific_address_mask: [ReadWrite<u32>; 2]),
(0x0D0 => pub rx_buffer_ahb_mask: ReadWrite<u32>),
(0x0D4 => pub ptp_rx_unicast_dst_ip: ReadWrite<u32>),
(0x0D8 => pub ptp_tx_unicast_dst_ip: ReadWrite<u32>),
(0x0DC => pub tsu_timer_cval_nanos: ReadWrite<u32>),
(0x0E0 => pub tsu_timer_cval_sec: ReadWrite<u32>),
(0x0E4 => _1),
(0x0FC => pub module_id: ReadOnly<u32, ModuleId::Register>),
(0x100 => _2),
(0x200 => pub pcs_control: ReadWrite<u32>),
(0x204 => pub pcs_status: ReadOnly<u32>),
(0x208 => pub pcs_phy_id_upper: ReadOnly<u32>),
(0x20C => pub pcs_phy_id_lower: ReadOnly<u32>),
(0x210 => pub pcs_aneg_adv: ReadWrite<u32>),
(0x214 => pub pcs_aneg_link_ability: ReadOnly<u32>),
(0x218 => pub pcs_aneg_expansion: ReadOnly<u32>),
(0x21C => pub pcs_aneg_next_page: ReadWrite<u32>),
(0x220 => pub pcs_aneg_link_next_page: ReadOnly<u32>),
(0x224 => _3),
(0x280 => pub design_config_1: ReadOnly<u32>),
(0x284 => pub design_config_2: ReadOnly<u32>),
(0x288 => pub design_config_3: ReadOnly<u32>),
(0x28C => pub design_config_4: ReadOnly<u32>),
(0x290 => pub design_config_5: ReadOnly<u32>),
(0x294 => pub design_config_6: ReadOnly<u32>),
(0x298 => pub design_config_7: ReadOnly<u32>),
(0x29C => _4),
(0x400 => @END),
}
}
impl Regs {
// pub fn probe_capabilities(&self) -> Capabilities {
// let mut flags = CapFlags::empty();
// let mut queue_mask = 0x1;
// let mut queue_count = 1;
// let module_id = self.module_id.read(ModuleId::IDNUM);
// if module_id >= 0x2 {
// flags |= CapFlags::MACB_IS_GEM;
// queue_mask |= self.design_config_6.get() & 0xFF;
// queue_count = queue_mask.count_ones() as usize;
// }
// Capabilities {
// flags,
// queue_mask,
// queue_count,
// }
// }
pub fn link_state(&self) -> LinkState {
todo!()
}
fn wait_mdio_idle(&self, timeout: Duration) -> Result<(), Error> {
pwait(timeout, Duration::from_millis(20), || {
self.network_status
.matches_all(NetworkStatus::MDIO_IDLE::SET)
})
}
}
impl MdioBus for Regs {
fn mii_read(&self, phyaddr: u8, regaddr: u8) -> Result<u16, Error> {
self.wait_mdio_idle(Duration::from_secs(1))?;
self.phy_maintenance.write(
PhyMaintenance::CODE.val(2)
+ PhyMaintenance::PHYA.val(phyaddr as _)
+ PhyMaintenance::REGA.val(regaddr as _)
+ PhyMaintenance::SOF.val(1)
+ PhyMaintenance::RW::Read,
);
self.wait_mdio_idle(Duration::from_secs(1))?;
let value = self.phy_maintenance.read(PhyMaintenance::DATA);
Ok(value as u16)
}
fn mii_write(&self, phyaddr: u8, regaddr: u8, value: u16) -> Result<(), Error> {
self.wait_mdio_idle(Duration::from_secs(1))?;
self.phy_maintenance.write(
PhyMaintenance::CODE.val(2)
+ PhyMaintenance::PHYA.val(phyaddr as _)
+ PhyMaintenance::REGA.val(regaddr as _)
+ PhyMaintenance::SOF.val(1)
+ PhyMaintenance::DATA.val(value as _)
+ PhyMaintenance::RW::Write,
);
self.wait_mdio_idle(Duration::from_secs(1))
}
}
+523
View File
@@ -0,0 +1,523 @@
use alloc::{boxed::Box, sync::Arc};
use async_trait::async_trait;
use device_api::{
clock::{ClockHandle, Hertz, IntoHertz},
device::{Device, DeviceInitContext},
i2c::{I2CAddress, I2CController, I2CMessage, I2CTransfer},
interrupt::{InterruptHandler, IrqHandle, IrqVector},
};
use device_tree::driver::{Node, ProbeContext, device_tree_driver};
use futures_util::task::AtomicWaker;
use libk::{block, device::manager::DEVICE_REGISTRY, error::Error};
use libk_mm::device::DeviceMemoryIo;
use libk_util::{OneTimeInit, event::BitmapEvent, sync::IrqSafeSpinlock};
use tock_registers::{
LocalRegisterCopy,
interfaces::{ReadWriteable, Readable, Writeable},
register_bitfields, register_structs,
registers::{Aliased, ReadWrite},
};
use yggdrasil_abi::io::device::i2c::I2CMasterInfo;
const CMD_START: u32 = 0x91;
const CMD_STOP: u32 = 0x41;
const CMD_WRITE: u32 = 0x11;
const CMD_READ_ACK: u32 = 0x21;
const CMD_READ_NACK: u32 = 0x29;
const CMD_IACK: u32 = 0x01;
register_bitfields! {
u32,
CTR [
EN OFFSET(7) NUMBITS(1) [],
IEN OFFSET(6) NUMBITS(1) [],
],
SR [
RXACK OFFSET(7) NUMBITS(1) [],
BUSY OFFSET(6) NUMBITS(1) [],
AL OFFSET(5) NUMBITS(1) [],
TIP OFFSET(1) NUMBITS(1) [],
IF OFFSET(0) NUMBITS(1) [],
],
}
register_structs! {
#[allow(non_snake_case)]
Regs {
(0x000 => PRER_LO: ReadWrite<u32>),
(0x004 => PRER_HI: ReadWrite<u32>),
(0x008 => CTR: ReadWrite<u32, CTR::Register>),
(0x00C => TXR_RXR: ReadWrite<u32>),
(0x010 => CR_SR: Aliased<u32, SR::Register, ()>),
(0x014 => _0),
(0x020 => @END),
}
}
struct AbortGuard<'a> {
i2c: &'a I2C,
abort: bool,
}
impl<'a> AbortGuard<'a> {
pub fn new(i2c: &'a I2C) -> Self {
Self { i2c, abort: true }
}
pub fn consume(mut self) {
self.abort = false;
}
}
impl Drop for AbortGuard<'_> {
fn drop(&mut self) {
if self.abort {
block!(self.i2c.finish_transfer(Err(Error::Interrupted)).await).ok();
}
}
}
pub struct I2C {
name: &'static str,
clock: ClockHandle,
irq: IrqHandle,
regs: IrqSafeSpinlock<DeviceMemoryIo<'static, Regs>>,
index: OneTimeInit<u32>,
sr: BitmapEvent<AtomicWaker>,
}
impl Regs {
fn set_clock_rate(&self, input_rate: Hertz, clock_rate: Hertz) -> Result<Hertz, Error> {
let divider = Hertz::divider(input_rate / 5, clock_rate).ok_or(Error::InvalidArgument)?;
if divider == 0 || divider > 0xFFFF {
return Err(Error::InvalidArgument);
}
log::info!("input_rate = {input_rate}, desired = {clock_rate}, divider = {divider}");
let divider = divider - 1;
self.PRER_LO.set(divider & 0xFF);
self.PRER_HI.set(divider >> 8);
let real_rate = input_rate / (5 * (divider + 1));
Ok(real_rate)
}
}
impl I2C {
async fn wait_for_event(&self) -> LocalRegisterCopy<u32, SR::Register> {
let value = self.sr.wait().await;
LocalRegisterCopy::new(value as u32)
}
async fn finish_transfer(&self, status: Result<usize, Error>) -> Result<usize, Error> {
if status == Err(Error::Interrupted) {
log::warn!("{}: transfer aborted", self.name);
let regs = self.regs.lock();
let stopped = {
if regs.CR_SR.matches_all(SR::TIP::CLEAR) {
true
} else {
false
}
};
if !stopped {
regs.CTR.modify(CTR::IEN::SET);
drop(regs);
loop {
let sr = self.wait_for_event().await;
if sr.matches_all(SR::TIP::CLEAR) {
break;
}
let regs = self.regs.lock();
regs.CTR.modify(CTR::IEN::SET); // Reenable IRQs
}
}
}
// Generate stop condition
{
let regs = self.regs.lock();
regs.CR_SR.set(CMD_STOP);
regs.CTR.modify(CTR::IEN::SET); // Reenable IRQs
}
loop {
let sr = self.wait_for_event().await;
if sr.matches_all(SR::BUSY::CLEAR) {
break;
}
let regs = self.regs.lock();
regs.CTR.modify(CTR::IEN::SET); // Reenable IRQs
}
let regs = self.regs.lock();
regs.CTR.modify(CTR::IEN::CLEAR); // Mask IRQs
regs.CR_SR.set(CMD_IACK);
match status {
Ok(count) => Ok(count),
Err(err) => Err(err),
}
}
}
impl Device for I2C {
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
let input_clk = self.clock.rate()?;
let regs = self.regs.lock();
regs.CTR.set(0);
regs.set_clock_rate(input_clk, 100u64.khz())?;
regs.CTR.write(CTR::EN::SET);
regs.CR_SR.set(CMD_IACK);
let index = DEVICE_REGISTRY.i2c.register_bus(self.clone())?;
self.index.init(index);
Ok(())
}
unsafe fn init_irq(self: Arc<Self>) -> Result<(), Error> {
self.irq.register(self.clone())?;
self.irq.enable()?;
Ok(())
}
fn display_name(&self) -> &str {
self.name
}
}
impl InterruptHandler for I2C {
fn handle_irq(self: Arc<Self>, _vector: IrqVector) -> bool {
let regs = self.regs.lock();
let status = regs.CR_SR.extract();
self.sr.signal(status.get() as u64);
regs.CTR.modify(CTR::IEN::CLEAR);
true
}
}
#[async_trait]
impl I2CController for I2C {
fn bus_number(&self) -> u32 {
*self.index.get()
}
fn child_number(&self) -> Option<u32> {
None
}
fn capabilities(&self) -> I2CMasterInfo {
let scl_max = self.clock.rate().expect("Couldn't get input clock rate") / 5;
I2CMasterInfo {
max_speed_hz: scl_max.0 as u32,
supports_10bit_addresses: true,
}
}
async fn i2c_transfer(
&self,
address: I2CAddress,
transfer: &mut I2CTransfer,
) -> Result<usize, Error> {
// TODO validate transfer
let address = address.as_8_bit().ok_or(Error::NotImplemented)?;
let repeat_start = transfer.repeat_start;
let mut nbytes = 0;
let mut error = None;
let mut abort = None;
for (i, message) in transfer.messages.iter_mut().enumerate() {
let (read, buffer_len) = match message {
I2CMessage::Read(buffer) => (true, buffer.len()),
I2CMessage::Write(buffer) => (false, buffer.len()),
};
if i == 0 || repeat_start {
//log::info!("{}: START {:#x} read={}", self.name, address, read);
let read_bit = if read { 1 << 0 } else { 0 << 0 };
let regs = self.regs.lock();
regs.TXR_RXR.set(((address as u32) << 1) | read_bit);
regs.CR_SR.set(CMD_START);
regs.CTR.modify(CTR::IEN::SET);
if abort.is_none() {
abort = Some(AbortGuard::new(self));
}
}
let mut pos = 0;
let mut start = true;
loop {
if pos == buffer_len {
break;
}
let sr = self.wait_for_event().await;
let regs = self.regs.lock();
if sr.matches_all(SR::AL::SET) {
error = Some(Error::HostUnreachable);
break;
}
match message {
I2CMessage::Read(buffer) => {
if pos == 0 && sr.matches_all(SR::RXACK::SET) {
error = Some(Error::HostUnreachable);
break;
}
if start {
regs.CR_SR.set(CMD_READ_ACK);
start = false;
} else if sr.matches_all(SR::TIP::CLEAR) {
let byte = regs.TXR_RXR.get() as u8;
//log::info!("{}: rx {:#x}", self.name, byte);
buffer[pos] = byte;
pos += 1;
nbytes += 1;
if pos >= buffer.len() {
regs.CR_SR.set(CMD_READ_NACK);
} else {
regs.CR_SR.set(CMD_READ_ACK);
}
}
}
I2CMessage::Write(buffer) => {
if sr.matches_all(SR::RXACK::SET) {
error = Some(Error::HostUnreachable);
break;
}
regs.TXR_RXR.set(buffer[pos] as u32);
//log::info!("{}: tx {:#x}", self.name, buffer[pos]);
pos += 1;
nbytes += 1;
regs.CR_SR.set(CMD_WRITE);
}
}
regs.CTR.modify(CTR::IEN::SET);
}
if error.is_some() {
break;
}
{
let regs = self.regs.lock();
if regs.CR_SR.matches_all(SR::TIP::CLEAR) {
regs.CTR.modify(CTR::IEN::SET); // Reenable IRQs
continue;
}
regs.CTR.modify(CTR::IEN::SET); // Reenable IRQs
}
loop {
let sr = self.wait_for_event().await;
if sr.matches_all(SR::TIP::CLEAR) {
break;
}
let regs = self.regs.lock();
regs.CTR.modify(CTR::IEN::SET); // Reenable IRQs
}
}
// Generate stop condition
{
//log::info!("{}: STOP", self.name);
let regs = self.regs.lock();
regs.CR_SR.set(CMD_STOP);
regs.CTR.modify(CTR::IEN::SET); // Reenable IRQs
}
loop {
let sr = self.wait_for_event().await;
if sr.matches_all(SR::BUSY::CLEAR) {
break;
}
let regs = self.regs.lock();
regs.CTR.modify(CTR::IEN::SET); // Reenable IRQs
}
if let Some(abort) = abort.take() {
abort.consume();
}
let regs = self.regs.lock();
regs.CTR.modify(CTR::IEN::CLEAR); // Mask IRQs
regs.CR_SR.set(CMD_IACK);
// log::info!(
// "{}: finish xfer, error={:?}, nbytes={}",
// self.name,
// error,
// nbytes
// );
match error {
Some(err) => Err(err),
None => Ok(nbytes),
}
}
// async fn i2c_read(&self, address: I2CAddress, buffer: &mut [u8]) -> Result<usize, Error> {
// self.start_transfer(address, true)?;
// let mut pos = 0;
// let status = loop {
// if pos >= buffer.len() {
// break Ok(pos);
// }
// let sr = self.wait_for_event().await;
// let regs = self.regs.lock();
// if sr.matches_all(SR::RXACK::SET) || sr.matches_all(SR::AL::SET) {
// break Err(sr);
// }
// };
// self.finish_transfer(status).await
// }
// async fn i2c_write(&self, address: I2CAddress, buffer: &[u8]) -> Result<usize, Error> {
// self.start_transfer(address, false)?;
// let mut pos = 0;
// let status = loop {
// if pos >= buffer.len() {
// break Ok(pos);
// }
// let sr = self.wait_for_event().await;
// let regs = self.regs.lock();
// if sr.matches_all(SR::RXACK::SET) || sr.matches_all(SR::AL::SET) {
// break Err(sr);
// }
// };
// self.finish_transfer(status).await
// }
// fn i2c_read(&self, address: I2CAddress, buffer: &mut [u8]) -> Result<usize, Error> {
// let address = address.as_8_bit().ok_or(Error::NotImplemented)?;
// let regs = self.regs.lock();
// log::info!(":::: start {:#x} read", address);
// regs.poll_wait(SR::BUSY::SET);
// regs.TXR_RXR.set(((address as u32) << 1) | 1);
// regs.CR_SR.set(CMD_START);
// let mut read = 0;
// let mut error = None;
// let mut start = true;
// loop {
// let sr = regs.poll_wait(SR::TIP::SET);
// if read == buffer.len() {
// break;
// }
// if sr.matches_all(SR::AL::SET) {
// error = Some(Error::WouldBlock);
// break;
// }
// if sr.matches_all(SR::RXACK::SET) {
// error = Some(Error::DoesNotExist);
// break;
// }
// if !start {
// let val = regs.TXR_RXR.get() as u8;
// buffer[read] = val;
// read += 1;
// if read >= buffer.len() {
// log::info!(":::: read_ack {:02x}", val);
// regs.CR_SR.set(CMD_READ_NACK);
// } else {
// log::info!(":::: read {:02x}", val);
// regs.CR_SR.set(CMD_READ_ACK);
// }
// } else {
// log::info!(":::: begin read");
// regs.CR_SR.set(CMD_READ);
// start = false;
// }
// }
// log::info!(":::: stop {:#x}", address);
// // Generate stop condition
// regs.CR_SR.set(CMD_STOP);
// regs.poll_wait(SR::BUSY::SET);
// regs.CR_SR.set(CMD_IACK);
// log::info!(":::: xfer finish {error:?}, read {read}");
// if let Some(error) = error {
// Err(error)
// } else {
// Ok(read)
// }
// }
// fn i2c_write(&self, address: I2CAddress, buffer: &[u8]) -> Result<usize, Error> {
// let address = address.as_8_bit().ok_or(Error::NotImplemented)?;
// let regs = self.regs.lock();
// log::info!(":::: start {:#x} write", address);
// regs.poll_wait(SR::BUSY::SET);
// regs.TXR_RXR.set((address as u32) << 1);
// regs.CR_SR.set(CMD_START);
// let mut sent = 0;
// let mut error = None;
// loop {
// let sr = regs.poll_wait(SR::TIP::SET);
// if sent == buffer.len() {
// // Generate stop condition
// regs.CR_SR.set(CMD_STOP);
// break;
// }
// if sr.matches_all(SR::AL::SET) {
// regs.CR_SR.set(CMD_STOP);
// error = Some(Error::WouldBlock);
// break;
// }
// if sr.matches_all(SR::RXACK::SET) {
// // Generate stop condition
// regs.CR_SR.set(CMD_STOP);
// error = Some(Error::DoesNotExist);
// break;
// }
// log::info!(":::: send {:#x}", buffer[sent]);
// regs.TXR_RXR.set(buffer[sent] as u32);
// regs.CR_SR.set(CMD_WRITE);
// sent += 1;
// }
// log::info!(":::: stop {:#x}", address);
// regs.poll_wait(SR::BUSY::SET);
// regs.CR_SR.set(CMD_IACK);
// log::info!(":::: xfer finish {error:?}, sent {sent}");
// if let Some(error) = error {
// Err(error)
// } else {
// Ok(sent)
// }
// }
fn set_speed(&self, speed: Hertz) -> Result<Hertz, Error> {
let input_rate = self.clock.rate()?;
let regs = self.regs.lock();
regs.set_clock_rate(input_rate, speed)
}
}
device_tree_driver! {
compatible: ["sifive,i2c0"],
driver: {
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
let name = node.name().unwrap_or("i2c");
let base = node.map_base(context, 0)?;
let clock = node.clock(0)?;
let irq = node.interrupt(0)?;
let regs = unsafe { DeviceMemoryIo::map(base, Default::default()) }.ok()?;
let i2c = Arc::new(I2C {
name,
regs: IrqSafeSpinlock::new(regs),
clock,
irq,
index: OneTimeInit::new(),
sr: BitmapEvent::new(AtomicWaker::new()),
});
node.make_i2c_controller(i2c.clone());
Some(i2c)
}
}
}
+11
View File
@@ -0,0 +1,11 @@
#![no_std]
extern crate alloc;
mod clock_fu540;
mod clock_fu740;
mod ethernet;
mod i2c;
mod pll;
mod pwm;
mod uart;
+35
View File
@@ -0,0 +1,35 @@
use device_api::clock::Hertz;
use tock_registers::register_bitfields;
#[derive(Debug)]
pub(crate) struct WrpllData {
pub(crate) divr: u32,
pub(crate) divf: u32,
pub(crate) divq: u32,
pub(crate) int_feedback: bool,
}
register_bitfields! {
u32,
pub PllCfg [
PLLR OFFSET(0) NUMBITS(6) [],
PLLF OFFSET(6) NUMBITS(9) [],
PLLQ OFFSET(15) NUMBITS(3) [],
PLLBYPASS OFFSET(24) NUMBITS(1) [],
PLLFSEBYPASS OFFSET(25) NUMBITS(1) [],
PLLLOCK OFFSET(31) NUMBITS(1) [],
]
}
impl WrpllData {
fn fbdiv(&self) -> u32 {
if self.int_feedback { 2 } else { 1 }
}
pub(crate) fn output_rate(&self, input_rate: Hertz) -> Hertz {
let fbdiv = self.fbdiv();
let n = input_rate * fbdiv * (self.divf + 1);
let n = n / (self.divr + 1);
n >> self.divq
}
}
+206
View File
@@ -0,0 +1,206 @@
use alloc::sync::Arc;
use device_api::{
clock::{ClockHandle, Hertz},
device::{Device, DeviceInitContext},
pwm::{PwmController, PwmControllerInfo},
};
use device_tree::driver::{Node, ProbeContext, device_tree_driver};
use libk::{device::manager::DEVICE_REGISTRY, error::Error};
use libk_mm::device::DeviceMemoryIo;
use libk_util::sync::IrqSafeSpinlock;
use tock_registers::{
interfaces::{ReadWriteable, Readable, Writeable},
register_bitfields, register_structs,
registers::ReadWrite,
};
use yggdrasil_abi::time::NANOSECONDS_IN_SECOND;
const CHANNEL_COUNT: usize = 4;
const CMPWIDTH: usize = 16;
const SCALE_MAX: u32 = (1 << 4) - 1;
const MAX_PERIOD_CYCLES: u64 = (1u64 << (CMPWIDTH + 16)) - 1;
const MAX_COMPARE_VALUE: u32 = (1u32 << 16) - 1;
register_bitfields! {
u32,
PwmCfg [
SCALE OFFSET(0) NUMBITS(4) [],
STICKY OFFSET(8) NUMBITS(1) [],
ZEROCMP OFFSET(9) NUMBITS(1) [],
DEGLITCH OFFSET(10) NUMBITS(1) [],
ENALWAYS OFFSET(12) NUMBITS(1) [],
ENONESHOT OFFSET(13) NUMBITS(1) [],
CMP_CENTER OFFSET(16) NUMBITS(4) [],
CMP_GANG OFFSET(24) NUMBITS(4) [],
CMP_IP OFFSET(28) NUMBITS(4) [],
]
}
register_structs! {
Regs {
(0x00 => pwmcfg: ReadWrite<u32, PwmCfg::Register>),
(0x04 => _0),
(0x08 => pwmcount: ReadWrite<u32>),
(0x0C => _1),
(0x10 => pwms: ReadWrite<u32>),
(0x14 => _2),
(0x20 => pwmcmp: [ReadWrite<u32>; CHANNEL_COUNT]),
(0x30 => @END),
}
}
struct Pwm {
name: &'static str,
clock: ClockHandle,
regs: IrqSafeSpinlock<DeviceMemoryIo<'static, Regs>>,
}
impl Regs {
fn ns_to_compare_value(&self, input_clk: Hertz, ns: u64) -> Option<u32> {
let scale = self.pwmcfg.read(PwmCfg::SCALE);
let tick_duration_ns = NANOSECONDS_IN_SECOND / (input_clk.0 >> scale);
let ticks = ns / tick_duration_ns;
if ticks < MAX_COMPARE_VALUE as u64 {
Some(ticks as u32)
} else {
None
}
}
fn compare_value_to_ns(&self, input_clk: Hertz, cval: u32) -> u64 {
let scale = self.pwmcfg.read(PwmCfg::SCALE);
let tick_duration_ns = NANOSECONDS_IN_SECOND / (input_clk.0 >> scale);
tick_duration_ns * cval as u64
}
}
impl Device for Pwm {
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
// pwm counter registers are (cmpwidth + 15) bits wide
self.clock.enable()?;
// Set all channels to max value and channel 0 sets the period
{
let regs = self.regs.lock();
regs.pwmcfg
.write(PwmCfg::ZEROCMP::SET + PwmCfg::ENALWAYS::SET);
for channel in 0..CHANNEL_COUNT {
regs.pwmcmp[channel].set(MAX_COMPARE_VALUE);
}
}
DEVICE_REGISTRY.pwm.register_controller(self)?;
Ok(())
}
fn display_name(&self) -> &str {
self.name
}
}
impl PwmController for Pwm {
fn controller_info(&self) -> Result<PwmControllerInfo, Error> {
Ok(PwmControllerInfo {
channel_count: CHANNEL_COUNT,
per_channel_period: false,
})
}
fn channel_period(&self, _channel: u32) -> Result<u64, Error> {
let input_clk = self.clock.rate()?;
let regs = self.regs.lock();
let compare_value = regs.pwmcmp[0].get();
if compare_value == MAX_COMPARE_VALUE {
return Ok(0);
}
let period_ns = regs.compare_value_to_ns(input_clk, compare_value);
Ok(period_ns)
}
fn set_channel_period(&self, _channel: u32, period: u64) -> Result<u64, Error> {
// Channel 0 sets the period
let input_clk = self.clock.rate()?;
let period_cycles = period * u64::from(input_clk) / NANOSECONDS_IN_SECOND;
let mut prescaler = 0;
if period_cycles > MAX_PERIOD_CYCLES {
return Err(Error::InvalidArgument);
}
while (period_cycles >> prescaler) >= MAX_COMPARE_VALUE as u64 {
prescaler += 1;
}
if prescaler > SCALE_MAX {
log::warn!("Cannot fit {prescaler} into 4-bit prescaler");
return Err(Error::InvalidArgument);
}
let period_ticks = period_cycles >> prescaler;
let regs = self.regs.lock();
regs.pwmcfg
.modify(PwmCfg::SCALE.val(prescaler) + PwmCfg::ZEROCMP::SET);
regs.pwmcmp[0].set(period_ticks as u32);
let tick_duration_ns = NANOSECONDS_IN_SECOND / (input_clk.0 >> prescaler);
let real_period_ns = tick_duration_ns * period_ticks;
Ok(real_period_ns)
}
fn set_duty_cycle(&self, channel: u32, t: u64) -> Result<(), Error> {
// Channel 0 sets the period
if channel == 0 {
return Ok(());
}
let input_clk = self.clock.rate()?;
let regs = self.regs.lock();
let cval = regs
.ns_to_compare_value(input_clk, t)
.ok_or(Error::InvalidArgument)?;
// If the cval exceeds the "period" comparator, the PWM will never fire
let cval = cval.min(regs.pwmcmp[0].get().saturating_sub(1));
regs.pwmcmp[channel as usize].set(cval);
Ok(())
}
fn duty_cycle(&self, channel: u32) -> Result<u64, Error> {
if channel == 0 {
return Ok(0);
}
let input_clk = self.clock.rate()?;
let regs = self.regs.lock();
Ok(regs.compare_value_to_ns(input_clk, regs.pwmcmp[channel as usize].get()))
}
fn stop_channel(&self, channel: u32) -> Result<(), Error> {
if channel == 0 {
return Ok(());
}
self.regs.lock().pwmcmp[channel as usize].set(MAX_COMPARE_VALUE);
Ok(())
}
}
device_tree_driver! {
compatible: ["sifive,pwm0"],
driver: {
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
let name = node.name().unwrap_or("pwm");
let base = node.map_base(context, 0)?;
let clock = node.clock(0)?;
let regs = unsafe { DeviceMemoryIo::map(base, Default::default()) }.ok()?;
let pwm = Arc::new(Pwm {
name,
regs: IrqSafeSpinlock::new(regs),
clock,
});
Some(pwm)
}
}
}
+282
View File
@@ -0,0 +1,282 @@
use core::sync::atomic::{AtomicBool, Ordering};
use alloc::sync::Arc;
use device_api::{
clock::{ClockHandle, Hertz},
device::{Device, DeviceInitContext},
interrupt::{InterruptHandler, IrqHandle, IrqPriority, IrqVector},
};
use device_tree::driver::{Node, ProbeContext, device_tree_driver};
use libk::{
debug::{self, DebugSink},
device::manager::DEVICE_REGISTRY,
error::Error,
vfs::{Terminal, TerminalInput, TerminalOutput},
};
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIo};
use libk_util::{OneTimeInit, sync::IrqSafeSpinlock};
use tock_registers::{
interfaces::{Readable, Writeable},
register_bitfields, register_structs,
registers::{ReadOnly, ReadWrite},
};
use yggdrasil_abi::{
io::{TerminalOptions, TerminalOutputOptions},
process::ProcessId,
};
register_bitfields! {
u32,
txctrl [
txen OFFSET(0) NUMBITS(1) [],
nstop OFFSET(1) NUMBITS(1) [
One = 0,
Two = 1
],
txcnt OFFSET(16) NUMBITS(3) [],
],
rxctrl [
rxen OFFSET(0) NUMBITS(1) [],
rxcnt OFFSET(16) NUMBITS(3) [],
],
interrupt [
txwm OFFSET(0) NUMBITS(1) [],
rxwm OFFSET(1) NUMBITS(1) [],
],
}
register_structs! {
Regs {
(0x00 => txdata: ReadWrite<u32>),
(0x04 => rxdata: ReadOnly<u32>),
(0x08 => txctrl: ReadWrite<u32, txctrl::Register>),
(0x0C => rxctrl: ReadWrite<u32, rxctrl::Register>),
(0x10 => ie: ReadWrite<u32, interrupt::Register>),
(0x14 => ip: ReadOnly<u32, interrupt::Register>),
(0x18 => div: ReadWrite<u32>),
(0x1C => _0),
(0x20 => @END),
}
}
struct Inner {
clock: ClockHandle,
regs: IrqSafeSpinlock<DeviceMemoryIo<'static, Regs>>,
active: AtomicBool,
}
struct Uart {
name: &'static str,
irq: IrqHandle,
clock: ClockHandle,
base: PhysicalAddress,
inner: OneTimeInit<Arc<Terminal<Inner>>>,
}
impl Regs {
fn set_baud_rate(&self, input_clk: Hertz, baud: u32) -> Result<(), Error> {
// Check: given f_in=130MHz, baud=115200 should give div = 0x468
let div = Hertz::divider(input_clk, Hertz::from(baud)).ok_or(Error::InvalidArgument)?;
if div >= (1 << 20) || div == 0 {
return Err(Error::InvalidArgument);
}
let div = div - 1;
self.div.set(div);
Ok(())
}
fn baud_rate(&self, input_clk: Hertz) -> u32 {
let div = self.div.get() + 1;
(input_clk / div).0 as u32
}
#[inline]
fn is_txfifo_full(&self) -> bool {
self.txdata.get() & (1 << 31) != 0
}
fn read(&self) -> Option<u8> {
let val = self.rxdata.get();
if val & (1 << 31) == 0 {
Some(val as u8)
} else {
None
}
}
fn write(&self, byte: u8) {
while self.is_txfifo_full() {
for _ in 0..10000 {
core::hint::spin_loop();
}
}
self.txdata.set(byte as u32);
}
}
impl Device for Uart {
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
let regs = unsafe { DeviceMemoryIo::<Regs>::map(self.base, Default::default()) }?;
self.clock.enable()?;
let input_clk = self.clock.rate()?;
log::info!("input_clk = {input_clk}");
{
let _guard = debug::MuteGuard::acquire();
regs.txctrl.write(txctrl::txen::CLEAR);
regs.rxctrl.write(rxctrl::rxen::CLEAR);
regs.set_baud_rate(input_clk, 115200)?;
regs.txctrl.write(txctrl::txen::SET + txctrl::txcnt.val(3));
}
// Drain Rx FIFO
while regs.read().is_some() {
core::hint::spin_loop();
}
let input = TerminalInput::with_capacity(64)?;
let output = Inner {
regs: IrqSafeSpinlock::new(regs),
clock: self.clock.clone(),
active: AtomicBool::new(false),
};
let terminal = self.inner.init(Arc::new(Terminal::from_parts(
TerminalOptions::const_default(),
input,
output,
)));
DEVICE_REGISTRY
.serial_terminal
.register(terminal.clone(), Some(self.clone()))
.ok();
Ok(())
}
unsafe fn init_irq(self: Arc<Self>) -> Result<(), Error> {
self.irq
.register_with_priority(IrqPriority::Low, self.clone())?;
self.irq.enable()?;
Ok(())
}
fn display_name(&self) -> &str {
self.name
}
}
impl InterruptHandler for Uart {
fn handle_irq(self: Arc<Self>, _vector: IrqVector) -> bool {
let terminal = self.inner.get();
let regs = terminal.output().regs.lock();
let ip = regs.ip.extract();
if ip.get() == 0 {
return false;
}
let mut count = 0;
let mut bytes = [0; 32];
// if ip.matches_all(interrupt::rxwm::SET) {
while let Some(byte) = regs.read() {
bytes[count] = byte;
count += 1;
}
// }
drop(regs);
for &byte in &bytes[..count] {
if byte != 0 {
terminal.write_to_input(byte);
}
}
true
}
}
impl DebugSink for Uart {
fn putc(&self, c: u8) -> Result<(), Error> {
self.inner.get().putc_to_output(c)
}
fn puts(&self, s: &str) -> Result<(), Error> {
self.inner.get().write_to_output(s.as_bytes())?;
Ok(())
}
fn supports_control_sequences(&self) -> bool {
true
}
}
impl TerminalOutput for Inner {
fn open(&self, _pid: ProcessId) -> Result<(), Error> {
if !self.active.swap(true, Ordering::Acquire) {
log::info!("sifive-uart: start");
let regs = self.regs.lock();
regs.rxctrl.write(rxctrl::rxen::SET + rxctrl::rxcnt.val(0));
// Drain Rx FIFO
while regs.read().is_some() {
core::hint::spin_loop();
}
regs.ie.write(interrupt::rxwm::SET);
}
Ok(())
}
fn write(&self, byte: u8, options: &TerminalOutputOptions) -> Result<(), Error> {
let regs = self.regs.lock();
if byte == b'\n' && options.contains(TerminalOutputOptions::NL_TO_CRNL) {
regs.write(b'\r');
}
regs.write(byte);
Ok(())
}
fn write_multiple(
&self,
bytes: &[u8],
options: &TerminalOutputOptions,
) -> Result<usize, Error> {
let regs = self.regs.lock();
for &byte in bytes {
if byte == b'\n' && options.contains(TerminalOutputOptions::NL_TO_CRNL) {
regs.write(b'\r');
}
regs.write(byte);
}
Ok(bytes.len())
}
fn baud_rate(&self) -> u32 {
let input_clk = self.clock.rate().unwrap();
self.regs.lock().baud_rate(input_clk)
}
fn set_baud_rate(&self, baud: u32) -> Result<(), Error> {
let input_clk = self.clock.rate().unwrap();
self.regs.lock().set_baud_rate(input_clk, baud)
}
}
device_tree_driver! {
compatible: ["sifive,uart0"],
driver: {
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
let name = node.name().unwrap_or("uart");
let base = node.map_base(context, 0)?;
let clock = node.clock(0)?;
let irq = node.interrupt(0)?;
let uart = Arc::new(Uart {
name,
irq,
clock,
base,
inner: OneTimeInit::new()
});
Some(uart)
}
}
}
+24 -6
View File
@@ -5,7 +5,7 @@ use device_api::{
device::Device,
interrupt::{
ExternalInterruptController, InterruptAffinity, InterruptHandler, Irq, IrqOptions,
MessageInterruptController, MsiInfo,
IrqPriority, MessageInterruptController, MsiInfo,
},
};
use libk::device::external_interrupt_controller;
@@ -215,9 +215,10 @@ impl PciDeviceInfo {
}
}
pub fn map_interrupt(
pub fn map_interrupt_with_priority(
&self,
affinity: InterruptAffinity,
priority: IrqPriority,
handler: Arc<dyn InterruptHandler>,
) -> Result<Option<MsiInfo>, Error> {
let mut irq = self.interrupt_config.get().write();
@@ -238,17 +239,25 @@ impl PciDeviceInfo {
Ok(Some(info[0]))
}
ConfiguredInterruptMode::LegacyPin(intc, pin) => {
self.try_map_legacy(intc.as_ref(), *pin, handler)?;
self.try_map_legacy(intc.as_ref(), *pin, priority, handler)?;
Ok(None)
}
ConfiguredInterruptMode::LegacyLine(intc, irq) => {
self.try_map_legacy_line(intc.as_ref(), *irq, handler)?;
self.try_map_legacy_line(intc.as_ref(), *irq, priority, handler)?;
Ok(None)
}
ConfiguredInterruptMode::None => Err(Error::InvalidOperation),
}
}
pub fn map_interrupt(
&self,
affinity: InterruptAffinity,
handler: Arc<dyn InterruptHandler>,
) -> Result<Option<MsiInfo>, Error> {
self.map_interrupt_with_priority(affinity, IrqPriority::Normal, handler)
}
pub fn map_interrupt_multiple(
&self,
vector_range: Range<usize>,
@@ -271,6 +280,7 @@ impl PciDeviceInfo {
&self,
intc: &dyn ExternalInterruptController,
pin: PciInterruptPin,
priority: IrqPriority,
handler: Arc<dyn InterruptHandler>,
) -> Result<(), Error> {
let src = PciInterrupt {
@@ -280,7 +290,7 @@ impl PciDeviceInfo {
let route = self
.segment
.irq_translation_map
.map_interrupt(&src)
.map_interrupt(&src, priority)
.inspect_err(|e| log::warn!("Could not map PCI IRQ {pin:?}: {e:?}"))?;
log::debug!(
@@ -299,12 +309,20 @@ impl PciDeviceInfo {
&self,
intc: &dyn ExternalInterruptController,
line: u8,
priority: IrqPriority,
handler: Arc<dyn InterruptHandler>,
) -> Result<(), Error> {
log::debug!("PCI {} -> IRQ#{}", self.address, line);
let irq = Irq::External(line as u32);
intc.register_irq(irq, Default::default(), handler)?;
intc.register_irq(
irq,
IrqOptions {
priority,
..Default::default()
},
handler,
)?;
intc.enable_irq(irq)
}
}
+11 -3
View File
@@ -1,7 +1,7 @@
use core::fmt;
use alloc::{collections::btree_map::BTreeMap, sync::Arc, vec::Vec};
use device_api::interrupt::MessageInterruptController;
use device_api::interrupt::{IrqPriority, MessageInterruptController};
use libk::error::Error;
use crate::{
@@ -36,7 +36,11 @@ pub enum PciMsiMap {
}
impl PciInterruptMap {
pub fn map_interrupt(&self, interrupt: &PciInterrupt) -> Result<PciInterruptRoute, Error> {
pub fn map_interrupt(
&self,
interrupt: &PciInterrupt,
#[allow(unused)] priority: IrqPriority,
) -> Result<PciInterruptRoute, Error> {
match self {
Self::Fixed(map) => map.get(interrupt).cloned().ok_or(Error::DoesNotExist),
#[cfg(any(target_arch = "x86_64", rust_analyzer))]
@@ -77,7 +81,11 @@ impl PciInterruptMap {
};
Ok(PciInterruptRoute {
options: IrqOptions { trigger, level },
options: IrqOptions {
trigger,
level,
priority,
},
number: aml_route.irq,
})
}
+1 -1
View File
@@ -1,7 +1,7 @@
[package]
name = "ygg_driver_usb"
version = "0.1.0"
edition = "2021"
edition = "2024"
authors = ["Mark Poliakov <mark@alnyan.me>"]
[dependencies]
+11 -2
View File
@@ -53,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
}
}
@@ -377,8 +377,8 @@ impl UsbDeviceDriver for UsbHubDriver {
"USB Hub"
}
fn probe(&self, class: u8, subclass: u8, protocol: u8) -> bool {
let _ = protocol;
fn probe(&self, class: u8, subclass: u8, protocol: u8, vid: u16, pid: u16) -> bool {
let _ = (protocol, vid, pid);
class == 0x09 && subclass == 0x00
}
}
@@ -12,13 +12,14 @@ use crate::{
mod hid;
mod hub;
mod mass_storage;
mod serial;
#[async_trait]
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: u8, subclass: u8, protocol: u8) -> bool;
fn probe(&self, class: u8, subclass: u8, protocol: u8, vid: u16, pid: u16) -> bool;
}
#[async_trait]
@@ -33,9 +34,11 @@ async fn spawn_device_driver(device: Arc<UsbDeviceAccess>) -> Result<bool, UsbEr
let class = device.device_descriptor.device_class;
let subclass = device.device_descriptor.device_subclass;
let protocol = device.device_descriptor.device_protocol;
let vid = device.device_descriptor.id_vendor;
let pid = device.device_descriptor.id_product;
let Some(driver) = USB_DEVICE_DRIVERS.read().iter().find_map(|driver| {
driver
.probe(class, subclass, protocol)
.probe(class, subclass, protocol, vid, pid)
.then(|| driver.clone())
}) else {
return Ok(false);
@@ -127,6 +130,7 @@ pub fn register_interface_driver(driver: Arc<dyn UsbInterfaceDriver + 'static>)
pub fn register_default_class_drivers() {
register_device_driver(Arc::new(hub::UsbHubDriver));
register_device_driver(Arc::new(serial::FT232Driver));
register_interface_driver(Arc::new(hid::UsbHidKeyboardDriver));
register_interface_driver(Arc::new(hid::UsbHidMouseDriver));
register_interface_driver(Arc::new(mass_storage::UsbMassStorageDriverBulkOnly));
@@ -0,0 +1,311 @@
use core::sync::atomic::{AtomicBool, Ordering};
use alloc::{boxed::Box, collections::btree_map::BTreeMap, sync::Arc};
use async_trait::async_trait;
use libk::{
block,
error::Error,
fs::devfs,
vfs::{Terminal, TerminalInput, TerminalOutput},
};
use libk_util::sync::{IrqSafeSpinlock, spin_rwlock::IrqSafeRwLock};
use yggdrasil_abi::{
io::{FileMode, TerminalOptions, TerminalOutputOptions},
process::ProcessId,
};
use crate::{
class_driver::UsbDeviceDriver,
communication::UsbDirection,
device::{UsbDeviceAccess, UsbDeviceDetachHandler},
error::UsbError,
info::UsbEndpointType,
pipe::{control::ControlTransferSetup, normal::UsbBulkOutPipeAccess},
};
pub struct FT232Driver;
struct FT232Device {
device: Arc<UsbDeviceAccess>,
bulk_out: UsbBulkOutPipeAccess,
baud_rate: IrqSafeRwLock<u32>,
}
pub struct UsbSerialDeviceWrapper {
device: Arc<dyn UsbSerialDevice>,
index: u32,
disconnected: AtomicBool,
}
#[async_trait]
pub trait UsbSerialDevice: Send + Sync + 'static {
async fn setup(&self) -> Result<(), UsbError>;
async fn set_baud_rate(&self, baud: u32) -> Result<(), UsbError>;
fn baud_rate(&self) -> u32;
async fn write(&self, buffer: &[u8], options: TerminalOutputOptions)
-> Result<usize, UsbError>;
fn display_name(&self) -> &str;
fn device(&self) -> &Arc<UsbDeviceAccess>;
}
#[async_trait]
impl UsbDeviceDetachHandler for UsbSerialDeviceWrapper {
async fn handle_device_detach(&self) {
self.disconnected.store(true, Ordering::Release);
log::info!("USB serial #{} disconnected", self.index);
remove_usb_serial(self.index);
}
}
impl TerminalOutput for UsbSerialDeviceWrapper {
fn write(&self, byte: u8, options: &TerminalOutputOptions) -> Result<(), Error> {
self.write_multiple(&[byte], options)?;
Ok(())
}
fn write_multiple(
&self,
bytes: &[u8],
options: &TerminalOutputOptions,
) -> Result<usize, Error> {
if self.disconnected.load(Ordering::Acquire) {
return Err(Error::InvalidOperation);
}
let result = block!(self.device.write(bytes, *options).await)?;
match result {
Ok(len) => Ok(len),
Err(UsbError::MemoryError(err) | UsbError::SystemError(err)) => Err(err),
Err(error) => {
log::warn!("{}: write error: {:?}", self.device.display_name(), error);
Err(Error::InvalidOperation)
}
}
}
fn baud_rate(&self) -> u32 {
self.device.baud_rate()
}
fn set_baud_rate(&self, baud: u32) -> Result<(), Error> {
if self.disconnected.load(Ordering::Acquire) {
return Err(Error::InvalidOperation);
}
let result = block!(self.device.set_baud_rate(baud).await)?;
match result {
Ok(()) => Ok(()),
Err(UsbError::MemoryError(err) | UsbError::SystemError(err)) => Err(err),
Err(error) => {
log::warn!(
"{}: baud rate set error: {:?}",
self.device.display_name(),
error
);
Err(Error::InvalidOperation)
}
}
}
fn open(&self, pid: ProcessId) -> Result<(), Error> {
if self.disconnected.load(Ordering::Acquire) {
return Err(Error::InvalidOperation);
}
let _ = pid;
let result = block! { self.device.setup().await }?;
match result {
Ok(()) => Ok(()),
Err(UsbError::MemoryError(err) | UsbError::SystemError(err)) => Err(err),
Err(error) => {
log::warn!("{}: setup error: {:?}", self.device.display_name(), error);
Err(Error::InvalidOperation)
}
}
}
}
impl FT232Device {
const FTDI_RESET: u8 = 0;
const FTDI_SET_BAUD_RATE: u8 = 3;
async fn ftdi_reset(&self, port: u16) -> Result<(), UsbError> {
self.device
.control_pipe()
.control_transfer(ControlTransferSetup {
bm_request_type: 0b01000000,
b_request: Self::FTDI_RESET,
w_value: 0,
w_index: port,
w_length: 0,
})
.await
}
async fn ftdi_set_baud_rate(&self, port: u16, baud: u32) -> Result<(), UsbError> {
let w_value = match baud {
300 => 0x2710,
600 => 0x1388,
1200 => 0x09C4,
2400 => 0x04E2,
4800 => 0x0271,
9600 => 0x4138,
19200 => 0x809C,
38400 => 0xC04E,
57600 => 0x0034,
115200 => 0x001A,
230400 => 0x000D,
460800 => 0x4006,
921600 => 0x8003,
_ => {
log::warn!("ft232: unsupported baud rate {baud}");
return Err(UsbError::InvalidConfiguration);
}
};
self.device
.control_pipe()
.control_transfer(ControlTransferSetup {
bm_request_type: 0b01000000,
b_request: Self::FTDI_SET_BAUD_RATE,
w_value,
w_index: port,
w_length: 0,
})
.await
}
}
#[async_trait]
impl UsbSerialDevice for FT232Device {
async fn setup(&self) -> Result<(), UsbError> {
log::info!("ft232: setup");
Ok(())
}
async fn set_baud_rate(&self, baud: u32) -> Result<(), UsbError> {
self.ftdi_set_baud_rate(0, baud).await?;
// *self.baud_rate.write() = baud;
log::info!("ft232: set baud rate {baud}");
Ok(())
}
fn baud_rate(&self) -> u32 {
*self.baud_rate.read()
}
async fn write(
&self,
buffer: &[u8],
options: TerminalOutputOptions,
) -> Result<usize, UsbError> {
if options.contains(TerminalOutputOptions::NL_TO_CRNL) {
for &byte in buffer {
if byte == b'\n' {
self.bulk_out.write(b"\r").await?;
}
self.bulk_out.write(&[byte]).await?;
}
Ok(buffer.len())
} else {
self.bulk_out.write(buffer).await
}
}
fn display_name(&self) -> &str {
"FT232 Serial Converter"
}
fn device(&self) -> &Arc<UsbDeviceAccess> {
&self.device
}
}
#[async_trait]
impl UsbDeviceDriver for FT232Driver {
async fn run(self: Arc<Self>, device: Arc<UsbDeviceAccess>) -> Result<(), UsbError> {
let interface = device.interface(0);
let endpoints = interface.endpoints();
let bulk_in = endpoints
.iter()
.find(|ep| ep.ty == UsbEndpointType::Bulk && ep.direction == UsbDirection::In)
.ok_or(UsbError::InvalidConfiguration)?;
let bulk_out = endpoints
.iter()
.find(|ep| ep.ty == UsbEndpointType::Bulk && ep.direction == UsbDirection::Out)
.ok_or(UsbError::InvalidConfiguration)?;
let bulk_in = device
.open_bulk_in_pipe(bulk_in.number, bulk_in.max_packet_size as _)
.await?;
let bulk_out = device
.open_bulk_out_pipe(bulk_out.number, bulk_out.max_packet_size as _)
.await?;
let ft232 = Arc::new(FT232Device {
device,
bulk_out,
baud_rate: IrqSafeRwLock::new(115200),
});
ft232.ftdi_reset(0).await?;
let terminal = register_usb_serial(ft232)?;
let mut buffer = [0; 3];
loop {
let len = bulk_in.read(&mut buffer).await?;
if len < 2 {
continue;
}
for &byte in &buffer[2..len] {
terminal.write_to_input(byte);
}
}
}
fn name(&self) -> &'static str {
"FT232 Serial Converter"
}
fn probe(&self, class: u8, subclass: u8, protocol: u8, vid: u16, pid: u16) -> bool {
let _ = (class, subclass, protocol);
vid == 0x0403 && pid == 0x6001
}
}
static USB_SERIALS: IrqSafeSpinlock<BTreeMap<u32, Arc<Terminal<Arc<UsbSerialDeviceWrapper>>>>> =
IrqSafeSpinlock::new(BTreeMap::new());
fn register_usb_serial(
serial: Arc<dyn UsbSerialDevice>,
) -> Result<Arc<Terminal<Arc<UsbSerialDeviceWrapper>>>, UsbError> {
let mut serials = USB_SERIALS.lock();
for i in 0..64 {
if serials.contains_key(&i) {
continue;
}
let wrapper = Arc::new(UsbSerialDeviceWrapper {
device: serial,
index: i,
disconnected: AtomicBool::new(false),
});
wrapper.device.device().set_detach_handler(wrapper.clone());
let input = TerminalInput::with_capacity(64).expect("Couldn't allocate input buffer");
let terminal = Arc::new(Terminal::from_parts(
TerminalOptions::const_default(),
input,
wrapper,
));
serials.insert(i, terminal.clone());
let name = alloc::format!("ttyUSB{i}");
devfs::add_named_char_device(terminal.clone(), name.clone(), FileMode::new(0o600)).ok();
return Ok(terminal);
}
Err(UsbError::DriverError)
}
fn remove_usb_serial(index: u32) {
let serial = USB_SERIALS.lock().remove(&index);
if serial.is_none() {
log::warn!("usb-serial #{index} doesn't exist in the table");
}
let name = alloc::format!("ttyUSB{index}");
devfs::remove_node(name).ok();
}
-1
View File
@@ -4,7 +4,6 @@
generic_const_exprs,
iter_array_chunks,
maybe_uninit_as_bytes,
maybe_uninit_fill,
maybe_uninit_array_assume_init
)]
+1 -1
View File
@@ -1,7 +1,7 @@
[package]
name = "ext2"
version = "0.1.0"
edition = "2021"
edition = "2024"
authors = ["Mark Poliakov <mark@alnyan.me>"]
[dependencies]
-1
View File
@@ -1,4 +1,3 @@
#![feature(if_let_guard, impl_trait_in_assoc_type)]
#![cfg_attr(not(test), no_std)]
#![allow(clippy::new_ret_no_self)]
+1 -1
View File
@@ -1,7 +1,7 @@
[package]
name = "kernel-fs"
version = "0.1.0"
edition = "2021"
edition = "2024"
authors = ["Mark Poliakov <mark@alnyan.me>"]
[dependencies]
+1 -1
View File
@@ -1,7 +1,7 @@
[package]
name = "memfs"
version = "0.1.0"
edition = "2021"
edition = "2024"
authors = ["Mark Poliakov <mark@alnyan.me>"]
[dependencies]
+2 -2
View File
@@ -159,7 +159,7 @@ impl<'a, A: BlockAllocator> BlockRaw<'a, A> {
panic!("Null block dereference");
}
&mut *(self.inner.ptr as *mut _)
unsafe { &mut *(self.inner.ptr as *mut _) }
}
#[inline]
@@ -197,7 +197,7 @@ impl<A: BlockAllocator> BlockData<'_, A> {
pub unsafe fn copy_on_write(address: usize) -> Self {
Self {
inner: BlockRaw {
inner: BlockRef::copy_on_write(address),
inner: unsafe { BlockRef::copy_on_write(address) },
},
}
}
+1 -1
View File
@@ -1,7 +1,7 @@
[package]
name = "ygg_driver_input"
version = "0.1.0"
edition = "2021"
edition = "2024"
[dependencies]
yggdrasil-abi.workspace = true
+1
View File
@@ -8,6 +8,7 @@ yggdrasil-abi = { workspace = true, features = ["serde_kernel", "bytemuck"] }
libk-mm.workspace = true
libk-util.workspace = true
libk.workspace = true
device-api.workspace = true
kernel-fs = { path = "../../fs/kernel-fs" }
-1
View File
@@ -1,4 +1,3 @@
#![feature(map_try_insert)]
#![allow(clippy::type_complexity, clippy::new_without_default)]
#![no_std]
+5
View File
@@ -0,0 +1,5 @@
pub use queue::*;
pub use reassembler::*;
mod queue;
mod reassembler;
+290
View File
@@ -0,0 +1,290 @@
use core::mem::{self, MaybeUninit};
use alloc::vec::Vec;
use device_api::dma::DmaAllocator;
use libk::{
dma::{BusAddress, DmaBuffer},
error::Error,
};
use libk_util::{sync::IrqSafeSpinlock, waker::QueueWaker};
pub trait GenericTxDescriptor: Sized {
const EMPTY: Self;
const EMPTY_LAST: Self = Self::EMPTY;
fn consume(&mut self) -> Option<Result<(), Error>>;
fn setup_tx(
&mut self,
buffer_address: BusAddress,
size: usize,
index: usize,
capacity: usize,
) -> Result<(), Error>;
}
pub trait GenericRxDescriptor: Sized {
// TODO Rx flags (short, runt, etc)
fn consume(&mut self) -> Option<Result<usize, Error>>;
fn setup_rx(buffer_address: BusAddress, buffer_size: usize, last: bool) -> Result<Self, Error>;
}
struct TxInFlight {
#[allow(unused)]
buffer: DmaBuffer<[u8]>,
}
struct TxRingInner<T: GenericTxDescriptor> {
entries: DmaBuffer<[T]>,
in_flight: Vec<Option<TxInFlight>>,
wr: usize,
rd: usize,
}
struct RxRingInner<T: GenericRxDescriptor> {
entries: DmaBuffer<[T]>,
buffers: Vec<DmaBuffer<[MaybeUninit<u8>]>>,
rd: usize,
}
pub struct GenericTxRing<T: GenericTxDescriptor> {
inner: IrqSafeSpinlock<TxRingInner<T>>,
buffer_base: BusAddress,
capacity: usize,
free_notify: QueueWaker,
}
pub struct GenericRxRing<T: GenericRxDescriptor> {
inner: IrqSafeSpinlock<RxRingInner<T>>,
rx_buffer_size: usize,
buffer_base: BusAddress,
capacity: usize,
}
pub struct GenericQueue<Tx: GenericTxDescriptor, Rx: GenericRxDescriptor> {
pub tx_ring: GenericTxRing<Tx>,
pub rx_ring: GenericRxRing<Rx>,
}
impl<Tx: GenericTxDescriptor, Rx: GenericRxDescriptor> GenericQueue<Tx, Rx> {
pub fn with_capacity(
dma: &dyn DmaAllocator,
tx_capacity: usize,
rx_capacity: usize,
rx_buffer_size: usize,
) -> Result<Self, Error> {
let tx_ring = GenericTxRing::with_capacity(dma, tx_capacity)?;
let rx_ring = GenericRxRing::with_capacity(dma, rx_capacity, rx_buffer_size)?;
Ok(Self { tx_ring, rx_ring })
}
pub fn tx_buffer_base(&self) -> BusAddress {
self.tx_ring.buffer_base
}
pub fn rx_buffer_base(&self) -> BusAddress {
self.rx_ring.buffer_base
}
pub fn try_push_xmit(&self, frame: DmaBuffer<[u8]>) -> Result<usize, Error> {
self.tx_ring.try_push_xmit(frame)
}
pub fn drop_tx_until(&self, head: usize) {
self.tx_ring.drop_until(head);
}
pub fn consume_tx(&self) -> Result<usize, Error> {
self.tx_ring.consume()
}
pub fn consume_rx<F: Fn(DmaBuffer<[u8]>, usize)>(
&self,
dma: &dyn DmaAllocator,
head: Option<usize>,
handler: F,
) -> Result<usize, Error> {
self.rx_ring.consume(dma, head, handler)
}
}
impl<T: GenericTxDescriptor> GenericTxRing<T> {
pub fn with_capacity(dma: &dyn DmaAllocator, capacity: usize) -> Result<Self, Error> {
let entries = DmaBuffer::new_slice_with(
dma,
|i| {
if i == capacity - 1 {
T::EMPTY_LAST
} else {
T::EMPTY
}
},
capacity,
)?;
let in_flight = (0..capacity).map(|_| None).collect();
let buffer_base = entries.bus_address();
let inner = TxRingInner {
entries,
in_flight,
wr: 0,
rd: 0,
};
Ok(Self {
inner: IrqSafeSpinlock::new(inner),
buffer_base,
capacity,
free_notify: QueueWaker::new(),
})
}
pub fn base(&self) -> BusAddress {
self.buffer_base
}
pub fn capacity(&self) -> usize {
self.capacity
}
pub async fn push_xmit(&self, _frame: DmaBuffer<[u8]>) -> Result<usize, Error> {
todo!()
}
pub fn try_push_xmit(&self, frame: DmaBuffer<[u8]>) -> Result<usize, Error> {
let mut inner = self.inner.lock();
if inner.wr.wrapping_add(1) % self.capacity == inner.rd % self.capacity {
return Err(Error::WouldBlock);
}
let buffer_address = frame.bus_address();
let buffer_len = frame.len();
let index = inner.wr % self.capacity;
frame.cache_flush_all(true);
inner.entries[index].setup_tx(buffer_address, buffer_len, index, self.capacity)?;
inner.entries.cache_flush_element(index, true);
inner.in_flight[index] = Some(TxInFlight { buffer: frame });
inner.wr = inner.wr.wrapping_add(1);
Ok(inner.wr % self.capacity)
}
pub fn drop_until(&self, head: usize) {
self.inner.lock().rd = head % self.capacity;
}
pub fn consume(&self) -> Result<usize, Error> {
let mut count = 0;
{
let mut inner = self.inner.lock();
while inner.rd != inner.wr {
let index = inner.rd % self.capacity;
inner.entries.cache_flush_element(index, false);
let entry = &mut inner.entries[index];
if let Some(_status) = entry.consume() {
let _ = inner.in_flight[index].take().unwrap();
inner.rd = inner.rd.wrapping_add(1);
count += 1;
} else {
break;
}
}
}
if count != 0 {
self.free_notify.wake_all();
}
Ok(count)
}
}
impl<T: GenericRxDescriptor> GenericRxRing<T> {
pub fn with_capacity(
dma: &dyn DmaAllocator,
capacity: usize,
rx_buffer_size: usize,
) -> Result<Self, Error> {
let buffers = (0..capacity)
.map(|_| DmaBuffer::new_uninit_slice(dma, rx_buffer_size))
.collect::<Result<Vec<_>, _>>()?;
let entries = DmaBuffer::new_slice_with(
dma,
|i| {
let buffer = buffers[i].bus_address();
let last = i == capacity - 1;
T::setup_rx(buffer, rx_buffer_size, last).expect("Rx buffer descriptor setup error")
},
capacity,
)?;
let buffer_base = entries.bus_address();
let inner = RxRingInner {
entries,
buffers,
rd: 0,
};
Ok(Self {
inner: IrqSafeSpinlock::new(inner),
rx_buffer_size,
buffer_base,
capacity,
})
}
pub fn base(&self) -> BusAddress {
self.buffer_base
}
pub fn capacity(&self) -> usize {
self.capacity
}
pub fn consume<F: Fn(DmaBuffer<[u8]>, usize)>(
&self,
dma: &dyn DmaAllocator,
head: Option<usize>,
handler: F,
) -> Result<usize, Error> {
let mut inner = self.inner.lock();
while head.is_none_or(|tail| tail != inner.rd) {
let index = inner.rd % self.capacity;
inner.entries.cache_flush_element(index, false);
if let Some(status) = inner.entries[index].consume() {
// Grab the current buffer (the one just written to by the DMA), replace it
// with the newly allocated one, and mark the descriptor as DMA-owned again
let new_buffer = DmaBuffer::new_uninit_slice(dma, self.rx_buffer_size)?;
let new_buffer_address = new_buffer.bus_address();
let buffer = mem::replace(&mut inner.buffers[index], new_buffer);
let old_buffer_address = buffer.bus_address();
let buffer = unsafe { DmaBuffer::assume_init_slice(buffer) };
match status {
Ok(len) => {
handler(buffer, len);
}
Err(_error) => {
log::warn!("Drop error packet {old_buffer_address:#x}");
}
}
inner.entries[index] = T::setup_rx(
new_buffer_address,
self.rx_buffer_size,
index == self.capacity - 1,
)?;
inner.rd = inner.rd.wrapping_add(1);
} else {
break;
}
}
Ok(inner.rd)
}
}
+1
View File
@@ -14,4 +14,5 @@ ygg_driver_pci.path = "../../bus/pci"
ygg_driver_net_core.path = "../core"
log.workspace = true
futures-util.workspace = true
tock-registers.workspace = true
+86 -64
View File
@@ -8,17 +8,16 @@ use device_api::{
dma::DmaAllocator,
interrupt::{InterruptAffinity, InterruptHandler, IrqVector},
};
use libk::{dma::DmaBuffer, error::Error};
use libk_util::{
OneTimeInit,
sync::{IrqSafeSpinlock, Spinlock},
};
use regs::{ICR, Regs};
use ring::{RxRing, TxRing};
use futures_util::task::AtomicWaker;
use libk::{dma::DmaBuffer, error::Error, task::runtime};
use libk_util::{OneTimeInit, event::BitmapEvent, sync::IrqSafeSpinlock};
use regs::Regs;
use tock_registers::{LocalRegisterCopy, fields::FieldValue};
use ygg_driver_net_core::{
RxPacket,
interface::{NetworkDevice, NetworkInterfaceType},
register_interface,
util::GenericQueue,
};
use ygg_driver_pci::{
PciBaseAddress, PciConfigurationSpace,
@@ -27,7 +26,10 @@ use ygg_driver_pci::{
};
use yggdrasil_abi::net::{MacAddress, link::LinkState};
use crate::regs::Revision;
use crate::{
regs::Revision,
ring::{RxDescriptor, TxDescriptor},
};
extern crate alloc;
@@ -41,8 +43,8 @@ struct Igbe {
pci: PciDeviceInfo,
mac: OneTimeInit<MacAddress>,
rx_ring: OneTimeInit<Spinlock<RxRing>>,
tx_ring: OneTimeInit<IrqSafeSpinlock<TxRing>>,
queue: OneTimeInit<GenericQueue<TxDescriptor, RxDescriptor>>,
softirq: BitmapEvent<AtomicWaker>,
nic: OneTimeInit<u32>,
}
@@ -55,11 +57,55 @@ impl Igbe {
mac: OneTimeInit::new(),
regs: IrqSafeSpinlock::new(regs),
rx_ring: OneTimeInit::new(),
tx_ring: OneTimeInit::new(),
queue: OneTimeInit::new(),
softirq: BitmapEvent::new(AtomicWaker::new()),
nic: OneTimeInit::new(),
}
}
async fn softirq(self: Arc<Self>) {
let queue = self.queue.get();
let rx_capacity = queue.rx_ring.capacity();
let nic = *self.nic.get();
loop {
let event = self.softirq.wait().await as u32;
let event = LocalRegisterCopy::<_, regs::ICR::Register>::new(event);
if event.matches_all(regs::ICR::LSC::SET) {
let status = self.regs.lock().read_link();
log::info!("igbe: link is {status}");
self.interrupt_handled(regs::ICR::LSC::SET);
}
if event.matches_any(&[regs::ICR::TXQE::SET, regs::ICR::TXDW::SET]) {
let head = self.regs.lock().tx_queue_head();
queue.drop_tx_until(head as usize);
// queue.consume_tx(Some(head as usize)).ok();
self.interrupt_handled(regs::ICR::TXQE::SET + regs::ICR::TXDW::SET);
}
if event.matches_any(&[regs::ICR::RXDMT0::SET, regs::ICR::RXT0::SET]) {
{
let mut regs = self.regs.lock();
let head = regs.rx_queue_head() as usize;
let tail = queue
.consume_rx(&*self.dma, Some(head), |packet, _| {
let packet = RxPacket::new(packet, 0, nic);
ygg_driver_net_core::receive_packet(packet).ok();
})
.expect("Rx ring handle error");
let tail = (tail + rx_capacity - 1) & (rx_capacity - 1);
regs.set_rx_queue_tail(tail as u16);
}
self.interrupt_handled(regs::ICR::RXDMT0::SET + regs::ICR::RXT0::SET);
}
}
}
fn interrupt_handled(&self, icr: FieldValue<u32, regs::ICR::Register>) {
let mut regs = self.regs.lock();
regs.clear_interrupts(icr.value);
regs.enable_interrupts(icr.value);
}
}
impl Device for Igbe {
@@ -68,33 +114,44 @@ impl Device for Igbe {
.pci
.map_interrupt(InterruptAffinity::Any, self.clone())?;
let rx_ring = RxRing::with_capacity(&*self.dma, 128, 2048 + 16)?;
let tx_ring = TxRing::with_capacity(&*self.dma, 128)?;
let queue = self.queue.init(GenericQueue::with_capacity(
&*self.dma,
128,
128,
2048 + 16,
)?);
let mut regs = self.regs.lock();
regs.disable_interrupts();
regs.disable_interrupts(0xFFFFFFFF);
let mac = regs.read_mac()?;
self.mac.init(mac);
regs.reset(Duration::from_millis(200))?;
// Intel 8257x manuals say an additional interrupt disable is needed after a global reset
regs.disable_interrupts();
regs.disable_interrupts(0xFFFFFFFF);
regs.set_link_up(self.chip)?;
// Initialize Rx
regs.initialize_receiver(&rx_ring);
regs.initialize_transmitter(&tx_ring);
regs.initialize_receiver(&queue.rx_ring);
regs.initialize_transmitter(&queue.tx_ring);
// If MSI(-x) was initialized, notify the NIC about it
if let Some(msi_info) = msi_info {
regs.initialize_ivar(msi_info.vector);
}
self.rx_ring.init(Spinlock::new(rx_ring));
self.tx_ring.init(IrqSafeSpinlock::new(tx_ring));
let nic = register_interface(NetworkInterfaceType::Ethernet, self.clone());
self.nic.init(nic.id());
regs.enable_interrupts();
regs.enable_interrupts(
(regs::IMS::TXDW::SET
+ regs::IMS::TXQE::SET
+ regs::IMS::LSC::SET
+ regs::IMS::RXT0::SET
+ regs::IMS::RXDMT0::SET)
.value,
);
runtime::spawn(self.clone().softirq())?;
Ok(())
}
@@ -107,43 +164,13 @@ impl Device for Igbe {
impl InterruptHandler for Igbe {
fn handle_irq(self: Arc<Self>, _vector: IrqVector) -> bool {
let mut regs = self.regs.lock();
let cause = regs.interrupt_cause();
if cause.get() == 0 {
let cause = regs.interrupt_cause().get();
if cause == 0 {
return false;
}
regs.clear_interrupts(cause.get());
let mut any = false;
if cause.matches_all(ICR::LSC::SET) {
let status = regs.read_link();
log::info!("igbe: link is {status}");
any = true;
}
if cause.matches_all(ICR::RXT0::SET) {
let mut rx = self.rx_ring.get().lock();
let nic = *self.nic.get();
let head = regs.rx_queue_head();
let tail = rx.handle_rx(&*self.dma, head, |packet, _| {
let packet = RxPacket::new(packet, 0, nic);
ygg_driver_net_core::receive_packet(packet).ok();
});
regs.set_rx_queue_tail(tail);
any = true;
}
if cause.matches_any(&[ICR::TXQE::SET, ICR::TXDW::SET]) {
let mut tx = self.tx_ring.get().lock();
let head = regs.tx_queue_head();
tx.handle_tx(head);
any = true;
}
if !any {
log::info!("igbe: unhandled irq {:#x}", cause.get());
}
any
regs.disable_interrupts(cause);
self.softirq.signal(cause as u64);
true
}
}
@@ -161,15 +188,10 @@ impl NetworkDevice for Igbe {
}
fn transmit_buffer(&self, buffer: DmaBuffer<[u8]>) -> Result<(), Error> {
let mut tx = self.tx_ring.get().lock();
let Ok(head) = tx.tx_now(buffer) else {
return Err(Error::WouldBlock);
};
let queue = self.queue.get();
let tail = queue.try_push_xmit(buffer)?;
let mut regs = self.regs.lock();
regs.set_tx_queue_tail(head);
regs.set_tx_queue_tail(tail as _);
Ok(())
}
+41 -23
View File
@@ -7,14 +7,17 @@ use libk::{
};
use libk_mm::{address::PhysicalAddress, device::RawDeviceMemoryMapping};
use tock_registers::{LocalRegisterCopy, RegisterLongName, fields::FieldValue, register_bitfields};
use ygg_driver_net_core::ephy::{GBESR, MdioBus, PhyAccess};
use ygg_driver_net_core::{
ephy::{GBESR, MdioBus, PhyAccess},
util::{GenericRxRing, GenericTxRing},
};
use ygg_driver_pci::PciBaseAddress;
use yggdrasil_abi::net::{
MacAddress,
link::{Duplex, EthernetLinkState, EthernetSpeed},
};
use crate::{RxRing, TxRing};
use crate::ring::{RxDescriptor, TxDescriptor};
enum Inner {
Memory(RawDeviceMemoryMapping),
@@ -79,12 +82,14 @@ register_bitfields! {
TXDW OFFSET(0) NUMBITS(1) [],
TXQE OFFSET(1) NUMBITS(1) [],
LSC OFFSET(2) NUMBITS(1) [],
RXDMT0 OFFSET(4) NUMBITS(1) [],
RXT0 OFFSET(7) NUMBITS(1) [],
],
pub IMS [
TXDW OFFSET(0) NUMBITS(1) [],
TXQE OFFSET(1) NUMBITS(1) [],
LSC OFFSET(2) NUMBITS(1) [],
RXDMT0 OFFSET(4) NUMBITS(1) [],
RXT0 OFFSET(7) NUMBITS(1) [],
],
pub RCTL [
@@ -459,36 +464,47 @@ impl Regs {
}
}
pub fn disable_interrupts(&mut self) {
self.inner.set::<IMC::Register>(0xFFFFFFFF);
}
pub fn clear_interrupts(&mut self, cause: u32) {
let _ = self.inner.get::<CPUVEC::Register>();
self.inner.set::<ICR::Register>(cause);
}
pub fn enable_interrupts(&mut self) {
self.inner.set::<IMS::Register>(0xFFFFFFFF);
// self.inner
// .modify(IMS::LSC::SET + IMS::RXT0::SET + IMS::TXDW::SET + IMS::TXQE::SET);
}
// pub fn disable_interrupts(&mut self) {
// self.inner.set::<IMC::Register>(0xFFFFFFFF);
// }
pub fn interrupt_cause(&mut self) -> LocalRegisterCopy<u32, ICR::Register> {
self.inner.extract()
}
pub fn initialize_receiver(&mut self, rx_ring: &RxRing) {
let rx_queue_base = rx_ring.descriptors.bus_address().into_u64();
pub fn clear_interrupts(&mut self, icr: u32) {
let _ = self.inner.get::<CPUVEC::Register>();
self.inner.set::<ICR::Register>(icr);
}
pub fn enable_interrupts(&mut self, mask: u32) {
self.inner.set::<IMS::Register>(mask);
}
pub fn disable_interrupts(&mut self, mask: u32) {
self.inner.set::<IMC::Register>(mask);
}
// pub fn enable_interrupts(&mut self) {
// self.inner.set::<IMS::Register>(0xFFFFFFFF);
// // self.inner
// // .modify(IMS::LSC::SET + IMS::RXT0::SET + IMS::TXDW::SET + IMS::TXQE::SET);
// }
pub fn initialize_receiver(&mut self, rx_ring: &GenericRxRing<RxDescriptor>) {
let rx_queue_base = rx_ring.base().into_u64();
let rx_queue_capacity = rx_ring.capacity();
// let rx_queue_base = rx_ring.descriptors.bus_address().into_u64();
self.inner.set::<RDBAL0::Register>(rx_queue_base as u32);
self.inner
.set::<RDBAH0::Register>((rx_queue_base >> 32) as u32);
self.inner
.write(RDLEN0::LEN0.val((rx_ring.descriptors.len() / 8) as u32));
.write(RDLEN0::LEN0.val((rx_queue_capacity / 8) as u32));
self.inner.set::<RDH0::Register>(0);
self.inner
.set::<RDT0::Register>((rx_ring.descriptors.len() - 1) as u32);
.set::<RDT0::Register>((rx_queue_capacity - 1) as u32);
self.inner.write(
RCTL::EN::SET
@@ -499,14 +515,16 @@ impl Regs {
);
}
pub fn initialize_transmitter(&mut self, tx_ring: &TxRing) {
let tx_queue_base = tx_ring.descriptors.bus_address().into_u64();
pub fn initialize_transmitter(&mut self, tx_ring: &GenericTxRing<TxDescriptor>) {
let tx_queue_base = tx_ring.base().into_u64();
let tx_queue_capacity = tx_ring.capacity();
self.inner.set::<TDBAL::Register>(tx_queue_base as u32);
self.inner
.set::<TDBAH::Register>((tx_queue_base >> 32) as u32);
self.inner
.write(TDLEN::LEN.val((tx_ring.descriptors.len() / 8) as u32));
.write(TDLEN::LEN.val((tx_queue_capacity / 8) as u32));
self.inner.set::<TDT::Register>(0);
self.inner.set::<TDH::Register>(0);
+64 -151
View File
@@ -1,31 +1,10 @@
use core::mem::{self, MaybeUninit};
use alloc::vec::Vec;
use device_api::dma::DmaAllocator;
use libk::{
dma::{BusAddress, DmaBuffer},
error::Error,
};
pub(crate) struct RxRing {
pub(crate) descriptors: DmaBuffer<[RxDescriptor]>,
buffers: Vec<DmaBuffer<[MaybeUninit<u8>]>>,
buffer_size: usize,
tail: u16,
}
pub(crate) struct TxRing {
pub(crate) descriptors: DmaBuffer<[TxDescriptor]>,
buffers: Vec<Option<DmaBuffer<[u8]>>>,
// Consumer end
tail: u16,
// Producer end
head: u16,
}
use libk::{dma::BusAddress, error::Error};
use ygg_driver_net_core::util::{GenericRxDescriptor, GenericTxDescriptor};
#[derive(Debug)]
#[repr(C)]
pub(crate) struct RxDescriptor {
address: BusAddress,
address: u64,
length: u16,
checksum: u16,
status: u8,
@@ -33,9 +12,10 @@ pub(crate) struct RxDescriptor {
special: u16,
}
#[derive(Debug)]
#[repr(C)]
pub(crate) struct TxDescriptor {
address: BusAddress,
address: u64,
length: u16,
cso: u8,
cmd: u8,
@@ -45,117 +25,73 @@ pub(crate) struct TxDescriptor {
special: u8,
}
impl RxRing {
pub fn with_capacity(
dma: &dyn DmaAllocator,
capacity: usize,
buffer_size: usize,
) -> Result<Self, Error> {
let buffers = (0..capacity)
.map(|_| DmaBuffer::new_uninit_slice(dma, buffer_size))
.collect::<Result<Vec<_>, _>>()?;
let descriptors = DmaBuffer::new_slice_with(
dma,
|i| RxDescriptor::new(buffers[i].bus_address(), buffer_size as u16),
capacity,
)?;
impl GenericTxDescriptor for TxDescriptor {
const EMPTY: Self = Self {
address: 0,
length: 0,
cso: 0,
cmd: 0,
sta: Self::STA_DD,
_0: 0,
css: 0,
special: 0,
};
Ok(Self {
descriptors,
buffers,
tail: 0,
buffer_size,
})
fn consume(&mut self) -> Option<Result<(), Error>> {
unreachable!()
}
// TODO move to background task/softirq to reduce amount of code run by the irq handler
pub fn handle_rx<F: FnMut(DmaBuffer<[u8]>, usize)>(
fn setup_tx(
&mut self,
dma: &dyn DmaAllocator,
head: u16,
mut handler: F,
) -> u16 {
let capacity = self.descriptors.len();
while self.tail != head {
let index = self.tail as usize;
// Replace the buffer
let new_buffer = DmaBuffer::new_uninit_slice(dma, self.buffer_size).unwrap();
let new_buffer_address = new_buffer.bus_address();
let buffer = mem::replace(&mut self.buffers[index], new_buffer);
let buffer = unsafe { DmaBuffer::assume_init_slice(buffer) };
let descriptor = &mut self.descriptors[index];
if descriptor.errors & !1 != 0 {
log::warn!("igbe: drop erroneous packet {:#x}", descriptor.errors);
} else {
let len = descriptor.length as usize;
handler(buffer, len);
}
// Replace the descriptor
*descriptor = RxDescriptor::new(new_buffer_address, self.buffer_size as u16);
self.tail = (self.tail + 1) & (capacity as u16 - 1);
buffer_address: BusAddress,
size: usize,
index: usize,
capacity: usize,
) -> Result<(), Error> {
if !(4..16288).contains(&size) {
return Err(Error::InvalidArgument);
}
(self.tail + capacity as u16 - 1) & (capacity as u16 - 1)
}
}
impl TxRing {
pub fn with_capacity(dma: &dyn DmaAllocator, capacity: usize) -> Result<Self, Error> {
let buffers = (0..capacity).map(|_| None).collect::<Vec<_>>();
let descriptors = DmaBuffer::new_slice_with(dma, |_| TxDescriptor::empty(), capacity)?;
Ok(Self {
descriptors,
buffers,
head: 0,
tail: 0,
})
}
pub fn handle_tx(&mut self, head: u16) {
self.tail = head;
}
pub fn tx_now(&mut self, buffer: DmaBuffer<[u8]>) -> Result<u16, DmaBuffer<[u8]>> {
// Queue full
let capacity = self.descriptors.len();
if (self.head + 1) & (capacity as u16 - 1) == self.tail {
log::warn!("igbe: tx queue full");
return Err(buffer);
}
let index = self.head as usize;
let descriptor = &mut self.descriptors[index];
// Only generate interrupts for every 1/4th of the buffer
let quarter = capacity / 4;
descriptor.setup_tx(
buffer.bus_address(),
buffer.len() as u16 - 4,
index % quarter == quarter - 1,
);
self.descriptors.cache_flush_element(index, true);
self.buffers[index] = Some(buffer);
self.head = (self.head + 1) & (capacity as u16 - 1);
Ok(self.head)
let ioc = index % quarter == quarter - 1;
let mut cmd = Self::CMD_EOP | Self::CMD_IFCS;
if ioc {
cmd |= Self::CMD_RS;
}
self.address = buffer_address.into_u64();
self.length = size as u16 - 4;
self.css = 0;
self.cso = 0;
self.sta = 0;
self.cmd = cmd;
self.special = 0;
Ok(())
}
}
impl RxDescriptor {
pub fn new(address: BusAddress, length: u16) -> Self {
Self {
address,
length,
impl GenericRxDescriptor for RxDescriptor {
fn consume(&mut self) -> Option<Result<usize, Error>> {
if self.status & Self::STA_DD == 0 {
None
} else if self.errors & !1 != 0 {
Some(Err(Error::InvalidArgument))
} else {
Some(Ok(self.length as _))
}
}
fn setup_rx(
buffer_address: BusAddress,
buffer_size: usize,
_last: bool,
) -> Result<Self, Error> {
Ok(Self {
address: buffer_address.into_u64(),
length: buffer_size as _,
checksum: 0,
status: 0,
errors: 0,
special: 0,
}
})
}
}
@@ -170,31 +106,8 @@ impl TxDescriptor {
// const CMD_IC: u8 = 1 << 2;
// Report status
const CMD_RS: u8 = 1 << 3;
pub const fn empty() -> Self {
Self {
address: BusAddress::ZERO,
length: 0,
cso: 0,
cmd: 0,
sta: Self::STA_DD,
_0: 0,
css: 0,
special: 0,
}
}
pub fn setup_tx(&mut self, address: BusAddress, length: u16, ioc: bool) {
let mut cmd = Self::CMD_EOP | Self::CMD_IFCS;
if ioc {
cmd |= Self::CMD_RS;
}
self.address = address;
self.length = length;
self.css = 0;
self.cso = 0;
self.sta = 0;
self.cmd = cmd;
self.special = 0;
}
}
impl RxDescriptor {
const STA_DD: u8 = 1 << 0;
}
+1 -1
View File
@@ -1,7 +1,7 @@
[package]
name = "ygg_driver_net_loopback"
version = "0.1.0"
edition = "2021"
edition = "2024"
[dependencies]
yggdrasil-abi.workspace = true
+18 -21
View File
@@ -29,12 +29,13 @@ use regs::{
},
mtl::{MTLOMR, MTLRXQiOMR, MTLTXQiOMR},
};
use ring::{RxDescriptor, RxRing, TxDescriptor, TxRing};
use ring::{RxDescriptor, TxDescriptor};
use tock_registers::interfaces::{ReadWriteable, Readable, Writeable};
use ygg_driver_net_core::{
RxPacket,
ephy::{GBESR, PhyAccess},
interface::{NetworkDevice, NetworkInterfaceType},
util::GenericQueue,
};
use yggdrasil_abi::net::{
MacAddress,
@@ -48,9 +49,7 @@ pub mod ring;
struct Inner {
regs: IrqSafeSpinlock<DeviceMemoryIo<'static, Regs>>,
tx_ring: IrqSafeSpinlock<TxRing>,
rx_ring: IrqSafeSpinlock<RxRing>,
queue: GenericQueue<TxDescriptor, RxDescriptor>,
}
struct Stmmac {
@@ -105,12 +104,12 @@ impl Stmmac {
pub fn start_xmit(&self, frame: DmaBuffer<[u8]>) -> Result<(), Error> {
let inner = self.inner.get();
let regs = inner.regs.lock();
let index = inner.queue.try_push_xmit(frame)?;
let mut tx_ring = inner.tx_ring.lock();
let index = tx_ring.push_xmit(frame)?;
let ring_pos = tx_ring
.buffer_base()
let regs = inner.regs.lock();
let ring_pos = inner
.queue
.tx_buffer_base()
.add(size_of::<TxDescriptor>() * index)
.try_into_u32()
.unwrap();
@@ -134,17 +133,17 @@ impl Stmmac {
}
if events & Self::SOFTIRQ_TX_STATUS != 0 {
inner.tx_ring.lock().consume().ok();
inner.queue.consume_tx().ok();
}
if events & Self::SOFTIRQ_RX_STATUS != 0 {
let mut rx_ring = inner.rx_ring.lock();
rx_ring
.consume(dma.as_ref(), |packet| {
inner
.queue
.consume_rx(dma.as_ref(), None, |packet, _| {
let packet = RxPacket::new(packet, 0, iface);
ygg_driver_net_core::receive_packet(packet).ok();
})
.ok();
.expect("Rx failed");
}
}
}
@@ -230,10 +229,10 @@ impl Device for Stmmac {
);
// Setup DMA Tx/Rx rings
let tx_ring = TxRing::with_capacity(dma.as_ref(), tx_ring_capacity)?;
let rx_ring = RxRing::with_capacity(dma.as_ref(), rx_ring_capacity)?;
let tx_ring_base = tx_ring.buffer_base().try_into_u32().unwrap();
let rx_ring_base = rx_ring.buffer_base().try_into_u32().unwrap();
let queue =
GenericQueue::with_capacity(dma.as_ref(), tx_ring_capacity, rx_ring_capacity, 4096)?;
let tx_ring_base = queue.tx_buffer_base().try_into_u32()?;
let rx_ring_base = queue.rx_buffer_base().try_into_u32()?;
regs.DMA.DMAC0TXRLR.set(tx_ring_capacity as u32 - 1);
regs.DMA.DMAC0TXDLAR.set(tx_ring_base);
@@ -338,9 +337,7 @@ impl Device for Stmmac {
self.inner.init(Inner {
regs: IrqSafeSpinlock::new(regs),
tx_ring: IrqSafeSpinlock::new(tx_ring),
rx_ring: IrqSafeSpinlock::new(rx_ring),
queue,
});
let iface =
+43 -200
View File
@@ -1,26 +1,5 @@
use core::mem::{self, MaybeUninit};
use alloc::vec::Vec;
use device_api::dma::DmaAllocator;
use libk::{
dma::{BusAddress, DmaBuffer},
error::Error,
};
pub struct TxRing {
entries: DmaBuffer<[TxDescriptor]>,
buffers: Vec<Option<DmaBuffer<[u8]>>>,
wr: usize,
rd: usize,
}
pub struct RxRing {
entries: DmaBuffer<[RxDescriptor]>,
buffers: Vec<DmaBuffer<[MaybeUninit<u8>]>>,
rd: usize,
}
use libk::{dma::BusAddress, error::Error};
use ygg_driver_net_core::util::{GenericRxDescriptor, GenericTxDescriptor};
#[derive(Clone, Copy, Debug)]
#[repr(C)]
@@ -40,215 +19,79 @@ pub struct RxDescriptor {
rdes3: u32,
}
impl TxRing {
pub fn with_capacity(dma: &dyn DmaAllocator, capacity: usize) -> Result<Self, Error> {
let entries = DmaBuffer::new_slice(dma, TxDescriptor::empty(), capacity)?;
let buffers = (0..capacity).map(|_| None).collect();
Ok(Self {
entries,
buffers,
wr: 0,
rd: 0,
})
}
pub fn buffer_base(&self) -> BusAddress {
self.entries.bus_address()
}
pub fn capacity(&self) -> usize {
self.entries.len()
}
pub fn can_xmit(&self) -> bool {
self.wr.wrapping_add(1) != self.rd
}
pub fn outstanding_tx(&self) -> bool {
self.wr != self.rd
}
pub fn push_xmit(&mut self, frame: DmaBuffer<[u8]>) -> Result<usize, Error> {
if !self.can_xmit() {
return Err(Error::WouldBlock);
}
let address = frame.bus_address();
let frame_len = frame.len();
let capacity = self.entries.len();
let index = self.wr % capacity;
assert!(self.buffers[index].is_none());
frame.cache_flush_all(true);
self.entries[index].setup_tx(address, frame_len, true)?;
self.entries.cache_flush_element(index, true);
self.buffers[index] = Some(frame);
self.wr = self.wr.wrapping_add(1);
Ok(self.wr % self.capacity())
}
pub fn consume(&mut self) -> Result<usize, Error> {
let mut count = 0;
loop {
let index = self.rd % self.entries.len();
let entry = &self.entries[index];
if self.rd == self.wr {
break;
}
if let Some(status) = entry.tx_status() {
if status != 0 {
log::warn!("tx_ring[{index}] error: {status:#x}");
}
let _ = self.buffers[index].take().unwrap();
self.rd = self.rd.wrapping_add(1);
count += 1;
} else {
break;
}
}
Ok(count)
}
}
impl TxDescriptor {
const TDES3_OWN: u32 = 1 << 31;
const TDES3_FD: u32 = 1 << 29;
const TDES3_LD: u32 = 1 << 28;
const TDES2_IOC: u32 = 1 << 31;
}
pub const fn empty() -> Self {
Self {
tdes0: 0,
tdes1: 0,
tdes2: 0,
tdes3: 0,
}
}
impl GenericTxDescriptor for TxDescriptor {
const EMPTY: Self = Self {
tdes0: 0,
tdes1: 0,
tdes2: 0,
tdes3: 0,
};
pub fn tx_status(&self) -> Option<u32> {
fn consume(&mut self) -> Option<Result<(), Error>> {
if self.tdes3 & Self::TDES3_OWN == 0 {
Some(self.tdes3 & !(0xFFFF << 16))
// Some(self.tdes3 & !(0xFFFF << 16))
Some(Ok(()))
} else {
None
}
}
pub fn setup_tx(
fn setup_tx(
&mut self,
frame: BusAddress,
frame_len: usize,
ioc: bool,
buffer_address: BusAddress,
size: usize,
_index: usize,
_capacity: usize,
) -> Result<(), Error> {
let tdes0 = frame.try_into_u32().map_err(|_| Error::InvalidArgument)?;
if frame_len & !0x3FFF != 0 {
if size & !0x3FFF != 0 {
return Err(Error::InvalidArgument);
}
let mut tdes2 = frame_len as u32;
if ioc {
tdes2 |= Self::TDES2_IOC;
}
let tdes3 = Self::TDES3_OWN | Self::TDES3_FD | Self::TDES3_LD;
self.tdes0 = tdes0;
// TODO ioc only on quarter buffer
self.tdes0 = buffer_address.try_into_u32()?;
self.tdes1 = 0;
self.tdes2 = tdes2;
self.tdes3 = tdes3;
self.tdes2 = size as u32 | Self::TDES2_IOC;
self.tdes3 = Self::TDES3_OWN | Self::TDES3_LD | Self::TDES3_FD;
Ok(())
}
}
impl RxRing {
pub fn with_capacity(dma: &dyn DmaAllocator, capacity: usize) -> Result<Self, Error> {
let mut entries = DmaBuffer::new_slice(dma, RxDescriptor::empty(), capacity)?;
let buffers = (0..capacity)
.map(|_| DmaBuffer::new_uninit_slice(dma, 4096))
.collect::<Result<Vec<_>, _>>()?;
for i in 0..capacity {
entries[i].setup_rx(buffers[i].bus_address(), true)?;
}
Ok(Self {
buffers,
entries,
rd: 0,
})
}
pub fn buffer_base(&self) -> BusAddress {
self.entries.bus_address()
}
pub fn consume<F: Fn(DmaBuffer<[u8]>)>(
&mut self,
dma: &dyn DmaAllocator,
packet_handler: F,
) -> Result<usize, Error> {
let mut count = 0;
loop {
let index = self.rd % self.entries.len();
let entry = &mut self.entries[index];
if entry.rx_completed().is_some() {
// Grab the current buffer (the one just written to by the DMA), replace it
// with the newly allocated one, and mark the descriptor as DMA-owned again
let new_buffer = DmaBuffer::new_uninit_slice(dma, 4096)?;
let new_buffer_address = new_buffer.bus_address();
let buffer = mem::replace(&mut self.buffers[index], new_buffer);
let buffer = unsafe { DmaBuffer::assume_init_slice(buffer) };
// TODO packet size hint
packet_handler(buffer);
entry.setup_rx(new_buffer_address, true)?;
self.rd = self.rd.wrapping_add(1);
count += 1;
} else {
break;
}
}
Ok(count)
}
}
impl RxDescriptor {
const RDES3_OWN: u32 = 1 << 31;
const RDES3_IOC: u32 = 1 << 30;
const RDES3_BUF1V: u32 = 1 << 24;
}
pub const fn empty() -> Self {
Self {
rdes0: 0,
rdes1: 0,
rdes2: 0,
rdes3: 0,
}
}
pub fn rx_completed(&self) -> Option<usize> {
impl GenericRxDescriptor for RxDescriptor {
fn consume(&mut self) -> Option<Result<usize, Error>> {
if self.rdes3 & Self::RDES3_OWN == 0 {
Some((self.rdes3 & 0x7FFF) as usize)
Some(Ok((self.rdes3 & 0x7FFF) as usize))
} else {
None
}
}
pub fn setup_rx(&mut self, buffer: BusAddress, ioc: bool) -> Result<(), Error> {
self.rdes0 = buffer.try_into_u32().map_err(|_| Error::InvalidArgument)?;
self.rdes1 = 0;
self.rdes2 = 0;
self.rdes3 = Self::RDES3_BUF1V;
if ioc {
self.rdes3 |= Self::RDES3_IOC;
}
self.rdes3 |= Self::RDES3_OWN;
Ok(())
fn setup_rx(
buffer_address: BusAddress,
_buffer_size: usize,
_last: bool,
) -> Result<Self, Error> {
let rdes0 = buffer_address.try_into_u32()?;
let rdes1 = 0;
let rdes2 = 0;
// TODO ioc
let rdes3 = Self::RDES3_BUF1V | Self::RDES3_IOC | Self::RDES3_OWN;
Ok(Self {
rdes0,
rdes1,
rdes2,
rdes3,
})
}
}
+21
View File
@@ -0,0 +1,21 @@
[package]
name = "ygg_driver_serial_8250"
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
[target.'cfg(any(target_arch = "x86", target_arch = "x86_64"))'.dependencies]
kernel-arch-x86.workspace = true
[dev-dependencies]
kernel-arch-x86.workspace = true
[lints]
workspace = true

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