diff --git a/arch/arm/boot/dts/bcm2835-rpi-zero-w-rust.dts b/arch/arm/boot/dts/bcm2835-rpi-zero-w-rust.dts deleted file mode 100644 index 18298115c1f5ce..00000000000000 --- a/arch/arm/boot/dts/bcm2835-rpi-zero-w-rust.dts +++ /dev/null @@ -1,156 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (C) 2017 Stefan Wahren - */ - -/dts-v1/; -#include "bcm2835.dtsi" -#include "bcm2835-rpi.dtsi" -#include "bcm283x-rpi-usb-otg.dtsi" - -/ { - compatible = "raspberrypi,model-zero-w", "brcm,bcm2835"; - model = "Raspberry Pi Zero W"; - - bcm2835-rng-rust { - compatible = "brcm,bcm2835-rng"; - status = "okay"; - }; - - memory@0 { - device_type = "memory"; - reg = <0 0x20000000>; - }; - - chosen { - /* 8250 auxiliary UART instead of pl011 */ - stdout-path = "serial1:115200n8"; - }; - - leds { - act { - gpios = <&gpio 47 GPIO_ACTIVE_LOW>; - }; - }; - - wifi_pwrseq: wifi-pwrseq { - compatible = "mmc-pwrseq-simple"; - reset-gpios = <&gpio 41 GPIO_ACTIVE_LOW>; - }; -}; - -&gpio { - /* - * This is based on the official GPU firmware DT blob. - * - * Legend: - * "NC" = not connected (no rail from the SoC) - * "FOO" = GPIO line named "FOO" on the schematic - * "FOO_N" = GPIO line named "FOO" on schematic, active low - */ - gpio-line-names = "ID_SDA", - "ID_SCL", - "SDA1", - "SCL1", - "GPIO_GCLK", - "GPIO5", - "GPIO6", - "SPI_CE1_N", - "SPI_CE0_N", - "SPI_MISO", - "SPI_MOSI", - "SPI_SCLK", - "GPIO12", - "GPIO13", - /* Serial port */ - "TXD0", - "RXD0", - "GPIO16", - "GPIO17", - "GPIO18", - "GPIO19", - "GPIO20", - "GPIO21", - "GPIO22", - "GPIO23", - "GPIO24", - "GPIO25", - "GPIO26", - "GPIO27", - "SDA0", - "SCL0", - "NC", /* GPIO30 */ - "NC", /* GPIO31 */ - "NC", /* GPIO32 */ - "NC", /* GPIO33 */ - "NC", /* GPIO34 */ - "NC", /* GPIO35 */ - "NC", /* GPIO36 */ - "NC", /* GPIO37 */ - "NC", /* GPIO38 */ - "NC", /* GPIO39 */ - "CAM_GPIO1", /* GPIO40 */ - "WL_ON", /* GPIO41 */ - "NC", /* GPIO42 */ - "WIFI_CLK", /* GPIO43 */ - "CAM_GPIO0", /* GPIO44 */ - "BT_ON", /* GPIO45 */ - "HDMI_HPD_N", - "STATUS_LED_N", - /* Used by SD Card */ - "SD_CLK_R", - "SD_CMD_R", - "SD_DATA0_R", - "SD_DATA1_R", - "SD_DATA2_R", - "SD_DATA3_R"; - - pinctrl-0 = <&gpioout &alt0>; -}; - -&hdmi { - hpd-gpios = <&gpio 46 GPIO_ACTIVE_LOW>; - power-domains = <&power RPI_POWER_DOMAIN_HDMI>; - status = "okay"; -}; - -&sdhci { - #address-cells = <1>; - #size-cells = <0>; - pinctrl-names = "default"; - pinctrl-0 = <&emmc_gpio34 &gpclk2_gpio43>; - bus-width = <4>; - mmc-pwrseq = <&wifi_pwrseq>; - non-removable; - status = "okay"; - - brcmf: wifi@1 { - reg = <1>; - compatible = "brcm,bcm4329-fmac"; - }; -}; - -&sdhost { - pinctrl-names = "default"; - pinctrl-0 = <&sdhost_gpio48>; - bus-width = <4>; - status = "okay"; -}; - -&uart0 { - pinctrl-names = "default"; - pinctrl-0 = <&uart0_gpio32 &uart0_ctsrts_gpio30>; - status = "okay"; - - bluetooth { - compatible = "brcm,bcm43438-bt"; - max-speed = <2000000>; - shutdown-gpios = <&gpio 45 GPIO_ACTIVE_HIGH>; - }; -}; - -&uart1 { - pinctrl-names = "default"; - pinctrl-0 = <&uart1_gpio14>; - status = "okay"; -}; diff --git a/arch/arm/boot/dts/bcm2836-rpi-2-b-rust.dts b/arch/arm/boot/dts/bcm2836-rpi-2-b-rust.dts deleted file mode 100644 index 1e3340097ec761..00000000000000 --- a/arch/arm/boot/dts/bcm2836-rpi-2-b-rust.dts +++ /dev/null @@ -1,135 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/dts-v1/; -#include "bcm2836.dtsi" -#include "bcm2836-rpi.dtsi" -#include "bcm283x-rpi-smsc9514.dtsi" -#include "bcm283x-rpi-usb-host.dtsi" - -/ { - compatible = "raspberrypi,2-model-b", "brcm,bcm2836"; - model = "Raspberry Pi 2 Model B"; - - memory@0 { - device_type = "memory"; - reg = <0 0x40000000>; - }; - - bcm2835-rng-rust { - compatible = "brcm,bcm2835-rng"; - status = "okay"; - }; - - leds { - act { - gpios = <&gpio 47 GPIO_ACTIVE_HIGH>; - }; - - pwr { - label = "PWR"; - gpios = <&gpio 35 GPIO_ACTIVE_HIGH>; - default-state = "keep"; - linux,default-trigger = "default-on"; - }; - }; -}; - -&gpio { - /* - * Taken from rpi_SCH_2b_1p2_reduced.pdf and - * the official GPU firmware DT blob. - * - * Legend: - * "NC" = not connected (no rail from the SoC) - * "FOO" = GPIO line named "FOO" on the schematic - * "FOO_N" = GPIO line named "FOO" on schematic, active low - */ - gpio-line-names = "ID_SDA", - "ID_SCL", - "SDA1", - "SCL1", - "GPIO_GCLK", - "GPIO5", - "GPIO6", - "SPI_CE1_N", - "SPI_CE0_N", - "SPI_MISO", - "SPI_MOSI", - "SPI_SCLK", - "GPIO12", - "GPIO13", - /* Serial port */ - "TXD0", - "RXD0", - "GPIO16", - "GPIO17", - "GPIO18", - "GPIO19", - "GPIO20", - "GPIO21", - "GPIO22", - "GPIO23", - "GPIO24", - "GPIO25", - "GPIO26", - "GPIO27", - "SDA0", - "SCL0", - "", /* GPIO30 */ - "LAN_RUN", - "CAM_GPIO1", - "", /* GPIO33 */ - "", /* GPIO34 */ - "PWR_LOW_N", - "", /* GPIO36 */ - "", /* GPIO37 */ - "USB_LIMIT", - "", /* GPIO39 */ - "PWM0_OUT", - "CAM_GPIO0", - "SMPS_SCL", - "SMPS_SDA", - "ETHCLK", - "PWM1_OUT", - "HDMI_HPD_N", - "STATUS_LED", - /* Used by SD Card */ - "SD_CLK_R", - "SD_CMD_R", - "SD_DATA0_R", - "SD_DATA1_R", - "SD_DATA2_R", - "SD_DATA3_R"; - - pinctrl-0 = <&gpioout &alt0 &i2s_alt0>; - - /* I2S interface */ - i2s_alt0: i2s_alt0 { - brcm,pins = <18 19 20 21>; - brcm,function = ; - }; -}; - -&hdmi { - hpd-gpios = <&gpio 46 GPIO_ACTIVE_LOW>; - power-domains = <&power RPI_POWER_DOMAIN_HDMI>; - status = "okay"; -}; - -&pwm { - pinctrl-names = "default"; - pinctrl-0 = <&pwm0_gpio40 &pwm1_gpio45>; - status = "okay"; -}; - -&sdhost { - pinctrl-names = "default"; - pinctrl-0 = <&sdhost_gpio48>; - bus-width = <4>; - status = "okay"; -}; - -&uart0 { - pinctrl-names = "default"; - pinctrl-0 = <&uart0_gpio14>; - status = "okay"; -}; diff --git a/drivers/char/hw_random/bcm2835_rng_rust.rs b/drivers/char/hw_random/bcm2835_rng_rust.rs index 0a89dc0ba87828..8b9e83b013b58a 100644 --- a/drivers/char/hw_random/bcm2835_rng_rust.rs +++ b/drivers/char/hw_random/bcm2835_rng_rust.rs @@ -7,6 +7,7 @@ use alloc::boxed::Box; use core::pin::Pin; +use kernel::of::OfMatchTable; use kernel::prelude::*; use kernel::{cstr, platdev}; @@ -24,7 +25,13 @@ struct RngModule { impl KernelModule for RngModule { fn init() -> Result { - let pdev = platdev::Registration::new_pinned(cstr!("bcm2835-rng-rust"), &THIS_MODULE)?; + let of_match_tbl = OfMatchTable::new(&cstr!("brcm,bcm2835-rng"))?; + + let pdev = platdev::Registration::new_pinned( + cstr!("bcm2835-rng-rust"), + Some(of_match_tbl), + &THIS_MODULE, + )?; Ok(RngModule { _pdev: pdev }) } diff --git a/rust/kernel/bindings_helper.h b/rust/kernel/bindings_helper.h index b66a326fc48ae8..d2cbed8566dea1 100644 --- a/rust/kernel/bindings_helper.h +++ b/rust/kernel/bindings_helper.h @@ -14,6 +14,7 @@ #include #include #include +#include // `bindgen` gets confused at certain things const gfp_t BINDINGS_GFP_KERNEL = GFP_KERNEL; diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 0989ea601314dc..b32db1ce0de182 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -62,6 +62,7 @@ pub mod sysctl; pub mod io_buffer; pub mod iov_iter; +pub mod of; pub mod platdev; mod types; pub mod user_ptr; diff --git a/rust/kernel/of.rs b/rust/kernel/of.rs new file mode 100644 index 00000000000000..10372458af2ab1 --- /dev/null +++ b/rust/kernel/of.rs @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Devicetree and Open Firmware abstractions. +//! +//! C header: [`include/linux/of_*.h`](../../../../include/linux/of_*.h) + +use alloc::boxed::Box; + +use crate::{ + bindings, c_types, + error::{Error, Result}, + types::PointerWrapper, + CStr, +}; + +use core::mem::transmute; + +type InnerTable = Box<[bindings::of_device_id; 2]>; + +/// Wraps a kernel Open Firmware / devicetree match table. +/// +/// Rust drivers may create this structure to match against devices +/// described in the devicetree. +/// +/// The ['PointerWrapper'] trait provides conversion to/from a raw pointer, +/// suitable to be assigned to a `bindings::device_driver::of_match_table`. +/// +/// # Invariants +/// +/// The final array element is always filled with zeros (the default). +pub struct OfMatchTable(InnerTable); + +impl OfMatchTable { + /// Creates a [`OfMatchTable`] from a single `compatible` string. + pub fn new(compatible: &CStr<'static>) -> Result { + let tbl: InnerTable = Box::try_new([ + Self::new_of_device_id(compatible)?, + bindings::of_device_id::default(), + ])?; + // INVARIANTS: we allocated an array with `default()` as its final + // element, therefore that final element will be filled with zeros, + // and the invariant above will hold. + Ok(Self(tbl)) + } + + fn new_of_device_id(compatible: &CStr<'static>) -> Result { + let mut buf = [0_u8; 128]; + if compatible.len() > buf.len() { + return Err(Error::EINVAL); + } + buf.get_mut(..compatible.len()) + .ok_or(Error::EINVAL)? + .copy_from_slice(compatible.as_bytes()); + Ok(bindings::of_device_id { + // SAFETY: re-interpretation from [u8] to [c_types::c_char] of same length is always safe. + compatible: unsafe { transmute::<[u8; 128], [c_types::c_char; 128]>(buf) }, + ..Default::default() + }) + } +} + +impl PointerWrapper for OfMatchTable { + fn into_pointer(self) -> *const c_types::c_void { + // Per the invariant above, the generated pointer points to an + // array of `bindings::of_device_id`, where the final element is + // filled with zeros (the sentinel). Therefore, it's suitable to + // be assigned to `bindings::device_driver::of_match_table`. + self.0.into_pointer() + } + + unsafe fn from_pointer(p: *const c_types::c_void) -> Self { + Self(InnerTable::from_pointer(p)) + } +} diff --git a/rust/kernel/platdev.rs b/rust/kernel/platdev.rs index 7cf35657227426..ff95300a1ae127 100644 --- a/rust/kernel/platdev.rs +++ b/rust/kernel/platdev.rs @@ -9,7 +9,10 @@ use crate::{ bindings, c_types, error::{Error, Result}, - pr_info, CStr, + of::OfMatchTable, + pr_info, + types::PointerWrapper, + CStr, }; use alloc::boxed::Box; use core::{marker::PhantomPinned, pin::Pin}; @@ -18,6 +21,7 @@ use core::{marker::PhantomPinned, pin::Pin}; #[derive(Default)] pub struct Registration { registered: bool, + of_table: Option<*const c_types::c_void>, pdrv: bindings::platform_driver, _pin: PhantomPinned, } @@ -40,6 +44,7 @@ impl Registration { fn register( self: Pin<&mut Self>, name: CStr<'static>, + of_match_table: Option, module: &'static crate::ThisModule, ) -> Result { // SAFETY: We must ensure that we never move out of `this`. @@ -49,6 +54,11 @@ impl Registration { return Err(Error::EINVAL); } this.pdrv.driver.name = name.as_ptr() as *const c_types::c_char; + if let Some(tbl) = of_match_table { + let ptr = tbl.into_pointer(); + this.of_table = Some(ptr); + this.pdrv.driver.of_match_table = ptr.cast(); + } this.pdrv.probe = Some(probe_callback); this.pdrv.remove = Some(remove_callback); // SAFETY: @@ -56,6 +66,10 @@ impl Registration { // - `name` pointer has static lifetime. // - `module.0` lives at least as long as the module. // - `probe()` and `remove()` are static functions. + // - `of_match_table` is either: + // - a raw pointer which lives until after the call to + // `bindings::platform_driver_unregister()`, or + // - null. let ret = unsafe { bindings::__platform_driver_register(&mut this.pdrv, module.0) }; if ret < 0 { return Err(Error::from_kernel_errno(ret)); @@ -69,10 +83,11 @@ impl Registration { /// Returns a pinned heap-allocated representation of the registration. pub fn new_pinned( name: CStr<'static>, + of_match_tbl: Option, module: &'static crate::ThisModule, ) -> Result>> { let mut r = Pin::from(Box::try_new(Self::default())?); - r.as_mut().register(name, module)?; + r.as_mut().register(name, of_match_tbl, module)?; Ok(r) } } @@ -85,5 +100,10 @@ impl Drop for Registration { // safe to call. unsafe { bindings::platform_driver_unregister(&mut self.pdrv) } } + if let Some(ptr) = self.of_table { + // SAFETY: `ptr` came from an `OfMatchTable`. + let tbl = unsafe { OfMatchTable::from_pointer(ptr) }; + drop(tbl); + } } }