public inbox for devicetree@vger.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH v2 0/4] iio: position: add Rust driver for ams AS5600
@ 2026-04-29 13:22 Muchamad Coirul Anwar
  2026-04-29 13:22 ` [RFC PATCH v2 1/4] i2c: rust: add smbus_read_byte_data and smbus_read_word_data Muchamad Coirul Anwar
                   ` (3 more replies)
  0 siblings, 4 replies; 8+ messages in thread
From: Muchamad Coirul Anwar @ 2026-04-29 13:22 UTC (permalink / raw)
  To: jic23, linux-iio, rust-for-linux, devicetree
  Cc: branstj, lars, ojeda, robh, krzk+dt, conor+dt, igor.korotin.linux,
	linux-kernel, Muchamad Coirul Anwar

This is v2 of the Rust driver for the ams AS5600 12-bit magnetic rotary
position sensor. v1 was a probe-only stub; this version adds minimal
Rust IIO abstractions and exposes in_angl_raw and in_angl_scale via
sysfs.

Link: https://lore.kernel.org/linux-iio/20260419151327.26306-1-muchamadcoirulanwar@gmail.com/

Changes since RFC v1:
  - Moved magnet validation from probe() to read_raw() per Jonathan's
    feedback. probe() now only verifies I2C communication.
  - Added minimal Rust IIO abstractions (rust/kernel/iio.rs) wrapping
    iio_device_alloc, iio_device_register, and the read_raw callback.
  - Added OF device table for devicetree matching (ams,as5600).
  - Replaced hex bit masks with kernel::bits::bit_u8() per Miguel's
    pointer.
  - Replaced magic numbers with bindings constants
    (iio_chan_info_enum_*, INDIO_DIRECT_MODE).
  - Downgraded log messages to dev_dbg!(), removed unbind noise.
  - Added devicetree binding documentation.
  - iio_info vtable is now a compile-time const.
  - Uses iio_device_alloc with PinnedDrop instead of devm_* to avoid
    lifetime conflicts with Rust's ownership model.

The IIO abstraction design was informed by earlier unpublished work
from Brandon Saint-John.

The IIO abstraction is intentionally minimal: it supports read_raw
with IIO_VAL_INT, IIO_VAL_INT_PLUS_NANO, IIO_VAL_INT_PLUS_MICRO,
and IIO_VAL_FRACTIONAL. write_raw and buffer support are left for
future work.

Muchamad Coirul Anwar (4):
  i2c: rust: add smbus_read_byte_data and smbus_read_word_data
  rust: add minimal IIO subsystem abstractions
  dt-bindings: iio: position: add ams,as5600
  iio: position: add Rust driver for ams AS5600

Signed-off-by: Muchamad Coirul Anwar <muchamadcoirulanwar@gmail.com>
---
Tested on BeagleBone Black (AM335x), kernel v7.0.0-rc3,
AS5600 on i2c-2 (0x36) at 3.3V, diametric neodymium magnet.

  $ cat /sys/bus/iio/devices/iio:device0/in_angl_raw
  3296
  $ cat /sys/bus/iio/devices/iio:device0/in_angl_raw
  1675
  $ cat /sys/bus/iio/devices/iio:device0/in_angl_raw
  468

  $ cat /sys/bus/iio/devices/iio:device0/in_angl_scale
  0.001533981

Magnet removed:

  $ cat /sys/bus/iio/devices/iio:device0/in_angl_raw
  cat: '/sys/bus/iio/devices/iio:device0/in_angl_raw': No data available

Muchamad Coirul Anwar (4):
  i2c: rust: add smbus_read_byte_data and smbus_read_word_data
  rust: add minimal IIO subsystem abstractions
  dt-bindings: iio: position: add ams,as5600
  iio: position: add Rust driver for ams AS5600

 .../bindings/iio/position/ams,as5600.yaml     |  40 ++++
 drivers/iio/position/Kconfig                  |  14 ++
 drivers/iio/position/Makefile                 |   1 +
 drivers/iio/position/as5600.rs                | 129 +++++
 rust/bindings/bindings_helper.h               |   2 +
 rust/kernel/i2c.rs                            |  24 +
 rust/kernel/iio.rs                            | 224 ++++++++++
 rust/kernel/lib.rs                            |   2 +
 8 files changed, 436 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/iio/position/ams,as5600.yaml
 create mode 100644 drivers/iio/position/as5600.rs
 create mode 100644 rust/kernel/iio.rs

-- 
2.50.0


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [RFC PATCH v2 1/4] i2c: rust: add smbus_read_byte_data and smbus_read_word_data
  2026-04-29 13:22 [RFC PATCH v2 0/4] iio: position: add Rust driver for ams AS5600 Muchamad Coirul Anwar
@ 2026-04-29 13:22 ` Muchamad Coirul Anwar
  2026-05-04 10:29   ` Igor Korotin
  2026-04-29 13:22 ` [RFC PATCH v2 2/4] rust: add minimal IIO subsystem abstractions Muchamad Coirul Anwar
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 8+ messages in thread
From: Muchamad Coirul Anwar @ 2026-04-29 13:22 UTC (permalink / raw)
  To: jic23, linux-iio, rust-for-linux, devicetree
  Cc: branstj, lars, ojeda, robh, krzk+dt, conor+dt, igor.korotin.linux,
	linux-kernel, Muchamad Coirul Anwar

Signed-off-by: Muchamad Coirul Anwar <muchamadcoirulanwar@gmail.com>
---
 rust/kernel/i2c.rs | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)

diff --git a/rust/kernel/i2c.rs b/rust/kernel/i2c.rs
index 7b908f0c5a58..6eaea1158fda 100644
--- a/rust/kernel/i2c.rs
+++ b/rust/kernel/i2c.rs
@@ -477,6 +477,30 @@ impl<Ctx: device::DeviceContext> I2cClient<Ctx> {
     fn as_raw(&self) -> *mut bindings::i2c_client {
         self.0.get()
     }
+
+    /// Reads a single byte from a register via SMBus.
+    pub fn smbus_read_byte_data(&self, reg: u8) -> Result<u8> {
+        // SAFETY: `self.as_raw()` is a valid pointer to a `struct i2c_client`
+        // by the type invariant of `I2cClient`.
+        let ret = unsafe { bindings::i2c_smbus_read_byte_data(self.as_raw(), reg) };
+        if ret < 0 {
+            Err(Error::from_errno(ret))
+        } else {
+            Ok(ret as u8)
+        }
+    }
+
+    /// Reads a 16-bit word from a register via SMBus.
+    pub fn smbus_read_word_data(&self, reg: u8) -> Result<u16> {
+        // SAFETY: `self.as_raw()` is a valid pointer to a `struct i2c_client`
+        // by the type invariant of `I2cClient`.
+        let ret = unsafe { bindings::i2c_smbus_read_word_data(self.as_raw(), reg) };
+        if ret < 0 {
+            Err(Error::from_errno(ret))
+        } else {
+            Ok(ret as u16)
+        }
+    }
 }
 
 // SAFETY: `I2cClient` is a transparent wrapper of `struct i2c_client`.
-- 
2.50.0


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [RFC PATCH v2 2/4] rust: add minimal IIO subsystem abstractions
  2026-04-29 13:22 [RFC PATCH v2 0/4] iio: position: add Rust driver for ams AS5600 Muchamad Coirul Anwar
  2026-04-29 13:22 ` [RFC PATCH v2 1/4] i2c: rust: add smbus_read_byte_data and smbus_read_word_data Muchamad Coirul Anwar
@ 2026-04-29 13:22 ` Muchamad Coirul Anwar
  2026-04-29 13:22 ` [RFC PATCH v2 3/4] dt-bindings: iio: position: add ams,as5600 Muchamad Coirul Anwar
  2026-04-29 13:22 ` [RFC PATCH v2 4/4] iio: position: add Rust driver for ams AS5600 Muchamad Coirul Anwar
  3 siblings, 0 replies; 8+ messages in thread
From: Muchamad Coirul Anwar @ 2026-04-29 13:22 UTC (permalink / raw)
  To: jic23, linux-iio, rust-for-linux, devicetree
  Cc: branstj, lars, ojeda, robh, krzk+dt, conor+dt, igor.korotin.linux,
	linux-kernel, Muchamad Coirul Anwar

Signed-off-by: Muchamad Coirul Anwar <muchamadcoirulanwar@gmail.com>
---
 rust/bindings/bindings_helper.h |   2 +
 rust/kernel/iio.rs              | 224 ++++++++++++++++++++++++++++++++
 rust/kernel/lib.rs              |   2 +
 3 files changed, 228 insertions(+)
 create mode 100644 rust/kernel/iio.rs

diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index 083cc44aa952..e732ee19b90c 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -58,6 +58,8 @@
 #include <linux/firmware.h>
 #include <linux/fs.h>
 #include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/types.h>
 #include <linux/interrupt.h>
 #include <linux/io-pgtable.h>
 #include <linux/ioport.h>
diff --git a/rust/kernel/iio.rs b/rust/kernel/iio.rs
new file mode 100644
index 000000000000..ee27d928e803
--- /dev/null
+++ b/rust/kernel/iio.rs
@@ -0,0 +1,224 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2026 Muchamad Coirul Anwar <muchamadcoirulanwar@gmail.com>
+//! IIO subsystem abstractions.
+//!
+//! Minimal safe Rust wrappers for the Linux IIO (Industrial I/O) subsystem.
+//! Provides [`Device`] for allocating and registering an IIO device, and the
+//! [`IioDriver`] trait for implementing `read_raw` callbacks in safe Rust.
+
+use crate::{
+    bindings::{
+        __iio_device_register, iio_chan_spec, iio_dev, iio_device_alloc, iio_device_free,
+        iio_device_unregister, iio_info, INDIO_DIRECT_MODE,
+    },
+    device,
+    error::{code::*, to_result, Result},
+    prelude::*,
+    ThisModule,
+};
+use core::{
+    ffi::c_int,
+    marker::PhantomData,
+    mem::{size_of, zeroed},
+    ptr::{drop_in_place, write},
+};
+use pin_init::{pin_data, pinned_drop};
+/// IIO value type: single integer (`IIO_VAL_INT`).
+pub const IIO_VAL_INT: c_int = crate::bindings::IIO_VAL_INT as c_int;
+/// IIO value type: integer plus micro part (`IIO_VAL_INT_PLUS_MICRO`).
+pub const IIO_VAL_INT_PLUS_MICRO: c_int = crate::bindings::IIO_VAL_INT_PLUS_MICRO as c_int;
+/// IIO value type: integer plus nano part (`IIO_VAL_INT_PLUS_NANO`).
+pub const IIO_VAL_INT_PLUS_NANO: c_int = crate::bindings::IIO_VAL_INT_PLUS_NANO as c_int;
+/// IIO value type: fractional (`IIO_VAL_FRACTIONAL`).
+pub const IIO_VAL_FRACTIONAL: c_int = crate::bindings::IIO_VAL_FRACTIONAL as c_int;
+
+/// Represents the return value of a `read_raw` operation.
+///
+/// Each variant corresponds to an `IIO_VAL_*` constant and tells the
+/// IIO core how to format `val` and `val2` for userspace.
+pub enum IioVal {
+    /// A single integer value.
+    Int(i32),
+    /// A fractional value represented as `val / val2`.
+    Fractional(i32, i32),
+    /// An integer plus a micro (10⁻⁶) fractional part: `val.val2`.
+    IntPlusMicro(i32, i32),
+    /// An integer plus a nano (10⁻⁹) fractional part: `val.val2`.
+    IntPlusNano(i32, i32),
+}
+
+/// Trait to be implemented by IIO driver private data.
+///
+/// Implementors supply the `read_raw` callback invoked by the IIO core
+/// when userspace reads a channel attribute (e.g. `in_angl_raw`).
+pub trait IioDriver {
+    /// Called by the IIO core when userspace reads a channel attribute.
+    ///
+    /// `chan` is the channel being read; `mask` selects the attribute
+    /// (e.g. `IIO_CHAN_INFO_RAW`, `IIO_CHAN_INFO_SCALE`).
+    fn read_raw(&self, chan: *const iio_chan_spec, mask: isize) -> Result<IioVal>;
+
+    /// Returns the channel specifications for this driver.
+    ///
+    /// The default implementation returns an empty slice.
+    fn channels(&self) -> &[iio_chan_spec] {
+        &[]
+    }
+}
+
+/// C-compatible trampoline for the `iio_info.read_raw` callback.
+///
+/// # Safety
+///
+/// This function is only called by the IIO core with valid pointers:
+/// - `indio_dev` is a valid `iio_dev` allocated by `iio_device_alloc`.
+/// - `chan` points to a valid channel spec from the device's channel array.
+/// - `val` and `val2` are valid pointers for writing the result.
+unsafe extern "C" fn read_raw_callback<T: IioDriver>(
+    indio_dev: *mut iio_dev,
+    chan: *const iio_chan_spec,
+    val: *mut c_int,
+    val2: *mut c_int,
+    mask: isize,
+) -> c_int {
+    // SAFETY: `indio_dev` is valid and was allocated with space for `T` in its
+    // private data area. The `priv_` field was initialized in `Device::new()`.
+    let priv_ptr = unsafe { (*indio_dev).priv_ as *mut T };
+    // SAFETY: `priv_ptr` points to a valid, initialized instance of `T` that
+    // lives as long as the `iio_dev` allocation.
+    let driver = unsafe { &*priv_ptr };
+
+    match driver.read_raw(chan, mask) {
+        Ok(IioVal::Int(v)) => {
+            // SAFETY: `val` is a valid pointer provided by the IIO core.
+            unsafe { *val = v };
+            IIO_VAL_INT
+        }
+        Ok(IioVal::Fractional(v, v2)) => {
+            // SAFETY: `val` and `val2` are valid pointers provided by the IIO core.
+            unsafe {
+                *val = v;
+                *val2 = v2;
+            };
+            IIO_VAL_FRACTIONAL
+        }
+        Ok(IioVal::IntPlusMicro(v, v2)) => {
+            // SAFETY: `val` and `val2` are valid pointers provided by the IIO core.
+            unsafe {
+                *val = v;
+                *val2 = v2;
+            }
+            IIO_VAL_INT_PLUS_MICRO
+        }
+        Ok(IioVal::IntPlusNano(v, v2)) => {
+            // SAFETY: `val` and `val2` are valid pointers provided by the IIO core.
+            unsafe {
+                *val = v;
+                *val2 = v2;
+            }
+            IIO_VAL_INT_PLUS_NANO
+        }
+        Err(e) => e.to_errno(),
+    }
+}
+
+/// A registered IIO device.
+///
+/// Wraps a C `struct iio_dev` and manages its lifetime. On drop the
+/// device is unregistered (if registered) and its memory freed.
+#[pin_data(PinnedDrop)]
+pub struct Device<T: IioDriver> {
+    indio_dev: *mut iio_dev,
+    registered: bool,
+    _p: PhantomData<T>,
+}
+
+// SAFETY: `Device` only contains a raw pointer to a kernel-managed `iio_dev`.
+// The IIO core serializes access to the device, and `T` is required to be `Send`.
+unsafe impl<T: IioDriver + Send> Send for Device<T> {}
+// SAFETY: All `&self` access to the `iio_dev` is read-only or goes through the
+// IIO core which provides its own synchronization. `T` is required to be `Sync`.
+unsafe impl<T: IioDriver + Sync> Sync for Device<T> {}
+
+#[pinned_drop]
+impl<T: IioDriver> PinnedDrop for Device<T> {
+    // SAFETY: `self.indio_dev` was allocated by `iio_device_alloc` in `new()`
+    // and is valid for the lifetime of this struct. We unregister first (if
+    // registered), then drop the private data, then free the `iio_dev`.
+    // This ordering is critical: unregister ensures no more callbacks can
+    // fire before we drop the driver data they reference.
+    fn drop(self: Pin<&mut Self>) {
+        unsafe {
+            if self.registered {
+                iio_device_unregister(self.indio_dev);
+            }
+            let priv_ptr = (*self.indio_dev).priv_ as *mut T;
+            drop_in_place(priv_ptr);
+            iio_device_free(self.indio_dev);
+        }
+    }
+}
+
+impl<T: IioDriver> Device<T> {
+    // SAFETY: The remaining fields of `iio_info` are pointers and function
+    // pointers. Zeroed values are NULL, and the IIO core checks for NULL
+    // before invoking callbacks or dereferencing attribute group pointers.
+    const VTABLE: iio_info = iio_info {
+        read_raw: Some(read_raw_callback::<T>),
+        ..unsafe { zeroed() }
+    };
+
+    /// Allocates a new IIO device with the given driver data.
+    ///
+    /// The device is not yet registered; call [`register`](Self::register)
+    /// to make it visible to userspace.
+    pub fn new(dev: &device::Device, data: T, name: &'static CStr) -> Result<Self> {
+        // SAFETY: `dev.as_raw()` returns a valid `struct device` pointer.
+        // `iio_device_alloc` allocates an `iio_dev` with `sizeof(T)` bytes of
+        // private data. Returns NULL on failure.
+        let indio_dev = unsafe { iio_device_alloc(dev.as_raw(), size_of::<T>() as _) };
+        if indio_dev.is_null() {
+            return Err(ENOMEM);
+        }
+
+        // SAFETY: `indio_dev` is a valid, newly allocated `iio_dev`.
+        // - `priv_` points to an uninitialized area of `sizeof(T)` bytes.
+        // - `core::ptr::write` initializes it without reading the old value.
+        // - `name` is a `'static` C string that outlives the device.
+        // - `VTABLE` is a `'static` const and outlives the device.
+        // - `channels()` returns a reference to data owned by `T` in `priv_`,
+        //   which remains at a fixed address for the lifetime of `indio_dev`
+        //   because `priv_` is heap-allocated by `iio_device_alloc`.
+        unsafe {
+            let priv_ptr = (*indio_dev).priv_ as *mut T;
+            write(priv_ptr, data);
+            (*indio_dev).name = name.as_char_ptr();
+            (*indio_dev).info = &Self::VTABLE;
+
+            let chans = (*priv_ptr).channels();
+            (*indio_dev).channels = chans.as_ptr() as *const iio_chan_spec;
+            (*indio_dev).num_channels = chans.len() as _;
+            (*indio_dev).modes = INDIO_DIRECT_MODE as i32;
+        }
+
+        Ok(Self {
+            indio_dev,
+            registered: false,
+            _p: PhantomData,
+        })
+    }
+
+    /// Registers the IIO device, making it visible to userspace via sysfs.
+    ///
+    /// On success, channel attributes like `in_angl_raw` become readable.
+    /// On failure the device stays unregistered and will be freed when
+    /// this [`Device`] is dropped.
+    pub fn register(&mut self, _dev: &device::Device, module: &'static ThisModule) -> Result {
+        // SAFETY: `self.indio_dev` is a valid, fully initialized `iio_dev`.
+        // `module.as_ptr()` provides the module owner for proper refcounting.
+        let ret = unsafe { __iio_device_register(self.indio_dev, module.as_ptr()) };
+        to_result(ret)?;
+        self.registered = true;
+        Ok(())
+    }
+}
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index 138d846f798d..ec6eb4dbdb6a 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -99,6 +99,8 @@
 #[cfg(CONFIG_I2C = "y")]
 pub mod i2c;
 pub mod id_pool;
+#[cfg(CONFIG_IIO)]
+pub mod iio;
 #[doc(hidden)]
 pub mod impl_flags;
 pub mod init;
-- 
2.50.0


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [RFC PATCH v2 3/4] dt-bindings: iio: position: add ams,as5600
  2026-04-29 13:22 [RFC PATCH v2 0/4] iio: position: add Rust driver for ams AS5600 Muchamad Coirul Anwar
  2026-04-29 13:22 ` [RFC PATCH v2 1/4] i2c: rust: add smbus_read_byte_data and smbus_read_word_data Muchamad Coirul Anwar
  2026-04-29 13:22 ` [RFC PATCH v2 2/4] rust: add minimal IIO subsystem abstractions Muchamad Coirul Anwar
@ 2026-04-29 13:22 ` Muchamad Coirul Anwar
  2026-04-29 13:46   ` Krzysztof Kozlowski
  2026-04-29 13:22 ` [RFC PATCH v2 4/4] iio: position: add Rust driver for ams AS5600 Muchamad Coirul Anwar
  3 siblings, 1 reply; 8+ messages in thread
From: Muchamad Coirul Anwar @ 2026-04-29 13:22 UTC (permalink / raw)
  To: jic23, linux-iio, rust-for-linux, devicetree
  Cc: branstj, lars, ojeda, robh, krzk+dt, conor+dt, igor.korotin.linux,
	linux-kernel, Muchamad Coirul Anwar

Signed-off-by: Muchamad Coirul Anwar <muchamadcoirulanwar@gmail.com>
---
 .../bindings/iio/position/ams,as5600.yaml     | 40 +++++++++++++++++++
 1 file changed, 40 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/iio/position/ams,as5600.yaml

diff --git a/Documentation/devicetree/bindings/iio/position/ams,as5600.yaml b/Documentation/devicetree/bindings/iio/position/ams,as5600.yaml
new file mode 100644
index 000000000000..d29543d85d1a
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/position/ams,as5600.yaml
@@ -0,0 +1,40 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/position/ams,as5600.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: ams AS5600 Magnetic Rotary Position Sensor
+
+maintainers:
+  - Muchamad Coirul Anwar <muchamadcoirulanwar@gmail.com>
+
+description: |
+  The ams AS5600 is a 12-bit programmable contactless
+  magnetic rotary position sensor communicating over I2C.
+  The device address is fixed at 0x36.
+
+properties:
+  compatible:
+    const: ams,as5600
+
+  reg:
+    const: 0x36
+
+required:
+  - compatible
+  - reg
+
+additionalProperties: false
+
+examples:
+  - |
+    i2c {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        position-sensor@36 {
+            compatible = "ams,as5600";
+            reg = <0x36>;
+        };
+    };
-- 
2.50.0


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [RFC PATCH v2 4/4] iio: position: add Rust driver for ams AS5600
  2026-04-29 13:22 [RFC PATCH v2 0/4] iio: position: add Rust driver for ams AS5600 Muchamad Coirul Anwar
                   ` (2 preceding siblings ...)
  2026-04-29 13:22 ` [RFC PATCH v2 3/4] dt-bindings: iio: position: add ams,as5600 Muchamad Coirul Anwar
@ 2026-04-29 13:22 ` Muchamad Coirul Anwar
  3 siblings, 0 replies; 8+ messages in thread
From: Muchamad Coirul Anwar @ 2026-04-29 13:22 UTC (permalink / raw)
  To: jic23, linux-iio, rust-for-linux, devicetree
  Cc: branstj, lars, ojeda, robh, krzk+dt, conor+dt, igor.korotin.linux,
	linux-kernel, Muchamad Coirul Anwar

Signed-off-by: Muchamad Coirul Anwar <muchamadcoirulanwar@gmail.com>
---
 drivers/iio/position/Kconfig   |  14 ++++
 drivers/iio/position/Makefile  |   1 +
 drivers/iio/position/as5600.rs | 129 +++++++++++++++++++++++++++++++++
 3 files changed, 144 insertions(+)
 create mode 100644 drivers/iio/position/as5600.rs

diff --git a/drivers/iio/position/Kconfig b/drivers/iio/position/Kconfig
index 1576a6380b53..dab9310e8079 100644
--- a/drivers/iio/position/Kconfig
+++ b/drivers/iio/position/Kconfig
@@ -6,6 +6,20 @@
 
 menu "Linear and angular position sensors"
 
+config AS5600
+	tristate "ams AS5600 magnetic rotary position sensor"
+	depends on I2C && RUST
+	help
+	  Say Y here to build support for the ams AS5600 12-bit
+	  magnetic rotary position sensor with IIO channel support
+	  (in_angl_raw and in_angl_scale).
+
+	  This is a Rust driver that exposes the 12-bit raw angle
+	  and radian scale via the IIO subsystem.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called as5600.
+
 config IQS624_POS
 	tristate "Azoteq IQS624/625 angular position sensors"
 	depends on MFD_IQS62X || COMPILE_TEST
diff --git a/drivers/iio/position/Makefile b/drivers/iio/position/Makefile
index d70902f2979d..2d26f6d6ace3 100644
--- a/drivers/iio/position/Makefile
+++ b/drivers/iio/position/Makefile
@@ -4,5 +4,6 @@
 
 # When adding new entries keep the list in alphabetical order
 
+obj-$(CONFIG_AS5600) += as5600.o
 obj-$(CONFIG_HID_SENSOR_CUSTOM_INTEL_HINGE) += hid-sensor-custom-intel-hinge.o
 obj-$(CONFIG_IQS624_POS)	+= iqs624-pos.o
diff --git a/drivers/iio/position/as5600.rs b/drivers/iio/position/as5600.rs
new file mode 100644
index 000000000000..0cbf8be58b64
--- /dev/null
+++ b/drivers/iio/position/as5600.rs
@@ -0,0 +1,129 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (C) 2026 Muchamad Coirul Anwar <muchamadcoirulanwar@gmail.com>
+//! Driver for ams AS5600 12-bit magnetic rotary position sensor.
+//!
+//! Datasheet: https://ams.com/documents/20143/36005/AS5600_DS000365_5-00.pdf
+
+use kernel::{
+    bindings::{
+        iio_chan_info_enum_IIO_CHAN_INFO_RAW, iio_chan_info_enum_IIO_CHAN_INFO_SCALE,
+        iio_chan_spec, iio_chan_type_IIO_ANGL, ENODATA,
+    },
+    bits::bit_u8,
+    device::Core,
+    i2c::{DeviceId, Driver, I2cClient, IdTable},
+    i2c_device_table,
+    iio::{Device, IioDriver, IioVal},
+    module_i2c_driver, of, of_device_table,
+    prelude::*,
+};
+
+const AS5600_REG_STATUS: u8 = 0x0B;
+const AS5600_REG_RAW_ANGLE_H: u8 = 0x0C;
+const AS5600_REG_RAW_ANGLE_L: u8 = 0x0D;
+
+const AS5600_STATUS_MD: u8 = bit_u8(5);
+
+module_i2c_driver! {
+    type: As5600,
+    name: "as5600",
+    authors: ["Muchamad Coirul Anwar"],
+    description: "I2C Driver for ams OSRAM AS5600 Magnetic Rotary Position Sensor",
+    license: "GPL",
+}
+
+i2c_device_table!(
+    I2C_TABLE,
+    MODULE_I2C_TABLE,
+    <As5600 as Driver>::IdInfo,
+    [(DeviceId::new(c"as5600"), ())]
+);
+
+of_device_table!(
+    OF_TABLE,
+    MODULE_OF_TABLE,
+    <As5600 as Driver>::IdInfo,
+    [(of::DeviceId::new(c"ams,as5600"), ())]
+);
+
+struct As5600Priv {
+    client_ptr: *const I2cClient<Core>,
+    channels: [iio_chan_spec; 1],
+}
+
+// SAFETY: `client_ptr` points to an `I2cClient` that is owned by the I2C
+// subsystem and outlives the driver binding. `iio_device_unregister` in
+// `Device<T>::PinnedDrop` drains pending callbacks before this struct is
+// dropped, so `client_ptr` is valid for every `read_raw` invocation.
+// Concurrent access is safe because the I2C adapter lock serializes all
+// SMBus transactions.
+unsafe impl Send for As5600Priv {}
+unsafe impl Sync for As5600Priv {}
+
+impl IioDriver for As5600Priv {
+    fn read_raw(&self, _chan: *const iio_chan_spec, mask: isize) -> Result<IioVal> {
+        // SAFETY: `client_ptr` was set from a valid `&I2cClient` in `probe()`.
+        // The I2C client outlives the driver binding, and `read_raw` is only
+        // called while the driver is bound.
+        let client = unsafe { &*self.client_ptr };
+
+        #[allow(non_upper_case_globals)]
+        match mask as u32 {
+            // IIO_CHAN_INFO_RAW
+            iio_chan_info_enum_IIO_CHAN_INFO_RAW => {
+                let status = client.smbus_read_byte_data(AS5600_REG_STATUS)?;
+                if (status & AS5600_STATUS_MD) == 0 {
+                    return Err(Error::from_errno(-(ENODATA as i32)));
+                }
+
+                let angle_h = client.smbus_read_byte_data(AS5600_REG_RAW_ANGLE_H)? as u16;
+                let angle_l = client.smbus_read_byte_data(AS5600_REG_RAW_ANGLE_L)? as u16;
+
+                let angle = (angle_h << 8 | angle_l) & 0x0FFF;
+                Ok(IioVal::Int(angle as i32))
+            }
+            // IIO_CHAN_INFO_SCALE
+            iio_chan_info_enum_IIO_CHAN_INFO_SCALE => Ok(IioVal::IntPlusNano(0, 1533981)),
+            _ => Err(EINVAL),
+        }
+    }
+
+    fn channels(&self) -> &[iio_chan_spec] {
+        &self.channels
+    }
+}
+
+struct As5600 {
+    _iio_dev: Device<As5600Priv>,
+}
+
+impl Driver for As5600 {
+    type IdInfo = ();
+    const I2C_ID_TABLE: Option<IdTable<Self::IdInfo>> = Some(&I2C_TABLE);
+    const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = Some(&OF_TABLE);
+
+    fn probe(dev: &I2cClient<Core>, _id_info: Option<&Self::IdInfo>) -> impl PinInit<Self, Error> {
+        let _status = dev.smbus_read_byte_data(AS5600_REG_STATUS)?;
+
+        // SAFETY: `iio_chan_spec` is a C struct whose fields are all integers
+        // and pointers. Zero is a valid initialization for all of them.
+        let mut channels: [iio_chan_spec; 1] = unsafe { core::mem::zeroed() };
+        channels[0].info_mask_separate = (1 << iio_chan_info_enum_IIO_CHAN_INFO_RAW)
+            | (1 << iio_chan_info_enum_IIO_CHAN_INFO_SCALE);
+        channels[0].type_ = iio_chan_type_IIO_ANGL;
+
+        let priv_data = As5600Priv {
+            client_ptr: dev as *const _,
+            channels,
+        };
+
+        let mut iio_dev = Device::new(dev.as_ref(), priv_data, c"as5600")?;
+
+        iio_dev.register(dev.as_ref(), &crate::THIS_MODULE)?;
+
+        dev_dbg!(dev.as_ref(), "AS5600: Sensor probed, driver ready\n");
+        Ok::<_, Error>(As5600 { _iio_dev: iio_dev })
+    }
+
+    fn unbind(_dev: &I2cClient<Core>, _this: Pin<&Self>) {}
+}
-- 
2.50.0


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [RFC PATCH v2 3/4] dt-bindings: iio: position: add ams,as5600
  2026-04-29 13:22 ` [RFC PATCH v2 3/4] dt-bindings: iio: position: add ams,as5600 Muchamad Coirul Anwar
@ 2026-04-29 13:46   ` Krzysztof Kozlowski
  0 siblings, 0 replies; 8+ messages in thread
From: Krzysztof Kozlowski @ 2026-04-29 13:46 UTC (permalink / raw)
  To: Muchamad Coirul Anwar, jic23, linux-iio, rust-for-linux,
	devicetree
  Cc: branstj, lars, ojeda, robh, krzk+dt, conor+dt, igor.korotin.linux,
	linux-kernel

On 29/04/2026 15:22, Muchamad Coirul Anwar wrote:
> Signed-off-by: Muchamad Coirul Anwar <muchamadcoirulanwar@gmail.com>

I guess this is still not ready and correctly marked as RFC, so I will
drop it from DT patchwork.

Best regards,
Krzysztof

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [RFC PATCH v2 1/4] i2c: rust: add smbus_read_byte_data and smbus_read_word_data
  2026-04-29 13:22 ` [RFC PATCH v2 1/4] i2c: rust: add smbus_read_byte_data and smbus_read_word_data Muchamad Coirul Anwar
@ 2026-05-04 10:29   ` Igor Korotin
  2026-05-06  7:46     ` Muchamad Coirul Anwar
  0 siblings, 1 reply; 8+ messages in thread
From: Igor Korotin @ 2026-05-04 10:29 UTC (permalink / raw)
  To: Muchamad Coirul Anwar, jic23, linux-iio, rust-for-linux,
	devicetree
  Cc: branstj, lars, ojeda, robh, krzk+dt, conor+dt, igor.korotin.linux,
	linux-kernel

Hello Muchamad

On 4/29/2026 2:22 PM, Muchamad Coirul Anwar wrote:
> Signed-off-by: Muchamad Coirul Anwar <muchamadcoirulanwar@gmail.com>
> ---
>   rust/kernel/i2c.rs | 24 ++++++++++++++++++++++++
>   1 file changed, 24 insertions(+)
> 
> diff --git a/rust/kernel/i2c.rs b/rust/kernel/i2c.rs
> index 7b908f0c5a58..6eaea1158fda 100644
> --- a/rust/kernel/i2c.rs
> +++ b/rust/kernel/i2c.rs
> @@ -477,6 +477,30 @@ impl<Ctx: device::DeviceContext> I2cClient<Ctx> {
>       fn as_raw(&self) -> *mut bindings::i2c_client {
>           self.0.get()
>       }
> +
> +    /// Reads a single byte from a register via SMBus.
> +    pub fn smbus_read_byte_data(&self, reg: u8) -> Result<u8> {
> +        // SAFETY: `self.as_raw()` is a valid pointer to a `struct i2c_client`
> +        // by the type invariant of `I2cClient`.
> +        let ret = unsafe { bindings::i2c_smbus_read_byte_data(self.as_raw(), reg) };
> +        if ret < 0 {
> +            Err(Error::from_errno(ret))
> +        } else {
> +            Ok(ret as u8)
> +        }
> +    }
> +
> +    /// Reads a 16-bit word from a register via SMBus.
> +    pub fn smbus_read_word_data(&self, reg: u8) -> Result<u16> {
> +        // SAFETY: `self.as_raw()` is a valid pointer to a `struct i2c_client`
> +        // by the type invariant of `I2cClient`.
> +        let ret = unsafe { bindings::i2c_smbus_read_word_data(self.as_raw(), reg) };
> +        if ret < 0 {
> +            Err(Error::from_errno(ret))
> +        } else {
> +            Ok(ret as u16)
> +        }
> +    }
>   }
>   
>   // SAFETY: `I2cClient` is a transparent wrapper of `struct i2c_client`.

Thanks for the patch. However, we've previously agreed [1] that 
I2cClient should implement the IO trait [2] rather than adding 
standalone methods like these. This patch would need to be reworked in 
that direction.

If you'd like to take that on, feel free to submit a patch series 
implementing the IO trait instead.

Cheers
Igor

[1] 
https://lore.kernel.org/rust-for-linux/20260131-i2c-adapter-v1-4-5a436e34cd1a@gmail.com/

[2] 
https://git.kernel.org/pub/scm/linux/kernel/git/driver-core/driver-core.git/commit/?h=driver-core-testing&id=121d87b28e1d9061d3aaa156c43a627d3cb5e620

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [RFC PATCH v2 1/4] i2c: rust: add smbus_read_byte_data and smbus_read_word_data
  2026-05-04 10:29   ` Igor Korotin
@ 2026-05-06  7:46     ` Muchamad Coirul Anwar
  0 siblings, 0 replies; 8+ messages in thread
From: Muchamad Coirul Anwar @ 2026-05-06  7:46 UTC (permalink / raw)
  To: Igor Korotin
  Cc: jic23, linux-iio, rust-for-linux, devicetree, branstj, lars,
	ojeda, robh, krzk+dt, conor+dt, igor.korotin.linux, linux-kernel

On Mon, 4 May 2026 at 17:29, Igor Korotin <igor.korotin@linux.dev> wrote:
>
> Hello Muchamad
>
> On 4/29/2026 2:22 PM, Muchamad Coirul Anwar wrote:
> > Signed-off-by: Muchamad Coirul Anwar <muchamadcoirulanwar@gmail.com>
> > ---
> >   rust/kernel/i2c.rs | 24 ++++++++++++++++++++++++
> >   1 file changed, 24 insertions(+)
[snip]
>
> Thanks for the patch. However, we've previously agreed [1] that
> I2cClient should implement the IO trait [2] rather than adding
> standalone methods like these. This patch would need to be reworked in
> that direction.
>
> If you'd like to take that on, feel free to submit a patch series
> implementing the IO trait instead.
>
> Cheers
> Igor
>
> [1] https://lore.kernel.org/rust-for-linux/20260131-i2c-adapter-v1-4-5a436e34cd1a@gmail.com/
> [2] https://git.kernel.org/pub/scm/linux/kernel/git/driver-core/driver-core.git/commit/?h=driver-core-testing&id=121d87b28e1d9061d3aaa156c43a627d3cb5e620

Hi Igor,

Thanks for the review. It makes sense that having the AS5600 as the
first consumer of the Io trait would be cleaner than using standalone
methods. I'll rework this accordingly.

Quick question on logistics: would you prefer the Io trait patches as
a separate series, or bundled as prerequisites in my v3 for the AS5600
driver?

Regards,
M. Coirul A.

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2026-05-06  7:46 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-04-29 13:22 [RFC PATCH v2 0/4] iio: position: add Rust driver for ams AS5600 Muchamad Coirul Anwar
2026-04-29 13:22 ` [RFC PATCH v2 1/4] i2c: rust: add smbus_read_byte_data and smbus_read_word_data Muchamad Coirul Anwar
2026-05-04 10:29   ` Igor Korotin
2026-05-06  7:46     ` Muchamad Coirul Anwar
2026-04-29 13:22 ` [RFC PATCH v2 2/4] rust: add minimal IIO subsystem abstractions Muchamad Coirul Anwar
2026-04-29 13:22 ` [RFC PATCH v2 3/4] dt-bindings: iio: position: add ams,as5600 Muchamad Coirul Anwar
2026-04-29 13:46   ` Krzysztof Kozlowski
2026-04-29 13:22 ` [RFC PATCH v2 4/4] iio: position: add Rust driver for ams AS5600 Muchamad Coirul Anwar

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox