* [PATCH v4 00/20] Rust support @ 2022-02-12 13:03 Miguel Ojeda 2022-02-12 13:03 ` [PATCH v4 19/20] [RFC] drivers: gpio: PrimeCell PL061 in Rust Miguel Ojeda 0 siblings, 1 reply; 2+ messages in thread From: Miguel Ojeda @ 2022-02-12 13:03 UTC (permalink / raw) To: Linus Torvalds, Greg Kroah-Hartman Cc: rust-for-linux, linux-kernel, Miguel Ojeda, linux-arm-kernel, linux-doc, linux-gpio, linux-kbuild, linux-perf-users, linuxppc-dev, linux-riscv, live-patching Rust support This is the patch series (v4) to add support for Rust as a second language to the Linux kernel. If you are interested in following this effort, please join us in the mailing list at: rust-for-linux@vger.kernel.org and take a look at the project itself at: https://github.com/Rust-for-Linux As usual, special thanks go to ISRG (Internet Security Research Group) and Google for their financial support on this endeavor. Cheers, Miguel -- # Rust support This cover letter explains the major changes and updates done since the previous ones. For those, please see: RFC: https://lore.kernel.org/lkml/20210414184604.23473-1-ojeda@kernel.org/ v1: https://lore.kernel.org/lkml/20210704202756.29107-1-ojeda@kernel.org/ v2: https://lore.kernel.org/lkml/20211206140313.5653-1-ojeda@kernel.org/ v3: https://lore.kernel.org/lkml/20220117053349.6804-1-ojeda@kernel.org/ ## Infrastructure updates There have been several improvements to the overall Rust support: - The Intel 0DAY/LKP kernel test robot is setting up Rust in their CI -- we already got the first reports from the bot which we could act upon, very useful! - KernelCI is also looking forward to enabling Rust in their runs. - The GitHub CI we use for quick pre-merge testing has been moved to use containers. - Kernel modules do not need to write the crate attributes `#![no_std]` and `#![feature(...)]` anymore, removing boilerplate. - Added single target support, including `.o`, `.s`, `.ll` and `.i` (i.e. macro expanded, similar to C preprocessed sources). - Explanation of the `helpers.c` file and licensing for helpers and exports. - The documentation logo is now vector based (SVG). In addition, a vector version of Tux has been proposed for upstream, and an RFC for improved custom logo support has been submitted to upstream Rust. - Added coding guidelines on comments (`//`) and code documentation (`///`). - `is_rust_module.sh` rework. - Generation of `.rmeta` for leaf modules is skipped now. - Other cleanups, fixes and improvements. ## Abstractions and driver updates Some of the improvements to the abstractions and example drivers are: - Added support for static (global shared variables) synchronization primitives. `CONFIG_CONSTRUCTORS` are used for the implementation. - Simplification of the lock guards by using marker types, i.e. `Guard` and `GuardMut` are unified into a single parametrized type. If the marker is `WriteLock`, then `Guard` implements `DerefMut` (only implemented by `GuardMut` previously). - Optional parameters added to the registration of misc devices, following the builder pattern., e.g. miscdev::Options::new() .mode(0o600) .minor(10) .parent(parent) .register(reg, c_str!("sample"), ()) - Added `RwSemaphore` abstraction that wraps the C side `struct rw_semaphore`. - New `mm` module and VMA abstraction (wrapping the C side `struct vm_area_struct`) for use with `mmap`. - GPIO PL061 now uses the recently added `dev_*!` Rust macros. - Support the `!CONFIG_PRINTK` case. - Other cleanups, fixes and improvements. ## Patch series status The Rust support is still to be considered experimental. However, support is good enough that kernel developers can start working on the Rust abstractions for subsystems and write drivers and other modules. The current series has just arrived in `linux-next`, as usual. Similarly, the preview docs for this series can be seen at: https://rust-for-linux.github.io/docs/kernel/ As usual, please see the following link for the live list of unstable Rust features we are using: https://github.com/Rust-for-Linux/linux/issues/2 ## Acknowledgements The signatures in the main commits correspond to the people that wrote code that has ended up in them at the present time. For details on contributions to code and discussions, please see our repository: https://github.com/Rust-for-Linux/linux However, we would like to give credit to everyone that has contributed in one way or another to the Rust for Linux project. Since the previous cover letter: - Philip Li, Yujie Liu et. al. for setting the Intel 0DAY/LKP kernel test robot with Rust support. - Maciej Falkowski for continuing his work on the Hardware Random Number Generator subsystem, Samsung Exynos true random number generator, clock subsystem, etc. - bjorn3 for an extensive re-review of the previous round. - Jonathan Corbet for his feedback on the previous round. - Garrett LeSage and IFo Hancroft for agreeing to the usage of their vector version of Tux within the kernel tree with the same license as the existing one from Larry Ewing. - Nathan Chancellor for noticing a case where a C module was being identified as a Rust one for purposes of skipping BTF generation and suggesting a few improvements around `is_rust_module.sh`. - Wei Liu for exporting the remaining helpers and triggering a useful discussion around it. - Miguel Cano for fixing an issue with compilers containing a full, 3-part version in the suffix part (e.g. Ubuntu Clang). - Abhik Jain for working on adding missing `// SAFETY` comments and `# Safety` sections to the code generated by the `module!` macro. - Jiapeng Chong and the Abaci Robot for reporting and fixing an unneeded header `#include`. - Finn Behrens for resuming his work on building Rust for Linux on Nix and spotting and fixing usability issues. - As usual, Gary Guo and bjorn3 for all the input on Rust compiler details and all the reviews and suggestions. - John Ericson, TennyZhuang and Xuanwo for their ongoing work on adding more fallible allocation methods (`try_*`) to the Rust standard library. - Stephan Sokolow and Mark Rousskov for their feedback on the custom documentation logo RFC. - Philip Herrons (and his supporters Open Source Security and Embecosm) et. al. for their ongoing work on GCC Rust. - Antoni Boucher (and his supporters) et. al. for their ongoing work on `rustc_codegen_gcc`. - Mats Larsen, Marc Poulhiès et. al. for their ongoing work on improving Rust support in Compiler Explorer. - Many folks that have reported issues, tested the project, helped spread the word, joined discussions and contributed in other ways! Please see also the acknowledgements on the previous cover letters. Boqun Feng (1): kallsyms: use the correct buffer size for symbols Gary Guo (2): rust: add `build_error` crate vsprintf: add new `%pA` format specifier Miguel Ojeda (13): kallsyms: support "big" kernel symbols kallsyms: increase maximum kernel symbol length to 512 rust: add C helpers rust: add `compiler_builtins` crate rust: add `alloc` crate rust: add `macros` crate rust: export generated symbols scripts: add `generate_rust_analyzer.py` scripts: decode_stacktrace: demangle Rust symbols docs: add Rust documentation Kbuild: add Rust support samples: add Rust examples MAINTAINERS: Rust Wedson Almeida Filho (4): rust: add `kernel` crate's `sync` module rust: add `kernel` crate [RFC] drivers: gpio: PrimeCell PL061 in Rust [RFC] drivers: android: Binder IPC in Rust .gitignore | 5 + .rustfmt.toml | 12 + Documentation/doc-guide/kernel-doc.rst | 3 + Documentation/index.rst | 1 + Documentation/kbuild/kbuild.rst | 4 + Documentation/process/changes.rst | 42 + Documentation/rust/arch-support.rst | 35 + Documentation/rust/coding-guidelines.rst | 153 + Documentation/rust/general-information.rst | 80 + Documentation/rust/index.rst | 21 + Documentation/rust/logo.svg | 357 ++ Documentation/rust/quick-start.rst | 231 ++ MAINTAINERS | 16 + Makefile | 154 +- arch/arm/rust/target.json | 27 + arch/arm64/rust/target.json | 34 + arch/powerpc/rust/target.json | 29 + arch/riscv/Makefile | 1 + arch/riscv/rust/rv32ima.json | 36 + arch/riscv/rust/rv32imac.json | 36 + arch/riscv/rust/rv64ima.json | 36 + arch/riscv/rust/rv64imac.json | 36 + arch/x86/rust/target.json | 36 + drivers/android/Kconfig | 7 + drivers/android/Makefile | 2 + drivers/android/allocation.rs | 266 ++ drivers/android/context.rs | 80 + drivers/android/defs.rs | 99 + drivers/android/node.rs | 476 +++ drivers/android/process.rs | 961 +++++ drivers/android/range_alloc.rs | 189 + drivers/android/rust_binder.rs | 111 + drivers/android/thread.rs | 871 +++++ drivers/android/transaction.rs | 326 ++ drivers/gpio/Kconfig | 8 + drivers/gpio/Makefile | 1 + drivers/gpio/gpio_pl061_rust.rs | 370 ++ include/linux/kallsyms.h | 2 +- include/linux/spinlock.h | 17 +- include/uapi/linux/android/binder.h | 28 +- init/Kconfig | 45 +- kernel/kallsyms.c | 26 +- kernel/livepatch/core.c | 4 +- kernel/printk/printk.c | 5 +- lib/Kconfig.debug | 144 + lib/rust.h | 14 + lib/vsprintf.c | 7 + rust/.gitignore | 7 + rust/Makefile | 374 ++ rust/alloc/README.md | 32 + rust/alloc/alloc.rs | 427 +++ rust/alloc/borrow.rs | 498 +++ rust/alloc/boxed.rs | 1847 ++++++++++ rust/alloc/collections/mod.rs | 156 + rust/alloc/fmt.rs | 601 ++++ rust/alloc/lib.rs | 217 ++ rust/alloc/macros.rs | 126 + rust/alloc/raw_vec.rs | 549 +++ rust/alloc/slice.rs | 1279 +++++++ rust/alloc/str.rs | 624 ++++ rust/alloc/string.rs | 2864 +++++++++++++++ rust/alloc/vec/drain.rs | 159 + rust/alloc/vec/drain_filter.rs | 145 + rust/alloc/vec/into_iter.rs | 356 ++ rust/alloc/vec/is_zero.rs | 106 + rust/alloc/vec/mod.rs | 3359 ++++++++++++++++++ rust/alloc/vec/partial_eq.rs | 49 + rust/alloc/vec/set_len_on_drop.rs | 30 + rust/alloc/vec/spec_extend.rs | 172 + rust/bindgen_parameters | 13 + rust/build_error.rs | 29 + rust/compiler_builtins.rs | 57 + rust/exports.c | 19 + rust/helpers.c | 529 +++ rust/kernel/allocator.rs | 65 + rust/kernel/amba.rs | 259 ++ rust/kernel/bindings.rs | 47 + rust/kernel/bindings_helper.h | 30 + rust/kernel/buffer.rs | 52 + rust/kernel/build_assert.rs | 80 + rust/kernel/c_types.rs | 119 + rust/kernel/chrdev.rs | 209 ++ rust/kernel/clk.rs | 75 + rust/kernel/cred.rs | 73 + rust/kernel/device.rs | 554 +++ rust/kernel/driver.rs | 440 +++ rust/kernel/error.rs | 542 +++ rust/kernel/file.rs | 147 + rust/kernel/file_operations.rs | 734 ++++ rust/kernel/gpio.rs | 477 +++ rust/kernel/io_buffer.rs | 153 + rust/kernel/io_mem.rs | 227 ++ rust/kernel/iov_iter.rs | 81 + rust/kernel/irq.rs | 409 +++ rust/kernel/lib.rs | 261 ++ rust/kernel/linked_list.rs | 247 ++ rust/kernel/miscdev.rs | 281 ++ rust/kernel/mm.rs | 149 + rust/kernel/module_param.rs | 497 +++ rust/kernel/of.rs | 63 + rust/kernel/pages.rs | 144 + rust/kernel/platform.rs | 224 ++ rust/kernel/power.rs | 118 + rust/kernel/prelude.rs | 36 + rust/kernel/print.rs | 417 +++ rust/kernel/random.rs | 50 + rust/kernel/raw_list.rs | 361 ++ rust/kernel/rbtree.rs | 562 +++ rust/kernel/revocable.rs | 163 + rust/kernel/security.rs | 36 + rust/kernel/static_assert.rs | 39 + rust/kernel/std_vendor.rs | 150 + rust/kernel/str.rs | 401 +++ rust/kernel/sync/arc.rs | 500 +++ rust/kernel/sync/condvar.rs | 138 + rust/kernel/sync/guard.rs | 150 + rust/kernel/sync/locked_by.rs | 112 + rust/kernel/sync/mod.rs | 157 + rust/kernel/sync/mutex.rs | 112 + rust/kernel/sync/revocable_mutex.rs | 184 + rust/kernel/sync/rwsem.rs | 147 + rust/kernel/sync/seqlock.rs | 202 ++ rust/kernel/sync/spinlock.rs | 180 + rust/kernel/sysctl.rs | 197 + rust/kernel/task.rs | 182 + rust/kernel/types.rs | 486 +++ rust/kernel/user_ptr.rs | 175 + rust/macros/helpers.rs | 79 + rust/macros/lib.rs | 94 + rust/macros/module.rs | 631 ++++ samples/Kconfig | 2 + samples/Makefile | 1 + samples/rust/Kconfig | 123 + samples/rust/Makefile | 13 + samples/rust/rust_chrdev.rs | 50 + samples/rust/rust_minimal.rs | 35 + samples/rust/rust_miscdev.rs | 144 + samples/rust/rust_module_parameters.rs | 69 + samples/rust/rust_platform.rs | 22 + samples/rust/rust_print.rs | 54 + samples/rust/rust_random.rs | 61 + samples/rust/rust_semaphore.rs | 172 + samples/rust/rust_semaphore_c.c | 212 ++ samples/rust/rust_stack_probing.rs | 36 + samples/rust/rust_sync.rs | 78 + scripts/Kconfig.include | 6 +- scripts/Makefile.build | 65 + scripts/Makefile.debug | 10 + scripts/Makefile.lib | 12 + scripts/Makefile.modfinal | 8 +- scripts/cc-version.sh | 12 +- scripts/decode_stacktrace.sh | 14 + scripts/generate_rust_analyzer.py | 133 + scripts/is_rust_module.sh | 13 + scripts/kallsyms.c | 40 +- scripts/kconfig/confdata.c | 75 + scripts/min-tool-version.sh | 6 + scripts/rust-is-available-bindgen-libclang.h | 2 + scripts/rust-is-available.sh | 158 + tools/include/linux/kallsyms.h | 2 +- tools/lib/perf/include/perf/event.h | 2 +- tools/lib/symbol/kallsyms.h | 2 +- 162 files changed, 34341 insertions(+), 57 deletions(-) create mode 100644 .rustfmt.toml create mode 100644 Documentation/rust/arch-support.rst create mode 100644 Documentation/rust/coding-guidelines.rst create mode 100644 Documentation/rust/general-information.rst create mode 100644 Documentation/rust/index.rst create mode 100644 Documentation/rust/logo.svg create mode 100644 Documentation/rust/quick-start.rst create mode 100644 arch/arm/rust/target.json create mode 100644 arch/arm64/rust/target.json create mode 100644 arch/powerpc/rust/target.json create mode 100644 arch/riscv/rust/rv32ima.json create mode 100644 arch/riscv/rust/rv32imac.json create mode 100644 arch/riscv/rust/rv64ima.json create mode 100644 arch/riscv/rust/rv64imac.json create mode 100644 arch/x86/rust/target.json create mode 100644 drivers/android/allocation.rs create mode 100644 drivers/android/context.rs create mode 100644 drivers/android/defs.rs create mode 100644 drivers/android/node.rs create mode 100644 drivers/android/process.rs create mode 100644 drivers/android/range_alloc.rs create mode 100644 drivers/android/rust_binder.rs create mode 100644 drivers/android/thread.rs create mode 100644 drivers/android/transaction.rs create mode 100644 drivers/gpio/gpio_pl061_rust.rs create mode 100644 lib/rust.h create mode 100644 rust/.gitignore create mode 100644 rust/Makefile create mode 100644 rust/alloc/README.md create mode 100644 rust/alloc/alloc.rs create mode 100644 rust/alloc/borrow.rs create mode 100644 rust/alloc/boxed.rs create mode 100644 rust/alloc/collections/mod.rs create mode 100644 rust/alloc/fmt.rs create mode 100644 rust/alloc/lib.rs create mode 100644 rust/alloc/macros.rs create mode 100644 rust/alloc/raw_vec.rs create mode 100644 rust/alloc/slice.rs create mode 100644 rust/alloc/str.rs create mode 100644 rust/alloc/string.rs create mode 100644 rust/alloc/vec/drain.rs create mode 100644 rust/alloc/vec/drain_filter.rs create mode 100644 rust/alloc/vec/into_iter.rs create mode 100644 rust/alloc/vec/is_zero.rs create mode 100644 rust/alloc/vec/mod.rs create mode 100644 rust/alloc/vec/partial_eq.rs create mode 100644 rust/alloc/vec/set_len_on_drop.rs create mode 100644 rust/alloc/vec/spec_extend.rs create mode 100644 rust/bindgen_parameters create mode 100644 rust/build_error.rs create mode 100644 rust/compiler_builtins.rs create mode 100644 rust/exports.c create mode 100644 rust/helpers.c create mode 100644 rust/kernel/allocator.rs create mode 100644 rust/kernel/amba.rs create mode 100644 rust/kernel/bindings.rs create mode 100644 rust/kernel/bindings_helper.h create mode 100644 rust/kernel/buffer.rs create mode 100644 rust/kernel/build_assert.rs create mode 100644 rust/kernel/c_types.rs create mode 100644 rust/kernel/chrdev.rs create mode 100644 rust/kernel/clk.rs create mode 100644 rust/kernel/cred.rs create mode 100644 rust/kernel/device.rs create mode 100644 rust/kernel/driver.rs create mode 100644 rust/kernel/error.rs create mode 100644 rust/kernel/file.rs create mode 100644 rust/kernel/file_operations.rs create mode 100644 rust/kernel/gpio.rs create mode 100644 rust/kernel/io_buffer.rs create mode 100644 rust/kernel/io_mem.rs create mode 100644 rust/kernel/iov_iter.rs create mode 100644 rust/kernel/irq.rs create mode 100644 rust/kernel/lib.rs create mode 100644 rust/kernel/linked_list.rs create mode 100644 rust/kernel/miscdev.rs create mode 100644 rust/kernel/mm.rs create mode 100644 rust/kernel/module_param.rs create mode 100644 rust/kernel/of.rs create mode 100644 rust/kernel/pages.rs create mode 100644 rust/kernel/platform.rs create mode 100644 rust/kernel/power.rs create mode 100644 rust/kernel/prelude.rs create mode 100644 rust/kernel/print.rs create mode 100644 rust/kernel/random.rs create mode 100644 rust/kernel/raw_list.rs create mode 100644 rust/kernel/rbtree.rs create mode 100644 rust/kernel/revocable.rs create mode 100644 rust/kernel/security.rs create mode 100644 rust/kernel/static_assert.rs create mode 100644 rust/kernel/std_vendor.rs create mode 100644 rust/kernel/str.rs create mode 100644 rust/kernel/sync/arc.rs create mode 100644 rust/kernel/sync/condvar.rs create mode 100644 rust/kernel/sync/guard.rs create mode 100644 rust/kernel/sync/locked_by.rs create mode 100644 rust/kernel/sync/mod.rs create mode 100644 rust/kernel/sync/mutex.rs create mode 100644 rust/kernel/sync/revocable_mutex.rs create mode 100644 rust/kernel/sync/rwsem.rs create mode 100644 rust/kernel/sync/seqlock.rs create mode 100644 rust/kernel/sync/spinlock.rs create mode 100644 rust/kernel/sysctl.rs create mode 100644 rust/kernel/task.rs create mode 100644 rust/kernel/types.rs create mode 100644 rust/kernel/user_ptr.rs create mode 100644 rust/macros/helpers.rs create mode 100644 rust/macros/lib.rs create mode 100644 rust/macros/module.rs create mode 100644 samples/rust/Kconfig create mode 100644 samples/rust/Makefile create mode 100644 samples/rust/rust_chrdev.rs create mode 100644 samples/rust/rust_minimal.rs create mode 100644 samples/rust/rust_miscdev.rs create mode 100644 samples/rust/rust_module_parameters.rs create mode 100644 samples/rust/rust_platform.rs create mode 100644 samples/rust/rust_print.rs create mode 100644 samples/rust/rust_random.rs create mode 100644 samples/rust/rust_semaphore.rs create mode 100644 samples/rust/rust_semaphore_c.c create mode 100644 samples/rust/rust_stack_probing.rs create mode 100644 samples/rust/rust_sync.rs create mode 100755 scripts/generate_rust_analyzer.py create mode 100755 scripts/is_rust_module.sh create mode 100644 scripts/rust-is-available-bindgen-libclang.h create mode 100755 scripts/rust-is-available.sh base-commit: dfd42facf1e4ada021b939b4e19c935dcdd55566 -- 2.35.1 ^ permalink raw reply [flat|nested] 2+ messages in thread
* [PATCH v4 19/20] [RFC] drivers: gpio: PrimeCell PL061 in Rust 2022-02-12 13:03 [PATCH v4 00/20] Rust support Miguel Ojeda @ 2022-02-12 13:03 ` Miguel Ojeda 0 siblings, 0 replies; 2+ messages in thread From: Miguel Ojeda @ 2022-02-12 13:03 UTC (permalink / raw) To: Linus Torvalds, Greg Kroah-Hartman Cc: rust-for-linux, linux-kernel, Miguel Ojeda, Wedson Almeida Filho, Linus Walleij, Bartosz Golaszewski, linux-gpio From: Wedson Almeida Filho <wedsonaf@google.com> A port to Rust of the PrimeCell PL061 GPIO driver. This module is a work in progress and will be sent for review later on, as well as separately from the Rust support. However, it is included to show how an actual working module written in Rust may look like. Signed-off-by: Wedson Almeida Filho <wedsonaf@google.com> Signed-off-by: Miguel Ojeda <ojeda@kernel.org> --- drivers/gpio/Kconfig | 8 + drivers/gpio/Makefile | 1 + drivers/gpio/gpio_pl061_rust.rs | 370 ++++++++++++++++++++++++++++++++ 3 files changed, 379 insertions(+) create mode 100644 drivers/gpio/gpio_pl061_rust.rs diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 1c211b4c63be..172295770c3d 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -471,6 +471,14 @@ config GPIO_PL061 help Say yes here to support the PrimeCell PL061 GPIO device. +config GPIO_PL061_RUST + tristate "PrimeCell PL061 GPIO support written in Rust" + depends on ARM_AMBA && RUST + select IRQ_DOMAIN + select GPIOLIB_IRQCHIP + help + Say yes here to support the PrimeCell PL061 GPIO device + config GPIO_PMIC_EIC_SPRD tristate "Spreadtrum PMIC EIC support" depends on MFD_SC27XX_PMIC || COMPILE_TEST diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index edbaa3cb343c..7dc334df9c3f 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -117,6 +117,7 @@ obj-$(CONFIG_GPIO_PCIE_IDIO_24) += gpio-pcie-idio-24.o obj-$(CONFIG_GPIO_PCI_IDIO_16) += gpio-pci-idio-16.o obj-$(CONFIG_GPIO_PISOSR) += gpio-pisosr.o obj-$(CONFIG_GPIO_PL061) += gpio-pl061.o +obj-$(CONFIG_GPIO_PL061_RUST) += gpio_pl061_rust.o obj-$(CONFIG_GPIO_PMIC_EIC_SPRD) += gpio-pmic-eic-sprd.o obj-$(CONFIG_GPIO_PXA) += gpio-pxa.o obj-$(CONFIG_GPIO_RASPBERRYPI_EXP) += gpio-raspberrypi-exp.o diff --git a/drivers/gpio/gpio_pl061_rust.rs b/drivers/gpio/gpio_pl061_rust.rs new file mode 100644 index 000000000000..fef046924dd5 --- /dev/null +++ b/drivers/gpio/gpio_pl061_rust.rs @@ -0,0 +1,370 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Driver for the ARM PrimeCell(tm) General Purpose Input/Output (PL061). +//! +//! Based on the C driver written by Baruch Siach <baruch@tkos.co.il>. + +use kernel::{ + amba, bit, bits_iter, define_amba_id_table, device, gpio, + io_mem::IoMem, + irq::{self, ExtraResult, IrqData, LockedIrqData}, + power, + prelude::*, + sync::{Ref, RefBorrow, SpinLock}, +}; + +const GPIODIR: usize = 0x400; +const GPIOIS: usize = 0x404; +const GPIOIBE: usize = 0x408; +const GPIOIEV: usize = 0x40C; +const GPIOIE: usize = 0x410; +const GPIOMIS: usize = 0x418; +const GPIOIC: usize = 0x41C; +const GPIO_SIZE: usize = 0x1000; + +const PL061_GPIO_NR: u16 = 8; + +#[derive(Default)] +struct ContextSaveRegs { + gpio_data: u8, + gpio_dir: u8, + gpio_is: u8, + gpio_ibe: u8, + gpio_iev: u8, + gpio_ie: u8, +} + +#[derive(Default)] +struct PL061DataInner { + csave_regs: ContextSaveRegs, +} + +struct PL061Data { + dev: device::Device, + inner: SpinLock<PL061DataInner>, +} + +struct PL061Resources { + base: IoMem<GPIO_SIZE>, + parent_irq: u32, +} + +type PL061Registrations = gpio::RegistrationWithIrqChip<PL061Device>; + +type DeviceData = device::Data<PL061Registrations, PL061Resources, PL061Data>; + +struct PL061Device; + +impl gpio::Chip for PL061Device { + type Data = Ref<DeviceData>; + + kernel::declare_gpio_chip_operations!( + get_direction, + direction_input, + direction_output, + get, + set + ); + + fn get_direction(data: RefBorrow<'_, DeviceData>, offset: u32) -> Result<gpio::LineDirection> { + let pl061 = data.resources().ok_or(Error::ENXIO)?; + Ok(if pl061.base.readb(GPIODIR) & bit(offset) != 0 { + gpio::LineDirection::Out + } else { + gpio::LineDirection::In + }) + } + + fn direction_input(data: RefBorrow<'_, DeviceData>, offset: u32) -> Result { + let _guard = data.inner.lock_irqdisable(); + let pl061 = data.resources().ok_or(Error::ENXIO)?; + let mut gpiodir = pl061.base.readb(GPIODIR); + gpiodir &= !bit(offset); + pl061.base.writeb(gpiodir, GPIODIR); + Ok(()) + } + + fn direction_output(data: RefBorrow<'_, DeviceData>, offset: u32, value: bool) -> Result { + let woffset = bit(offset + 2).into(); + let _guard = data.inner.lock_irqdisable(); + let pl061 = data.resources().ok_or(Error::ENXIO)?; + pl061.base.try_writeb((value as u8) << offset, woffset)?; + let mut gpiodir = pl061.base.readb(GPIODIR); + gpiodir |= bit(offset); + pl061.base.writeb(gpiodir, GPIODIR); + + // gpio value is set again, because pl061 doesn't allow to set value of a gpio pin before + // configuring it in OUT mode. + pl061.base.try_writeb((value as u8) << offset, woffset)?; + Ok(()) + } + + fn get(data: RefBorrow<'_, DeviceData>, offset: u32) -> Result<bool> { + let pl061 = data.resources().ok_or(Error::ENXIO)?; + Ok(pl061.base.try_readb(bit(offset + 2).into())? != 0) + } + + fn set(data: RefBorrow<'_, DeviceData>, offset: u32, value: bool) { + if let Some(pl061) = data.resources() { + let woffset = bit(offset + 2).into(); + let _ = pl061.base.try_writeb((value as u8) << offset, woffset); + } + } +} + +impl gpio::ChipWithIrqChip for PL061Device { + fn handle_irq_flow( + data: RefBorrow<'_, DeviceData>, + desc: &irq::Descriptor, + domain: &irq::Domain, + ) { + let chained = desc.enter_chained(); + + if let Some(pl061) = data.resources() { + let pending = pl061.base.readb(GPIOMIS); + for offset in bits_iter(pending) { + domain.generic_handle_chained(offset, &chained); + } + } + } +} + +impl irq::Chip for PL061Device { + type Data = Ref<DeviceData>; + + kernel::declare_irq_chip_operations!(set_type, set_wake); + + fn set_type( + data: RefBorrow<'_, DeviceData>, + irq_data: &mut LockedIrqData, + trigger: u32, + ) -> Result<ExtraResult> { + let offset = irq_data.hwirq(); + let bit = bit(offset); + + if offset >= PL061_GPIO_NR.into() { + return Err(Error::EINVAL); + } + + if trigger & (irq::Type::LEVEL_HIGH | irq::Type::LEVEL_LOW) != 0 + && trigger & (irq::Type::EDGE_RISING | irq::Type::EDGE_FALLING) != 0 + { + dev_err!( + data.dev, + "trying to configure line {} for both level and edge detection, choose one!\n", + offset + ); + return Err(Error::EINVAL); + } + + let _guard = data.inner.lock_irqdisable(); + let pl061 = data.resources().ok_or(Error::ENXIO)?; + + let mut gpioiev = pl061.base.readb(GPIOIEV); + let mut gpiois = pl061.base.readb(GPIOIS); + let mut gpioibe = pl061.base.readb(GPIOIBE); + + if trigger & (irq::Type::LEVEL_HIGH | irq::Type::LEVEL_LOW) != 0 { + let polarity = trigger & irq::Type::LEVEL_HIGH != 0; + + // Disable edge detection. + gpioibe &= !bit; + // Enable level detection. + gpiois |= bit; + // Select polarity. + if polarity { + gpioiev |= bit; + } else { + gpioiev &= !bit; + } + irq_data.set_level_handler(); + dev_dbg!( + data.dev, + "line {}: IRQ on {} level\n", + offset, + if polarity { "HIGH" } else { "LOW" } + ); + } else if (trigger & irq::Type::EDGE_BOTH) == irq::Type::EDGE_BOTH { + // Disable level detection. + gpiois &= !bit; + // Select both edges, settings this makes GPIOEV be ignored. + gpioibe |= bit; + irq_data.set_edge_handler(); + dev_dbg!(data.dev, "line {}: IRQ on both edges\n", offset); + } else if trigger & (irq::Type::EDGE_RISING | irq::Type::EDGE_FALLING) != 0 { + let rising = trigger & irq::Type::EDGE_RISING != 0; + + // Disable level detection. + gpiois &= !bit; + // Clear detection on both edges. + gpioibe &= !bit; + // Select edge. + if rising { + gpioiev |= bit; + } else { + gpioiev &= !bit; + } + irq_data.set_edge_handler(); + dev_dbg!( + data.dev, + "line {}: IRQ on {} edge\n", + offset, + if rising { "RISING" } else { "FALLING}" } + ); + } else { + // No trigger: disable everything. + gpiois &= !bit; + gpioibe &= !bit; + gpioiev &= !bit; + irq_data.set_bad_handler(); + dev_warn!(data.dev, "no trigger selected for line {}\n", offset); + } + + pl061.base.writeb(gpiois, GPIOIS); + pl061.base.writeb(gpioibe, GPIOIBE); + pl061.base.writeb(gpioiev, GPIOIEV); + + Ok(ExtraResult::None) + } + + fn mask(data: RefBorrow<'_, DeviceData>, irq_data: &IrqData) { + let mask = bit(irq_data.hwirq() % irq::HwNumber::from(PL061_GPIO_NR)); + let _guard = data.inner.lock(); + if let Some(pl061) = data.resources() { + let gpioie = pl061.base.readb(GPIOIE) & !mask; + pl061.base.writeb(gpioie, GPIOIE); + } + } + + fn unmask(data: RefBorrow<'_, DeviceData>, irq_data: &IrqData) { + let mask = bit(irq_data.hwirq() % irq::HwNumber::from(PL061_GPIO_NR)); + let _guard = data.inner.lock(); + if let Some(pl061) = data.resources() { + let gpioie = pl061.base.readb(GPIOIE) | mask; + pl061.base.writeb(gpioie, GPIOIE); + } + } + + // This gets called from the edge IRQ handler to ACK the edge IRQ in the GPIOIC + // (interrupt-clear) register. For level IRQs this is not needed: these go away when the level + // signal goes away. + fn ack(data: RefBorrow<'_, DeviceData>, irq_data: &IrqData) { + let mask = bit(irq_data.hwirq() % irq::HwNumber::from(PL061_GPIO_NR)); + let _guard = data.inner.lock(); + if let Some(pl061) = data.resources() { + pl061.base.writeb(mask.into(), GPIOIC); + } + } + + fn set_wake(data: RefBorrow<'_, DeviceData>, _irq_data: &IrqData, on: bool) -> Result { + let pl061 = data.resources().ok_or(Error::ENXIO)?; + irq::set_wake(pl061.parent_irq, on) + } +} + +impl amba::Driver for PL061Device { + type Data = Ref<DeviceData>; + type PowerOps = Self; + + define_amba_id_table! {(), [ + ({id: 0x00041061, mask: 0x000fffff}, None), + ]} + + fn probe(dev: &mut amba::Device, _data: Option<&Self::IdInfo>) -> Result<Ref<DeviceData>> { + let res = dev.take_resource().ok_or(Error::ENXIO)?; + let irq = dev.irq(0).ok_or(Error::ENXIO)?; + + let mut data = kernel::new_device_data!( + gpio::RegistrationWithIrqChip::new(), + PL061Resources { + // SAFETY: This device doesn't support DMA. + base: unsafe { IoMem::try_new(res)? }, + parent_irq: irq, + }, + PL061Data { + dev: device::Device::from_dev(dev), + // SAFETY: We call `spinlock_init` below. + inner: unsafe { SpinLock::new(PL061DataInner::default()) }, + }, + "PL061::Registrations" + )?; + + // SAFETY: General part of the data is pinned when `data` is. + let gen_inner = unsafe { data.as_mut().map_unchecked_mut(|d| &mut (**d).inner) }; + kernel::spinlock_init!(gen_inner, "PL061Data::inner"); + + let data = Ref::<DeviceData>::from(data); + + data.resources().ok_or(Error::ENXIO)?.base.writeb(0, GPIOIE); // disable irqs + + data.registrations() + .ok_or(Error::ENXIO)? + .as_pinned_mut() + .register::<Self>(PL061_GPIO_NR, None, dev, data.clone(), irq)?; + + dev_info!(data.dev, "PL061 GPIO chip registered\n"); + + Ok(data) + } +} + +impl power::Operations for PL061Device { + type Data = Ref<DeviceData>; + + fn suspend(data: RefBorrow<'_, DeviceData>) -> Result { + let mut inner = data.inner.lock(); + let pl061 = data.resources().ok_or(Error::ENXIO)?; + inner.csave_regs.gpio_data = 0; + inner.csave_regs.gpio_dir = pl061.base.readb(GPIODIR); + inner.csave_regs.gpio_is = pl061.base.readb(GPIOIS); + inner.csave_regs.gpio_ibe = pl061.base.readb(GPIOIBE); + inner.csave_regs.gpio_iev = pl061.base.readb(GPIOIEV); + inner.csave_regs.gpio_ie = pl061.base.readb(GPIOIE); + + for offset in 0..PL061_GPIO_NR { + if inner.csave_regs.gpio_dir & bit(offset) != 0 { + if let Ok(v) = <Self as gpio::Chip>::get(data, offset.into()) { + inner.csave_regs.gpio_data |= (v as u8) << offset; + } + } + } + + Ok(()) + } + + fn resume(data: RefBorrow<'_, DeviceData>) -> Result { + let inner = data.inner.lock(); + let pl061 = data.resources().ok_or(Error::ENXIO)?; + + for offset in 0..PL061_GPIO_NR { + if inner.csave_regs.gpio_dir & bit(offset) != 0 { + let value = inner.csave_regs.gpio_data & bit(offset) != 0; + let _ = <Self as gpio::Chip>::direction_output(data, offset.into(), value); + } else { + let _ = <Self as gpio::Chip>::direction_input(data, offset.into()); + } + } + + pl061.base.writeb(inner.csave_regs.gpio_is, GPIOIS); + pl061.base.writeb(inner.csave_regs.gpio_ibe, GPIOIBE); + pl061.base.writeb(inner.csave_regs.gpio_iev, GPIOIEV); + pl061.base.writeb(inner.csave_regs.gpio_ie, GPIOIE); + + Ok(()) + } + + fn freeze(data: RefBorrow<'_, DeviceData>) -> Result { + Self::suspend(data) + } + + fn restore(data: RefBorrow<'_, DeviceData>) -> Result { + Self::resume(data) + } +} + +module_amba_driver! { + type: PL061Device, + name: b"pl061_gpio", + author: b"Wedson Almeida Filho", + license: b"GPL v2", +} -- 2.35.1 ^ permalink raw reply related [flat|nested] 2+ messages in thread
end of thread, other threads:[~2022-02-12 13:07 UTC | newest] Thread overview: 2+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2022-02-12 13:03 [PATCH v4 00/20] Rust support Miguel Ojeda 2022-02-12 13:03 ` [PATCH v4 19/20] [RFC] drivers: gpio: PrimeCell PL061 in Rust Miguel Ojeda
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).