diff --git a/drivers/pinctrl/bcm/Kconfig b/drivers/pinctrl/bcm/Kconfig index 35b51ce4298e25..fb36a760202ac7 100644 --- a/drivers/pinctrl/bcm/Kconfig +++ b/drivers/pinctrl/bcm/Kconfig @@ -29,6 +29,18 @@ config PINCTRL_BCM2835 help Say Y here to enable the Broadcom BCM2835 GPIO driver. +config PINCTRL_BCM2835_RUST + tristate "Broadcom BCM2835 GPIO (with PINCONF) driver written in rust" + depends on OF && (ARCH_BCM2835 || ARCH_BRCMSTB || COMPILE_TEST) && RUST + select PINMUX + select PINCONF + select GENERIC_PINCONF + select GPIOLIB + select GPIOLIB_IRQCHIP + default ARCH_BCM2835 || ARCH_BRCMSTB + help + Say Y here to enable the Broadcom BCM2835 GPIO driver. + config PINCTRL_BCM4908 tristate "Broadcom BCM4908 pinmux driver" depends on OF && (ARCH_BCMBCA || COMPILE_TEST) diff --git a/drivers/pinctrl/bcm/Makefile b/drivers/pinctrl/bcm/Makefile index 82b868ec14716d..b200e52ea0ac95 100644 --- a/drivers/pinctrl/bcm/Makefile +++ b/drivers/pinctrl/bcm/Makefile @@ -3,6 +3,7 @@ obj-$(CONFIG_PINCTRL_BCM281XX) += pinctrl-bcm281xx.o obj-$(CONFIG_PINCTRL_BCM2835) += pinctrl-bcm2835.o +obj-$(CONFIG_PINCTRL_BCM2835_RUST) += pinctrl_bcm2835_rust.o obj-$(CONFIG_PINCTRL_BCM4908) += pinctrl-bcm4908.o obj-$(CONFIG_PINCTRL_BCM63XX) += pinctrl-bcm63xx.o obj-$(CONFIG_PINCTRL_BCM6318) += pinctrl-bcm6318.o @@ -17,3 +18,5 @@ obj-$(CONFIG_PINCTRL_NS) += pinctrl-ns.o obj-$(CONFIG_PINCTRL_NSP_GPIO) += pinctrl-nsp-gpio.o obj-$(CONFIG_PINCTRL_NS2_MUX) += pinctrl-ns2-mux.o obj-$(CONFIG_PINCTRL_NSP_MUX) += pinctrl-nsp-mux.o + + diff --git a/drivers/pinctrl/bcm/pinctrl_bcm2835_rust.rs b/drivers/pinctrl/bcm/pinctrl_bcm2835_rust.rs new file mode 100644 index 00000000000000..ae73124d3338ea --- /dev/null +++ b/drivers/pinctrl/bcm/pinctrl_bcm2835_rust.rs @@ -0,0 +1,222 @@ +//SPDX-License-Identifier: GPL-2.0 + +//! Driver for Boradcom BCM2835 GPIO unit (pinctrl + GPIO) +//! +//! Based on the C driver + +use core::result::Result::Ok; + +use kernel::{ + bit, define_of_id_table, device, gpio, + io_mem::IoMem, + module_platform_driver, of, platform, + prelude::*, + sync::{Arc, ArcBorrow}, +}; + +macro_rules! FSEL_REG { + ($p:expr) => { + GPFSEL0 + (($p / 10) * 4) + }; +} + +macro_rules! FSEL_SHIFT { + ($p:expr) => { + (($p % 10) * 3) + }; +} + +macro_rules! GPIO_REG_OFFSET { + ($p:expr) => { + $p / 32 + }; +} + +macro_rules! GPIO_REG_SHIFT { + ($p:expr) => { + $p % 32 + }; +} + +//GPIO register offsets +const GPFSEL0: usize = 0x0; //function select +const GPSET0: usize = 0x1c; //pin output set +const GPCLR0: usize = 0x28; //pin output clear +const GPLEV0: usize = 0x34; //pin level +const GPIO_SIZE: usize = 0x1000; + +const BCM2835_NUM_GPIOS: u16 = 54; + +// bcm2835_fsel +const BCM2835_FSEL_MASK: u32 = 0x7; +// brcm, function property +const BCM2835_FSEL_GPIO_IN: u32 = 0; +const BCM2835_FSEL_GPIO_OUT: u32 = 1; + + + +struct BCM2835Resources { + base: IoMem, +} + +struct BCM2835Data { + dev: device::Device, +} + +type BCM2835Registrations = gpio::Registration; + +type DeviceData = device::Data; + +struct BCM2835Device; + +impl BCM2835Device { + #[inline] + fn bcm2835_gpio_rd(data: ArcBorrow<'_, DeviceData>, reg: usize) -> Result { + let bcm2835 = data.resources().ok_or(ENXIO)?; + bcm2835.base.try_readl(reg) + } + + #[inline] + fn bcm2835_gpio_wr(data: ArcBorrow<'_, DeviceData>, reg: usize, val: u32) -> Result { + let bcm2835 = data.resources().ok_or(ENXIO)?; + bcm2835.base.try_writel(val, reg)?; + Ok(()) + } + + #[inline] + fn bcm2835_gpio_get_bit( + data: ArcBorrow<'_, DeviceData>, + reg: usize, + offset: u32, + ) -> Result { + let reg = reg + GPIO_REG_OFFSET!(offset as usize) * 4; + Ok(((Self::bcm2835_gpio_rd(data, reg)? >> (GPIO_REG_SHIFT!(offset))) & 1) != 0) + } + + #[inline] + fn bcm2835_gpio_set_bit(data: ArcBorrow<'_, DeviceData>, reg: usize, offset: u32) -> Result { + let reg = reg + GPIO_REG_OFFSET!(offset as usize) * 4; + let val = bit(GPIO_REG_SHIFT!(offset)).into(); + Self::bcm2835_gpio_wr(data, reg, val)?; + Ok(()) + } + + #[inline] + fn bcm2835_pinctrl_fsel_get(data: ArcBorrow<'_, DeviceData>, pin: usize) -> Result { + let val = Self::bcm2835_gpio_rd(data, FSEL_REG!(pin))?; + let status = (val >> FSEL_SHIFT!(pin as u32)) & BCM2835_FSEL_MASK; + Ok(status) + } + + #[inline] + fn bcm2835_pinctrl_fsel_set(data: ArcBorrow<'_, DeviceData>, pin: usize, fsel: u32) -> Result { + let mut val = Self::bcm2835_gpio_rd(data, FSEL_REG!(pin))?; + let cur = (val >> FSEL_SHIFT!(pin as u32)) & BCM2835_FSEL_MASK; + + if cur == fsel { + return Ok(()); + } + + if cur != BCM2835_FSEL_GPIO_IN && fsel != BCM2835_FSEL_GPIO_IN { + val &= !(BCM2835_FSEL_MASK << FSEL_SHIFT!(pin as u32)); + val |= fsel << FSEL_SHIFT!(pin as u32); + + Self::bcm2835_gpio_wr(data, FSEL_REG!(pin), val)?; + } + + val &= !(BCM2835_FSEL_MASK << FSEL_SHIFT!(pin as u32)); + val |= fsel << FSEL_SHIFT!(pin as u32); + + Self::bcm2835_gpio_wr(data, FSEL_REG!(pin), val)?; + Ok(()) + } +} + +//TODO: implement the items in trait gpio::Chip +#[vtable] +impl gpio::Chip for BCM2835Device { + type Data = Arc; + + fn get_direction(data: ArcBorrow<'_, DeviceData>, offset: u32) -> Result { + let fsel = Self::bcm2835_pinctrl_fsel_get(data, offset as usize)?; + + //Alternative function doesn't clearly provide a direction + if fsel > BCM2835_FSEL_GPIO_OUT { + //FIXME: Err(EINVAL) + return Err(ENOTSUPP); + } + + Ok(if fsel == BCM2835_FSEL_GPIO_IN { + gpio::LineDirection::In + } else { + gpio::LineDirection::Out + }) + } + + fn direction_input(data: ArcBorrow<'_, DeviceData>, offset: u32) -> Result { + Self::bcm2835_pinctrl_fsel_set(data,offset as usize,BCM2835_FSEL_GPIO_IN) + } + + fn direction_output(data: ArcBorrow<'_, DeviceData>, offset: u32, value: bool) -> Result { + let reg = if value { GPSET0 } else { GPCLR0 }; + Self::bcm2835_gpio_set_bit(data, reg, offset)?; + Self::bcm2835_pinctrl_fsel_set(data, offset as usize, BCM2835_FSEL_GPIO_OUT)?; + Ok(()) + } + + fn set(data: ArcBorrow<'_, DeviceData>, offset: u32, value: bool) { + let reg = if value { GPSET0 } else { GPCLR0 }; + let _ = Self::bcm2835_pinctrl_fsel_set(data, reg, offset); + } + + fn get(data: ArcBorrow<'_, DeviceData>, offset: u32) -> Result { + Self::bcm2835_gpio_get_bit(data, GPLEV0, offset) + } +} + +impl platform::Driver for BCM2835Device { + type Data = Arc; + + define_of_id_table! {(),[ + //FIXME: None is likely not correct, should fix it maybe + (of::DeviceId::Compatible(b"brcm,bcm2835-gpio"),None), + ]} + + fn probe(dev: &mut platform::Device, _data: Option<&Self::IdInfo>) -> Result> { + let res = dev.res().ok_or(ENXIO)?; + + let data = kernel::new_device_data!( + gpio::Registration::new(), + BCM2835Resources { + //SAFETY:This device doesn't support DMA. + base: unsafe { IoMem::try_new(res)? }, + }, + BCM2835Data { + dev: device::Device::from_dev(dev), + }, + "BCM2835::Regsiterations" + )?; + + let data = Arc::::from(data); + + kernel::gpio_chip_register!( + data.registrations().ok_or(ENXIO)?.as_pinned_mut(), + BCM2835_NUM_GPIOS, + None, + dev, + data.clone() + )?; + + dev_info!(data.dev, "RUST BCM2835 GPIO CHIP registered!!!\n"); + + Ok(data) + } +} + +module_platform_driver! { + type: BCM2835Device, + name: "pinctrl_bcm2835_rust", + author: "Tianyu She", + description: "BCM2835 GPIO Part", + license: "GPL", +} diff --git a/rust/kernel/platform.rs b/rust/kernel/platform.rs index d358e9da81fac3..1eba3d9d95da17 100644 --- a/rust/kernel/platform.rs +++ b/rust/kernel/platform.rs @@ -6,11 +6,14 @@ //! //! C header: [`include/linux/platform_device.h`](../../../../include/linux/platform_device.h) +// use bindings::slab; + use crate::{ bindings, device::{self, RawDevice}, driver, error::{from_kernel_result, Result}, + io_mem::Resource, of, str::CStr, to_result, @@ -183,6 +186,15 @@ impl Device { // SAFETY: By the type invariants, we know that `self.ptr` is non-null and valid. unsafe { (*self.ptr).id } } + + /// Returns the Resource of the platform device + pub fn res(&self) -> Option { + //SAFETY: By the type invariants, we know that 'self.ptr' is non-null and valid + let pdev = unsafe { &*self.ptr }; + //SAFETY: we know that 'platform_device.resource[0]' is non-null + let res = unsafe { *pdev.resource }; + Resource::new(res.start, res.end) + } } // SAFETY: The device returned by `raw_device` is the raw platform device.