Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0e979a9e09 | |||
| ed9d7a7145 | |||
| 6b5dd9f673 | |||
| 58dbaddf11 | |||
| 218e391505 | |||
| 2a49c655c2 | |||
| d108494314 | |||
| 195c19e225 | |||
| 21a8361eec | |||
| fd0a3f50ea |
Generated
+45
-10
@@ -469,6 +469,7 @@ dependencies = [
|
||||
name = "device-api"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"device-api-macros",
|
||||
"yggdrasil-abi",
|
||||
]
|
||||
@@ -1199,9 +1200,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.169"
|
||||
version = "0.2.180"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
|
||||
checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc"
|
||||
dependencies = [
|
||||
"rustc-std-workspace-core",
|
||||
]
|
||||
@@ -1353,6 +1354,12 @@ version = "0.4.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039"
|
||||
|
||||
[[package]]
|
||||
name = "litemap"
|
||||
version = "0.7.4"
|
||||
@@ -1787,7 +1794,20 @@ dependencies = [
|
||||
"bitflags 2.8.0",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"linux-raw-sys 0.4.15",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34"
|
||||
dependencies = [
|
||||
"bitflags 2.8.0",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys 0.11.0",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
@@ -2004,15 +2024,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.16.0"
|
||||
version = "3.24.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38c246215d7d24f48ae091a2902398798e05d978b24315d6efbc00ede9a8bb91"
|
||||
checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"fastrand",
|
||||
"getrandom 0.3.1",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"rustix 1.1.3",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
@@ -2388,7 +2407,7 @@ checksum = "b4ee928febd44d98f2f459a4a79bd4d928591333a494a10a868418ac1b39cf1f"
|
||||
dependencies = [
|
||||
"either",
|
||||
"home",
|
||||
"rustix",
|
||||
"rustix 0.38.44",
|
||||
"winsafe",
|
||||
]
|
||||
|
||||
@@ -2557,8 +2576,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e105d177a3871454f754b33bb0ee637ecaaac997446375fd3e5d43a2ed00c909"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"rustix",
|
||||
"linux-raw-sys 0.4.15",
|
||||
"rustix 0.38.44",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2589,6 +2608,7 @@ dependencies = [
|
||||
"semver 1.0.25",
|
||||
"serde",
|
||||
"tar",
|
||||
"tempfile",
|
||||
"thiserror",
|
||||
"toml",
|
||||
"walkdir",
|
||||
@@ -2657,6 +2677,7 @@ dependencies = [
|
||||
name = "ygg_driver_bsp_bcm283x"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"bytemuck",
|
||||
"device-api",
|
||||
"device-tree",
|
||||
@@ -2855,6 +2876,19 @@ dependencies = [
|
||||
"yggdrasil-abi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ygg_driver_serial_8250"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"device-api",
|
||||
"device-tree",
|
||||
"kernel-arch-x86",
|
||||
"libk",
|
||||
"libk-mm",
|
||||
"libk-util",
|
||||
"yggdrasil-abi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ygg_driver_usb"
|
||||
version = "0.1.0"
|
||||
@@ -3028,6 +3062,7 @@ dependencies = [
|
||||
"ygg_driver_net_stmmac",
|
||||
"ygg_driver_nvme",
|
||||
"ygg_driver_pci",
|
||||
"ygg_driver_serial_8250",
|
||||
"ygg_driver_usb",
|
||||
"ygg_driver_usb_xhci",
|
||||
"ygg_driver_virtio_blk",
|
||||
|
||||
@@ -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...
|
||||
|
||||
|
||||
+3
-2
@@ -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:
|
||||
|
||||
Binary file not shown.
@@ -264,16 +264,15 @@
|
||||
compatible = "gpio-keys";
|
||||
|
||||
poweroff {
|
||||
gpios = <0x8007 0x03 0x00>;
|
||||
gpios = <&gpio 0x03 0x00>;
|
||||
linux,code = <0x74>;
|
||||
label = "GPIO Key Poweroff";
|
||||
};
|
||||
};
|
||||
|
||||
pl061@9030000 {
|
||||
phandle = <0x8007>;
|
||||
gpio: pl061@9030000 {
|
||||
clock-names = "apb_pclk";
|
||||
clocks = <0x8000>;
|
||||
clocks = <&clk_24mhz>;
|
||||
interrupts = <0x00 0x07 0x04>;
|
||||
gpio-controller;
|
||||
#gpio-cells = <0x02>;
|
||||
@@ -299,15 +298,15 @@
|
||||
|
||||
pl031@9010000 {
|
||||
clock-names = "apb_pclk";
|
||||
clocks = <0x8000>;
|
||||
clocks = <&clk_24mhz>;
|
||||
interrupts = <0x00 0x02 0x04>;
|
||||
reg = <0x00 0x9010000 0x00 0x1000>;
|
||||
compatible = "arm,pl031", "arm,primecell";
|
||||
};
|
||||
|
||||
pl011@9000000 {
|
||||
uart0: pl011@9000000 {
|
||||
clock-names = "uartclk", "apb_pclk";
|
||||
clocks = <0x8000 0x8000>;
|
||||
clocks = <&clk_24mhz &clk_24mhz>;
|
||||
interrupts = <0x00 0x01 0x04>;
|
||||
reg = <0x00 0x9000000 0x00 0x1000>;
|
||||
compatible = "arm,pl011", "arm,primecell";
|
||||
@@ -411,8 +410,7 @@
|
||||
compatible = "arm,armv8-timer", "arm,armv7-timer";
|
||||
};
|
||||
|
||||
apb-pclk {
|
||||
phandle = <0x8000>;
|
||||
clk_24mhz: apb-pclk {
|
||||
clock-output-names = "clk24mhz";
|
||||
clock-frequency = <0x16e3600>;
|
||||
#clock-cells = <0x00>;
|
||||
@@ -0,0 +1,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>;
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#define GIC_SPI 0
|
||||
#define GIC_PPI 1
|
||||
|
||||
#define GIC_CPU_MASK_RAW(x) ((x) << 8)
|
||||
#define GIC_CPU_MASK_SIMPLE(num) GIC_CPU_MASK_RAW((1 << (num)) - 1)
|
||||
|
||||
#define IRQ_TYPE_NONE 0
|
||||
#define IRQ_TYPE_EDGE_RISING 1
|
||||
#define IRQ_TYPE_EDGE_FALLING 2
|
||||
#define IRQ_TYPE_EDGE_BOTH (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)
|
||||
#define IRQ_TYPE_LEVEL_HIGH 4
|
||||
#define IRQ_TYPE_LEVEL_LOW 8
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -0,0 +1,308 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 OR MIT */
|
||||
/*
|
||||
* Copyright (C) 2022 Emil Renner Berthing <kernel@esmil.dk>
|
||||
* Copyright (C) 2022 StarFive Technology Co., Ltd.
|
||||
*/
|
||||
|
||||
#ifndef __JH7110_PINFUNC_H__
|
||||
#define __JH7110_PINFUNC_H__
|
||||
|
||||
/*
|
||||
* mux bits:
|
||||
* | 31 - 24 | 23 - 16 | 15 - 10 | 9 - 8 | 7 - 0 |
|
||||
* | din | dout | doen | function | gpio nr |
|
||||
*
|
||||
* dout: output signal
|
||||
* doen: output enable signal
|
||||
* din: optional input signal, 0xff = none
|
||||
* function: function selector
|
||||
* gpio nr: gpio number, 0 - 63
|
||||
*/
|
||||
#define GPIOMUX(n, dout, doen, din) ( \
|
||||
(((din) & 0xff) << 24) | \
|
||||
(((dout) & 0xff) << 16) | \
|
||||
(((doen) & 0x3f) << 10) | \
|
||||
((n) & 0x3f))
|
||||
|
||||
#define PINMUX(n, func) ((1 << 10) | (((func) & 0x3) << 8) | ((n) & 0xff))
|
||||
|
||||
/* sys_iomux dout */
|
||||
#define GPOUT_LOW 0
|
||||
#define GPOUT_HIGH 1
|
||||
#define GPOUT_SYS_WAVE511_UART_TX 2
|
||||
#define GPOUT_SYS_CAN0_STBY 3
|
||||
#define GPOUT_SYS_CAN0_TST_NEXT_BIT 4
|
||||
#define GPOUT_SYS_CAN0_TST_SAMPLE_POINT 5
|
||||
#define GPOUT_SYS_CAN0_TXD 6
|
||||
#define GPOUT_SYS_USB_DRIVE_VBUS 7
|
||||
#define GPOUT_SYS_QSPI_CS1 8
|
||||
#define GPOUT_SYS_SPDIF 9
|
||||
#define GPOUT_SYS_HDMI_CEC_SDA 10
|
||||
#define GPOUT_SYS_HDMI_DDC_SCL 11
|
||||
#define GPOUT_SYS_HDMI_DDC_SDA 12
|
||||
#define GPOUT_SYS_WATCHDOG 13
|
||||
#define GPOUT_SYS_I2C0_CLK 14
|
||||
#define GPOUT_SYS_I2C0_DATA 15
|
||||
#define GPOUT_SYS_SDIO0_BACK_END_POWER 16
|
||||
#define GPOUT_SYS_SDIO0_CARD_POWER_EN 17
|
||||
#define GPOUT_SYS_SDIO0_CCMD_OD_PULLUP_EN 18
|
||||
#define GPOUT_SYS_SDIO0_RST 19
|
||||
#define GPOUT_SYS_UART0_TX 20
|
||||
#define GPOUT_SYS_HIFI4_JTAG_TDO 21
|
||||
#define GPOUT_SYS_JTAG_TDO 22
|
||||
#define GPOUT_SYS_PDM_MCLK 23
|
||||
#define GPOUT_SYS_PWM_CHANNEL0 24
|
||||
#define GPOUT_SYS_PWM_CHANNEL1 25
|
||||
#define GPOUT_SYS_PWM_CHANNEL2 26
|
||||
#define GPOUT_SYS_PWM_CHANNEL3 27
|
||||
#define GPOUT_SYS_PWMDAC_LEFT 28
|
||||
#define GPOUT_SYS_PWMDAC_RIGHT 29
|
||||
#define GPOUT_SYS_SPI0_CLK 30
|
||||
#define GPOUT_SYS_SPI0_FSS 31
|
||||
#define GPOUT_SYS_SPI0_TXD 32
|
||||
#define GPOUT_SYS_GMAC_PHYCLK 33
|
||||
#define GPOUT_SYS_I2SRX_BCLK 34
|
||||
#define GPOUT_SYS_I2SRX_LRCK 35
|
||||
#define GPOUT_SYS_I2STX0_BCLK 36
|
||||
#define GPOUT_SYS_I2STX0_LRCK 37
|
||||
#define GPOUT_SYS_MCLK 38
|
||||
#define GPOUT_SYS_TDM_CLK 39
|
||||
#define GPOUT_SYS_TDM_SYNC 40
|
||||
#define GPOUT_SYS_TDM_TXD 41
|
||||
#define GPOUT_SYS_TRACE_DATA0 42
|
||||
#define GPOUT_SYS_TRACE_DATA1 43
|
||||
#define GPOUT_SYS_TRACE_DATA2 44
|
||||
#define GPOUT_SYS_TRACE_DATA3 45
|
||||
#define GPOUT_SYS_TRACE_REF 46
|
||||
#define GPOUT_SYS_CAN1_STBY 47
|
||||
#define GPOUT_SYS_CAN1_TST_NEXT_BIT 48
|
||||
#define GPOUT_SYS_CAN1_TST_SAMPLE_POINT 49
|
||||
#define GPOUT_SYS_CAN1_TXD 50
|
||||
#define GPOUT_SYS_I2C1_CLK 51
|
||||
#define GPOUT_SYS_I2C1_DATA 52
|
||||
#define GPOUT_SYS_SDIO1_BACK_END_POWER 53
|
||||
#define GPOUT_SYS_SDIO1_CARD_POWER_EN 54
|
||||
#define GPOUT_SYS_SDIO1_CLK 55
|
||||
#define GPOUT_SYS_SDIO1_CMD_OD_PULLUP_EN 56
|
||||
#define GPOUT_SYS_SDIO1_CMD 57
|
||||
#define GPOUT_SYS_SDIO1_DATA0 58
|
||||
#define GPOUT_SYS_SDIO1_DATA1 59
|
||||
#define GPOUT_SYS_SDIO1_DATA2 60
|
||||
#define GPOUT_SYS_SDIO1_DATA3 61
|
||||
#define GPOUT_SYS_SDIO1_DATA4 62
|
||||
#define GPOUT_SYS_SDIO1_DATA5 63
|
||||
#define GPOUT_SYS_SDIO1_DATA6 64
|
||||
#define GPOUT_SYS_SDIO1_DATA7 65
|
||||
#define GPOUT_SYS_SDIO1_RST 66
|
||||
#define GPOUT_SYS_UART1_RTS 67
|
||||
#define GPOUT_SYS_UART1_TX 68
|
||||
#define GPOUT_SYS_I2STX1_SDO0 69
|
||||
#define GPOUT_SYS_I2STX1_SDO1 70
|
||||
#define GPOUT_SYS_I2STX1_SDO2 71
|
||||
#define GPOUT_SYS_I2STX1_SDO3 72
|
||||
#define GPOUT_SYS_SPI1_CLK 73
|
||||
#define GPOUT_SYS_SPI1_FSS 74
|
||||
#define GPOUT_SYS_SPI1_TXD 75
|
||||
#define GPOUT_SYS_I2C2_CLK 76
|
||||
#define GPOUT_SYS_I2C2_DATA 77
|
||||
#define GPOUT_SYS_UART2_RTS 78
|
||||
#define GPOUT_SYS_UART2_TX 79
|
||||
#define GPOUT_SYS_SPI2_CLK 80
|
||||
#define GPOUT_SYS_SPI2_FSS 81
|
||||
#define GPOUT_SYS_SPI2_TXD 82
|
||||
#define GPOUT_SYS_I2C3_CLK 83
|
||||
#define GPOUT_SYS_I2C3_DATA 84
|
||||
#define GPOUT_SYS_UART3_TX 85
|
||||
#define GPOUT_SYS_SPI3_CLK 86
|
||||
#define GPOUT_SYS_SPI3_FSS 87
|
||||
#define GPOUT_SYS_SPI3_TXD 88
|
||||
#define GPOUT_SYS_I2C4_CLK 89
|
||||
#define GPOUT_SYS_I2C4_DATA 90
|
||||
#define GPOUT_SYS_UART4_RTS 91
|
||||
#define GPOUT_SYS_UART4_TX 92
|
||||
#define GPOUT_SYS_SPI4_CLK 93
|
||||
#define GPOUT_SYS_SPI4_FSS 94
|
||||
#define GPOUT_SYS_SPI4_TXD 95
|
||||
#define GPOUT_SYS_I2C5_CLK 96
|
||||
#define GPOUT_SYS_I2C5_DATA 97
|
||||
#define GPOUT_SYS_UART5_RTS 98
|
||||
#define GPOUT_SYS_UART5_TX 99
|
||||
#define GPOUT_SYS_SPI5_CLK 100
|
||||
#define GPOUT_SYS_SPI5_FSS 101
|
||||
#define GPOUT_SYS_SPI5_TXD 102
|
||||
#define GPOUT_SYS_I2C6_CLK 103
|
||||
#define GPOUT_SYS_I2C6_DATA 104
|
||||
#define GPOUT_SYS_SPI6_CLK 105
|
||||
#define GPOUT_SYS_SPI6_FSS 106
|
||||
#define GPOUT_SYS_SPI6_TXD 107
|
||||
|
||||
/* aon_iomux dout */
|
||||
#define GPOUT_AON_CLK_32K_OUT 2
|
||||
#define GPOUT_AON_PTC0_PWM4 3
|
||||
#define GPOUT_AON_PTC0_PWM5 4
|
||||
#define GPOUT_AON_PTC0_PWM6 5
|
||||
#define GPOUT_AON_PTC0_PWM7 6
|
||||
#define GPOUT_AON_CLK_GCLK0 7
|
||||
#define GPOUT_AON_CLK_GCLK1 8
|
||||
#define GPOUT_AON_CLK_GCLK2 9
|
||||
|
||||
/* sys_iomux doen */
|
||||
#define GPOEN_ENABLE 0
|
||||
#define GPOEN_DISABLE 1
|
||||
#define GPOEN_SYS_HDMI_CEC_SDA 2
|
||||
#define GPOEN_SYS_HDMI_DDC_SCL 3
|
||||
#define GPOEN_SYS_HDMI_DDC_SDA 4
|
||||
#define GPOEN_SYS_I2C0_CLK 5
|
||||
#define GPOEN_SYS_I2C0_DATA 6
|
||||
#define GPOEN_SYS_HIFI4_JTAG_TDO 7
|
||||
#define GPOEN_SYS_JTAG_TDO 8
|
||||
#define GPOEN_SYS_PWM0_CHANNEL0 9
|
||||
#define GPOEN_SYS_PWM0_CHANNEL1 10
|
||||
#define GPOEN_SYS_PWM0_CHANNEL2 11
|
||||
#define GPOEN_SYS_PWM0_CHANNEL3 12
|
||||
#define GPOEN_SYS_SPI0_NSSPCTL 13
|
||||
#define GPOEN_SYS_SPI0_NSSP 14
|
||||
#define GPOEN_SYS_TDM_SYNC 15
|
||||
#define GPOEN_SYS_TDM_TXD 16
|
||||
#define GPOEN_SYS_I2C1_CLK 17
|
||||
#define GPOEN_SYS_I2C1_DATA 18
|
||||
#define GPOEN_SYS_SDIO1_CMD 19
|
||||
#define GPOEN_SYS_SDIO1_DATA0 20
|
||||
#define GPOEN_SYS_SDIO1_DATA1 21
|
||||
#define GPOEN_SYS_SDIO1_DATA2 22
|
||||
#define GPOEN_SYS_SDIO1_DATA3 23
|
||||
#define GPOEN_SYS_SDIO1_DATA4 24
|
||||
#define GPOEN_SYS_SDIO1_DATA5 25
|
||||
#define GPOEN_SYS_SDIO1_DATA6 26
|
||||
#define GPOEN_SYS_SDIO1_DATA7 27
|
||||
#define GPOEN_SYS_SPI1_NSSPCTL 28
|
||||
#define GPOEN_SYS_SPI1_NSSP 29
|
||||
#define GPOEN_SYS_I2C2_CLK 30
|
||||
#define GPOEN_SYS_I2C2_DATA 31
|
||||
#define GPOEN_SYS_SPI2_NSSPCTL 32
|
||||
#define GPOEN_SYS_SPI2_NSSP 33
|
||||
#define GPOEN_SYS_I2C3_CLK 34
|
||||
#define GPOEN_SYS_I2C3_DATA 35
|
||||
#define GPOEN_SYS_SPI3_NSSPCTL 36
|
||||
#define GPOEN_SYS_SPI3_NSSP 37
|
||||
#define GPOEN_SYS_I2C4_CLK 38
|
||||
#define GPOEN_SYS_I2C4_DATA 39
|
||||
#define GPOEN_SYS_SPI4_NSSPCTL 40
|
||||
#define GPOEN_SYS_SPI4_NSSP 41
|
||||
#define GPOEN_SYS_I2C5_CLK 42
|
||||
#define GPOEN_SYS_I2C5_DATA 43
|
||||
#define GPOEN_SYS_SPI5_NSSPCTL 44
|
||||
#define GPOEN_SYS_SPI5_NSSP 45
|
||||
#define GPOEN_SYS_I2C6_CLK 46
|
||||
#define GPOEN_SYS_I2C6_DATA 47
|
||||
#define GPOEN_SYS_SPI6_NSSPCTL 48
|
||||
#define GPOEN_SYS_SPI6_NSSP 49
|
||||
|
||||
/* aon_iomux doen */
|
||||
#define GPOEN_AON_PTC0_OE_N_4 2
|
||||
#define GPOEN_AON_PTC0_OE_N_5 3
|
||||
#define GPOEN_AON_PTC0_OE_N_6 4
|
||||
#define GPOEN_AON_PTC0_OE_N_7 5
|
||||
|
||||
/* sys_iomux gin */
|
||||
#define GPI_NONE 255
|
||||
|
||||
#define GPI_SYS_WAVE511_UART_RX 0
|
||||
#define GPI_SYS_CAN0_RXD 1
|
||||
#define GPI_SYS_USB_OVERCURRENT 2
|
||||
#define GPI_SYS_SPDIF 3
|
||||
#define GPI_SYS_JTAG_RST 4
|
||||
#define GPI_SYS_HDMI_CEC_SDA 5
|
||||
#define GPI_SYS_HDMI_DDC_SCL 6
|
||||
#define GPI_SYS_HDMI_DDC_SDA 7
|
||||
#define GPI_SYS_HDMI_HPD 8
|
||||
#define GPI_SYS_I2C0_CLK 9
|
||||
#define GPI_SYS_I2C0_DATA 10
|
||||
#define GPI_SYS_SDIO0_CD 11
|
||||
#define GPI_SYS_SDIO0_INT 12
|
||||
#define GPI_SYS_SDIO0_WP 13
|
||||
#define GPI_SYS_UART0_RX 14
|
||||
#define GPI_SYS_HIFI4_JTAG_TCK 15
|
||||
#define GPI_SYS_HIFI4_JTAG_TDI 16
|
||||
#define GPI_SYS_HIFI4_JTAG_TMS 17
|
||||
#define GPI_SYS_HIFI4_JTAG_RST 18
|
||||
#define GPI_SYS_JTAG_TDI 19
|
||||
#define GPI_SYS_JTAG_TMS 20
|
||||
#define GPI_SYS_PDM_DMIC0 21
|
||||
#define GPI_SYS_PDM_DMIC1 22
|
||||
#define GPI_SYS_I2SRX_SDIN0 23
|
||||
#define GPI_SYS_I2SRX_SDIN1 24
|
||||
#define GPI_SYS_I2SRX_SDIN2 25
|
||||
#define GPI_SYS_SPI0_CLK 26
|
||||
#define GPI_SYS_SPI0_FSS 27
|
||||
#define GPI_SYS_SPI0_RXD 28
|
||||
#define GPI_SYS_JTAG_TCK 29
|
||||
#define GPI_SYS_MCLK_EXT 30
|
||||
#define GPI_SYS_I2SRX_BCLK 31
|
||||
#define GPI_SYS_I2SRX_LRCK 32
|
||||
#define GPI_SYS_I2STX1_BCLK 33
|
||||
#define GPI_SYS_I2STX1_LRCK 34
|
||||
#define GPI_SYS_TDM_CLK 35
|
||||
#define GPI_SYS_TDM_RXD 36
|
||||
#define GPI_SYS_TDM_SYNC 37
|
||||
#define GPI_SYS_CAN1_RXD 38
|
||||
#define GPI_SYS_I2C1_CLK 39
|
||||
#define GPI_SYS_I2C1_DATA 40
|
||||
#define GPI_SYS_SDIO1_CD 41
|
||||
#define GPI_SYS_SDIO1_INT 42
|
||||
#define GPI_SYS_SDIO1_WP 43
|
||||
#define GPI_SYS_SDIO1_CMD 44
|
||||
#define GPI_SYS_SDIO1_DATA0 45
|
||||
#define GPI_SYS_SDIO1_DATA1 46
|
||||
#define GPI_SYS_SDIO1_DATA2 47
|
||||
#define GPI_SYS_SDIO1_DATA3 48
|
||||
#define GPI_SYS_SDIO1_DATA4 49
|
||||
#define GPI_SYS_SDIO1_DATA5 50
|
||||
#define GPI_SYS_SDIO1_DATA6 51
|
||||
#define GPI_SYS_SDIO1_DATA7 52
|
||||
#define GPI_SYS_SDIO1_STRB 53
|
||||
#define GPI_SYS_UART1_CTS 54
|
||||
#define GPI_SYS_UART1_RX 55
|
||||
#define GPI_SYS_SPI1_CLK 56
|
||||
#define GPI_SYS_SPI1_FSS 57
|
||||
#define GPI_SYS_SPI1_RXD 58
|
||||
#define GPI_SYS_I2C2_CLK 59
|
||||
#define GPI_SYS_I2C2_DATA 60
|
||||
#define GPI_SYS_UART2_CTS 61
|
||||
#define GPI_SYS_UART2_RX 62
|
||||
#define GPI_SYS_SPI2_CLK 63
|
||||
#define GPI_SYS_SPI2_FSS 64
|
||||
#define GPI_SYS_SPI2_RXD 65
|
||||
#define GPI_SYS_I2C3_CLK 66
|
||||
#define GPI_SYS_I2C3_DATA 67
|
||||
#define GPI_SYS_UART3_RX 68
|
||||
#define GPI_SYS_SPI3_CLK 69
|
||||
#define GPI_SYS_SPI3_FSS 70
|
||||
#define GPI_SYS_SPI3_RXD 71
|
||||
#define GPI_SYS_I2C4_CLK 72
|
||||
#define GPI_SYS_I2C4_DATA 73
|
||||
#define GPI_SYS_UART4_CTS 74
|
||||
#define GPI_SYS_UART4_RX 75
|
||||
#define GPI_SYS_SPI4_CLK 76
|
||||
#define GPI_SYS_SPI4_FSS 77
|
||||
#define GPI_SYS_SPI4_RXD 78
|
||||
#define GPI_SYS_I2C5_CLK 79
|
||||
#define GPI_SYS_I2C5_DATA 80
|
||||
#define GPI_SYS_UART5_CTS 81
|
||||
#define GPI_SYS_UART5_RX 82
|
||||
#define GPI_SYS_SPI5_CLK 83
|
||||
#define GPI_SYS_SPI5_FSS 84
|
||||
#define GPI_SYS_SPI5_RXD 85
|
||||
#define GPI_SYS_I2C6_CLK 86
|
||||
#define GPI_SYS_I2C6_DATA 87
|
||||
#define GPI_SYS_SPI6_CLK 88
|
||||
#define GPI_SYS_SPI6_FSS 89
|
||||
#define GPI_SYS_SPI6_RXD 90
|
||||
|
||||
/* aon_iomux gin */
|
||||
#define GPI_AON_PMU_GPIO_WAKEUP_0 0
|
||||
#define GPI_AON_PMU_GPIO_WAKEUP_1 1
|
||||
#define GPI_AON_PMU_GPIO_WAKEUP_2 2
|
||||
#define GPI_AON_PMU_GPIO_WAKEUP_3 3
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -38,7 +38,7 @@
|
||||
#size-cells = <0x00>;
|
||||
timebase-frequency = <0x989680>;
|
||||
|
||||
cpu@0 {
|
||||
cpu0: cpu@0 {
|
||||
phandle = <0x01>;
|
||||
device_type = "cpu";
|
||||
reg = <0x00>;
|
||||
@@ -52,7 +52,7 @@
|
||||
riscv,isa = "rv64imafdch_zic64b_zicbom_zicbop_zicboz_ziccamoa_ziccif_zicclsm_ziccrse_zicntr_zicsr_zifencei_zihintntl_zihintpause_zihpm_zmmul_za64rs_zaamo_zalrsc_zawrs_zfa_zca_zcd_zba_zbb_zbc_zbs_shcounterenw_shgatpa_shtvala_shvsatpa_shvstvala_shvstvecd_ssccptr_sscounterenw_sstc_sstvala_sstvecd_ssu64xl_svadu_svvptc";
|
||||
mmu-type = "riscv,sv57";
|
||||
|
||||
interrupt-controller {
|
||||
cpu0_intc: interrupt-controller {
|
||||
#interrupt-cells = <0x01>;
|
||||
interrupt-controller;
|
||||
compatible = "riscv,cpu-intc";
|
||||
@@ -61,11 +61,9 @@
|
||||
};
|
||||
|
||||
cpu-map {
|
||||
|
||||
cluster0 {
|
||||
|
||||
core0 {
|
||||
cpu = <0x01>;
|
||||
cpu = <&cpu0>;
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -186,7 +184,8 @@
|
||||
phandle = <0x03>;
|
||||
riscv,ndev = <0x5f>;
|
||||
reg = <0x00 0xc000000 0x00 0x600000>;
|
||||
interrupts-extended = <0x02 0x0b 0x02 0x09>;
|
||||
interrupts-extended = <&cpu0_intc 0x0b>,
|
||||
<&cpu0_intc 0x09>;
|
||||
interrupt-controller;
|
||||
compatible = "sifive,plic-1.0.0", "riscv,plic0";
|
||||
#address-cells = <0x00>;
|
||||
@@ -194,7 +193,8 @@
|
||||
};
|
||||
|
||||
clint@2000000 {
|
||||
interrupts-extended = <0x02 0x03 0x02 0x07>;
|
||||
interrupts-extended = <&cpu0_intc 0x03>,
|
||||
<&cpu0_intc 0x07>;
|
||||
reg = <0x00 0x2000000 0x00 0x10000>;
|
||||
compatible = "sifive,clint0", "riscv,clint0";
|
||||
};
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -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" }
|
||||
|
||||
@@ -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>"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,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(b'\n');
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_multiple(&self, bytes: &[u8]) -> Result<usize, Error> {
|
||||
fn write_multiple(
|
||||
&self,
|
||||
bytes: &[u8],
|
||||
options: &TerminalOutputOptions,
|
||||
) -> Result<usize, Error> {
|
||||
let mut lock = self.io.lock();
|
||||
for &byte in bytes {
|
||||
if byte == b'\n' && options.contains(TerminalOutputOptions::NL_TO_CRNL) {
|
||||
lock.send(b'\r');
|
||||
}
|
||||
lock.send(byte);
|
||||
}
|
||||
Ok(bytes.len())
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -16,3 +16,4 @@ tock-registers.workspace = true
|
||||
log.workspace = true
|
||||
bytemuck.workspace = true
|
||||
futures-util.workspace = true
|
||||
async-trait.workspace = true
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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(),
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,290 @@
|
||||
use alloc::sync::Arc;
|
||||
use device_api::{
|
||||
clock::{ClockHandle, Hertz},
|
||||
device::{Device, DeviceInitContext},
|
||||
i2c::{I2CAddress, I2CController},
|
||||
interrupt::{InterruptHandler, IrqHandle, IrqVector},
|
||||
};
|
||||
use device_tree::driver::{Node, ProbeContext, device_tree_driver};
|
||||
use libk::{device::manager::DEVICE_REGISTRY, error::Error};
|
||||
use libk_mm::device::DeviceMemoryIo;
|
||||
use libk_util::{OneTimeInit, sync::spin_rwlock::IrqSafeRwLock};
|
||||
use tock_registers::{
|
||||
interfaces::{ReadWriteable, Readable, Writeable},
|
||||
register_bitfields, register_structs,
|
||||
registers::ReadWrite,
|
||||
};
|
||||
use yggdrasil_abi::io::device::i2c::I2CMasterInfo;
|
||||
|
||||
register_bitfields! {
|
||||
u32,
|
||||
C [
|
||||
I2CEN OFFSET(15) NUMBITS(1) [],
|
||||
INTR OFFSET(10) NUMBITS(1) [],
|
||||
INTT OFFSET(9) NUMBITS(1) [],
|
||||
INTD OFFSET(8) NUMBITS(1) [],
|
||||
ST OFFSET(7) NUMBITS(1) [],
|
||||
CLEAR OFFSET(4) NUMBITS(2) [],
|
||||
READ OFFSET(0) NUMBITS(1) [],
|
||||
],
|
||||
S [
|
||||
CLKT OFFSET(9) NUMBITS(1) [],
|
||||
ERR OFFSET(8) NUMBITS(1) [],
|
||||
RXF OFFSET(7) NUMBITS(1) [],
|
||||
TXE OFFSET(6) NUMBITS(1) [],
|
||||
RXD OFFSET(5) NUMBITS(1) [],
|
||||
TXD OFFSET(4) NUMBITS(1) [],
|
||||
RXR OFFSET(3) NUMBITS(1) [],
|
||||
TXW OFFSET(2) NUMBITS(1) [],
|
||||
DONE OFFSET(1) NUMBITS(1) [],
|
||||
TA OFFSET(0) NUMBITS(1) [],
|
||||
],
|
||||
}
|
||||
|
||||
register_structs! {
|
||||
#[allow(non_snake_case)]
|
||||
Regs {
|
||||
(0x00 => C: ReadWrite<u32, C::Register>),
|
||||
(0x04 => S: ReadWrite<u32, S::Register>),
|
||||
(0x08 => DLEN: ReadWrite<u32>),
|
||||
(0x0C => A: ReadWrite<u32>),
|
||||
(0x10 => FIFO: ReadWrite<u32>),
|
||||
(0x14 => DIV: ReadWrite<u32>),
|
||||
(0x18 => DEL: ReadWrite<u32>),
|
||||
(0x1C => CLKT: ReadWrite<u32>),
|
||||
(0x20 => @END),
|
||||
}
|
||||
}
|
||||
|
||||
struct I2C {
|
||||
name: &'static str,
|
||||
clock_frequency: Option<Hertz>,
|
||||
irq: IrqHandle,
|
||||
clock: Option<ClockHandle>,
|
||||
regs: IrqSafeRwLock<DeviceMemoryIo<'static, Regs>>,
|
||||
index: OneTimeInit<u32>,
|
||||
}
|
||||
|
||||
impl Regs {
|
||||
fn start_transfer(&self, name: &str, buflen: u16, address: I2CAddress, read: bool) {
|
||||
log::debug!(
|
||||
"{}: start address={}, read={}, len={}",
|
||||
name,
|
||||
address,
|
||||
read,
|
||||
buflen
|
||||
);
|
||||
|
||||
let address = address.as_8_bit().unwrap();
|
||||
let read = if read { C::READ::SET } else { C::READ::CLEAR };
|
||||
|
||||
self.S.write(S::ERR::SET + S::DONE::SET);
|
||||
self.DLEN.set(buflen as u32);
|
||||
self.C.modify(C::ST::CLEAR + C::I2CEN::CLEAR);
|
||||
self.A.set(address as u32);
|
||||
self.C.modify(read + C::ST::SET + C::I2CEN::SET);
|
||||
}
|
||||
|
||||
fn finish_transfer(&self, name: &str) -> Result<(), Error> {
|
||||
log::debug!("{}: finish transfer", name);
|
||||
let status = self.S.extract();
|
||||
self.C.set(0);
|
||||
if status.matches_all(S::ERR::SET) {
|
||||
self.S.write(S::DONE::SET + S::ERR::SET);
|
||||
return Err(Error::HostUnreachable);
|
||||
}
|
||||
if status.matches_all(S::DONE::SET) {
|
||||
self.S.write(S::DONE::SET);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_byte(&self, byte: u8) -> Result<bool, Error> {
|
||||
loop {
|
||||
let status = self.S.extract();
|
||||
if status.matches_all(S::ERR::SET) {
|
||||
self.C.write(C::CLEAR.val(1));
|
||||
self.S.write(S::ERR::SET + S::DONE::SET);
|
||||
// TODO better code
|
||||
return Err(Error::HostUnreachable);
|
||||
}
|
||||
if status.matches_all(S::DONE::SET) {
|
||||
self.C.set(0);
|
||||
self.S.write(S::DONE::SET);
|
||||
return Ok(false);
|
||||
}
|
||||
if status.matches_all(S::TXD::SET) {
|
||||
self.FIFO.set(byte as u32);
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
}
|
||||
|
||||
fn read_byte(&self) -> Result<Option<u8>, Error> {
|
||||
loop {
|
||||
let status = self.S.extract();
|
||||
if status.matches_all(S::ERR::SET) {
|
||||
self.C.write(C::CLEAR.val(1));
|
||||
self.S.write(S::ERR::SET + S::DONE::SET);
|
||||
// TODO better code
|
||||
return Err(Error::HostUnreachable);
|
||||
}
|
||||
if status.matches_all(S::DONE::SET) {
|
||||
self.C.set(0);
|
||||
self.S.write(S::DONE::SET);
|
||||
return Ok(None);
|
||||
}
|
||||
if status.matches_all(S::RXD::SET) {
|
||||
let val = self.FIFO.get() as u8;
|
||||
return Ok(Some(val));
|
||||
}
|
||||
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for I2C {
|
||||
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
|
||||
// TODO
|
||||
let _ = &self.clock;
|
||||
|
||||
log::info!("{}: initialize", self.name);
|
||||
let regs = self.regs.write();
|
||||
regs.C.set(0);
|
||||
|
||||
let index = DEVICE_REGISTRY.i2c.register_bus(self.clone())?;
|
||||
self.index.init(index);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn init_irq(self: Arc<Self>) -> Result<(), Error> {
|
||||
// TODO
|
||||
let _ = &self.irq;
|
||||
// self.irq.register(self.clone())?;
|
||||
// self.irq.enable()?;
|
||||
// let regs = self.regs.write();
|
||||
// regs.C.modify(C::INTD::SET);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn display_name(&self) -> &str {
|
||||
self.name
|
||||
}
|
||||
}
|
||||
|
||||
impl InterruptHandler for I2C {
|
||||
fn handle_irq(self: Arc<Self>, _vector: IrqVector) -> bool {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl I2CController for I2C {
|
||||
fn bus_number(&self) -> u32 {
|
||||
*self.index.get()
|
||||
}
|
||||
|
||||
fn child_number(&self) -> Option<u32> {
|
||||
None
|
||||
}
|
||||
|
||||
fn set_speed(&self, speed: Hertz) -> Result<Hertz, Error> {
|
||||
if let Some(frequency) = self.clock_frequency {
|
||||
// Fixed frequency
|
||||
if speed == frequency {
|
||||
Ok(speed)
|
||||
} else {
|
||||
Err(Error::InvalidArgument)
|
||||
}
|
||||
} else {
|
||||
todo!("Set i2c speed = {speed}")
|
||||
}
|
||||
}
|
||||
|
||||
fn capabilities(&self) -> I2CMasterInfo {
|
||||
let max_speed_hz = if let Some(frequency) = self.clock_frequency {
|
||||
frequency
|
||||
} else {
|
||||
todo!()
|
||||
}
|
||||
.0 as u32;
|
||||
I2CMasterInfo {
|
||||
max_speed_hz,
|
||||
supports_10bit_addresses: true,
|
||||
}
|
||||
}
|
||||
|
||||
fn i2c_write(&self, address: I2CAddress, buffer: &[u8]) -> Result<usize, Error> {
|
||||
let buflen: u16 = buffer
|
||||
.len()
|
||||
.try_into()
|
||||
.map_err(|_| Error::InvalidArgument)?;
|
||||
let regs = self.regs.write();
|
||||
// TODO DMA/interrupts
|
||||
regs.start_transfer(self.name, buflen, address, false);
|
||||
let mut bytes_written = 0;
|
||||
// let mut done = false;
|
||||
for &byte in buffer {
|
||||
if regs.write_byte(byte)? {
|
||||
bytes_written += 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
regs.finish_transfer(self.name)?;
|
||||
Ok(bytes_written)
|
||||
}
|
||||
|
||||
fn i2c_read(&self, address: I2CAddress, buffer: &mut [u8]) -> Result<usize, Error> {
|
||||
let buflen: u16 = buffer
|
||||
.len()
|
||||
.try_into()
|
||||
.map_err(|_| Error::InvalidArgument)?;
|
||||
let regs = self.regs.write();
|
||||
// TODO DMA/interrupts
|
||||
regs.start_transfer(self.name, buflen, address, true);
|
||||
let mut bytes_read = 0;
|
||||
// let mut done = false;
|
||||
for byte in buffer {
|
||||
if let Some(val) = regs.read_byte()? {
|
||||
*byte = val;
|
||||
bytes_read += 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
regs.finish_transfer(self.name)?;
|
||||
Ok(bytes_read)
|
||||
}
|
||||
}
|
||||
|
||||
device_tree_driver! {
|
||||
compatible: ["brcm,bcm2711-i2c", "brcm,bcm2835-i2c"],
|
||||
driver: {
|
||||
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
|
||||
let clock_frequency = node.prop_usize("clock-frequency").map(|p| Hertz(p as u64));
|
||||
let base = node.map_base(context, 0)?;
|
||||
let name = node.name().unwrap_or("bcm2835-i2c");
|
||||
let irq = node.interrupt(0)?;
|
||||
let clock = node.clock(0);
|
||||
|
||||
let regs = unsafe { DeviceMemoryIo::map(base, Default::default()) }.ok()?;
|
||||
|
||||
let i2c = Arc::new(I2C {
|
||||
name,
|
||||
clock_frequency,
|
||||
irq,
|
||||
clock,
|
||||
index: OneTimeInit::new(),
|
||||
regs: IrqSafeRwLock::new(regs)
|
||||
});
|
||||
|
||||
node.make_i2c_controller(i2c.clone());
|
||||
|
||||
Some(i2c)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -2,8 +2,7 @@ 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, IrqVector,
|
||||
},
|
||||
};
|
||||
use device_tree::{
|
||||
@@ -13,7 +12,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 +79,7 @@ register_structs! {
|
||||
struct Context {
|
||||
enable: IrqSafeRwLock<DeviceMemoryIo<'static, ContextEnableRegs>>,
|
||||
control: IrqSafeRwLock<DeviceMemoryIo<'static, ContextControlRegs>>,
|
||||
// TODO scale the table depending on effective MAX_IRQS value
|
||||
table: IrqSafeRwLock<FixedInterruptTable<128>>,
|
||||
table: FixedInterruptTable,
|
||||
}
|
||||
|
||||
struct Inner {
|
||||
@@ -163,13 +164,13 @@ impl ExternalInterruptController for Plic {
|
||||
.inspect_err(|_| log::error!("plic: no context for hart {bsp_hart_id}"))?
|
||||
.context
|
||||
.get();
|
||||
let mut table = context.table.write();
|
||||
// let mut table = context.table.write();
|
||||
|
||||
log::info!(
|
||||
"Bind irq #{irq} -> hart {bsp_hart_id}, {:?}",
|
||||
handler.display_name()
|
||||
);
|
||||
|
||||
table.insert(irq as usize, handler)?;
|
||||
context.table.insert(irq as usize, handler);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -183,7 +184,7 @@ impl ExternalInterruptController for Plic {
|
||||
let context = context.context.get();
|
||||
|
||||
let control = context.control.write();
|
||||
let table = context.table.read();
|
||||
// let table = context.table.read();
|
||||
|
||||
loop {
|
||||
let irq = control.CLAIM.get();
|
||||
@@ -192,9 +193,7 @@ impl ExternalInterruptController for Plic {
|
||||
}
|
||||
let vector = IrqVector::Irq(Irq::External(irq));
|
||||
|
||||
if let Some(handler) = table.handler(irq as usize) {
|
||||
handler.clone().handle_irq(vector);
|
||||
} else {
|
||||
if !context.table.handle_irq_line(irq as usize, vector) {
|
||||
log::warn!("plic: no handler for IRQ #{irq}");
|
||||
}
|
||||
|
||||
@@ -242,7 +241,7 @@ impl Device for Plic {
|
||||
context.context.init(Context {
|
||||
enable: IrqSafeRwLock::new(enable),
|
||||
control: IrqSafeRwLock::new(control),
|
||||
table: IrqSafeRwLock::new(FixedInterruptTable::new()),
|
||||
table: FixedInterruptTable::new(MAX_IRQS), // table: IrqSafeRwLock::new(FixedInterruptTable::new()),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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
|
||||
@@ -0,0 +1,198 @@
|
||||
use alloc::{sync::Arc, vec::Vec};
|
||||
use device_api::{
|
||||
clock::{ClockHandle, Hertz, ResetHandle},
|
||||
device::Device,
|
||||
interrupt::{InterruptHandler, IrqHandle},
|
||||
};
|
||||
use device_tree::driver::{Node, ProbeContext, device_tree_driver};
|
||||
use libk::error::Error;
|
||||
use libk_mm::{address::PhysicalAddress, device::RawDeviceMemoryMapping};
|
||||
|
||||
use crate::{Access, Config, PortConfig, Uart8250, Vendor};
|
||||
|
||||
pub struct Fdt8250Access {
|
||||
mapping: RawDeviceMemoryMapping,
|
||||
offset: usize,
|
||||
shift: usize,
|
||||
io_width: usize,
|
||||
}
|
||||
|
||||
pub struct Fdt8250Config {
|
||||
irq: IrqHandle,
|
||||
ports: [Fdt8250PortConfig; 1],
|
||||
}
|
||||
|
||||
pub struct Fdt8250PortConfig {
|
||||
base: PhysicalAddress,
|
||||
reg_offset: usize,
|
||||
reg_shift: usize,
|
||||
reg_io_width: usize,
|
||||
|
||||
resets: Vec<ResetHandle>,
|
||||
clock_frequency: Option<Hertz>,
|
||||
clk_baud: Option<ClockHandle>,
|
||||
clk_apb: Option<ClockHandle>,
|
||||
}
|
||||
|
||||
impl Access for Fdt8250Access {
|
||||
type Config = Fdt8250Config;
|
||||
|
||||
unsafe fn map(config: &Fdt8250PortConfig) -> Result<Self, Error> {
|
||||
let mapping = unsafe {
|
||||
RawDeviceMemoryMapping::map(config.base.into_u64(), 0x1000, Default::default())
|
||||
}?;
|
||||
Ok(Self {
|
||||
offset: config.reg_offset,
|
||||
shift: config.reg_shift,
|
||||
io_width: config.reg_io_width,
|
||||
mapping,
|
||||
})
|
||||
}
|
||||
|
||||
fn read(&self, reg: u8) -> u8 {
|
||||
let addr = ((reg as usize) << self.shift) + self.offset;
|
||||
assert!(addr + self.io_width <= 0x1000);
|
||||
let ptr = unsafe { self.mapping.as_non_null::<u8>().add(addr) };
|
||||
match self.io_width {
|
||||
4 => unsafe { ptr.cast::<u32>().read_volatile() as u8 },
|
||||
1 => unsafe { ptr.cast::<u8>().read_volatile() },
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn write(&mut self, reg: u8, val: u8) {
|
||||
let addr = ((reg as usize) << self.shift) + self.offset;
|
||||
assert!(addr + self.io_width <= 0x1000);
|
||||
let ptr = unsafe { self.mapping.as_non_null::<u8>().add(addr) };
|
||||
match self.io_width {
|
||||
4 => unsafe { ptr.cast::<u32>().write_volatile(val as u32) },
|
||||
1 => unsafe { ptr.cast::<u8>().write_volatile(val) },
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
// fn read(&self, reg: usize) -> u32 {
|
||||
// }
|
||||
|
||||
// fn write(&mut self, reg: usize, val: u32) {
|
||||
// }
|
||||
}
|
||||
|
||||
impl PortConfig for Fdt8250PortConfig {
|
||||
fn input_clock_frequency(&self) -> Result<Hertz, Error> {
|
||||
if let Some(clock_frequency) = self.clock_frequency {
|
||||
Ok(clock_frequency)
|
||||
} else if let Some(clk_baud) = self.clk_baud.as_ref() {
|
||||
clk_baud.rate()
|
||||
} else {
|
||||
Err(Error::InvalidArgument)
|
||||
}
|
||||
}
|
||||
|
||||
fn deassert_resets(&self) -> Result<(), Error> {
|
||||
for reset in self.resets.iter() {
|
||||
reset.deassert()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn enable_clocks(&self) -> Result<(), Error> {
|
||||
if let Some(clk_apb) = self.clk_apb.as_ref() {
|
||||
clk_apb.enable()?;
|
||||
}
|
||||
if let Some(clk_baud) = self.clk_baud.as_ref() {
|
||||
clk_baud.enable()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Config for Fdt8250Config {
|
||||
type Port = Fdt8250PortConfig;
|
||||
|
||||
fn ports(&self) -> &[Self::Port] {
|
||||
&self.ports[..]
|
||||
}
|
||||
|
||||
fn enable_interrupt(&self, handler: Arc<dyn InterruptHandler>) -> Result<(), Error> {
|
||||
self.irq.register(handler)?;
|
||||
self.irq.enable()?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
// };
|
||||
|
||||
impl Fdt8250Config {
|
||||
fn from_device_tree(node: &Arc<Node>, context: &mut ProbeContext) -> Option<(Self, Vendor)> {
|
||||
let (vendor, reg_io_width, reg_shift) = if node.is_compatible("snps,dw-apb-uart") {
|
||||
(Vendor::DwApbUart, 1, 0)
|
||||
} else if node.is_compatible("ns16550a") {
|
||||
(Vendor::Generic16550, 1, 0)
|
||||
} else if node.is_compatible("brcm,bcm2835-aux-uart") {
|
||||
(Vendor::Generic8250, 4, 2)
|
||||
} else {
|
||||
(Vendor::Generic8250, 1, 0)
|
||||
};
|
||||
|
||||
let base = node.map_base(context, 0)?;
|
||||
let irq = node.interrupt(0)?;
|
||||
let resets: Vec<_> = node.resets().map(|r| r.collect()).unwrap_or_default();
|
||||
let clock_frequency = node
|
||||
.prop_usize("clock-frequency")
|
||||
.map(|clk| Hertz(clk as _));
|
||||
let mut clk_baud = node.named_clock("baudclk");
|
||||
let clk_apb = node.named_clock("apb_pclk");
|
||||
if clk_baud.is_none() && clk_apb.is_none() {
|
||||
clk_baud = node.clock(0);
|
||||
}
|
||||
|
||||
let reg_io_width = node.prop_usize("reg-io-width").unwrap_or(reg_io_width);
|
||||
let reg_shift = node.prop_usize("reg-shift").unwrap_or(reg_shift);
|
||||
let reg_offset = node.prop_usize("reg-offset").unwrap_or(0);
|
||||
|
||||
Some((
|
||||
Self {
|
||||
irq,
|
||||
ports: [Fdt8250PortConfig {
|
||||
clk_apb,
|
||||
clk_baud,
|
||||
resets,
|
||||
clock_frequency,
|
||||
|
||||
reg_io_width,
|
||||
reg_offset,
|
||||
reg_shift,
|
||||
|
||||
base,
|
||||
}],
|
||||
},
|
||||
vendor,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
device_tree_driver! {
|
||||
compatible: [
|
||||
"snps,dw-apb-uart",
|
||||
"brcm,bcm2835-aux-uart",
|
||||
"ns16550a",
|
||||
],
|
||||
driver: {
|
||||
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
|
||||
let (config, vendor) = Fdt8250Config::from_device_tree(node, context)?;
|
||||
let uart = Arc::new(Uart8250::<Fdt8250Access>::new(config, vendor));
|
||||
Some(uart)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
use alloc::sync::Arc;
|
||||
use device_api::{
|
||||
clock::Hertz,
|
||||
interrupt::{InterruptHandler, Irq},
|
||||
};
|
||||
use kernel_arch_x86::intrinsics;
|
||||
use libk::{device::external_interrupt_controller, error::Error};
|
||||
|
||||
use crate::{Access, Config, PortConfig};
|
||||
|
||||
pub struct Io8250Access {
|
||||
io_base: u16,
|
||||
}
|
||||
|
||||
pub struct Io8250Config {
|
||||
ports: [Io8250PortConfig; 2],
|
||||
irq: Irq,
|
||||
}
|
||||
|
||||
pub struct Io8250PortConfig {
|
||||
io_base: u16,
|
||||
}
|
||||
|
||||
impl Io8250Config {
|
||||
pub fn new(port_a: u16, port_b: u16, irq: Irq) -> Self {
|
||||
Self {
|
||||
ports: [
|
||||
Io8250PortConfig { io_base: port_a },
|
||||
Io8250PortConfig { io_base: port_b },
|
||||
],
|
||||
irq,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Access for Io8250Access {
|
||||
type Config = Io8250Config;
|
||||
|
||||
unsafe fn map(config: &Io8250PortConfig) -> Result<Self, Error> {
|
||||
Ok(Self {
|
||||
io_base: config.io_base,
|
||||
})
|
||||
}
|
||||
|
||||
fn read(&self, reg: u8) -> u8 {
|
||||
unsafe { intrinsics::inb(self.io_base + reg as u16) }
|
||||
}
|
||||
|
||||
fn write(&mut self, reg: u8, val: u8) {
|
||||
unsafe { intrinsics::outb(self.io_base + reg as u16, val) };
|
||||
}
|
||||
}
|
||||
|
||||
impl Config for Io8250Config {
|
||||
type Port = Io8250PortConfig;
|
||||
|
||||
fn ports(&self) -> &[Self::Port] {
|
||||
&self.ports
|
||||
}
|
||||
|
||||
fn enable_interrupt(&self, handler: Arc<dyn InterruptHandler>) -> Result<(), Error> {
|
||||
let intc = external_interrupt_controller()?;
|
||||
intc.register_irq(self.irq, Default::default(), handler)?;
|
||||
intc.enable_irq(self.irq)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl PortConfig for Io8250PortConfig {
|
||||
fn input_clock_frequency(&self) -> Result<Hertz, Error> {
|
||||
Ok(Hertz(115200 * 16))
|
||||
}
|
||||
|
||||
fn enable_clocks(&self) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn deassert_resets(&self) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
#![no_std]
|
||||
|
||||
use core::iter;
|
||||
|
||||
use alloc::{sync::Arc, vec::Vec};
|
||||
use device_api::{
|
||||
clock::Hertz,
|
||||
device::{Device, DeviceInitContext},
|
||||
interrupt::{InterruptHandler, IrqVector},
|
||||
};
|
||||
use libk::error::Error;
|
||||
use libk_util::OneTimeInit;
|
||||
|
||||
use crate::port::Port8250;
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
#[cfg(any(rust_analyzer, target_arch = "aarch64", target_arch = "riscv64"))]
|
||||
pub mod fdt;
|
||||
#[cfg(any(rust_analyzer, target_arch = "x86_64", target_arch = "x86"))]
|
||||
pub mod io;
|
||||
pub mod port;
|
||||
|
||||
pub trait Config: Sync + Send {
|
||||
type Port: PortConfig;
|
||||
|
||||
fn ports(&self) -> &[Self::Port];
|
||||
fn enable_interrupt(&self, handler: Arc<dyn InterruptHandler>) -> Result<(), Error>;
|
||||
}
|
||||
|
||||
pub trait PortConfig: Sync + Send {
|
||||
fn input_clock_frequency(&self) -> Result<Hertz, Error>;
|
||||
fn enable_clocks(&self) -> Result<(), Error>;
|
||||
fn deassert_resets(&self) -> Result<(), Error>;
|
||||
}
|
||||
|
||||
pub trait Access: Sized + Send + 'static {
|
||||
type Config: Config;
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// Unsafe: allows physical memory access.
|
||||
unsafe fn map(config: &<Self::Config as Config>::Port) -> Result<Self, Error>;
|
||||
|
||||
fn read(&self, reg: u8) -> u8;
|
||||
fn write(&mut self, reg: u8, val: u8);
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum Vendor {
|
||||
DwApbUart,
|
||||
Bcm2835Aux,
|
||||
Generic8250,
|
||||
Generic16550,
|
||||
}
|
||||
|
||||
pub struct Uart8250<A: Access> {
|
||||
config: A::Config,
|
||||
vendor: Vendor,
|
||||
ports: Vec<Arc<Port8250<A>>>,
|
||||
}
|
||||
|
||||
impl<A: Access> Uart8250<A> {
|
||||
pub fn new(config: A::Config, vendor: Vendor) -> Self {
|
||||
let ports = config
|
||||
.ports()
|
||||
.iter()
|
||||
.map(|_| {
|
||||
Arc::new(Port8250 {
|
||||
vendor,
|
||||
inner: OneTimeInit::new(),
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
Self {
|
||||
config,
|
||||
vendor,
|
||||
ports,
|
||||
}
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// Unsafe: the function is meant only to be called once, directly
|
||||
/// interacts with the device state.
|
||||
pub unsafe fn init_inner(self: &Arc<Self>) -> Result<(), Error> {
|
||||
let ports = self.ports.iter();
|
||||
let port_configs = self.config.ports().iter();
|
||||
for (port, config) in iter::zip(ports, port_configs) {
|
||||
port.clone().init(config)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Access> Device for Uart8250<A> {
|
||||
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
|
||||
unsafe { self.init_inner() }
|
||||
}
|
||||
|
||||
unsafe fn init_irq(self: Arc<Self>) -> Result<(), Error> {
|
||||
self.config.enable_interrupt(self.clone())?;
|
||||
for port in self.ports.iter() {
|
||||
port.enable_irq();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn display_name(&self) -> &str {
|
||||
match self.vendor {
|
||||
Vendor::DwApbUart => "Synopsys DesignWare 16550 UART",
|
||||
Vendor::Bcm2835Aux => "bcm2835 AUX UART",
|
||||
Vendor::Generic16550 => "16550 UART",
|
||||
Vendor::Generic8250 => "8250 UART",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Access> InterruptHandler for Uart8250<A> {
|
||||
fn handle_irq(self: Arc<Self>, _vector: IrqVector) -> bool {
|
||||
for port in self.ports.iter() {
|
||||
if port.handle_irq() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,271 @@
|
||||
use core::ops::{Deref, DerefMut};
|
||||
|
||||
use alloc::sync::Arc;
|
||||
use device_api::clock::Hertz;
|
||||
use libk::{
|
||||
debug::{self, DebugSink},
|
||||
device::manager::DEVICE_REGISTRY,
|
||||
error::Error,
|
||||
vfs::{Terminal, TerminalInput, TerminalOutput},
|
||||
};
|
||||
use libk_util::{OneTimeInit, sync::IrqSafeSpinlock};
|
||||
use yggdrasil_abi::io::{TerminalOptions, TerminalOutputOptions};
|
||||
|
||||
use crate::{Access, Config, PortConfig, Vendor};
|
||||
|
||||
const REG_RBR: u8 = 0;
|
||||
const REG_THR: u8 = 0;
|
||||
const REG_DLL: u8 = 0;
|
||||
const REG_IER: u8 = 1;
|
||||
const REG_DLH: u8 = 1;
|
||||
const REG_IIR: u8 = 2;
|
||||
const REG_FCR: u8 = 2;
|
||||
const REG_LCR: u8 = 3;
|
||||
const REG_MCR: u8 = 4;
|
||||
const REG_LSR: u8 = 5;
|
||||
|
||||
// Vendor-specific
|
||||
const DW_REG_USR: u8 = 31;
|
||||
const DW_REG_SRR: u8 = 34;
|
||||
const DW_REG_HTX: u8 = 41;
|
||||
const BCM2835_REG_AUX_MU_CNTL: u8 = 24;
|
||||
|
||||
/// Received Data available interrupt
|
||||
const IER_RXDE: u8 = 1 << 0;
|
||||
/// Received Data available
|
||||
const IIR_RXDA: u8 = 0b100;
|
||||
const IIR_MASK: u8 = 0x7;
|
||||
const FCR_FIFO_ENABLE: u8 = 1 << 0;
|
||||
const FCR_RX_FIFO_RESET: u8 = 1 << 1;
|
||||
const FCR_TX_FIFO_RESET: u8 = 1 << 2;
|
||||
const LCR_BITS_8: u8 = 0x3;
|
||||
const LCR_DLAB: u8 = 1 << 7;
|
||||
const MCR_DTR: u8 = 1 << 0;
|
||||
const MCR_RTS: u8 = 1 << 1;
|
||||
const LSR_TEMT: u8 = 1 << 6;
|
||||
|
||||
const BCM2835_AUX_MU_CNTL_RE: u8 = 1 << 0;
|
||||
const BCM2835_AUX_MU_CNTL_TE: u8 = 1 << 1;
|
||||
|
||||
pub struct Port8250<A: Access> {
|
||||
pub(crate) vendor: Vendor,
|
||||
pub(crate) inner: OneTimeInit<Arc<Terminal<Inner<A>>>>,
|
||||
}
|
||||
|
||||
struct PortRegs<A: Access> {
|
||||
regs: A,
|
||||
baud_rate: u32,
|
||||
input_clock: Hertz,
|
||||
}
|
||||
|
||||
pub(crate) struct Inner<A: Access> {
|
||||
regs: IrqSafeSpinlock<PortRegs<A>>,
|
||||
}
|
||||
|
||||
impl<A: Access> Port8250<A> {
|
||||
pub(crate) fn init(self: Arc<Self>, config: &<A::Config as Config>::Port) -> Result<(), Error> {
|
||||
let _guard = debug::MuteGuard::acquire();
|
||||
// Deassert reset signal and enable clocks
|
||||
config.enable_clocks()?;
|
||||
config.deassert_resets()?;
|
||||
|
||||
let input_clock = config.input_clock_frequency()?;
|
||||
|
||||
let mut regs = PortRegs {
|
||||
regs: unsafe { A::map(config) }?,
|
||||
baud_rate: 0,
|
||||
input_clock,
|
||||
};
|
||||
|
||||
regs.init(self.vendor, 115200)?;
|
||||
|
||||
let input = TerminalInput::with_capacity(64)?;
|
||||
let output = Inner {
|
||||
regs: IrqSafeSpinlock::new(regs),
|
||||
};
|
||||
|
||||
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(())
|
||||
}
|
||||
|
||||
pub(crate) fn enable_irq(&self) {
|
||||
let mut regs = self.inner.get().output().regs.lock();
|
||||
regs.write(REG_IER, IER_RXDE);
|
||||
}
|
||||
|
||||
pub(crate) fn handle_irq(&self) -> bool {
|
||||
let inner = self.inner.get();
|
||||
let byte = {
|
||||
let regs = inner.output().regs.lock();
|
||||
let iir = regs.read(REG_IIR) & IIR_MASK;
|
||||
if iir == IIR_RXDA {
|
||||
Some(regs.read(REG_RBR))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(byte) = byte {
|
||||
inner.write_to_input(byte);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Access> DebugSink for Port8250<A> {
|
||||
fn putc(&self, c: u8) -> Result<(), Error> {
|
||||
self.inner.get().putc_to_output(c)
|
||||
}
|
||||
|
||||
fn supports_control_sequences(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Access> TerminalOutput for Inner<A> {
|
||||
fn write(&self, byte: u8, options: &TerminalOutputOptions) -> Result<(), Error> {
|
||||
let mut regs = self.regs.lock();
|
||||
if byte == b'\n' && options.contains(TerminalOutputOptions::NL_TO_CRNL) {
|
||||
regs.serial_out(b'\r')?;
|
||||
}
|
||||
regs.serial_out(byte)
|
||||
}
|
||||
|
||||
fn write_multiple(
|
||||
&self,
|
||||
bytes: &[u8],
|
||||
options: &TerminalOutputOptions,
|
||||
) -> Result<usize, Error> {
|
||||
let mut regs = self.regs.lock();
|
||||
for &byte in bytes {
|
||||
if byte == b'\n' && options.contains(TerminalOutputOptions::NL_TO_CRNL) {
|
||||
regs.serial_out(b'\r')?;
|
||||
}
|
||||
regs.serial_out(byte)?;
|
||||
}
|
||||
Ok(bytes.len())
|
||||
}
|
||||
|
||||
fn set_baud_rate(&self, baud: u32) -> Result<(), Error> {
|
||||
let mut regs = self.regs.lock();
|
||||
regs.set_baud_rate(baud)
|
||||
}
|
||||
|
||||
fn baud_rate(&self) -> u32 {
|
||||
let regs = self.regs.lock();
|
||||
regs.baud_rate
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Access> PortRegs<A> {
|
||||
fn init(&mut self, vendor: Vendor, baud_rate: u32) -> Result<(), Error> {
|
||||
match vendor {
|
||||
Vendor::Bcm2835Aux => {
|
||||
self.write(
|
||||
BCM2835_REG_AUX_MU_CNTL,
|
||||
BCM2835_AUX_MU_CNTL_RE | BCM2835_AUX_MU_CNTL_TE,
|
||||
);
|
||||
delay();
|
||||
}
|
||||
Vendor::DwApbUart => {
|
||||
self.write(DW_REG_SRR, 1);
|
||||
delay();
|
||||
self.write(DW_REG_HTX, 0);
|
||||
delay();
|
||||
|
||||
// wait while DW USR.BUSY == 1
|
||||
while self.read(DW_REG_USR) & (1 << 0) != 0 {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
self.set_baud_rate(baud_rate)
|
||||
}
|
||||
|
||||
fn serial_out(&mut self, byte: u8) -> Result<(), Error> {
|
||||
let mut timeout = 1000000;
|
||||
while timeout > 0 && self.read(REG_LSR) & LSR_TEMT == 0 {
|
||||
timeout -= 1;
|
||||
}
|
||||
if timeout == 0 {
|
||||
return Err(Error::TimedOut);
|
||||
}
|
||||
self.write(REG_THR, byte);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_baud_rate(&mut self, baud_rate: u32) -> Result<(), Error> {
|
||||
// TODO check that the divisor actually fits
|
||||
|
||||
self.baud_rate = baud_rate;
|
||||
|
||||
let divisor = (self.input_clock.0 / (baud_rate as u64 * 16)) as u32;
|
||||
|
||||
// Disable interrupts, clear FCR/MCR
|
||||
self.write(REG_IER, 0x00);
|
||||
self.write(REG_MCR, 0x00);
|
||||
self.write(REG_FCR, 0x00);
|
||||
|
||||
// Program divisor
|
||||
// DLAB=1
|
||||
let lcr = self.read(REG_LCR);
|
||||
self.write(REG_LCR, lcr | LCR_DLAB);
|
||||
self.write(REG_DLH, ((divisor >> 8) & 0xFF) as u8);
|
||||
self.write(REG_DLL, (divisor & 0xFF) as u8);
|
||||
delay();
|
||||
let lcr = self.read(REG_LCR);
|
||||
self.write(REG_LCR, lcr & !LCR_DLAB);
|
||||
delay();
|
||||
|
||||
// 8n1 setting
|
||||
let lcr = self.read(REG_LCR);
|
||||
self.write(REG_LCR, lcr | LCR_BITS_8);
|
||||
delay();
|
||||
|
||||
// DTR+RTS
|
||||
self.write(REG_MCR, MCR_DTR | MCR_RTS);
|
||||
|
||||
// Enable and drain FIFO
|
||||
self.write(REG_FCR, FCR_FIFO_ENABLE);
|
||||
delay();
|
||||
let fcr = self.read(REG_FCR);
|
||||
self.write(REG_FCR, fcr | FCR_RX_FIFO_RESET | FCR_TX_FIFO_RESET);
|
||||
delay();
|
||||
// TODO flush Rx
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Access> Deref for PortRegs<A> {
|
||||
type Target = A;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.regs
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Access> DerefMut for PortRegs<A> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.regs
|
||||
}
|
||||
}
|
||||
|
||||
fn delay() {
|
||||
for _ in 0..10000 {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ authors = ["Mark Poliakov <mark@alnyan.me>"]
|
||||
[dependencies]
|
||||
yggdrasil-abi.workspace = true
|
||||
device-api-macros = { workspace = true, optional = true }
|
||||
async-trait.workspace = true
|
||||
|
||||
[features]
|
||||
default = []
|
||||
|
||||
@@ -26,6 +26,28 @@ pub trait IntoHertz {
|
||||
}
|
||||
}
|
||||
|
||||
impl Hertz {
|
||||
pub fn divider(base: Self, desired: Self) -> Option<u32> {
|
||||
if desired > base || desired.0 == 0 {
|
||||
return None;
|
||||
}
|
||||
let value = base.0 / desired.0;
|
||||
value.try_into().ok()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u32> for Hertz {
|
||||
fn from(value: u32) -> Self {
|
||||
Self(value as u64)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u64> for Hertz {
|
||||
fn from(value: u64) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<u32> for Hertz {
|
||||
type Output = Hertz;
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use core::any::Any;
|
||||
|
||||
use alloc::sync::Arc;
|
||||
use yggdrasil_abi::error::Error;
|
||||
|
||||
@@ -51,4 +53,8 @@ pub trait Device: Sync + Send {
|
||||
fn as_clock_controller(self: Arc<Self>) -> Option<Arc<dyn ClockController>> {
|
||||
None
|
||||
}
|
||||
|
||||
fn as_any(self: Arc<Self>) -> Arc<dyn Any + Send + Sync> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
use core::{
|
||||
fmt,
|
||||
ops::Deref,
|
||||
sync::atomic::{AtomicU16, AtomicU32, Ordering},
|
||||
};
|
||||
|
||||
use alloc::sync::Arc;
|
||||
use yggdrasil_abi::{error::Error, io::device::i2c::I2CMasterInfo, process::ProcessId};
|
||||
|
||||
use crate::{
|
||||
clock::Hertz,
|
||||
device::{Device, DeviceInitContext},
|
||||
};
|
||||
|
||||
pub struct I2CAddress(u16);
|
||||
|
||||
pub struct I2CDevice {
|
||||
device: Arc<dyn I2CController>,
|
||||
address: AtomicU16,
|
||||
user: AtomicU32,
|
||||
}
|
||||
|
||||
pub trait I2CController: Device {
|
||||
fn bus_number(&self) -> u32;
|
||||
fn child_number(&self) -> Option<u32>;
|
||||
|
||||
fn set_speed(&self, speed: Hertz) -> Result<Hertz, Error>;
|
||||
fn capabilities(&self) -> I2CMasterInfo;
|
||||
|
||||
fn i2c_write(&self, address: I2CAddress, buffer: &[u8]) -> Result<usize, Error>;
|
||||
fn i2c_read(&self, address: I2CAddress, buffer: &mut [u8]) -> Result<usize, Error>;
|
||||
}
|
||||
|
||||
impl I2CAddress {
|
||||
pub fn as_8_bit(&self) -> Option<u8> {
|
||||
self.0.try_into().ok()
|
||||
}
|
||||
|
||||
pub fn as_10_bit(&self) -> u16 {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn is_8_bit(&self) -> bool {
|
||||
self.0 <= 0xFF
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for I2CAddress {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Display::fmt(&self.0, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u8> for I2CAddress {
|
||||
fn from(value: u8) -> Self {
|
||||
Self(value as u16)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<u16> for I2CAddress {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: u16) -> Result<Self, Self::Error> {
|
||||
if value >= (1 << 10) {
|
||||
return Err(Error::InvalidArgument);
|
||||
}
|
||||
Ok(Self(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for I2CDevice {
|
||||
type Target = dyn I2CController;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&*self.device
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Arc<dyn I2CController>> for I2CDevice {
|
||||
fn from(value: Arc<dyn I2CController>) -> Self {
|
||||
Self {
|
||||
device: value,
|
||||
address: AtomicU16::new(0),
|
||||
user: AtomicU32::new(0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl I2CDevice {
|
||||
pub fn set_slave_address(&self, address: I2CAddress) {
|
||||
self.address.store(address.0, Ordering::Release);
|
||||
}
|
||||
|
||||
pub fn set_speed(&self, speed: Hertz) -> Result<Hertz, Error> {
|
||||
self.device.set_speed(speed)
|
||||
}
|
||||
|
||||
pub fn slave_address(&self) -> I2CAddress {
|
||||
I2CAddress(self.address.load(Ordering::Acquire))
|
||||
}
|
||||
|
||||
pub fn check_lock(&self, id: ProcessId) -> Result<(), Error> {
|
||||
if self.user.load(Ordering::Acquire) == id.bits() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::PermissionDenied)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lock(&self, process: ProcessId) -> Result<(), Error> {
|
||||
if self
|
||||
.user
|
||||
.compare_exchange(0, process.bits(), Ordering::Acquire, Ordering::Relaxed)
|
||||
.is_ok()
|
||||
{
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::WouldBlock)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn release(&self, process: ProcessId) -> Result<(), Error> {
|
||||
if self
|
||||
.user
|
||||
.compare_exchange(process.bits(), 0, Ordering::Release, Ordering::Relaxed)
|
||||
.is_ok()
|
||||
{
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::InvalidOperation)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for I2CDevice {
|
||||
unsafe fn init(self: Arc<Self>, cx: DeviceInitContext) -> Result<(), Error> {
|
||||
self.device.clone().init(cx)
|
||||
}
|
||||
|
||||
unsafe fn init_irq(self: Arc<Self>) -> Result<(), Error> {
|
||||
self.device.clone().init_irq()
|
||||
}
|
||||
|
||||
fn display_name(&self) -> &str {
|
||||
self.device.display_name()
|
||||
}
|
||||
}
|
||||
@@ -83,10 +83,6 @@ pub trait InterruptHandler: Device {
|
||||
fn handle_irq(self: Arc<Self>, vector: IrqVector) -> bool;
|
||||
}
|
||||
|
||||
pub trait InterruptTable: Sync {
|
||||
fn handler(&self, index: usize) -> Option<&Arc<dyn InterruptHandler>>;
|
||||
}
|
||||
|
||||
pub trait ExternalInterruptController: Device {
|
||||
/// Performs IRQ delivery method configuration and registers a handler to execute when it is
|
||||
/// fired
|
||||
@@ -151,10 +147,6 @@ pub trait LocalInterruptController: Device {
|
||||
fn send_ipi(&self, target: IpiDeliveryTarget, msg: IpiMessage) -> Result<(), Error>;
|
||||
}
|
||||
|
||||
pub struct FixedInterruptTable<const N: usize> {
|
||||
rows: [Option<Arc<dyn InterruptHandler>>; N],
|
||||
}
|
||||
|
||||
impl IrqHandle {
|
||||
pub fn register(&self, handler: Arc<dyn InterruptHandler>) -> Result<(), Error> {
|
||||
self.intc.register_irq(self.irq, self.options, handler)
|
||||
@@ -165,43 +157,6 @@ impl IrqHandle {
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> FixedInterruptTable<N> {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
rows: [const { None }; N],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, index: usize, entry: Arc<dyn InterruptHandler>) -> Result<(), Error> {
|
||||
let row = self.rows.get_mut(index).ok_or(Error::InvalidArgument)?;
|
||||
if row.is_some() {
|
||||
return Err(Error::AlreadyExists);
|
||||
}
|
||||
*row = Some(entry);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn insert_least_loaded(
|
||||
&mut self,
|
||||
handler: Arc<dyn InterruptHandler>,
|
||||
) -> Result<usize, Error> {
|
||||
let index = self
|
||||
.rows
|
||||
.iter()
|
||||
.position(|p| p.is_none())
|
||||
.ok_or(Error::InvalidArgument)?;
|
||||
self.rows[index].replace(handler);
|
||||
Ok(index)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> InterruptTable for FixedInterruptTable<N> {
|
||||
#[inline]
|
||||
fn handler(&self, index: usize) -> Option<&Arc<dyn InterruptHandler>> {
|
||||
self.rows.get(index).and_then(|p| p.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl IrqLevel {
|
||||
pub fn override_default(self, value: IrqLevel) -> Self {
|
||||
match self {
|
||||
|
||||
@@ -8,8 +8,10 @@ pub mod bus;
|
||||
pub mod clock;
|
||||
pub mod device;
|
||||
pub mod gpio;
|
||||
pub mod i2c;
|
||||
pub mod interrupt;
|
||||
pub mod serial;
|
||||
pub mod spi;
|
||||
pub mod timer;
|
||||
|
||||
pub mod dma;
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
use alloc::boxed::Box;
|
||||
use async_trait::async_trait;
|
||||
use yggdrasil_abi::{error::Error, io::device::spi::SpiConfig};
|
||||
|
||||
use crate::device::Device;
|
||||
|
||||
#[async_trait]
|
||||
pub trait SpiController: Device {
|
||||
async fn spi_transfer(
|
||||
&self,
|
||||
tx_buffer: &[u8],
|
||||
rx_buffer: &mut [u8],
|
||||
config: &SpiConfig,
|
||||
) -> Result<usize, Error>;
|
||||
}
|
||||
@@ -9,6 +9,10 @@
|
||||
/// `.init_array`-based mechanism.
|
||||
pub macro device_tree_driver(
|
||||
compatible: [$($compatible:literal),+ $(,)?],
|
||||
$(config: {
|
||||
$($config_field:ident : $config_value:expr),+
|
||||
$(,)?
|
||||
},)?
|
||||
driver: $driver:tt
|
||||
) {
|
||||
struct __DtDriver;
|
||||
@@ -21,8 +25,16 @@ pub macro device_tree_driver(
|
||||
static __REGISTER_FN: extern "C" fn() = __register_fn;
|
||||
|
||||
extern "C" fn __register_fn() {
|
||||
#[allow(clippy::needless_update)]
|
||||
let config = $crate::driver::DriverConfig {
|
||||
$(
|
||||
$($config_field: $config_value,)+
|
||||
)?
|
||||
..Default::default()
|
||||
};
|
||||
$crate::driver::register_driver(
|
||||
&[$($compatible),+],
|
||||
config,
|
||||
&__DT_DRIVER
|
||||
)
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ pub mod util;
|
||||
|
||||
pub use controller::{map_interrupt, map_interrupt_at};
|
||||
pub use macros::device_tree_driver;
|
||||
pub use registry::{lookup_phandle, register_driver};
|
||||
pub use registry::{DriverConfig, lookup_phandle, register_driver};
|
||||
pub use syscon::DeviceTreeSyscon;
|
||||
pub use traits::{
|
||||
DeviceTreeClockController, DeviceTreeInterruptController, DeviceTreePinController,
|
||||
|
||||
@@ -7,8 +7,22 @@ use crate::DeviceTree;
|
||||
|
||||
use super::{Driver, Node};
|
||||
|
||||
/// Device tree driver configuration
|
||||
#[derive(Debug)]
|
||||
pub struct DriverConfig {
|
||||
/// If `false`, pinctrl setup for the node needs to be performed manually
|
||||
pub auto_pinctrl: bool,
|
||||
}
|
||||
|
||||
impl Default for DriverConfig {
|
||||
fn default() -> Self {
|
||||
Self { auto_pinctrl: true }
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct DriverRegistration {
|
||||
pub(crate) compatible: &'static [&'static str],
|
||||
pub(crate) config: DriverConfig,
|
||||
pub(crate) imp: &'static dyn Driver,
|
||||
}
|
||||
|
||||
@@ -25,9 +39,14 @@ impl DriverRegistration {
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn register_driver(compatible: &'static [&'static str], driver: &'static dyn Driver) {
|
||||
pub fn register_driver(
|
||||
compatible: &'static [&'static str],
|
||||
config: DriverConfig,
|
||||
driver: &'static dyn Driver,
|
||||
) {
|
||||
DRIVERS.write().push(DriverRegistration {
|
||||
compatible,
|
||||
config,
|
||||
imp: driver,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ use device_api::{
|
||||
clock::{ClockController, ClockHandle, ResetHandle},
|
||||
device::{Device, DeviceInitContext},
|
||||
gpio::PinHandle,
|
||||
i2c::I2CController,
|
||||
interrupt::{ExternalInterruptController, IrqHandle, MessageInterruptController},
|
||||
};
|
||||
use fdt_rs::spec::Phandle;
|
||||
@@ -61,6 +62,7 @@ pub struct Node {
|
||||
pub(crate) reset_controller: OneTimeInit<Arc<dyn DeviceTreeResetController>>,
|
||||
pub(crate) system_controller: OneTimeInit<Arc<dyn DeviceTreeSyscon>>,
|
||||
pub(crate) pin_controller: OneTimeInit<Arc<dyn DeviceTreePinController>>,
|
||||
pub(crate) i2c_controller: OneTimeInit<Arc<dyn I2CController>>,
|
||||
}
|
||||
|
||||
pub(crate) struct ProbedDevice {
|
||||
@@ -105,14 +107,17 @@ impl Node {
|
||||
.as_str_list()
|
||||
.find_map(|c| drivers.iter().find(|d| d.matches(c)));
|
||||
|
||||
if libk::config::get().device_tree.log_missing && driver.is_none() {
|
||||
// FIXME don't spam virtio missing stuff
|
||||
if !name.is_some_and(|n| n.starts_with("virtio_mmio")) {
|
||||
for (i, compatible) in compatible.as_str_list().enumerate() {
|
||||
if i == 0 {
|
||||
log::warn!("No driver for {name:?} ({compatible:?})");
|
||||
} else {
|
||||
log::warn!(" also {compatible:?}");
|
||||
#[cfg(any(not(target_arch = "x86_64"), rust_analyzer))]
|
||||
{
|
||||
if libk::config::get().device_tree.log_missing && driver.is_none() {
|
||||
// FIXME don't spam virtio missing stuff
|
||||
if !name.is_some_and(|n| n.starts_with("virtio_mmio")) {
|
||||
for (i, compatible) in compatible.as_str_list().enumerate() {
|
||||
if i == 0 {
|
||||
log::warn!("No driver for {name:?} ({compatible:?})");
|
||||
} else {
|
||||
log::warn!(" also {compatible:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -120,9 +125,11 @@ impl Node {
|
||||
let driver = driver?;
|
||||
|
||||
// Initialize default pinctrl before probing
|
||||
node.setup_pins()
|
||||
.inspect_err(|e| log::error!("{name:?}: pinctrl init error {e:?}"))
|
||||
.ok()?;
|
||||
if driver.config.auto_pinctrl {
|
||||
node.setup_pins(0)
|
||||
.inspect_err(|e| log::error!("{name:?}: pinctrl init error {e:?}"))
|
||||
.ok()?;
|
||||
}
|
||||
|
||||
let device = driver.imp.probe(node, cx);
|
||||
|
||||
@@ -198,6 +205,11 @@ impl Node {
|
||||
self.interrupt_controller.init(intc);
|
||||
}
|
||||
|
||||
/// Makes node an I²C controller node
|
||||
pub fn make_i2c_controller(&self, i2c: Arc<dyn I2CController>) {
|
||||
self.i2c_controller.init(i2c);
|
||||
}
|
||||
|
||||
/// When called from an msi-controller driver, informs the node of its capability as an MSI
|
||||
/// controller.
|
||||
pub fn make_msi_controller(&self, intc: Arc<dyn MessageInterruptController>) {
|
||||
@@ -314,6 +326,11 @@ impl Node {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the device associated with this node
|
||||
pub fn device(self: Arc<Self>) -> Option<Arc<dyn Device>> {
|
||||
Some(self.probe()?.device.clone())
|
||||
}
|
||||
|
||||
/// Returns an iterator over the node's children
|
||||
pub fn children(&self) -> impl Iterator<Item = &Arc<Node>> {
|
||||
self.children.get().iter()
|
||||
@@ -451,6 +468,11 @@ impl Node {
|
||||
self.pin_controller.try_get().cloned()
|
||||
}
|
||||
|
||||
/// Attempts to get an I2C controller represented by this node, if any
|
||||
pub fn as_i2c_controller(&self) -> Option<Arc<dyn I2CController>> {
|
||||
self.i2c_controller.try_get().cloned()
|
||||
}
|
||||
|
||||
/// Returns the `#address-cells` value of the node's parent bus
|
||||
pub fn bus_address_cells(&self) -> usize {
|
||||
self.bus_address_cells
|
||||
@@ -527,9 +549,11 @@ impl Node {
|
||||
tree::dump(self.dt_node.clone(), 0);
|
||||
}
|
||||
|
||||
fn setup_pins(&self) -> Result<(), Error> {
|
||||
/// Configure the `index`th pinctrl mode for the peripheral node
|
||||
pub fn setup_pins(&self, index: usize) -> Result<(), Error> {
|
||||
// TODO lookup pin state by name
|
||||
let Some(pinctrl0) = self.prop_usize("pinctrl-0") else {
|
||||
let field_name = alloc::format!("pinctrl-{index}");
|
||||
let Some(pinctrl0) = self.prop_usize(&field_name) else {
|
||||
return Ok(());
|
||||
};
|
||||
let pinctrl0 = lookup_phandle(pinctrl0 as Phandle, false).ok_or(Error::DoesNotExist)?;
|
||||
@@ -596,6 +620,7 @@ fn unflatten_node(
|
||||
reset_controller: OneTimeInit::new(),
|
||||
system_controller: OneTimeInit::new(),
|
||||
pin_controller: OneTimeInit::new(),
|
||||
i2c_controller: OneTimeInit::new(),
|
||||
});
|
||||
|
||||
if let Some(phandle) = phandle {
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
use core::task::{Context, Poll};
|
||||
|
||||
use alloc::boxed::Box;
|
||||
use async_trait::async_trait;
|
||||
use device_api::{
|
||||
clock::Hertz,
|
||||
i2c::{I2CAddress, I2CDevice},
|
||||
};
|
||||
use yggdrasil_abi::{
|
||||
error::Error,
|
||||
io::device::i2c::{self, I2CRequestVariant},
|
||||
option::RequestValue,
|
||||
process::ProcessId,
|
||||
};
|
||||
|
||||
use crate::{device::char::CharDevice, task::thread::Thread, vfs::FileReadiness};
|
||||
|
||||
// TODO timeouts and better access
|
||||
#[async_trait]
|
||||
impl CharDevice for I2CDevice {
|
||||
async fn read(&self, buffer: &mut [u8]) -> Result<usize, Error> {
|
||||
self.read_nonblocking(buffer)
|
||||
}
|
||||
async fn write(&self, buffer: &[u8]) -> Result<usize, Error> {
|
||||
self.write_nonblocking(buffer)
|
||||
}
|
||||
fn read_nonblocking(&self, buffer: &mut [u8]) -> Result<usize, Error> {
|
||||
let pid = Thread::current().process_id();
|
||||
self.check_lock(pid)?;
|
||||
self.i2c_read(self.slave_address(), buffer)
|
||||
}
|
||||
fn write_nonblocking(&self, buffer: &[u8]) -> Result<usize, Error> {
|
||||
let pid = Thread::current().process_id();
|
||||
self.check_lock(pid)?;
|
||||
self.i2c_write(self.slave_address(), buffer)
|
||||
}
|
||||
fn device_request(&self, option: u32, buffer: &mut [u8], len: usize) -> Result<usize, Error> {
|
||||
if let Ok(req) = I2CRequestVariant::try_from(option) {
|
||||
match req {
|
||||
I2CRequestVariant::SetAddress => {
|
||||
let address = i2c::SetAddress::load_request(&buffer[..len])?;
|
||||
let address = I2CAddress::try_from(address)?;
|
||||
self.set_slave_address(address);
|
||||
Ok(0)
|
||||
}
|
||||
I2CRequestVariant::SetSpeed => {
|
||||
let speed = i2c::SetSpeed::load_request(&buffer[..len])?;
|
||||
let speed = Hertz::from(speed);
|
||||
let actual = self.set_speed(speed)?.0 as u32;
|
||||
let len = i2c::SetSpeed::store_response(&actual, buffer)?;
|
||||
Ok(len)
|
||||
}
|
||||
I2CRequestVariant::GetMasterInfo => {
|
||||
let info = self.capabilities();
|
||||
let len = i2c::GetMasterInfo::store_response(&info, buffer)?;
|
||||
Ok(len)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Err(Error::InvalidArgument)
|
||||
}
|
||||
}
|
||||
|
||||
fn open(&self, process: ProcessId) -> Result<(), Error> {
|
||||
I2CDevice::lock(self, process)
|
||||
}
|
||||
fn close(&self, process: ProcessId) -> Result<(), Error> {
|
||||
I2CDevice::release(self, process)
|
||||
}
|
||||
}
|
||||
impl FileReadiness for I2CDevice {
|
||||
fn poll_read(&self, cx: &mut Context<'_>) -> Poll<Result<(), Error>> {
|
||||
let _ = cx;
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
@@ -3,10 +3,13 @@ use core::{any::Any, ops::Deref};
|
||||
use alloc::{boxed::Box, sync::Arc};
|
||||
use async_trait::async_trait;
|
||||
use device_api::device::Device;
|
||||
use yggdrasil_abi::error::Error;
|
||||
use yggdrasil_abi::{error::Error, process::ProcessId};
|
||||
|
||||
use crate::vfs::{CommonImpl, FileReadiness, NodeRef};
|
||||
|
||||
pub mod i2c;
|
||||
pub mod spi;
|
||||
|
||||
#[async_trait]
|
||||
pub trait CharDevice: Device + FileReadiness {
|
||||
async fn read(&self, buffer: &mut [u8]) -> Result<usize, Error> {
|
||||
@@ -25,6 +28,15 @@ pub trait CharDevice: Device + FileReadiness {
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
|
||||
fn open(&self, process: ProcessId) -> Result<(), Error> {
|
||||
let _ = process;
|
||||
Ok(())
|
||||
}
|
||||
fn close(&self, process: ProcessId) -> Result<(), Error> {
|
||||
let _ = process;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn is_readable(&self) -> bool {
|
||||
true
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
use core::task::{Context, Poll};
|
||||
|
||||
use alloc::{boxed::Box, sync::Arc};
|
||||
use async_trait::async_trait;
|
||||
use device_api::{device::Device, spi::SpiController};
|
||||
use libk_util::sync::spin_rwlock::IrqSafeRwLock;
|
||||
use yggdrasil_abi::{
|
||||
error::Error,
|
||||
io::device::spi::{self, SpiConfig, SpiRequestVariant},
|
||||
option::RequestValue,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
device::char::CharDevice,
|
||||
task::{mem::ForeignPointer, thread::Thread},
|
||||
vfs::FileReadiness,
|
||||
};
|
||||
|
||||
pub struct SpiControllerDevice {
|
||||
device: Arc<dyn SpiController>,
|
||||
config: IrqSafeRwLock<SpiConfig>,
|
||||
}
|
||||
|
||||
impl From<Arc<dyn SpiController>> for SpiControllerDevice {
|
||||
fn from(value: Arc<dyn SpiController>) -> Self {
|
||||
Self {
|
||||
device: value,
|
||||
config: IrqSafeRwLock::new(Default::default()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for SpiControllerDevice {
|
||||
fn display_name(&self) -> &str {
|
||||
self.device.display_name()
|
||||
}
|
||||
}
|
||||
|
||||
// TODO timeouts and better access
|
||||
#[async_trait]
|
||||
impl CharDevice for SpiControllerDevice {
|
||||
async fn read(&self, _buffer: &mut [u8]) -> Result<usize, Error> {
|
||||
Err(Error::InvalidOperation)
|
||||
}
|
||||
async fn write(&self, _buffer: &[u8]) -> Result<usize, Error> {
|
||||
Err(Error::InvalidOperation)
|
||||
}
|
||||
fn read_nonblocking(&self, _buffer: &mut [u8]) -> Result<usize, Error> {
|
||||
Err(Error::InvalidOperation)
|
||||
}
|
||||
fn write_nonblocking(&self, _buffer: &[u8]) -> Result<usize, Error> {
|
||||
Err(Error::InvalidOperation)
|
||||
}
|
||||
fn device_request(&self, option: u32, buffer: &mut [u8], len: usize) -> Result<usize, Error> {
|
||||
if let Ok(request) = SpiRequestVariant::try_from(option) {
|
||||
match request {
|
||||
SpiRequestVariant::Transfer => {
|
||||
let xfer = spi::Transfer::load_request(&buffer[..len])?;
|
||||
let thread = Thread::current();
|
||||
let space = thread.address_space();
|
||||
let len = xfer.len;
|
||||
let tx_buffer = unsafe { xfer.tx_buffer.validate_user_slice(len, space) }?;
|
||||
let rx_buffer = unsafe { xfer.rx_buffer.validate_user_slice_mut(len, space) }?;
|
||||
let config = self.config.read();
|
||||
|
||||
let len = block!(
|
||||
self.device
|
||||
.spi_transfer(tx_buffer, rx_buffer, &config)
|
||||
.await
|
||||
)??;
|
||||
|
||||
let len = spi::Transfer::store_response(&len, buffer)?;
|
||||
|
||||
Ok(len)
|
||||
}
|
||||
SpiRequestVariant::SetConfig => {
|
||||
// TODO validate config against min/max controller capabilities
|
||||
let config = spi::SetConfig::load_request(&buffer[..len])?;
|
||||
*self.config.write() = config;
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FileReadiness for SpiControllerDevice {
|
||||
fn poll_read(&self, cx: &mut Context<'_>) -> Poll<Result<(), Error>> {
|
||||
let _ = cx;
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,11 @@ use libk_util::{
|
||||
StaticVector,
|
||||
sync::{IrqSafeSpinlock, spin_rwlock::IrqSafeRwLock},
|
||||
};
|
||||
use yggdrasil_abi::{error::Error, io::TerminalSize, primitive_enum};
|
||||
use yggdrasil_abi::{
|
||||
error::Error,
|
||||
io::{TerminalOutputOptions, TerminalSize},
|
||||
primitive_enum,
|
||||
};
|
||||
|
||||
use crate::{task::runtime, vfs::TerminalOutput};
|
||||
|
||||
@@ -520,7 +524,8 @@ impl TerminalOutput for ConsoleWrapper {
|
||||
TerminalSize { rows, columns }
|
||||
}
|
||||
|
||||
fn write(&self, byte: u8) -> Result<(), Error> {
|
||||
fn write(&self, byte: u8, _options: &TerminalOutputOptions) -> Result<(), Error> {
|
||||
// TODO handle options
|
||||
self.0.write_char(byte);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
use alloc::{sync::Arc, vec::Vec};
|
||||
use device_api::interrupt::{InterruptHandler, IrqVector};
|
||||
use libk_util::sync::spin_rwlock::IrqSafeRwLock;
|
||||
|
||||
pub struct FixedInterruptTable {
|
||||
rows: Vec<IrqSafeRwLock<Vec<Arc<dyn InterruptHandler>>>>,
|
||||
}
|
||||
|
||||
impl FixedInterruptTable {
|
||||
pub fn new(irq_lines: usize) -> Self {
|
||||
Self {
|
||||
rows: (0..irq_lines)
|
||||
.map(|_| IrqSafeRwLock::new(Vec::new()))
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert(&self, line: usize, handler: Arc<dyn InterruptHandler>) {
|
||||
let mut row = self.rows[line].write();
|
||||
row.push(handler);
|
||||
}
|
||||
|
||||
pub fn insert_least_loaded(&self, handler: Arc<dyn InterruptHandler>) -> usize {
|
||||
let min_line = self
|
||||
.rows
|
||||
.iter()
|
||||
.enumerate()
|
||||
.min_by_key(|(_, row)| row.read().len())
|
||||
.unwrap()
|
||||
.0;
|
||||
self.insert(min_line, handler);
|
||||
min_line
|
||||
}
|
||||
|
||||
pub fn handle_irq_line(&self, line: usize, vector: IrqVector) -> bool {
|
||||
let row = self.rows[line].read();
|
||||
for handler in row.iter() {
|
||||
if handler.clone().handle_irq(vector) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
@@ -4,12 +4,17 @@ use core::{
|
||||
};
|
||||
|
||||
use alloc::{collections::BTreeMap, format, sync::Arc, vec::Vec};
|
||||
use device_api::{
|
||||
i2c::{I2CController, I2CDevice},
|
||||
spi::SpiController,
|
||||
};
|
||||
use libk_util::{OneTimeInit, sync::spin_rwlock::IrqSafeRwLock};
|
||||
use yggdrasil_abi::{error::Error, io::FileMode};
|
||||
|
||||
use crate::{
|
||||
config,
|
||||
debug::{self, DebugSink},
|
||||
device::char::spi::SpiControllerDevice,
|
||||
fs::devfs,
|
||||
vfs::NodeRef,
|
||||
};
|
||||
@@ -46,16 +51,30 @@ pub struct SerialTerminalRegistry {
|
||||
registry: GenericRegistry<Arc<dyn CharDevice>>,
|
||||
}
|
||||
|
||||
// Manages devices: i2c<N>, i2c<N>-<M>
|
||||
pub struct I2CDeviceRegistry {
|
||||
registry: GenericRegistry<Arc<I2CDevice>>,
|
||||
}
|
||||
|
||||
// Manages devices: spi<X>.<Y>
|
||||
pub struct SpiDeviceRegistry {
|
||||
registry: GenericRegistry<Arc<SpiControllerDevice>>,
|
||||
}
|
||||
|
||||
pub struct DeviceRegistry {
|
||||
pub display: DisplayDeviceRegistry,
|
||||
pub terminal: TerminalRegistry,
|
||||
pub serial_terminal: SerialTerminalRegistry,
|
||||
pub i2c: I2CDeviceRegistry,
|
||||
pub spi: SpiDeviceRegistry,
|
||||
}
|
||||
|
||||
pub static DEVICE_REGISTRY: DeviceRegistry = DeviceRegistry {
|
||||
display: DisplayDeviceRegistry::new(),
|
||||
terminal: TerminalRegistry::new(),
|
||||
serial_terminal: SerialTerminalRegistry::new(),
|
||||
i2c: I2CDeviceRegistry::new(),
|
||||
spi: SpiDeviceRegistry::new(),
|
||||
};
|
||||
|
||||
impl TerminalRegistry {
|
||||
@@ -117,6 +136,38 @@ impl DisplayDeviceRegistry {
|
||||
}
|
||||
}
|
||||
|
||||
impl I2CDeviceRegistry {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
registry: GenericRegistry::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn register_bus(&self, device: Arc<dyn I2CController>) -> Result<u32, Error> {
|
||||
let wrapper = Arc::new(I2CDevice::from(device));
|
||||
let id = self.registry.register(wrapper.clone())?;
|
||||
let name = format!("i2c{id}");
|
||||
devfs::add_named_char_device(wrapper.clone(), &name, FileMode::new(0o600))?;
|
||||
Ok(id)
|
||||
}
|
||||
}
|
||||
|
||||
impl SpiDeviceRegistry {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
registry: GenericRegistry::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn register_bus(&self, device: Arc<dyn SpiController>) -> Result<u32, Error> {
|
||||
let wrapper = Arc::new(SpiControllerDevice::from(device));
|
||||
let id = self.registry.register(wrapper.clone())?;
|
||||
let name = format!("spi{id}");
|
||||
devfs::add_named_char_device(wrapper.clone(), &name, FileMode::new(0o600))?;
|
||||
Ok(id)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> GenericRegistry<T> {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
|
||||
@@ -7,6 +7,7 @@ use yggdrasil_abi::error::Error;
|
||||
pub mod block;
|
||||
pub mod char;
|
||||
pub mod display;
|
||||
pub mod interrupt;
|
||||
pub mod manager;
|
||||
|
||||
// TODO get rid of this, this does not work when there are multiple interrupt controllers
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use libk_util::sync::IrqSafeSpinlock;
|
||||
use yggdrasil_abi::{error::Error, io::SeekFrom};
|
||||
use yggdrasil_abi::{error::Error, io::SeekFrom, process::ProcessId};
|
||||
|
||||
use crate::{
|
||||
device::{block::BlockDeviceFile, char::CharDeviceFile},
|
||||
@@ -20,6 +20,7 @@ pub struct CharFile {
|
||||
pub(super) node: NodeRef,
|
||||
pub(super) read: bool,
|
||||
pub(super) write: bool,
|
||||
pub(super) pid: ProcessId,
|
||||
}
|
||||
|
||||
impl BlockFile {
|
||||
@@ -103,3 +104,10 @@ impl CharFile {
|
||||
self.device.0.is_terminal()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for CharFile {
|
||||
fn drop(&mut self) {
|
||||
// TODO doesn't work with fork
|
||||
self.device.close(self.pid).ok();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ use yggdrasil_abi::{
|
||||
|
||||
use crate::{
|
||||
device::{block::BlockDeviceFile, char::CharDeviceFile},
|
||||
task::process::Process,
|
||||
task::{process::Process, thread::Thread},
|
||||
vfs::{
|
||||
FdPoll, FileReadiness, Node, SharedMemory, TimerFile,
|
||||
node::NodeRef,
|
||||
@@ -254,6 +254,10 @@ impl File {
|
||||
node: NodeRef,
|
||||
opts: OpenOptions,
|
||||
) -> Result<Arc<Self>, Error> {
|
||||
let pid = Thread::current().process_id();
|
||||
|
||||
device.open(pid)?;
|
||||
|
||||
let read = opts.contains(OpenOptions::READ);
|
||||
let write = opts.contains(OpenOptions::WRITE);
|
||||
|
||||
@@ -269,6 +273,7 @@ impl File {
|
||||
node,
|
||||
read,
|
||||
write,
|
||||
pid,
|
||||
})))
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ use async_trait::async_trait;
|
||||
use libk_util::{ring::BlockingRingQueue, sync::spin_rwlock::IrqSafeRwLock};
|
||||
use yggdrasil_abi::{
|
||||
error::Error,
|
||||
io::{TerminalOptions, TerminalSize},
|
||||
io::{TerminalOptions, TerminalOutputOptions, TerminalSize},
|
||||
};
|
||||
|
||||
use super::{
|
||||
@@ -28,11 +28,16 @@ struct PtyOutput {
|
||||
}
|
||||
|
||||
impl TerminalOutput for PtyOutput {
|
||||
fn write(&self, byte: u8) -> Result<(), Error> {
|
||||
// TODO options
|
||||
fn write(&self, byte: u8, _options: &TerminalOutputOptions) -> Result<(), Error> {
|
||||
block!(self.ring.write(byte).await)
|
||||
}
|
||||
|
||||
fn write_multiple(&self, bytes: &[u8]) -> Result<usize, Error> {
|
||||
fn write_multiple(
|
||||
&self,
|
||||
bytes: &[u8],
|
||||
_options: &TerminalOutputOptions,
|
||||
) -> Result<usize, Error> {
|
||||
block!(self.ring.write_all(bytes).await)?;
|
||||
Ok(bytes.len())
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use core::{
|
||||
mem::MaybeUninit,
|
||||
ops::Deref,
|
||||
sync::atomic::{AtomicBool, Ordering},
|
||||
task::{Context, Poll},
|
||||
};
|
||||
@@ -19,7 +20,7 @@ use yggdrasil_abi::{
|
||||
device::{self, TerminalRequestVariant},
|
||||
},
|
||||
option::RequestValue,
|
||||
process::{ProcessGroupId, Signal},
|
||||
process::{ProcessGroupId, ProcessId, Signal},
|
||||
};
|
||||
|
||||
use crate::{device::char::CharDevice, task::process::Process};
|
||||
@@ -41,7 +42,7 @@ pub struct TerminalInput {
|
||||
|
||||
// Program -> screen, PTY master, terminal, etc.
|
||||
pub trait TerminalOutput: Sync + Send {
|
||||
fn write(&self, byte: u8) -> Result<(), Error>;
|
||||
fn write(&self, byte: u8, options: &TerminalOutputOptions) -> Result<(), Error>;
|
||||
|
||||
fn notify_readers(&self) {}
|
||||
|
||||
@@ -56,10 +57,14 @@ pub trait TerminalOutput: Sync + Send {
|
||||
Err(Error::InvalidOperation)
|
||||
}
|
||||
|
||||
fn write_multiple(&self, bytes: &[u8]) -> Result<usize, Error> {
|
||||
fn write_multiple(
|
||||
&self,
|
||||
bytes: &[u8],
|
||||
options: &TerminalOutputOptions,
|
||||
) -> Result<usize, Error> {
|
||||
let mut written = 0;
|
||||
for &byte in bytes {
|
||||
self.write(byte)?;
|
||||
self.write(byte, options)?;
|
||||
written += 1;
|
||||
}
|
||||
Ok(written)
|
||||
@@ -78,6 +83,15 @@ pub trait TerminalOutput: Sync + Send {
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
}
|
||||
|
||||
fn open(&self, pid: ProcessId) -> Result<(), Error> {
|
||||
let _ = pid;
|
||||
Ok(())
|
||||
}
|
||||
fn close(&self, pid: ProcessId) -> Result<(), Error> {
|
||||
let _ = pid;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
struct InputBuffer {
|
||||
@@ -126,12 +140,14 @@ impl<O: TerminalOutput> Terminal<O> {
|
||||
}
|
||||
|
||||
pub fn putc_to_output(&self, byte: u8) -> Result<(), Error> {
|
||||
self.output.write(byte)
|
||||
let options = self.config.read().output;
|
||||
self.output.write(byte, &options)
|
||||
}
|
||||
|
||||
pub fn write_to_output(&self, buffer: &[u8]) -> Result<usize, Error> {
|
||||
let options = self.config.read().output;
|
||||
// TODO handle options
|
||||
self.output.write_multiple(buffer)
|
||||
self.output.write_multiple(buffer, &options)
|
||||
}
|
||||
|
||||
pub fn write_to_input(&self, mut byte: u8) {
|
||||
@@ -151,7 +167,9 @@ impl<O: TerminalOutput> Terminal<O> {
|
||||
buffer.erase_pending() && config.line.contains(TerminalLineOptions::ECHO_ERASE);
|
||||
|
||||
if echo {
|
||||
self.output.write_multiple(b"\x1B[D \x1B[D").ok();
|
||||
self.output
|
||||
.write_multiple(b"\x1B[D \x1B[D", &config.output)
|
||||
.ok();
|
||||
}
|
||||
|
||||
return;
|
||||
@@ -160,17 +178,16 @@ impl<O: TerminalOutput> Terminal<O> {
|
||||
if byte == b'\n' {
|
||||
// TODO NL_TO_CRNL
|
||||
if config.is_echo_newline() {
|
||||
if config.output.contains(TerminalOutputOptions::NL_TO_CRNL) {
|
||||
self.output.write(b'\r').ok();
|
||||
}
|
||||
self.output.write(byte).ok();
|
||||
self.output.write(byte, &config.output).ok();
|
||||
}
|
||||
} else if byte.is_ascii_control() {
|
||||
if config.line.contains(TerminalLineOptions::ECHO) {
|
||||
self.output.write_multiple(&[b'^', byte + 0x40]).ok();
|
||||
self.output
|
||||
.write_multiple(&[b'^', byte + 0x40], &config.output)
|
||||
.ok();
|
||||
}
|
||||
} else if config.line.contains(TerminalLineOptions::ECHO) {
|
||||
self.output.write(byte).ok();
|
||||
self.output.write(byte, &config.output).ok();
|
||||
}
|
||||
|
||||
if byte == config.chars.interrupt {
|
||||
@@ -417,6 +434,13 @@ impl<O: TerminalOutput> CharDevice for Terminal<O> {
|
||||
fn device_request(&self, option: u32, buffer: &mut [u8], len: usize) -> Result<usize, Error> {
|
||||
self.handle_device_request(option, buffer, len)
|
||||
}
|
||||
|
||||
fn open(&self, pid: ProcessId) -> Result<(), Error> {
|
||||
self.output.open(pid)
|
||||
}
|
||||
fn close(&self, pid: ProcessId) -> Result<(), Error> {
|
||||
self.output.close(pid)
|
||||
}
|
||||
}
|
||||
|
||||
impl<O: TerminalOutput> FileReadiness for Terminal<O> {
|
||||
@@ -431,6 +455,44 @@ impl<O: TerminalOutput> Device for Terminal<O> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: TerminalOutput> TerminalOutput for Arc<T> {
|
||||
fn write(&self, byte: u8, options: &TerminalOutputOptions) -> Result<(), Error> {
|
||||
self.deref().write(byte, options)
|
||||
}
|
||||
fn write_multiple(
|
||||
&self,
|
||||
bytes: &[u8],
|
||||
options: &TerminalOutputOptions,
|
||||
) -> Result<usize, Error> {
|
||||
self.deref().write_multiple(bytes, options)
|
||||
}
|
||||
|
||||
fn notify_readers(&self) {
|
||||
self.deref().notify_readers();
|
||||
}
|
||||
|
||||
fn size(&self) -> TerminalSize {
|
||||
self.deref().size()
|
||||
}
|
||||
fn set_size(&self, size: TerminalSize) -> Result<(), Error> {
|
||||
self.deref().set_size(size)
|
||||
}
|
||||
|
||||
fn baud_rate(&self) -> u32 {
|
||||
self.deref().baud_rate()
|
||||
}
|
||||
fn set_baud_rate(&self, baud: u32) -> Result<(), Error> {
|
||||
self.deref().set_baud_rate(baud)
|
||||
}
|
||||
|
||||
fn open(&self, pid: ProcessId) -> Result<(), Error> {
|
||||
self.deref().open(pid)
|
||||
}
|
||||
fn close(&self, pid: ProcessId) -> Result<(), Error> {
|
||||
self.deref().close(pid)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_all(source: &mut RingBuffer<u8>, target: &mut [u8], eof: Option<u8>) -> usize {
|
||||
let mut pos = 0;
|
||||
while pos < target.len()
|
||||
|
||||
@@ -39,7 +39,7 @@ use crate::{
|
||||
|
||||
static BSP_STACK: BootStack<BOOT_STACK_SIZE> = BootStack::zeroed();
|
||||
pub(crate) static SPIN_TABLE_STACK: AtomicUsize = AtomicUsize::new(0);
|
||||
static mut KERNEL_LOAD_BASE: u64 = 0;
|
||||
pub(crate) static mut KERNEL_LOAD_BASE: u64 = 0;
|
||||
static mut DTB_PHYSICAL_ADDRESS: PhysicalAddress = PhysicalAddress::ZERO;
|
||||
|
||||
unsafe extern "C" fn relocate_kernel(image_base: i64, rela_start: usize, rela_end: usize) {
|
||||
|
||||
@@ -16,7 +16,7 @@ use abi::{SyscallFunction, process::Signal};
|
||||
use kernel_arch::{Architecture, ArchitectureImpl, sync::hack_locks};
|
||||
use kernel_arch_aarch64::{
|
||||
context::ExceptionFrame,
|
||||
mem::table::{EntryType, L1, L2, L3, PageTable},
|
||||
mem::table::{EntryType, L1, L2, L3, PageAttributes, PageTable},
|
||||
};
|
||||
use libk::{device::external_interrupt_controller, task::thread::Thread};
|
||||
use libk_mm::{PageFaultKind, address::PhysicalAddress, table::EntryLevelExt};
|
||||
@@ -55,24 +55,24 @@ unsafe fn perform_ptw<F: Fn(u32, EntryType)>(virt: usize, handler: F) {
|
||||
handler(0, EntryType::Invalid);
|
||||
return;
|
||||
};
|
||||
handler(0, EntryType::Table(ttbr_phys));
|
||||
handler(0, EntryType::Table(PageAttributes::empty(), ttbr_phys));
|
||||
|
||||
let l2 = l1.walk(l1i);
|
||||
handler(1, l2);
|
||||
let l2 = match l2 {
|
||||
EntryType::Table(l2) => PageTable::<L2>::from_physical(l2).unwrap(),
|
||||
EntryType::Table(_, l2) => PageTable::<L2>::from_physical(l2).unwrap(),
|
||||
_ => return,
|
||||
};
|
||||
|
||||
let l3 = l2.walk(l2i);
|
||||
handler(2, l3);
|
||||
let l3 = match l3 {
|
||||
EntryType::Table(l3) => PageTable::<L3>::from_physical(l3).unwrap(),
|
||||
EntryType::Table(_, l3) => PageTable::<L3>::from_physical(l3).unwrap(),
|
||||
_ => return,
|
||||
};
|
||||
|
||||
let l3e = match l3[l3i].as_page() {
|
||||
Some(page) => EntryType::Page(page),
|
||||
Some(page) => EntryType::Page(l3[l3i].attributes(), page),
|
||||
None => EntryType::Invalid,
|
||||
};
|
||||
handler(3, l3e);
|
||||
|
||||
@@ -8,9 +8,8 @@ use alloc::sync::Arc;
|
||||
use device_api::{
|
||||
device::{Device, DeviceInitContext},
|
||||
interrupt::{
|
||||
ExternalInterruptController, FixedInterruptTable, InterruptHandler, InterruptTable,
|
||||
IpiDeliveryTarget, IpiMessage, Irq, IrqHandle, IrqLevel, IrqOptions, IrqTrigger, IrqVector,
|
||||
LocalInterruptController,
|
||||
ExternalInterruptController, InterruptHandler, IpiDeliveryTarget, IpiMessage, Irq,
|
||||
IrqHandle, IrqLevel, IrqOptions, IrqTrigger, IrqVector, LocalInterruptController,
|
||||
},
|
||||
};
|
||||
use device_tree::{
|
||||
@@ -18,12 +17,15 @@ use device_tree::{
|
||||
driver::{DeviceTreeInterruptController, Node, ProbeContext, device_tree_driver},
|
||||
};
|
||||
use kernel_arch_aarch64::{CPU_COUNT, GicInterface};
|
||||
use libk::{arch::Cpu, device::register_external_interrupt_controller, task::cpu_index};
|
||||
use libk::{
|
||||
arch::Cpu,
|
||||
device::{interrupt::FixedInterruptTable, register_external_interrupt_controller},
|
||||
task::cpu_index,
|
||||
};
|
||||
use libk_mm::{
|
||||
address::PhysicalAddress,
|
||||
device::{DeviceMemoryIo, RawDeviceMemoryMapping},
|
||||
};
|
||||
use libk_util::sync::spin_rwlock::IrqSafeRwLock;
|
||||
|
||||
use self::{gicc::Gicc, gicd::Gicd};
|
||||
|
||||
@@ -43,7 +45,7 @@ pub mod gicv2m;
|
||||
pub struct Gic {
|
||||
gicc: Gicc,
|
||||
gicd: Gicd,
|
||||
table: IrqSafeRwLock<FixedInterruptTable<MAX_IRQ>>,
|
||||
table: FixedInterruptTable,
|
||||
}
|
||||
|
||||
unsafe impl Sync for Gic {}
|
||||
@@ -72,7 +74,7 @@ impl ExternalInterruptController for Gic {
|
||||
options: IrqOptions,
|
||||
handler: Arc<dyn InterruptHandler>,
|
||||
) -> Result<(), Error> {
|
||||
let mut table = self.table.write();
|
||||
// let mut table = self.table.write();
|
||||
|
||||
let index = match irq {
|
||||
Irq::External(i) => i + GIC_SPI_START,
|
||||
@@ -89,7 +91,7 @@ impl ExternalInterruptController for Gic {
|
||||
if index >= GIC_SPI_START as usize {
|
||||
self.gicd.configure_irq(index, options);
|
||||
}
|
||||
table.insert(index, handler)?;
|
||||
self.table.insert(index, handler);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -128,17 +130,9 @@ impl ExternalInterruptController for Gic {
|
||||
Irq::External(irq_number as u32 - GIC_SPI_START)
|
||||
};
|
||||
|
||||
{
|
||||
let table = self.table.read();
|
||||
let entry = match table.handler(irq_number) {
|
||||
Some(handler) => handler.clone(),
|
||||
None => {
|
||||
log::warn!("No handler for irq{}", irq_number);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
entry.handle_irq(IrqVector::Irq(irq));
|
||||
let vector = IrqVector::Irq(irq);
|
||||
if !self.table.handle_irq_line(irq_number, vector) {
|
||||
log::warn!("No handler for irq{irq_number}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -242,11 +236,12 @@ impl Gic {
|
||||
|
||||
// self.gicd.init(gicd);
|
||||
// self.gicc.init(gicc);
|
||||
let table = FixedInterruptTable::new(MAX_IRQ);
|
||||
|
||||
Ok(Self {
|
||||
gicd,
|
||||
gicc,
|
||||
table: IrqSafeRwLock::new(FixedInterruptTable::new()),
|
||||
table, // table: IrqSafeRwLock::new(FixedInterruptTable::new()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -287,6 +287,7 @@ impl AArch64 {
|
||||
MACHINE_NAME.init(machine.into());
|
||||
}
|
||||
log::info!("Boot arguments: {bootargs:?}");
|
||||
log::info!("Boot address: {:#x}", unsafe { boot::KERNEL_LOAD_BASE });
|
||||
|
||||
log::info!("Initializing aarch64 platform");
|
||||
|
||||
|
||||
@@ -168,6 +168,11 @@ impl Riscv64 {
|
||||
if is_bsp {
|
||||
call_init_array();
|
||||
|
||||
unsafe extern "C" {
|
||||
static __kernel_start: u8;
|
||||
}
|
||||
log::info!("Boot address: {:#x}", (&raw const __kernel_start).addr());
|
||||
|
||||
atomic::compiler_fence(Ordering::SeqCst);
|
||||
|
||||
libk::debug::init();
|
||||
|
||||
+28
-11
@@ -20,8 +20,12 @@ use libk_mm::{
|
||||
phys::{self, PhysicalMemoryRegion},
|
||||
table::EntryLevelExt,
|
||||
};
|
||||
use peripherals::{i8253::I8253, ps2::PS2Controller, rtc::Rtc, serial::ComPort};
|
||||
use peripherals::{i8253::I8253, ps2::PS2Controller, rtc::Rtc};
|
||||
use ygg_driver_pci::PciBusManager;
|
||||
use ygg_driver_serial_8250::{
|
||||
Uart8250,
|
||||
io::{Io8250Access, Io8250Config},
|
||||
};
|
||||
|
||||
use crate::fs::{INITRD_DATA, Initrd};
|
||||
|
||||
@@ -43,8 +47,8 @@ struct ProbeClockSource {
|
||||
}
|
||||
|
||||
pub struct EarlyPlatformDevices {
|
||||
pub com1_3: Arc<ComPort>,
|
||||
|
||||
com1_3: Arc<Uart8250<Io8250Access>>,
|
||||
com2_4: Arc<Uart8250<Io8250Access>>,
|
||||
clock_sources: Vec<ProbeClockSource>,
|
||||
}
|
||||
|
||||
@@ -72,12 +76,18 @@ pub fn init_platform_early(cmdline: &str) -> Result<EarlyPlatformDevices, Error>
|
||||
// Initialize async executor queue
|
||||
runtime::init_task_queue();
|
||||
|
||||
let com1_3 = ComPort::setup(
|
||||
dummy_init_context(),
|
||||
0x3F8,
|
||||
0x3E8,
|
||||
Irq::External(ISA_IRQ_OFFSET + 4),
|
||||
)?;
|
||||
let com1_3_config = Io8250Config::new(0x3F8, 0x3E8, Irq::External(ISA_IRQ_OFFSET + 4));
|
||||
let com2_4_config = Io8250Config::new(0x2F8, 0x2E8, Irq::External(ISA_IRQ_OFFSET + 3));
|
||||
|
||||
let com1_3 = Arc::new(Uart8250::<Io8250Access>::new(
|
||||
com1_3_config,
|
||||
ygg_driver_serial_8250::Vendor::Generic8250,
|
||||
));
|
||||
unsafe { com1_3.init_inner() }?;
|
||||
let com2_4 = Arc::new(Uart8250::<Io8250Access>::new(
|
||||
com2_4_config,
|
||||
ygg_driver_serial_8250::Vendor::Generic8250,
|
||||
));
|
||||
// let i8259 = I8259::setup().expect("Could not initialize i8259 PIC");
|
||||
|
||||
// disable_i8259();
|
||||
@@ -85,6 +95,7 @@ pub fn init_platform_early(cmdline: &str) -> Result<EarlyPlatformDevices, Error>
|
||||
|
||||
Ok(EarlyPlatformDevices {
|
||||
com1_3,
|
||||
com2_4,
|
||||
clock_sources: Vec::new(),
|
||||
})
|
||||
}
|
||||
@@ -116,8 +127,14 @@ pub fn init_platform_devices(early: EarlyPlatformDevices) {
|
||||
if let Err(error) = Rtc::setup() {
|
||||
log::error!("RTC init error: {error:?}");
|
||||
}
|
||||
if let Err(error) = unsafe { early.com1_3.port_a().clone().init_irq() } {
|
||||
log::error!("COM port IRQ init error: {error:?}");
|
||||
if let Err(error) = unsafe { early.com2_4.init_inner() } {
|
||||
log::error!("COM port 2/4 init error: {error:?}");
|
||||
}
|
||||
if let Err(error) = unsafe { early.com1_3.init_irq() } {
|
||||
log::error!("COM port 1/3 IRQ init error: {error:?}");
|
||||
}
|
||||
if let Err(error) = unsafe { early.com2_4.init_irq() } {
|
||||
log::error!("COM port 2/4 IRQ init error: {error:?}");
|
||||
}
|
||||
|
||||
if let Err(error) = PciBusManager::probe_bus_devices() {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
pub mod i8253;
|
||||
pub mod ps2;
|
||||
pub mod rtc;
|
||||
pub mod serial;
|
||||
|
||||
#[cfg(any(target_arch = "x86_64", rust_analyzer))]
|
||||
pub mod hpet;
|
||||
|
||||
@@ -1,218 +0,0 @@
|
||||
//! Driver for x86 COM ports
|
||||
use abi::{error::Error, io::TerminalOptions};
|
||||
use alloc::sync::Arc;
|
||||
use device_api::{
|
||||
device::{Device, DeviceInitContext},
|
||||
interrupt::{InterruptHandler, Irq, IrqVector},
|
||||
};
|
||||
use kernel_arch_x86::intrinsics::{IoPort, IoPortAccess};
|
||||
use libk::{
|
||||
debug::DebugSink,
|
||||
device::{external_interrupt_controller, manager::DEVICE_REGISTRY},
|
||||
vfs::{Terminal, TerminalInput, TerminalOutput},
|
||||
};
|
||||
use libk_util::sync::IrqSafeSpinlock;
|
||||
|
||||
// Single port
|
||||
struct Regs {
|
||||
dr: IoPort<u8>,
|
||||
lsr: IoPort<u8>,
|
||||
lcr: IoPort<u8>,
|
||||
ier: IoPort<u8>,
|
||||
isr: IoPort<u8>,
|
||||
|
||||
baud_rate: u32,
|
||||
}
|
||||
|
||||
struct PortInner {
|
||||
regs: IrqSafeSpinlock<Regs>,
|
||||
}
|
||||
|
||||
/// Single port of the COM port pair
|
||||
pub struct Port {
|
||||
terminal: Arc<Terminal<PortInner>>,
|
||||
irq: Irq,
|
||||
}
|
||||
|
||||
/// COM port pair
|
||||
#[allow(unused)]
|
||||
pub struct ComPort {
|
||||
port_a: Arc<Port>,
|
||||
port_b: Arc<Port>,
|
||||
}
|
||||
|
||||
impl Regs {
|
||||
fn write(&mut self, byte: u8) -> Result<(), Error> {
|
||||
while self.lsr.read() & Port::LSR_THRE == 0 {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
|
||||
self.dr.write(byte);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl PortInner {
|
||||
fn handle_irq(&self) -> Option<u8> {
|
||||
let (status, value) = {
|
||||
let inner = self.regs.lock();
|
||||
let status = inner.isr.read();
|
||||
let value = inner.dr.read();
|
||||
(status, value)
|
||||
};
|
||||
|
||||
if status & Port::ISR_IRQ_MASK != 0 {
|
||||
Some(value)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TerminalOutput for PortInner {
|
||||
fn write(&self, byte: u8) -> Result<(), Error> {
|
||||
self.regs.lock().write(byte)
|
||||
}
|
||||
|
||||
fn write_multiple(&self, bytes: &[u8]) -> Result<usize, Error> {
|
||||
let mut regs = self.regs.lock();
|
||||
for &b in bytes {
|
||||
regs.write(b)?;
|
||||
}
|
||||
Ok(bytes.len())
|
||||
}
|
||||
|
||||
fn baud_rate(&self) -> u32 {
|
||||
self.regs.lock().baud_rate
|
||||
}
|
||||
|
||||
fn set_baud_rate(&self, baud: u32) -> Result<(), Error> {
|
||||
if baud > 115200 {
|
||||
log::warn!("Tried to set baud rate for COM port beyond 115200: {baud}");
|
||||
return Err(Error::InvalidArgument);
|
||||
}
|
||||
|
||||
let div = 115200 / baud;
|
||||
|
||||
let mut regs = self.regs.lock();
|
||||
|
||||
regs.lcr.write(regs.lcr.read() | (1 << 7));
|
||||
regs.dr.write(div as u8);
|
||||
regs.ier.write((div >> 8) as u8);
|
||||
regs.lcr.write(regs.lcr.read() & !(1 << 7));
|
||||
|
||||
regs.baud_rate = baud;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl DebugSink for Port {
|
||||
fn putc(&self, c: u8) -> Result<(), Error> {
|
||||
self.terminal.putc_to_output(c).ok();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn puts(&self, s: &str) -> Result<(), Error> {
|
||||
self.terminal.write_to_output(s.as_bytes()).ok();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn supports_control_sequences(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl InterruptHandler for Port {
|
||||
fn handle_irq(self: Arc<Self>, _vector: IrqVector) -> bool {
|
||||
let inner = self.terminal.output();
|
||||
if let Some(byte) = inner.handle_irq() {
|
||||
self.terminal.write_to_input(byte);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for Port {
|
||||
fn display_name(&self) -> &'static str {
|
||||
"COM port"
|
||||
}
|
||||
|
||||
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
|
||||
self.terminal.output().set_baud_rate(115200)?;
|
||||
|
||||
DEVICE_REGISTRY
|
||||
.serial_terminal
|
||||
.register(self.terminal.clone(), Some(self.clone()))
|
||||
.ok();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn init_irq(self: Arc<Self>) -> Result<(), Error> {
|
||||
let intc = external_interrupt_controller()?;
|
||||
|
||||
// TODO check that the same IRQ is not bound already for another port
|
||||
intc.register_irq(self.irq, Default::default(), self.clone())?;
|
||||
intc.enable_irq(self.irq)?;
|
||||
|
||||
let regs = self.terminal.output().regs.lock();
|
||||
|
||||
regs.ier.modify(|v| v | Self::IER_RXDA);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Port {
|
||||
const LSR_THRE: u8 = 1 << 5;
|
||||
const IER_RXDA: u8 = 1 << 0;
|
||||
const ISR_IRQ_MASK: u8 = 3 << 1;
|
||||
|
||||
fn new(base: u16, irq: Irq) -> Result<Self, Error> {
|
||||
let input = TerminalInput::with_capacity(64)?;
|
||||
let output = PortInner {
|
||||
regs: IrqSafeSpinlock::new(Regs {
|
||||
dr: IoPort::new(base),
|
||||
lsr: IoPort::new(base + 5),
|
||||
ier: IoPort::new(base + 1),
|
||||
isr: IoPort::new(base + 2),
|
||||
lcr: IoPort::new(base + 3),
|
||||
|
||||
baud_rate: 0,
|
||||
}),
|
||||
};
|
||||
let terminal = Terminal::from_parts(TerminalOptions::const_default(), input, output);
|
||||
Ok(Self {
|
||||
terminal: Arc::new(terminal),
|
||||
irq,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ComPort {
|
||||
/// Constructs a COM port pair
|
||||
fn new(port_a: u16, port_b: u16, irq: Irq) -> Result<Self, Error> {
|
||||
Ok(Self {
|
||||
port_a: Arc::new(Port::new(port_a, irq)?),
|
||||
port_b: Arc::new(Port::new(port_b, irq)?),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn setup(
|
||||
cx: DeviceInitContext,
|
||||
port_a: u16,
|
||||
port_b: u16,
|
||||
irq: Irq,
|
||||
) -> Result<Arc<Self>, Error> {
|
||||
let this = Arc::new(Self::new(port_a, port_b, irq)?);
|
||||
unsafe { this.port_a().clone().init(cx) }?;
|
||||
Ok(this)
|
||||
}
|
||||
|
||||
/// Returns a reference to the A port of this COM pair
|
||||
pub fn port_a(&self) -> &Arc<Port> {
|
||||
&self.port_a
|
||||
}
|
||||
}
|
||||
@@ -5,13 +5,14 @@ use alloc::sync::Arc;
|
||||
use device_api::{
|
||||
device::{Device, DeviceInitContext},
|
||||
interrupt::{
|
||||
ExternalInterruptController, FixedInterruptTable, InterruptHandler, InterruptTable, Irq,
|
||||
IrqLevel, IrqOptions, IrqTrigger, IrqVector,
|
||||
ExternalInterruptController, InterruptHandler, Irq, IrqLevel, IrqOptions, IrqTrigger,
|
||||
IrqVector,
|
||||
},
|
||||
};
|
||||
use kernel_arch_x86::ISA_IRQ_OFFSET;
|
||||
use libk::device::interrupt::FixedInterruptTable;
|
||||
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIo};
|
||||
use libk_util::sync::{IrqSafeSpinlock, spin_rwlock::IrqSafeRwLock};
|
||||
use libk_util::sync::IrqSafeSpinlock;
|
||||
use tock_registers::{
|
||||
interfaces::{Readable, Writeable},
|
||||
register_structs,
|
||||
@@ -64,7 +65,7 @@ struct Inner {
|
||||
pub struct IoApic {
|
||||
inner: IrqSafeSpinlock<Inner>,
|
||||
isa_redirections: [Option<IsaRedirection>; 16],
|
||||
table: IrqSafeRwLock<FixedInterruptTable<{ POPULATED_EXTERNAL_VECTORS as usize }>>,
|
||||
table: FixedInterruptTable,
|
||||
}
|
||||
|
||||
impl Regs {
|
||||
@@ -170,7 +171,8 @@ impl ExternalInterruptController for IoApic {
|
||||
handler: Arc<dyn InterruptHandler>,
|
||||
) -> Result<(), Error> {
|
||||
let mut inner = self.inner.lock();
|
||||
let table_vector = self.table.write().insert_least_loaded(handler.clone())?;
|
||||
let table_vector = self.table.insert_least_loaded(handler.clone());
|
||||
// let table_vector = self.table.write().insert_least_loaded(handler.clone())?;
|
||||
|
||||
let gsi_target_vector = (table_vector as u32) + IO_APIC_VECTOR_OFFSET;
|
||||
let bsp_apic = *BSP_APIC_ID.get();
|
||||
@@ -216,12 +218,8 @@ impl ExternalInterruptController for IoApic {
|
||||
}
|
||||
|
||||
fn handle_specific_irq(&self, gsi: usize) {
|
||||
let table = self.table.read();
|
||||
let vector = IrqVector::Irq(Irq::External(gsi as u32));
|
||||
|
||||
if let Some(handler) = table.handler(gsi) {
|
||||
handler.clone().handle_irq(vector);
|
||||
} else {
|
||||
if !self.table.handle_irq_line(gsi, vector) {
|
||||
log::warn!("No handler set for GSI #{}", gsi);
|
||||
}
|
||||
}
|
||||
@@ -295,7 +293,7 @@ impl IoApic {
|
||||
Ok(Arc::new(Self {
|
||||
isa_redirections,
|
||||
inner: IrqSafeSpinlock::new(inner),
|
||||
table: IrqSafeRwLock::new(FixedInterruptTable::new()),
|
||||
table: FixedInterruptTable::new(POPULATED_EXTERNAL_VECTORS as usize),
|
||||
}))
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
#![allow(missing_docs)]
|
||||
|
||||
use abi::{
|
||||
error::Error,
|
||||
io::{FileMode, device::i2c::I2CMasterInfo},
|
||||
};
|
||||
use alloc::{string::String, sync::Arc};
|
||||
use device_api::{
|
||||
clock::Hertz,
|
||||
device::{Device, DeviceInitContext},
|
||||
i2c::{I2CAddress, I2CController, I2CDevice},
|
||||
};
|
||||
use device_tree::driver::{Node, ProbeContext, device_tree_driver, lookup_phandle};
|
||||
use libk::fs::devfs;
|
||||
|
||||
pub struct I2CMuxChild {
|
||||
name: String,
|
||||
channel: u32,
|
||||
parent: Arc<dyn I2CController>,
|
||||
}
|
||||
|
||||
pub struct I2CMux {
|
||||
name: &'static str,
|
||||
}
|
||||
|
||||
impl I2CMux {
|
||||
pub fn from_fdt(node: &Arc<Node>, _context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
|
||||
let i2c_parent = node.prop_usize("i2c-parent")? as u32;
|
||||
let i2c_parent = lookup_phandle(i2c_parent, true)?;
|
||||
let i2c_parent = i2c_parent.as_i2c_controller()?;
|
||||
let name = node.name().unwrap_or("i2c-mux-pinctrl");
|
||||
|
||||
let bus_index = i2c_parent.bus_number();
|
||||
|
||||
for (index, child) in node.children().enumerate() {
|
||||
node.setup_pins(index).unwrap();
|
||||
let channel_index = child.prop_usize("reg")? as u32;
|
||||
let name = alloc::format!("{name}@{channel_index}");
|
||||
let channel: Arc<dyn I2CController> = Arc::new(I2CMuxChild {
|
||||
name,
|
||||
channel: channel_index,
|
||||
parent: i2c_parent.clone(),
|
||||
});
|
||||
|
||||
let device = Arc::new(I2CDevice::from(channel));
|
||||
let fs_name = alloc::format!("i2c{bus_index}-{channel_index}");
|
||||
devfs::add_named_char_device(device, fs_name, FileMode::new(0o600)).ok();
|
||||
}
|
||||
|
||||
Some(Arc::new(I2CMux { name }))
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for I2CMux {
|
||||
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn display_name(&self) -> &str {
|
||||
self.name
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for I2CMuxChild {
|
||||
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
fn display_name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
}
|
||||
|
||||
impl I2CController for I2CMuxChild {
|
||||
fn bus_number(&self) -> u32 {
|
||||
self.parent.bus_number()
|
||||
}
|
||||
|
||||
fn child_number(&self) -> Option<u32> {
|
||||
Some(self.channel)
|
||||
}
|
||||
|
||||
fn set_speed(&self, speed: Hertz) -> Result<Hertz, Error> {
|
||||
self.parent.set_speed(speed)
|
||||
}
|
||||
|
||||
fn capabilities(&self) -> I2CMasterInfo {
|
||||
self.parent.capabilities()
|
||||
}
|
||||
|
||||
fn i2c_write(&self, address: I2CAddress, buffer: &[u8]) -> Result<usize, Error> {
|
||||
// TODO channel select?
|
||||
self.parent.i2c_write(address, buffer)
|
||||
}
|
||||
|
||||
fn i2c_read(&self, address: I2CAddress, buffer: &mut [u8]) -> Result<usize, Error> {
|
||||
// TODO channel select?
|
||||
self.parent.i2c_read(address, buffer)
|
||||
}
|
||||
}
|
||||
|
||||
device_tree_driver! {
|
||||
compatible: ["i2c-mux-pinctrl"],
|
||||
config: {
|
||||
auto_pinctrl: false,
|
||||
},
|
||||
driver: {
|
||||
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
|
||||
I2CMux::from_fdt(node, context)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,8 +6,9 @@ use libk_util::OneTimeInit;
|
||||
pub mod bus;
|
||||
pub mod clock;
|
||||
pub mod display;
|
||||
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64", rust_analyzer))]
|
||||
pub mod i2c;
|
||||
pub mod power;
|
||||
pub mod serial;
|
||||
// pub mod timer;
|
||||
|
||||
#[cfg(any(rust_analyzer, not(target_arch = "x86_64")))]
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
//! Serial device interfaces
|
||||
|
||||
#[cfg(any(target_arch = "riscv64", rust_analyzer))]
|
||||
pub mod ns16550a;
|
||||
#[cfg(any(target_arch = "riscv64", rust_analyzer))]
|
||||
pub mod snps_dw_apb_uart;
|
||||
@@ -1,204 +0,0 @@
|
||||
//! 16550-style UART device driver
|
||||
use abi::{error::Error, io::TerminalOptions};
|
||||
use alloc::sync::Arc;
|
||||
use device_api::{
|
||||
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, WriteOnly},
|
||||
};
|
||||
|
||||
register_bitfields!(
|
||||
u8,
|
||||
IER [
|
||||
/// Received data ready
|
||||
RDR OFFSET(0) NUMBITS(1) [],
|
||||
/// Trasmitter holding register empty
|
||||
THRE OFFSET(1) NUMBITS(1) [],
|
||||
/// Receiver line status
|
||||
RLS OFFSET(2) NUMBITS(1) [],
|
||||
/// Modem status
|
||||
MS OFFSET(3) NUMBITS(1) [],
|
||||
],
|
||||
LSR [
|
||||
/// Data ready indicator
|
||||
DR OFFSET(0) NUMBITS(1) [],
|
||||
/// Transmitter FIFO empty
|
||||
TFE OFFSET(5) NUMBITS(1) [],
|
||||
],
|
||||
LCR [
|
||||
BITS OFFSET(0) NUMBITS(2) [
|
||||
Bits8 = 3
|
||||
],
|
||||
STOPBITS OFFSET(2) NUMBITS(1) [],
|
||||
PARITY OFFSET(3) NUMBITS(1) [],
|
||||
PARITY_EVEN OFFSET(4) NUMBITS(1) [],
|
||||
PARITY_STICK OFFSET(5) NUMBITS(1) [],
|
||||
BREAK OFFSET(6) NUMBITS(1) [],
|
||||
DLAB OFFSET(7) NUMBITS(1) [],
|
||||
]
|
||||
);
|
||||
|
||||
register_structs! {
|
||||
#[allow(non_snake_case)]
|
||||
Regs {
|
||||
// Read: receive buffer, write: transmit buffer
|
||||
(0x00 => DR: ReadWrite<u8>),
|
||||
(0x01 => IER: ReadWrite<u8, IER::Register>),
|
||||
// Read: interrupt idenditication, write: FIFO control
|
||||
(0x02 => FCR: ReadWrite<u8>),
|
||||
(0x03 => LCR: ReadWrite<u8, LCR::Register>),
|
||||
(0x04 => MCR: WriteOnly<u8>),
|
||||
(0x05 => LSR: ReadOnly<u8, LSR::Register>),
|
||||
(0x06 => MSR: ReadOnly<u8>),
|
||||
(0x07 => _0),
|
||||
(0x08 => @END),
|
||||
}
|
||||
}
|
||||
|
||||
struct Io {
|
||||
regs: DeviceMemoryIo<'static, Regs>,
|
||||
}
|
||||
|
||||
struct Inner {
|
||||
io: IrqSafeSpinlock<Io>,
|
||||
}
|
||||
|
||||
/// ns16550a-style UART driver
|
||||
pub struct Ns16550a {
|
||||
inner: OneTimeInit<Arc<Terminal<Inner>>>,
|
||||
base: PhysicalAddress,
|
||||
irq: IrqHandle,
|
||||
}
|
||||
|
||||
impl Io {
|
||||
fn init(&mut self) {
|
||||
self.regs.LCR.write(
|
||||
LCR::BITS::Bits8 + LCR::BREAK::CLEAR + LCR::STOPBITS::CLEAR + LCR::PARITY::CLEAR,
|
||||
);
|
||||
self.regs.IER.set(0);
|
||||
}
|
||||
|
||||
fn send(&mut self, byte: u8) {
|
||||
while self.regs.LSR.matches_all(LSR::TFE::CLEAR) {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
self.regs.DR.set(byte);
|
||||
}
|
||||
|
||||
fn handle_irq(&self) -> Option<u8> {
|
||||
let status = self.regs.FCR.get() & 0xF;
|
||||
if status == 0b1100 || status == 0b0100 {
|
||||
Some(self.regs.DR.get())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for Ns16550a {
|
||||
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
|
||||
log::info!("Init ns16550a @ {:#x}", self.base);
|
||||
let mut io = Io {
|
||||
regs: DeviceMemoryIo::map(self.base, Default::default())?,
|
||||
};
|
||||
io.init();
|
||||
|
||||
let input = TerminalInput::with_capacity(64)?;
|
||||
let output = Inner {
|
||||
io: IrqSafeSpinlock::new(io),
|
||||
};
|
||||
|
||||
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(self.clone())?;
|
||||
self.irq.enable()?;
|
||||
let io = self.inner.get().output().io.lock();
|
||||
io.regs.IER.modify(IER::RDR::SET);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn display_name(&self) -> &str {
|
||||
"ns16550a UART"
|
||||
}
|
||||
}
|
||||
|
||||
impl InterruptHandler for Ns16550a {
|
||||
fn handle_irq(self: Arc<Self>, _vector: IrqVector) -> bool {
|
||||
let inner = self.inner.get();
|
||||
let output = inner.output();
|
||||
let byte = output.io.lock().handle_irq();
|
||||
if let Some(byte) = byte {
|
||||
inner.write_to_input(byte);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TerminalOutput for Inner {
|
||||
fn write(&self, byte: u8) -> Result<(), Error> {
|
||||
self.io.lock().send(byte);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_multiple(&self, bytes: &[u8]) -> Result<usize, Error> {
|
||||
let mut lock = self.io.lock();
|
||||
for &byte in bytes {
|
||||
lock.send(byte);
|
||||
}
|
||||
Ok(bytes.len())
|
||||
}
|
||||
}
|
||||
|
||||
impl DebugSink for Ns16550a {
|
||||
fn putc(&self, c: u8) -> Result<(), Error> {
|
||||
self.inner.get().putc_to_output(c)
|
||||
}
|
||||
|
||||
fn supports_control_sequences(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
device_tree_driver!(
|
||||
compatible: ["ns16550a"],
|
||||
driver: {
|
||||
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
|
||||
let base = node.map_base(context, 0)?;
|
||||
log::debug!("ns16550a base = {base:#x}");
|
||||
let irq = node.interrupt(0)?;
|
||||
|
||||
Some(Arc::new(Ns16550a {
|
||||
base,
|
||||
irq,
|
||||
inner: OneTimeInit::new(),
|
||||
}))
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -1,304 +0,0 @@
|
||||
//! Synopsys DesignWare 8250 driver
|
||||
use abi::{error::Error, io::TerminalOptions};
|
||||
use alloc::sync::Arc;
|
||||
use device_api::{
|
||||
clock::{ClockHandle, Hertz, ResetHandle},
|
||||
device::{Device, DeviceInitContext},
|
||||
interrupt::{InterruptHandler, IrqHandle, IrqVector},
|
||||
};
|
||||
use device_tree::driver::{Node, ProbeContext, device_tree_driver};
|
||||
use libk::{
|
||||
debug::{self, 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, WriteOnly},
|
||||
};
|
||||
|
||||
register_bitfields! {
|
||||
u32,
|
||||
LCR [
|
||||
DLAB OFFSET(7) NUMBITS(1) [],
|
||||
DLS OFFSET(0) NUMBITS(2) [
|
||||
DL8b = 0b11,
|
||||
],
|
||||
],
|
||||
IER [
|
||||
PTIME OFFSET(7) NUMBITS(1) [],
|
||||
EDSSI OFFSET(3) NUMBITS(1) [],
|
||||
ELSI OFFSET(2) NUMBITS(1) [],
|
||||
// Transmit buffer available
|
||||
ETBEI OFFSET(1) NUMBITS(1) [],
|
||||
// Receive data available
|
||||
ERBFI OFFSET(0) NUMBITS(1) [],
|
||||
],
|
||||
LSR [
|
||||
// Data ready bit
|
||||
DR OFFSET(0) NUMBITS(1) [],
|
||||
// Transmitter holding register empty
|
||||
THRE OFFSET(5) NUMBITS(1) [],
|
||||
],
|
||||
USR [
|
||||
BUSY OFFSET(0) NUMBITS(1) [],
|
||||
]
|
||||
}
|
||||
|
||||
register_structs! {
|
||||
#[allow(non_snake_case)]
|
||||
Regs {
|
||||
// DLAB=0, Write: transmitter holding register/Read: receiver buffer register
|
||||
// DLAB=1, Read/Write: divisor latch low
|
||||
(0x000 => DR: ReadWrite<u32>),
|
||||
// DLAB=0: Interrupt enable register
|
||||
// DLAB=1: Divisor latch high
|
||||
(0x004 => IER: ReadWrite<u32, IER::Register>),
|
||||
// Read: interrupt identification register/Write: frame control register
|
||||
(0x008 => IIR: ReadWrite<u32>),
|
||||
// Line control register
|
||||
(0x00C => LCR: ReadWrite<u32, LCR::Register>),
|
||||
// Modem control register
|
||||
(0x010 => MCR: ReadWrite<u32>),
|
||||
// Line status register
|
||||
(0x014 => LSR: ReadOnly<u32, LSR::Register>),
|
||||
// Modem status register
|
||||
(0x018 => MSR: ReadOnly<u32>),
|
||||
// Scratchpad
|
||||
(0x01C => SCR: ReadWrite<u32>),
|
||||
// Low-power divisor latch low
|
||||
(0x020 => LPDLL: ReadWrite<u32>),
|
||||
// Low-power divisor latch high
|
||||
(0x024 => LPDLH: ReadWrite<u32>),
|
||||
(0x028 => _0),
|
||||
// Shadow receive/transmit buffer
|
||||
(0x030 => SDR: [ReadWrite<u32>; 16]),
|
||||
(0x070 => FAR: ReadWrite<u32>),
|
||||
(0x074 => TFR: ReadOnly<u32>),
|
||||
(0x078 => RFW: WriteOnly<u32>),
|
||||
(0x07C => USR: ReadOnly<u32, USR::Register>),
|
||||
(0x080 => TFL: ReadOnly<u32>),
|
||||
(0x084 => RFL: ReadOnly<u32>),
|
||||
(0x088 => SRR: WriteOnly<u32>),
|
||||
(0x08C => SRTS: ReadWrite<u32>),
|
||||
(0x090 => SBCR: ReadWrite<u32>),
|
||||
(0x094 => SDMAM: ReadWrite<u32>),
|
||||
(0x098 => SFE: ReadWrite<u32>),
|
||||
(0x09C => SRT: ReadWrite<u32>),
|
||||
(0x0A0 => STET: ReadWrite<u32>),
|
||||
(0x0A4 => HTX: ReadWrite<u32>),
|
||||
(0x0A8 => DMASA: WriteOnly<u32>),
|
||||
(0x0AC => _1),
|
||||
(0x0F4 => CPR: ReadOnly<u32>),
|
||||
(0x0F8 => UCV: ReadOnly<u32>),
|
||||
(0x0FC => CTR: ReadOnly<u32>),
|
||||
(0x100 => @END),
|
||||
}
|
||||
}
|
||||
|
||||
struct Io {
|
||||
regs: DeviceMemoryIo<'static, Regs>,
|
||||
}
|
||||
|
||||
struct Inner {
|
||||
io: IrqSafeSpinlock<Io>,
|
||||
}
|
||||
|
||||
/// Synopsys DesignWare 8250 UART
|
||||
pub struct DwUart {
|
||||
base: PhysicalAddress,
|
||||
irq: IrqHandle,
|
||||
clk_baud: ClockHandle,
|
||||
#[allow(unused)]
|
||||
clk_apb: Option<ClockHandle>,
|
||||
rst: Option<ResetHandle>,
|
||||
|
||||
inner: OneTimeInit<Arc<Terminal<Inner>>>,
|
||||
}
|
||||
|
||||
impl Io {
|
||||
fn send(&mut self, byte: u8) {
|
||||
// TODO
|
||||
if byte == b'\n' {
|
||||
self.send(b'\r');
|
||||
}
|
||||
|
||||
while !self.regs.LSR.matches_all(LSR::THRE::SET) {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
self.regs.DR.set(byte as u32);
|
||||
}
|
||||
|
||||
fn init(&mut self, baud_clock: Hertz, baud_rate: u64) {
|
||||
let divisor = (baud_clock.0 / (baud_rate * 16)) as u32;
|
||||
|
||||
self.wait_busy();
|
||||
self.regs.IER.set(0);
|
||||
for _ in 0..100 {
|
||||
let _ = self.regs.LSR.get();
|
||||
}
|
||||
for _ in 0..100 {
|
||||
let _ = self.regs.DR.get();
|
||||
}
|
||||
self.wait_busy();
|
||||
self.regs.LCR.write(LCR::DLAB::SET);
|
||||
self.wait_busy();
|
||||
self.regs.DR.set(divisor & 0xFF);
|
||||
self.regs.IER.set((divisor >> 8) & 0xFF);
|
||||
self.wait_busy();
|
||||
self.regs.LCR.write(LCR::DLS::DL8b);
|
||||
self.wait_busy();
|
||||
self.regs.IIR.set(0x01);
|
||||
self.wait_busy();
|
||||
self.regs.MCR.set(0x00);
|
||||
let _ = self.regs.LSR.get();
|
||||
for _ in 0..100 {
|
||||
let _ = self.regs.DR.get();
|
||||
}
|
||||
self.regs.SCR.set(0x00);
|
||||
}
|
||||
|
||||
fn handle_irq(&mut self) -> Option<u8> {
|
||||
let status = self.regs.IIR.get();
|
||||
|
||||
if status & 0xF == 4 {
|
||||
Some(self.regs.DR.get() as u8)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn wait_busy(&self) {
|
||||
for _ in 0..100000 {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
let mut timeout = 1000000;
|
||||
while timeout > 0 && self.regs.USR.matches_all(USR::BUSY::SET) {
|
||||
core::hint::spin_loop();
|
||||
timeout -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl InterruptHandler for DwUart {
|
||||
fn handle_irq(self: Arc<Self>, _vector: IrqVector) -> bool {
|
||||
let inner = self.inner.get();
|
||||
let output = inner.output();
|
||||
let byte = output.io.lock().handle_irq();
|
||||
|
||||
if let Some(byte) = byte {
|
||||
inner.write_to_input(byte);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for DwUart {
|
||||
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
|
||||
let baud_rate = 115200;
|
||||
let clk_baud_rate = self.clk_baud.rate()?;
|
||||
|
||||
// Prevent firmware (SBI in riscv64) from printing to UART while it's being
|
||||
// reset/initialized
|
||||
let guard = debug::MuteGuard::acquire();
|
||||
|
||||
let regs = DeviceMemoryIo::<Regs>::map(self.base, Default::default())?;
|
||||
let mut io = Io { regs };
|
||||
|
||||
if let Some(reset) = self.rst.as_ref() {
|
||||
reset.assert_for_cycles(100000)?;
|
||||
}
|
||||
|
||||
io.init(clk_baud_rate, baud_rate);
|
||||
|
||||
io.send(b'\r');
|
||||
io.send(b'\n');
|
||||
|
||||
drop(guard);
|
||||
|
||||
let input = TerminalInput::with_capacity(64)?;
|
||||
let output = Inner {
|
||||
io: IrqSafeSpinlock::new(io),
|
||||
};
|
||||
|
||||
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(self.clone())?;
|
||||
self.irq.enable()?;
|
||||
|
||||
let output = self.inner.get().output();
|
||||
let io = output.io.lock();
|
||||
io.regs.IER.modify(IER::ERBFI::SET);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn display_name(&self) -> &str {
|
||||
"Synopsys DesignWare 8250 UART"
|
||||
}
|
||||
}
|
||||
|
||||
impl DebugSink for DwUart {
|
||||
fn putc(&self, c: u8) -> Result<(), Error> {
|
||||
self.inner.get().putc_to_output(c)
|
||||
}
|
||||
|
||||
fn supports_control_sequences(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl TerminalOutput for Inner {
|
||||
fn write(&self, byte: u8) -> Result<(), Error> {
|
||||
self.io.lock().send(byte);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_multiple(&self, bytes: &[u8]) -> Result<usize, Error> {
|
||||
let mut lock = self.io.lock();
|
||||
for &byte in bytes {
|
||||
lock.send(byte);
|
||||
}
|
||||
Ok(bytes.len())
|
||||
}
|
||||
}
|
||||
|
||||
device_tree_driver! {
|
||||
compatible: ["snps,dw-apb-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 clk_baud = node.named_clock("baudclk")?;
|
||||
let clk_apb = node.named_clock("apb_pclk");
|
||||
let rst = node.reset(0);
|
||||
|
||||
Some(Arc::new(DwUart {
|
||||
base,
|
||||
irq,
|
||||
clk_baud,
|
||||
clk_apb,
|
||||
rst,
|
||||
|
||||
inner: OneTimeInit::new()
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -65,6 +65,7 @@ extern crate compiler_builtins;
|
||||
extern crate ygg_driver_ahci;
|
||||
extern crate ygg_driver_net_rtl81xx;
|
||||
extern crate ygg_driver_nvme;
|
||||
extern crate ygg_driver_serial_8250;
|
||||
extern crate ygg_driver_usb_xhci;
|
||||
extern crate ygg_driver_virtio_blk;
|
||||
extern crate ygg_driver_virtio_gpu;
|
||||
|
||||
@@ -39,6 +39,34 @@ impl_primitive_serde!(
|
||||
usize: [read_usize, write_usize]
|
||||
);
|
||||
|
||||
impl<T> Serialize for *const T {
|
||||
fn serialize<S: Serializer>(&self, serializer: &mut S) -> Result<(), S::Error> {
|
||||
serializer.write_usize(self.addr())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Serialize for *mut T {
|
||||
fn serialize<S: Serializer>(&self, serializer: &mut S) -> Result<(), S::Error> {
|
||||
serializer.write_usize(self.addr())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, T: 'de> Deserialize<'de> for *const T {
|
||||
fn deserialize<D: Deserializer<'de>>(deserializer: &mut D) -> Result<Self, D::Error> {
|
||||
deserializer
|
||||
.read_usize()
|
||||
.map(core::ptr::with_exposed_provenance)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, T: 'de> Deserialize<'de> for *mut T {
|
||||
fn deserialize<D: Deserializer<'de>>(deserializer: &mut D) -> Result<Self, D::Error> {
|
||||
deserializer
|
||||
.read_usize()
|
||||
.map(core::ptr::with_exposed_provenance_mut)
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for () {
|
||||
fn serialize<S: Serializer>(&self, serializer: &mut S) -> Result<(), S::Error> {
|
||||
let _ = serializer;
|
||||
|
||||
@@ -55,6 +55,117 @@ request_group!(
|
||||
}
|
||||
);
|
||||
|
||||
/// I²C device requests
|
||||
pub mod i2c {
|
||||
/// I²C master device information
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[repr(C)]
|
||||
pub struct I2CMasterInfo {
|
||||
/// Maximum clock rate supported by the controller
|
||||
pub max_speed_hz: u32,
|
||||
/// `true` if the controller supports 10-bit addressing mode
|
||||
pub supports_10bit_addresses: bool,
|
||||
}
|
||||
|
||||
request_group!(
|
||||
#[doc = "I²C device requests"]
|
||||
pub enum I2CRequestVariant<'de> {
|
||||
#[doc = "Selects the address on the I²C bus"]
|
||||
0x2000: SetAddress(u16, ()),
|
||||
#[doc = "Configures device speed (in Hertz)"]
|
||||
0x2001: SetSpeed(u32, u32),
|
||||
#[doc = "Queries I²C master capabilities"]
|
||||
0x2010: GetMasterInfo((), I2CMasterInfo),
|
||||
}
|
||||
);
|
||||
|
||||
abi_serde::impl_struct_serde!(I2CMasterInfo: [
|
||||
max_speed_hz, supports_10bit_addresses
|
||||
]);
|
||||
}
|
||||
|
||||
/// SPI device requests
|
||||
pub mod spi {
|
||||
#![allow(missing_docs)]
|
||||
|
||||
use crate::{bitflags, primitive_enum};
|
||||
|
||||
/// Represents a single SPI transfer
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[repr(C)]
|
||||
pub struct SpiTransfer {
|
||||
/// Tx buffer base pointer
|
||||
pub tx_buffer: *const u8,
|
||||
/// Rx buffer base pointer
|
||||
pub rx_buffer: *mut u8,
|
||||
/// Transfer size
|
||||
pub len: usize,
|
||||
}
|
||||
|
||||
primitive_enum! {
|
||||
pub enum SpiClockPolarity: u32 {
|
||||
IdleLow = 0,
|
||||
IdleHigh = 1,
|
||||
}
|
||||
[with_serde]
|
||||
}
|
||||
|
||||
primitive_enum! {
|
||||
pub enum SpiClockPhase: u32 {
|
||||
CaptureOnFirstTransition = 0,
|
||||
CaptureOnSecondTransition = 1
|
||||
}
|
||||
[with_serde]
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
#[repr(C)]
|
||||
pub struct SpiConfig {
|
||||
pub clock_polarity: Option<SpiClockPolarity>,
|
||||
pub clock_phase: Option<SpiClockPhase>,
|
||||
pub frequency: Option<u32>,
|
||||
pub chip_select_index: Option<u32>,
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
#[doc = "SPI supported bits-per-word settings"]
|
||||
pub struct SpiSupportedBitsPerWord: u32 {
|
||||
#[doc = "8-bit words support"]
|
||||
const BITS_8: bit 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// SPI controller capabilities
|
||||
pub struct SpiCapabilities {
|
||||
/// Maximum clock rate supported
|
||||
pub max_speed_hz: u32,
|
||||
/// Supports SPI master mode
|
||||
pub supports_master_mode: bool,
|
||||
/// Supports SPI slave mode
|
||||
pub supports_slave_mode: bool,
|
||||
/// Mask
|
||||
pub supported_bits_per_word: u32,
|
||||
}
|
||||
|
||||
request_group!(
|
||||
#[doc = "SPI device requests"]
|
||||
pub enum SpiRequestVariant<'de> {
|
||||
#[doc = "Perform a SPI transfer"]
|
||||
0x2000: Transfer(SpiTransfer, usize),
|
||||
#[doc = "Set SPI configuration"]
|
||||
0x2001: SetConfig(SpiConfig, ()),
|
||||
}
|
||||
);
|
||||
|
||||
abi_serde::impl_struct_serde!(SpiTransfer: [
|
||||
tx_buffer, rx_buffer, len
|
||||
]);
|
||||
|
||||
abi_serde::impl_struct_serde!(SpiConfig: [
|
||||
clock_polarity, clock_phase, frequency, chip_select_index
|
||||
]);
|
||||
}
|
||||
|
||||
abi_serde::impl_struct_serde!(Framebuffer: [
|
||||
width, height, stride, size
|
||||
]);
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
use std::{
|
||||
io::{self, Read, Write},
|
||||
path::Path,
|
||||
};
|
||||
|
||||
use crate::sys::{I2CMaster as SysI2CMaster, i2c::I2CMasterImpl};
|
||||
|
||||
pub use crate::sys::I2CMasterConfig;
|
||||
|
||||
pub struct I2CMaster(I2CMasterImpl);
|
||||
|
||||
impl Read for I2CMaster {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.0.read(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for I2CMaster {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.0.write(buf)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.0.flush()
|
||||
}
|
||||
}
|
||||
|
||||
impl I2CMaster {
|
||||
pub fn open<P: AsRef<Path>>(device: P, config: &I2CMasterConfig) -> io::Result<Self> {
|
||||
I2CMasterImpl::open(device, config).map(Self)
|
||||
}
|
||||
|
||||
pub fn set_slave_address(&mut self, address: u16) -> io::Result<()> {
|
||||
self.0.set_slave_address(address)
|
||||
}
|
||||
|
||||
pub fn set_speed_hz(&mut self, speed_hz: u32) -> io::Result<()> {
|
||||
self.0.set_speed_hz(speed_hz)
|
||||
}
|
||||
|
||||
pub fn smbus_read(&mut self, reg: u8, data: &mut [u8]) -> io::Result<usize> {
|
||||
self.0.smbus_read(reg, data)
|
||||
}
|
||||
|
||||
pub fn smbus_write(&mut self, reg: u8, data: &[u8]) -> io::Result<usize> {
|
||||
self.0.smbus_write(reg, data)
|
||||
}
|
||||
}
|
||||
@@ -154,6 +154,14 @@ impl RawStdin {
|
||||
pub fn set_options(&mut self, options: TerminalOptionsImpl) -> io::Result<TerminalOptionsImpl> {
|
||||
self.0.set_options(options)
|
||||
}
|
||||
|
||||
pub fn options(&mut self) -> io::Result<TerminalOptionsImpl> {
|
||||
self.0.options()
|
||||
}
|
||||
|
||||
pub fn saved_options(&self) -> &TerminalOptionsImpl {
|
||||
self.0.saved_options()
|
||||
}
|
||||
}
|
||||
|
||||
impl Read for RawStdin {
|
||||
|
||||
@@ -5,12 +5,14 @@
|
||||
pub(crate) mod sys;
|
||||
|
||||
pub mod fs;
|
||||
pub mod i2c;
|
||||
pub mod io;
|
||||
pub mod mem;
|
||||
pub mod net;
|
||||
pub mod path;
|
||||
pub mod process;
|
||||
pub mod signal;
|
||||
pub mod spi;
|
||||
pub mod term;
|
||||
|
||||
pub mod time {
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
use std::{io, path::Path};
|
||||
|
||||
use crate::sys::{SpiDevice as SysSpiDevice, spi::SpiDeviceImpl};
|
||||
|
||||
pub use crate::sys::{SpiClockPhase, SpiClockPolarity, SpiConfig};
|
||||
|
||||
pub struct SpiDevice(SpiDeviceImpl);
|
||||
|
||||
impl SpiDevice {
|
||||
pub fn open<P: AsRef<Path>>(device: P, config: &SpiConfig) -> io::Result<Self> {
|
||||
SpiDeviceImpl::open(device, config).map(Self)
|
||||
}
|
||||
|
||||
pub fn transfer(&mut self, tx: &[u8], rx: &mut [u8]) -> io::Result<usize> {
|
||||
self.0.transfer(tx, rx)
|
||||
}
|
||||
|
||||
pub fn set_config(&mut self, config: &SpiConfig) -> io::Result<()> {
|
||||
self.0.set_config(config)
|
||||
}
|
||||
}
|
||||
@@ -48,6 +48,8 @@ pub(crate) trait Pipe: Read + Write + AsRawFd + Sized {
|
||||
pub(crate) trait RawStdin: Sized + Read + AsRawFd {
|
||||
fn open() -> io::Result<Self>;
|
||||
fn set_options(&mut self, options: TerminalOptionsImpl) -> io::Result<TerminalOptionsImpl>;
|
||||
fn options(&mut self) -> io::Result<TerminalOptionsImpl>;
|
||||
fn saved_options(&self) -> &TerminalOptionsImpl;
|
||||
// fn new(stdin: &'a mut Stdin) -> io::Result<Self>;
|
||||
}
|
||||
|
||||
@@ -62,10 +64,54 @@ pub(crate) trait SerialPort: Read + Write + AsRawFd + IntoRawFd + Sized {
|
||||
fn open<P: AsRef<Path>>(device: P, options: &SerialPortSettings) -> io::Result<Self>;
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct I2CMasterConfig {
|
||||
pub slave_address: Option<u16>,
|
||||
pub speed_hz: Option<u32>,
|
||||
}
|
||||
|
||||
pub(crate) trait I2CMaster: Read + Write + AsRawFd + Sized {
|
||||
fn open<P: AsRef<Path>>(device: P, config: &I2CMasterConfig) -> io::Result<Self>;
|
||||
fn set_slave_address(&mut self, address: u16) -> io::Result<()>;
|
||||
fn set_speed_hz(&mut self, speed_hz: u32) -> io::Result<()>;
|
||||
fn smbus_read(&mut self, reg: u8, data: &mut [u8]) -> io::Result<usize>;
|
||||
fn smbus_write(&mut self, reg: u8, data: &[u8]) -> io::Result<usize>;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum SpiClockPolarity {
|
||||
IdleLow,
|
||||
IdleHigh,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum SpiClockPhase {
|
||||
CaptureOnFirstTransition,
|
||||
CaptureOnSecondTransition,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct SpiConfig {
|
||||
pub clock_polarity: Option<SpiClockPolarity>,
|
||||
pub clock_phase: Option<SpiClockPhase>,
|
||||
pub frequency: Option<u32>,
|
||||
pub chip_select_index: Option<u32>,
|
||||
}
|
||||
|
||||
pub(crate) trait SpiDevice: AsRawFd + Sized {
|
||||
fn open<P: AsRef<Path>>(device: P, config: &SpiConfig) -> io::Result<Self>;
|
||||
fn set_config(&mut self, config: &SpiConfig) -> io::Result<()>;
|
||||
fn transfer(&mut self, tx: &[u8], rx: &mut [u8]) -> io::Result<usize>;
|
||||
}
|
||||
|
||||
pub trait TerminalOptions: Copy {
|
||||
fn normal() -> Self;
|
||||
fn raw() -> Self;
|
||||
|
||||
// Output
|
||||
fn set_nl_to_crnl(&mut self, set: bool);
|
||||
|
||||
// Chars
|
||||
fn erase_char(&self) -> u8;
|
||||
fn word_erase_char(&self) -> u8;
|
||||
fn eof_char(&self) -> u8;
|
||||
|
||||
@@ -23,7 +23,7 @@ pub use mem::{FileMappingImpl, SharedMemoryImpl};
|
||||
pub use pid::PidFdImpl;
|
||||
pub use pipe::PipeImpl;
|
||||
pub use poll::PollImpl;
|
||||
pub use pty::{open as open_pty, PtyMasterImpl, PtySlaveImpl, TerminalOptionsImpl};
|
||||
pub use pty::{PtyMasterImpl, PtySlaveImpl, TerminalOptionsImpl, open as open_pty};
|
||||
pub use serial::SerialPortImpl;
|
||||
pub use socket::{BorrowedAddressImpl, LocalPacketSocketImpl, OwnedAddressImpl};
|
||||
pub use term::RawStdinImpl;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use std::{
|
||||
collections::{hash_map::Entry, HashMap},
|
||||
collections::{HashMap, hash_map::Entry},
|
||||
io,
|
||||
mem::MaybeUninit,
|
||||
os::fd::{AsRawFd, FromRawFd, OwnedFd, RawFd},
|
||||
|
||||
@@ -56,6 +56,14 @@ impl TerminalOptions for TerminalOptionsImpl {
|
||||
this
|
||||
}
|
||||
|
||||
fn set_nl_to_crnl(&mut self, set: bool) {
|
||||
if set {
|
||||
self.c_oflag |= libc::ONLCR;
|
||||
} else {
|
||||
self.c_oflag &= !libc::ONLCR;
|
||||
}
|
||||
}
|
||||
|
||||
fn eof_char(&self) -> u8 {
|
||||
self.c_cc[libc::VEOF]
|
||||
}
|
||||
|
||||
@@ -78,6 +78,14 @@ impl RawStdin for RawStdinImpl {
|
||||
*t = options;
|
||||
})
|
||||
}
|
||||
|
||||
fn options(&mut self) -> io::Result<TerminalOptionsImpl> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn saved_options(&self) -> &TerminalOptionsImpl {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl io::Read for RawStdinImpl {
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
use std::{
|
||||
fs::{File, OpenOptions},
|
||||
io::{self, Read, Write},
|
||||
os::fd::{AsRawFd, RawFd},
|
||||
path::Path,
|
||||
};
|
||||
|
||||
use runtime::{abi::io::device::i2c, rt::io::device::device_request};
|
||||
|
||||
use crate::sys::{I2CMaster, I2CMasterConfig};
|
||||
|
||||
pub struct I2CMasterImpl {
|
||||
file: File,
|
||||
}
|
||||
|
||||
impl Read for I2CMasterImpl {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.file.read(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for I2CMasterImpl {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.file.write(buf)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.file.flush()
|
||||
}
|
||||
}
|
||||
|
||||
impl I2CMaster for I2CMasterImpl {
|
||||
fn open<P: AsRef<Path>>(device: P, config: &I2CMasterConfig) -> io::Result<Self> {
|
||||
let file = OpenOptions::new().read(true).write(true).open(device)?;
|
||||
let mut this = Self { file };
|
||||
if let Some(address) = config.slave_address {
|
||||
this.set_slave_address(address)?;
|
||||
}
|
||||
if let Some(speed_hz) = config.speed_hz {
|
||||
this.set_speed_hz(speed_hz)?;
|
||||
}
|
||||
Ok(this)
|
||||
}
|
||||
|
||||
fn set_slave_address(&mut self, address: u16) -> io::Result<()> {
|
||||
let mut buffer = [0; 4];
|
||||
device_request::<i2c::SetAddress>(self.file.as_raw_fd(), &mut buffer, &address)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_speed_hz(&mut self, speed_hz: u32) -> io::Result<()> {
|
||||
let mut buffer = [0; 4];
|
||||
device_request::<i2c::SetSpeed>(self.file.as_raw_fd(), &mut buffer, &speed_hz)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn smbus_read(&mut self, reg: u8, data: &mut [u8]) -> io::Result<usize> {
|
||||
self.write_all(&[reg])?;
|
||||
self.read(data)
|
||||
}
|
||||
|
||||
fn smbus_write(&mut self, reg: u8, data: &[u8]) -> io::Result<usize> {
|
||||
self.write_all(&[reg])?;
|
||||
self.write(data)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRawFd for I2CMasterImpl {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.file.as_raw_fd()
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
pub mod fs;
|
||||
pub mod i2c;
|
||||
pub mod mem;
|
||||
pub mod path;
|
||||
pub mod pid;
|
||||
@@ -7,6 +8,7 @@ pub mod poll;
|
||||
pub mod pty;
|
||||
pub mod serial;
|
||||
pub mod socket;
|
||||
pub mod spi;
|
||||
pub mod term;
|
||||
pub mod time;
|
||||
pub mod timer;
|
||||
@@ -18,7 +20,7 @@ use std::{
|
||||
fd::RawFd,
|
||||
yggdrasil::{
|
||||
process::CommandExt,
|
||||
signal::{set_signal_handler, SignalHandler},
|
||||
signal::{SignalHandler, set_signal_handler},
|
||||
},
|
||||
},
|
||||
process::Command,
|
||||
@@ -29,7 +31,7 @@ pub use mem::{FileMappingImpl, SharedMemoryImpl};
|
||||
pub use pid::PidFdImpl;
|
||||
pub use pipe::PipeImpl;
|
||||
pub use poll::PollImpl;
|
||||
pub use pty::{open as open_pty, PtyMasterImpl, PtySlaveImpl, TerminalOptionsImpl};
|
||||
pub use pty::{PtyMasterImpl, PtySlaveImpl, TerminalOptionsImpl, open as open_pty};
|
||||
pub use serial::SerialPortImpl;
|
||||
pub use socket::{BorrowedAddressImpl, LocalPacketSocketImpl, OwnedAddressImpl};
|
||||
pub use term::RawStdinImpl;
|
||||
|
||||
@@ -47,6 +47,14 @@ impl TerminalOptions for TerminalOptionsImpl {
|
||||
}
|
||||
}
|
||||
|
||||
fn set_nl_to_crnl(&mut self, set: bool) {
|
||||
if set {
|
||||
self.output |= TerminalOutputOptions::NL_TO_CRNL;
|
||||
} else {
|
||||
self.output &= !TerminalOutputOptions::NL_TO_CRNL;
|
||||
}
|
||||
}
|
||||
|
||||
fn eof_char(&self) -> u8 {
|
||||
self.chars.eof
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ use std::{
|
||||
};
|
||||
|
||||
use runtime::{
|
||||
abi::net::{types::LocalSocketAddress, AncillaryMessage, MessageHeader, MessageHeaderMut},
|
||||
abi::net::{AncillaryMessage, MessageHeader, MessageHeaderMut, types::LocalSocketAddress},
|
||||
abi_serde::wire,
|
||||
rt as yggdrasil_rt,
|
||||
};
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
use std::{
|
||||
cmp,
|
||||
fs::{File, OpenOptions},
|
||||
io,
|
||||
os::fd::{AsRawFd, RawFd},
|
||||
path::Path,
|
||||
};
|
||||
|
||||
use runtime::{abi::io::device::spi as sys_spi, rt::io::device::device_request};
|
||||
|
||||
use crate::sys::{SpiClockPhase, SpiClockPolarity, SpiConfig, SpiDevice};
|
||||
|
||||
pub struct SpiDeviceImpl {
|
||||
file: File,
|
||||
}
|
||||
|
||||
impl SpiDevice for SpiDeviceImpl {
|
||||
fn open<P: AsRef<Path>>(device: P, config: &SpiConfig) -> io::Result<Self> {
|
||||
let file = OpenOptions::new().read(true).write(true).open(device)?;
|
||||
let mut this = Self { file };
|
||||
this.set_config(config)?;
|
||||
Ok(this)
|
||||
}
|
||||
|
||||
fn set_config(&mut self, config: &SpiConfig) -> io::Result<()> {
|
||||
let clock_polarity = config.clock_polarity.map(Into::into);
|
||||
let clock_phase = config.clock_phase.map(Into::into);
|
||||
|
||||
let sys_config = sys_spi::SpiConfig {
|
||||
clock_polarity,
|
||||
clock_phase,
|
||||
frequency: config.frequency,
|
||||
chip_select_index: config.chip_select_index,
|
||||
};
|
||||
|
||||
let mut buffer = [0; 256];
|
||||
device_request::<sys_spi::SetConfig>(self.file.as_raw_fd(), &mut buffer, &sys_config)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn transfer(&mut self, tx: &[u8], rx: &mut [u8]) -> io::Result<usize> {
|
||||
let xfer = sys_spi::SpiTransfer {
|
||||
tx_buffer: tx.as_ptr(),
|
||||
rx_buffer: rx.as_mut_ptr(),
|
||||
len: cmp::min(tx.len(), rx.len()),
|
||||
};
|
||||
let mut buffer = [0; 32];
|
||||
let len = device_request::<sys_spi::Transfer>(self.file.as_raw_fd(), &mut buffer, &xfer)?;
|
||||
Ok(len)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRawFd for SpiDeviceImpl {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.file.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SpiClockPolarity> for sys_spi::SpiClockPolarity {
|
||||
fn from(value: SpiClockPolarity) -> Self {
|
||||
match value {
|
||||
SpiClockPolarity::IdleLow => Self::IdleLow,
|
||||
SpiClockPolarity::IdleHigh => Self::IdleHigh,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SpiClockPhase> for sys_spi::SpiClockPhase {
|
||||
fn from(value: SpiClockPhase) -> Self {
|
||||
match value {
|
||||
SpiClockPhase::CaptureOnFirstTransition => Self::CaptureOnFirstTransition,
|
||||
SpiClockPhase::CaptureOnSecondTransition => Self::CaptureOnSecondTransition,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -37,6 +37,13 @@ impl Inner {
|
||||
let old = runtime::rt::io::terminal::update_terminal_options(fd, f)?;
|
||||
Ok(old)
|
||||
}
|
||||
|
||||
fn options(&self) -> io::Result<TerminalOptions> {
|
||||
let fd = self.as_raw_fd();
|
||||
let options = runtime::rt::io::terminal::get_terminal_options(fd)?;
|
||||
|
||||
Ok(options)
|
||||
}
|
||||
}
|
||||
|
||||
impl Read for Inner {
|
||||
@@ -67,6 +74,14 @@ impl RawStdin for RawStdinImpl {
|
||||
fn set_options(&mut self, options: TerminalOptions) -> io::Result<TerminalOptions> {
|
||||
self.inner.update_options(|_| options)
|
||||
}
|
||||
|
||||
fn options(&mut self) -> io::Result<TerminalOptions> {
|
||||
self.inner.options()
|
||||
}
|
||||
|
||||
fn saved_options(&self) -> &TerminalOptions {
|
||||
&self.saved
|
||||
}
|
||||
}
|
||||
|
||||
impl io::Read for RawStdinImpl {
|
||||
|
||||
@@ -79,6 +79,14 @@ impl TerminalInput {
|
||||
self.stdin.set_options(options)
|
||||
}
|
||||
|
||||
pub fn options(&mut self) -> io::Result<TerminalOptionsImpl> {
|
||||
self.stdin.options()
|
||||
}
|
||||
|
||||
pub fn saved_options(&self) -> &TerminalOptionsImpl {
|
||||
self.stdin.saved_options()
|
||||
}
|
||||
|
||||
fn take(&mut self, count: usize) {
|
||||
self.buffer.copy_within(count..self.buffer_len, 0);
|
||||
self.buffer_len -= count;
|
||||
|
||||
@@ -1,17 +1,38 @@
|
||||
#![feature(yggdrasil_os, rustc_private)]
|
||||
|
||||
use std::{
|
||||
env,
|
||||
io::{self, BufRead, Write, stdin, stdout},
|
||||
os::{
|
||||
fd::AsRawFd,
|
||||
yggdrasil::{io::terminal::start_terminal_session, rt::io::device},
|
||||
},
|
||||
io::{self, BufRead, Stdout, Write, stdin, stdout},
|
||||
os::{fd::AsRawFd, yggdrasil::io::terminal::start_terminal_session},
|
||||
path::PathBuf,
|
||||
process::{Command, ExitCode},
|
||||
thread,
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use clap::Parser;
|
||||
use cross::io::{TerminalOptions, TerminalOptionsImpl};
|
||||
use runtime::rt::io::terminal;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
struct Args {
|
||||
#[clap(
|
||||
short,
|
||||
long,
|
||||
help = "Serial terminal baud rate",
|
||||
default_value_t = 115200
|
||||
)]
|
||||
baud: u32,
|
||||
#[clap(
|
||||
short = 'N',
|
||||
long,
|
||||
help = "Convert NL to CR+NL",
|
||||
default_value_t = true
|
||||
)]
|
||||
nl_to_crnl: bool,
|
||||
|
||||
terminal: PathBuf,
|
||||
}
|
||||
|
||||
fn login_readline<R: BufRead>(
|
||||
reader: &mut R,
|
||||
buf: &mut String,
|
||||
@@ -20,14 +41,26 @@ fn login_readline<R: BufRead>(
|
||||
reader.read_line(buf)
|
||||
}
|
||||
|
||||
fn login_as(username: &str, _password: &str) -> Result<(), io::Error> {
|
||||
fn login_as_inner(username: &str, _password: &str) -> Result<(), io::Error> {
|
||||
let mut shell = Command::new("/bin/sh").arg("-l").spawn()?;
|
||||
println!("Hello {:?}", username);
|
||||
shell.wait()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn login_attempt(erase: bool) -> Result<(), io::Error> {
|
||||
fn login_as(
|
||||
stdout: Stdout,
|
||||
options: &TerminalOptionsImpl,
|
||||
username: &str,
|
||||
password: &str,
|
||||
) -> Result<(), io::Error> {
|
||||
let old_termios = terminal::update_terminal_options(stdout.as_raw_fd(), |_| *options)?;
|
||||
let result = login_as_inner(username, password);
|
||||
terminal::set_terminal_options(stdout.as_raw_fd(), &old_termios)?;
|
||||
result
|
||||
}
|
||||
|
||||
fn login_attempt(erase: bool, options: &TerminalOptionsImpl) -> Result<(), io::Error> {
|
||||
let stdin = stdin();
|
||||
let mut stdout = stdout();
|
||||
|
||||
@@ -44,25 +77,29 @@ fn login_attempt(erase: bool) -> Result<(), io::Error> {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
login_as(username.trim(), "")
|
||||
login_as(stdout, options, username.trim(), "")
|
||||
}
|
||||
|
||||
fn main() -> ExitCode {
|
||||
logsink::setup_logging(false);
|
||||
let args: Vec<_> = env::args().skip(1).collect();
|
||||
if args.len() != 1 {
|
||||
eprintln!("Usage: /sbin/login TTY");
|
||||
return ExitCode::FAILURE;
|
||||
}
|
||||
let terminal = args[0].as_str();
|
||||
let args = Args::parse();
|
||||
// let args: Vec<_> = env::args().skip(1).collect();
|
||||
// if args.len() != 1 {
|
||||
// eprintln!("Usage: /sbin/login TTY");
|
||||
// return ExitCode::FAILURE;
|
||||
// }
|
||||
|
||||
// TODO check that `terminal` is a terminal
|
||||
log::info!("Starting a session at {}", terminal);
|
||||
log::info!("Starting a session at {}", args.terminal.display());
|
||||
let mut success = false;
|
||||
for _ in 0..3 {
|
||||
if let Err(err) = unsafe { start_terminal_session(terminal) } {
|
||||
log::error!("{terminal}: {err:?}");
|
||||
eprintln!("Could not setup a session at {terminal}: {err:?}");
|
||||
if let Err(err) = unsafe { start_terminal_session(&args.terminal) } {
|
||||
log::error!("{}: {:?}", args.terminal.display(), err);
|
||||
eprintln!(
|
||||
"Could not setup a session at {}: {:?}",
|
||||
args.terminal.display(),
|
||||
err
|
||||
);
|
||||
} else {
|
||||
success = true;
|
||||
break;
|
||||
@@ -74,21 +111,19 @@ fn main() -> ExitCode {
|
||||
return ExitCode::FAILURE;
|
||||
}
|
||||
|
||||
let mut terminal_options = TerminalOptionsImpl::normal();
|
||||
terminal_options.set_nl_to_crnl(args.nl_to_crnl);
|
||||
|
||||
let mut attempt_number = 0;
|
||||
loop {
|
||||
log::debug!("Login attempt {}", attempt_number);
|
||||
|
||||
// "Attach" the terminal
|
||||
let group_id = std::os::yggdrasil::process::group_id();
|
||||
let mut buffer = [0; 8];
|
||||
device::device_request::<device::SetTerminalGroup>(
|
||||
stdin().as_raw_fd(),
|
||||
&mut buffer,
|
||||
&group_id,
|
||||
)
|
||||
.expect("Could not attach the terminal");
|
||||
terminal::set_terminal_group(stdin().as_raw_fd(), group_id)
|
||||
.expect("Could not set terminal group id");
|
||||
|
||||
if let Err(err) = login_attempt(attempt_number % 3 == 0) {
|
||||
if let Err(err) = login_attempt(attempt_number % 3 == 0, &terminal_options) {
|
||||
eprintln!("login: {}", err);
|
||||
}
|
||||
attempt_number += 1;
|
||||
|
||||
+142
-44
@@ -1,62 +1,160 @@
|
||||
#![feature(let_chains)]
|
||||
|
||||
use std::collections::{HashSet, VecDeque};
|
||||
|
||||
use stuff::{
|
||||
autocomplete,
|
||||
readline::{Buffer, NonEmptyVec, Readline, ReadlineProvider},
|
||||
use std::{
|
||||
io::{self, Write, stdout},
|
||||
path::{Path, PathBuf},
|
||||
process::ExitCode,
|
||||
thread,
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
#[derive(Default)]
|
||||
struct P {
|
||||
history: VecDeque<String>,
|
||||
use clap::Parser;
|
||||
use cross::{
|
||||
i2c::{I2CMaster, I2CMasterConfig},
|
||||
spi::{SpiConfig, SpiDevice},
|
||||
};
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
struct Args {
|
||||
#[clap(subcommand)]
|
||||
command: Subcommand,
|
||||
}
|
||||
|
||||
impl ReadlineProvider for P {
|
||||
fn completions(&mut self, buffer: &Buffer) -> Option<NonEmptyVec<String>> {
|
||||
let word_before_cursor = buffer.word_before_cursor();
|
||||
let is_first = buffer.is_first_word();
|
||||
#[derive(Debug, clap::Subcommand)]
|
||||
enum Subcommand {
|
||||
M41T80 {
|
||||
#[clap(short, long, default_value_t = 0x40, help = "I²C device address")]
|
||||
address: u8,
|
||||
#[clap(help = "I²C controller path")]
|
||||
device: PathBuf,
|
||||
},
|
||||
JedecFlash {
|
||||
#[clap(short, long, default_value_t = 4 * 1024 * 1024, help = "SPI flash capacity")]
|
||||
capacity: usize,
|
||||
#[clap(help = "SPI device path")]
|
||||
device: PathBuf,
|
||||
},
|
||||
}
|
||||
|
||||
let mut entries = HashSet::new();
|
||||
if let Some(word) = word_before_cursor
|
||||
&& !is_first
|
||||
{
|
||||
autocomplete::complete_from_cwd(&mut entries, word, &Default::default());
|
||||
} else {
|
||||
let prefix = word_before_cursor.unwrap_or("");
|
||||
autocomplete::complete_binary_from_env(&mut entries, prefix);
|
||||
}
|
||||
struct M41T80 {
|
||||
master: I2CMaster,
|
||||
}
|
||||
|
||||
let mut entries = NonEmptyVec::try_from_iter(entries.drain()).ok()?;
|
||||
entries.sort();
|
||||
#[derive(Debug, PartialEq)]
|
||||
struct Time {
|
||||
year: u16,
|
||||
mon: u8,
|
||||
day: u8,
|
||||
|
||||
Some(entries)
|
||||
hour: u8,
|
||||
min: u8,
|
||||
sec: u8,
|
||||
millis: u16,
|
||||
}
|
||||
|
||||
impl M41T80 {
|
||||
fn open<P: AsRef<Path>>(path: P, config: &I2CMasterConfig) -> io::Result<Self> {
|
||||
let master = I2CMaster::open(path, config)?;
|
||||
Ok(Self { master })
|
||||
}
|
||||
|
||||
fn push_history(&mut self, entry: &str) {
|
||||
self.history.push_back(entry.into());
|
||||
}
|
||||
fn read_time(&mut self) -> io::Result<Time> {
|
||||
let mut buffer = [0; 9];
|
||||
self.master.smbus_read(0x00, &mut buffer)?;
|
||||
|
||||
fn lookup_history(&mut self, depth: usize) -> Option<String> {
|
||||
if depth >= self.history.len() {
|
||||
return None;
|
||||
}
|
||||
let index = self.history.len() - depth - 1;
|
||||
Some(self.history[index].clone())
|
||||
let millis = ((buffer[0] >> 4) as u16 * 100) | ((buffer[0] & 0xF) as u16 * 10);
|
||||
let sec = ((buffer[1] >> 4) & 0x7) * 10 + (buffer[1] & 0xF);
|
||||
let min = ((buffer[2] >> 4) & 0x7) * 10 + (buffer[2] & 0xF);
|
||||
let hour = ((buffer[3] >> 4) & 0x3) * 10 + (buffer[3] & 0xF);
|
||||
|
||||
let day = ((buffer[5] >> 4) & 0x3) * 10 + (buffer[5] & 0xF);
|
||||
let mon = ((buffer[6] >> 4) & 0x1) * 10 + (buffer[6] & 0xF);
|
||||
let year = ((buffer[7] >> 4) as u16 * 10) + ((buffer[7] & 0xF) as u16) + 2000;
|
||||
|
||||
Ok(Time {
|
||||
millis,
|
||||
sec,
|
||||
min,
|
||||
hour,
|
||||
|
||||
year,
|
||||
day,
|
||||
mon,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
logsink::setup_logging(true);
|
||||
let mut readline = Readline::new(P::default()).unwrap();
|
||||
|
||||
fn run_m41t80(device: PathBuf, address: u8) -> io::Result<()> {
|
||||
// let device = "/dev/i2c0";
|
||||
let i2c_config = I2CMasterConfig {
|
||||
slave_address: Some(address as _),
|
||||
speed_hz: None,
|
||||
};
|
||||
let mut m41t80 = M41T80::open(device, &i2c_config)?;
|
||||
loop {
|
||||
let Some(line) = readline.readline().unwrap() else {
|
||||
break;
|
||||
};
|
||||
println!("{line:?}");
|
||||
if line == "exit" {
|
||||
let time = m41t80.read_time()?;
|
||||
print!(
|
||||
"\r{:04}/{:02}/{:02} {:02}:{:02}:{:02}",
|
||||
time.year, time.mon, time.day, time.hour, time.min, time.sec,
|
||||
);
|
||||
stdout().flush().ok();
|
||||
thread::sleep(Duration::from_millis(100));
|
||||
}
|
||||
}
|
||||
|
||||
fn run_jedec(device: PathBuf, capacity: usize) -> io::Result<()> {
|
||||
let config = SpiConfig::default();
|
||||
let mut spi = SpiDevice::open(device, &config)?;
|
||||
|
||||
let tx = [0x13, 0x00, 0x00, 0x00, 0x00];
|
||||
let mut rx = [0; 5];
|
||||
spi.transfer(&tx, &mut rx).unwrap();
|
||||
|
||||
let mut memory = vec![0xFF; capacity];
|
||||
|
||||
const READ_BUFFER: usize = 1024;
|
||||
let tx_word = [0xFF; READ_BUFFER];
|
||||
let mut rx_word = [0; READ_BUFFER];
|
||||
let mut bytes_read = 0;
|
||||
for i in 0..capacity / READ_BUFFER {
|
||||
if spi.transfer(&tx_word, &mut rx_word)? != READ_BUFFER {
|
||||
break;
|
||||
}
|
||||
memory[i * READ_BUFFER..i * READ_BUFFER + READ_BUFFER].copy_from_slice(&rx_word);
|
||||
bytes_read += READ_BUFFER;
|
||||
}
|
||||
|
||||
for (i, &byte) in memory.iter().enumerate() {
|
||||
if i >= bytes_read {
|
||||
print!("??");
|
||||
} else {
|
||||
print!("{:02x}", byte);
|
||||
}
|
||||
if (i + 1) % 2 == 0 {
|
||||
print!(" ");
|
||||
}
|
||||
if (i + 1) % 32 == 0 {
|
||||
println!();
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run(args: Args) -> io::Result<()> {
|
||||
match args.command {
|
||||
Subcommand::M41T80 { address, device } => run_m41t80(device, address),
|
||||
Subcommand::JedecFlash { capacity, device } => run_jedec(device, capacity),
|
||||
}
|
||||
}
|
||||
|
||||
fn main() -> ExitCode {
|
||||
logsink::setup_logging(true);
|
||||
let args = Args::parse();
|
||||
|
||||
match run(args) {
|
||||
Ok(()) => ExitCode::SUCCESS,
|
||||
Err(error) => {
|
||||
eprintln!("{error}");
|
||||
ExitCode::FAILURE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::{
|
||||
fs::File,
|
||||
io::{self, stdin, stdout, BufRead, BufReader, BufWriter, Read, Stdin, Stdout, Write},
|
||||
io::{self, BufRead, BufReader, BufWriter, Read, Stdin, Stdout, Write, stdin, stdout},
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@ use std::process::ExitCode;
|
||||
|
||||
use clap::Parser;
|
||||
use crypt::{
|
||||
rsa::RsaKeyOptions, AsymmetricDecryptOptions, AsymmetricEncryptOptions, AsymmetricKeyOptions,
|
||||
Error,
|
||||
AsymmetricDecryptOptions, AsymmetricEncryptOptions, AsymmetricKeyOptions, Error,
|
||||
rsa::RsaKeyOptions,
|
||||
};
|
||||
use rsa::pkcs8::{EncodePrivateKey, EncodePublicKey, LineEnding};
|
||||
|
||||
|
||||
@@ -4,9 +4,9 @@ use std::{
|
||||
};
|
||||
|
||||
use rsa::{
|
||||
Pkcs1v15Encrypt, RsaPrivateKey, RsaPublicKey,
|
||||
pkcs1::{DecodeRsaPrivateKey, DecodeRsaPublicKey, EncodeRsaPrivateKey, EncodeRsaPublicKey},
|
||||
pkcs8::LineEnding,
|
||||
Pkcs1v15Encrypt, RsaPrivateKey, RsaPublicKey,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
use std::{
|
||||
fs,
|
||||
io::{self, stdout, IsTerminal},
|
||||
io::{self, IsTerminal, stdout},
|
||||
path::PathBuf,
|
||||
process::ExitCode,
|
||||
};
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user