From patchwork Wed Dec 18 23:36:31 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Fabien Parent X-Patchwork-Id: 851769 Received: from mail-pl1-f173.google.com (mail-pl1-f173.google.com [209.85.214.173]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id EFBEA1FD78C; Wed, 18 Dec 2024 23:37:19 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.173 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1734565042; cv=none; b=XfXKVEzYpfAmiXuSxVnBojUd0OOi0gYhCjfhME0i3zdVtfJ01NxqDvG85BFIWEJEzioQK3BzSh/U1vJ7rTsKBvdPOcf0aYPPWSsDdG7yUbpZDpNQojGl9pWw4aMC5ILQj2JOx5wMShUXqwiMQKK/oY0sR8eQV0StVqc8qwavlHM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1734565042; c=relaxed/simple; bh=4SE9setPSSrH0hJjJY1xCu9tx2HYa/TBhF8z5FVIs4g=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=q8sC7ocsogEHQ0o7Jz9kSi0vjPb6XeKj+aNKDkhlCBUEC3tAzJgflGeTdLst18oez23Nl0zByt+5R+Ct59Xi1T3iqhPGRZrs6Cuxs6ua+AMCVk6KfSaHXz9VhC3pzs9j1LoS9101o9c0FHPi8kE4zXup83rkuQ6EawKZYPZpGSg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=b4es2Ivx; arc=none smtp.client-ip=209.85.214.173 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="b4es2Ivx" Received: by mail-pl1-f173.google.com with SMTP id d9443c01a7336-21654fdd5daso1958805ad.1; Wed, 18 Dec 2024 15:37:19 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1734565039; x=1735169839; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=UhAzgwSjNox/FIb+hLHlHMNNI9yeP1VLZ/ht4Xnzx9w=; b=b4es2Ivx440lTh6viRB9LN5BYyxzNn55qYZowsx6aFWnjagV4qKvrwqBh9UkKIH7Sy LI3NG61EhxWMxbzPeBKEGqOyy0jhUT2dZmxlQrZhxv3YtkDlFUjDDEJU2WDk6fLosDMV tmzO7njsMRSUwbLQ7yiyuy3AdZXl3HK/mRHpr1ihMDIEyuyWfA6khYXJ6jb57F8g8ROE l8wMzYnq7hKdAgPL3rPcuOxUy4G6o2Y2wi1zvsvFyKLfYr/9ZwDEyEXGZrqtih9o796W 93/6o9MC2tAFW8opb8klHeLuq14oteCmRNzDcgQDBToJbAprKtIRBuPqUuRLtM6l+Jzx 7R2A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1734565039; x=1735169839; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=UhAzgwSjNox/FIb+hLHlHMNNI9yeP1VLZ/ht4Xnzx9w=; b=K4H0pzWnvI/aGwSASqsn5TC+IwJ8GTjJ+FD6TGk0fZRMROelEdx0Bu6fcTihavjRuV V64OxIDEzvsck+xmTUkeA00NA5acAVA0zwgJ7apAeqjzvN2gZqk+HAMjgthCMkdXC/eg fIQJpUiXNzbpIXWGlSTtnV6409U0vKuopTao45WSLQwaUmCBbS4R5DCS5Z8TYD2QKdEs xZUNQlc0S0KclK83p2Nbac5wPoOODtxjEKehk+WDUWj+XqATRmQNManMWm7zq4ThNpqz sJsH7nS1539sKhqlo/v4ApDOp5yAwi4RND6HoOKwJYeBK4DmyABdkzZc/np1ArvzMyyp g8ww== X-Forwarded-Encrypted: i=1; AJvYcCUFlKmqjaToa95W6uLYp2mD1cgENnVZOCDjRufS+VRrgm7fRC8nidX8vabYjlVbG64hHuS3qODB9BGk@vger.kernel.org, AJvYcCWVwhcqW7QwmgtQST7GT8rcQ+wkGV6vy+F9buB17XkZqzRB0shc8R6VzwDp8e6zYIA6RZLMk479sbur57le@vger.kernel.org, AJvYcCWnrtplwpG8FZiNZ/WTuziE+Brd3O7W6/KVe44a1yGZ36Auy4lFQM6Qqy5EHGhEP7rTaIg2yZ9QSamviEgl@vger.kernel.org, AJvYcCXHUAm7KmTsM5H7Gvh12Jr/0KJk3XxJxwhlM1ZM/CG1+Dh86sTu9HrUWZe3HbHkuhqfVinBhymULPMi+KdLWAo=@vger.kernel.org X-Gm-Message-State: AOJu0YyVnUYUmb/nwrLtfrAj76ZR8f9WSnjWu2SCY/XRr8bFo2/crtHf DV6LdIfYE6xQO+Eu50i/BBOkNEfxCQVAM3R0i5UUrJgsPoj3yy+a X-Gm-Gg: ASbGncvwhtRtHg/zmbj3HOhOLbf7m9PkrRhPv+Kt4MMbE9v4873uRcAsfjf7SwECm10 3gPVKC4JZY6bPSk+nG48yTrLt/lEasI6CiDf9B51vgO5V+GNLEdBuHRI6dWM5M3NYf7IUe0nPSa a0Hya5ywlVGHtklFti11UqnwYCLJHVpxu6p49RFCVTahJxOcW8+9hVZYJjlp5OdchmWm2w4pdmO bdsn2NqPaMRDa4IWNeoltS+99oxBJjEl1Nc/YeG0wV3ANbhF/lqExkL X-Google-Smtp-Source: AGHT+IG25MGGi4G1Umt/pBxrQoT0UKWAYNG0VN2GMrkgQXxU0hwxukqEiX52e1fIsrdjr9zWG8xfmg== X-Received: by 2002:a17:902:ec81:b0:216:5568:38c9 with SMTP id d9443c01a7336-218d72349edmr71021735ad.31.1734565039076; Wed, 18 Dec 2024 15:37:19 -0800 (PST) Received: from [127.0.1.1] ([2001:569:fcea:600:2e06:283e:5daa:4d0f]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-219dc9f6967sm802335ad.214.2024.12.18.15.37.17 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 18 Dec 2024 15:37:18 -0800 (PST) From: Fabien Parent Date: Wed, 18 Dec 2024 15:36:31 -0800 Subject: [PATCH 1/9] rust: i2c: add basic I2C client abstraction Precedence: bulk X-Mailing-List: linux-arm-msm@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20241218-ncv6336-v1-1-b8d973747f7a@gmail.com> References: <20241218-ncv6336-v1-0-b8d973747f7a@gmail.com> In-Reply-To: <20241218-ncv6336-v1-0-b8d973747f7a@gmail.com> To: Rob Herring , Saravana Kannan , Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Greg Kroah-Hartman , "Rafael J. Wysocki" , Wolfram Sang , Mark Brown , Liam Girdwood , Krzysztof Kozlowski , Conor Dooley , Bjorn Andersson , Konrad Dybcio , Fabien Parent Cc: devicetree@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-i2c@vger.kernel.org, linux-arm-msm@vger.kernel.org, vinod.koul@linaro.org, Fabien Parent , Fiona Behrens X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=13005; i=parent.f@gmail.com; h=from:subject:message-id; bh=kd7a0C90uxuWTBjnlJWT2Q5dPINXmEd9sR6MvOUkVYg=; b=LS0tLS1CRUdJTiBQR1AgTUVTU0FHRS0tLS0tCgpvd0o0bkFGdEFwTDlrQTBEQUFvQmlKc2d4Q kw5cktRQnl5WmlBR2RqWEtMNE1RVFdRTFpsRGxBdVl0WHJwNFVPCjFvc1d6OTYvZUZFOVByUXNZ Z1NYWjRrQ013UUFBUW9BSFJZaEJONk0wZElMeXpzd3JmRnNxWWliSU1RUy9heWsKQlFKblkxeWl BQW9KRUlpYklNUVMvYXlrOFNZUC9SQzVQZGIvdktiZ2RaOXBoY2pvQWMzWElFemFzTm1SMHVmMA orRE5FQlQ2YmhISzFyQVM2NStoTU5ET1g4VlZVTFBBejNjQTVjWGJmSm1uVGtoelgzaEt6Z1pUS TNocjdrUXgwCmxXU3JpU1J1QnUzYVZCN2pJS3FlanhKU3JCNytUZStWeC9NbDNaYWNsWHJkZzFD NnNRc3lWSWRkbjNPd3hTVEEKenZERmxxZDN5d3FaR25ocXZkLy9iL3ZXdzlYcEJ3cXF3ZnJLL0R MdDRDQnZxeTB6eDhsYk9SYUNCa3pxVHUrMAp1cGkzRmUvQ2FIbXBUNXpjOFpMN3pZalNtRzU5WH p2c2g2RndRcklVT0N6eTcvUVZqMDhaNXpEMVZRdzhsYWRZCmw0YStacENWR3V2Smtld1hMVkJOa EV3U0MvbDFuOW9SUFhyVFVYUmlNWlNPcDZyV01hZ3NXdmhyem1aUk1GakcKU2NySkNUdEhZWTNL OFhnZmt3WlhZS1preVFkS2ZRUjV1Z3lxcjVDbnhNRWwzbisvZVlIMTFPQWhGU1BhY2lDNgpjNVJ RQWI2MGNUeHcvTCtKUjM4ckE4UnZiM2JHY25JTnRMRkZyUHZWYy9PN2ZJYnNoSS8rZHdFMGt0Nj ZoMCsvCmk3YjVMR01BM3ZIcGR0VUNmRDJyZDZxd0VOelJoODdMdERRWDNDcXpkeHJ5SG1FYnhhZ E52OTZaVVBhTWtuZnMKMEhjZzZmRERtYU9ycU5ZWnVhcDBxcWJ0VWlaM00vQ29OOGRqd0VPemho NDhBRjBUcUJjbUc5UU4ybUhRUE1TLwpVaW94UWEvbUZscDM0N1lKUWRqRzZNTVB6YUV5TGZrUFJ NbTJjN0o1bUUrZDNhbmxacllyQXdTNFl4QUFrMVNJCk5meVpWaHUvT0NBMytnPT0KPWpHVUMKLS 0tLS1FTkQgUEdQIE1FU1NBR0UtLS0tLQo= X-Developer-Key: i=parent.f@gmail.com; a=openpgp; fpr=07BB034F9E8E3AF0DF4CF7607AE864A1E16FA383 From: Fiona Behrens Implement an abstraction to write I2C device drivers. The abstraction is pretty basic and provides just the infrastructure to probe a device from I2C/OF device_id and abstract `i2c_client`. The client will be used by the Regmap abstraction to perform I/O on the I2C bus. Signed-off-by: Fiona Behrens Co-developed-by: Fabien Parent Signed-off-by: Fabien Parent --- MAINTAINERS | 1 + rust/bindings/bindings_helper.h | 1 + rust/helpers/helpers.c | 1 + rust/helpers/i2c.c | 13 ++ rust/kernel/i2c.rs | 288 ++++++++++++++++++++++++++++++++++++++++ rust/kernel/lib.rs | 2 + 6 files changed, 306 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 6b9e10551392c185b9314c9f94edeaf6e85af58f..961fe4ed39605bf489d1d9e473f47bccb692ff14 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -10796,6 +10796,7 @@ F: include/linux/i2c-smbus.h F: include/linux/i2c.h F: include/uapi/linux/i2c-*.h F: include/uapi/linux/i2c.h +F: rust/kernel/i2c.rs I2C SUBSYSTEM HOST DRIVERS M: Andi Shyti diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index e9fdceb568b8f94e602ee498323e5768a40a6cba..a882efb90bfc27960ef1fd5f2dc8cc40533a1c27 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c index 0640b7e115be1553549312dcfdf842bcae3bde1b..630e903f516ee14a51f46ff0bcc68e8f9a64021a 100644 --- a/rust/helpers/helpers.c +++ b/rust/helpers/helpers.c @@ -15,6 +15,7 @@ #include "device.c" #include "err.c" #include "fs.c" +#include "i2c.c" #include "io.c" #include "jump_label.c" #include "kunit.c" diff --git a/rust/helpers/i2c.c b/rust/helpers/i2c.c new file mode 100644 index 0000000000000000000000000000000000000000..8ffdc454e7597cc61909da5b3597057aeb5f7299 --- /dev/null +++ b/rust/helpers/i2c.c @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include + +void *rust_helper_i2c_get_clientdata(const struct i2c_client *client) +{ + return i2c_get_clientdata(client); +} + +void rust_helper_i2c_set_clientdata(struct i2c_client *client, void *data) +{ + i2c_set_clientdata(client, data); +} diff --git a/rust/kernel/i2c.rs b/rust/kernel/i2c.rs new file mode 100644 index 0000000000000000000000000000000000000000..efa03335e5b59e72738380e94213976b2464c25b --- /dev/null +++ b/rust/kernel/i2c.rs @@ -0,0 +1,288 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Abstractions for the I2C bus. +//! +//! C header: [`include/linux/i2c.h`](srctree/include/linux/i2c.h) + +use crate::{ + bindings, container_of, + device::Device, + device_id::{self, RawDeviceId}, + driver, + error::{to_result, Result}, + of, + prelude::*, + str::CStr, + types::{ARef, ForeignOwnable, Opaque}, + ThisModule, +}; + +/// Abstraction for `bindings::i2c_device_id`. +#[repr(transparent)] +#[derive(Clone, Copy)] +pub struct DeviceId(bindings::i2c_device_id); + +impl DeviceId { + /// Create a new device id from an I2C name. + pub const fn new(name: &CStr) -> Self { + let src = name.as_bytes_with_nul(); + // TODO: Replace with `bindings::i2c_device_id::default()` once stabilized for `const`. + // SAFETY: FFI type is valid to be zero-initialized. + let mut i2c: bindings::i2c_device_id = unsafe { core::mem::zeroed() }; + + let mut i = 0; + while i < src.len() { + i2c.name[i] = src[i] as _; + i += 1; + } + + Self(i2c) + } +} + +// SAFETY: +// * `DeviceId` is a `#[repr(transparent)` wrapper of `i2c_device_id` and does not add +// additional invariants, so it's safe to transmute to `RawType`. +// * `DRIVER_DATA_OFFSET` is the offset to the `data` field. +unsafe impl RawDeviceId for DeviceId { + type RawType = bindings::i2c_device_id; + + const DRIVER_DATA_OFFSET: usize = core::mem::offset_of!(bindings::i2c_device_id, driver_data); + + fn index(&self) -> usize { + self.0.driver_data as _ + } +} + +/// I2C [`DeviceId`] table. +pub type IdTable = &'static dyn device_id::IdTable; + +/// An adapter for the registration of I2C drivers. +#[doc(hidden)] +pub struct Adapter(T); + +impl driver::RegistrationOps for Adapter { + type RegType = bindings::i2c_driver; + + fn register( + i2cdrv: &Opaque, + name: &'static CStr, + module: &'static ThisModule, + ) -> Result { + // SAFETY: It's safe to set the fields of `struct i2c_driver` on initialization. + unsafe { + (*i2cdrv.get()).driver.name = name.as_char_ptr(); + (*i2cdrv.get()).probe = Some(Self::probe_callback); + (*i2cdrv.get()).remove = Some(Self::remove_callback); + if let Some(t) = T::I2C_ID_TABLE { + (*i2cdrv.get()).id_table = t.as_ptr(); + } + if let Some(t) = T::OF_ID_TABLE { + (*i2cdrv.get()).driver.of_match_table = t.as_ptr(); + } + } + + // SAFETY: `i2cdrv` is guaranteed to be a valid `RegType`. + to_result(unsafe { bindings::i2c_register_driver(module.0, i2cdrv.get()) }) + } + + fn unregister(i2cdrv: &Opaque) { + // SAFETY: `i2cdrv` is guaranteed to be a valid `RegType`. + unsafe { bindings::i2c_del_driver(i2cdrv.get()) }; + } +} + +impl Adapter { + /// Get the [`Self::IdInfo`] that matched during probe. + fn id_info(client: &mut Client) -> Option<&'static T::IdInfo> { + let id = ::id_info(client.as_ref()); + if id.is_some() { + return id; + } + + // SAFETY: `client` and `client.as_raw()` are guaranteed to be valid. + let id = unsafe { bindings::i2c_client_get_device_id(client.as_raw()) }; + if !id.is_null() { + // SAFETY: `DeviceId` is a `#[repr(transparent)` wrapper of `struct i2c_device_id` and + // does not add additional invariants, so it's safe to transmute. + let id = unsafe { &*id.cast::() }; + return Some(T::I2C_ID_TABLE?.info(id.index())); + } + + None + } + + extern "C" fn probe_callback(client: *mut bindings::i2c_client) -> core::ffi::c_int { + // SAFETY: The i2c bus only ever calls the probe callback with a valid `client`. + let dev = unsafe { Device::get_device(core::ptr::addr_of_mut!((*client).dev)) }; + // SAFETY: `dev` is guaranteed to be embedded in a valid `struct i2c_client` by the + // call above. + let mut client = unsafe { Client::from_dev(dev) }; + + let info = Self::id_info(&mut client); + match T::probe(&mut client, info) { + Ok(data) => { + // Let the `struct i2c_client` own a reference of the driver's private data. + // SAFETY: By the type invariant `client.as_raw` returns a valid pointer to a + // `struct i2c_client`. + unsafe { bindings::i2c_set_clientdata(client.as_raw(), data.into_foreign() as _) }; + } + Err(err) => return Error::to_errno(err), + } + + 0 + } + + extern "C" fn remove_callback(client: *mut bindings::i2c_client) { + // SAFETY: `client` is a valid pointer to a `struct i2c_client`. + let ptr = unsafe { bindings::i2c_get_clientdata(client) }; + + // SAFETY: `remove_callback` is only ever called after a successful call to + // `probe_callback`, hence it's guaranteed that `ptr` points to a valid and initialized + // `KBox` pointer created through `KBox::into_foreign`. + let _ = unsafe { KBox::::from_foreign(ptr) }; + } +} + +impl driver::Adapter for Adapter { + type IdInfo = T::IdInfo; + + fn of_id_table() -> Option> { + T::OF_ID_TABLE + } +} + +/// The I2C driver trait. +/// +/// Drivers must implement this trait in order to get a i2c driver registered. +/// +/// # Example +/// +///``` +/// # use kernel::{bindings, c_str, i2c, of}; +/// # +/// kernel::of_device_table!( +/// OF_ID_TABLE, +/// MODULE_OF_ID_TABLE, +/// ::IdInfo, +/// [(of::DeviceId::new(c_str!("onnn,ncv6336")), ()),] +/// ); +/// +/// kernel::i2c_device_table!( +/// I2C_ID_TABLE, +/// MODULE_I2C_ID_TABLE, +/// ::IdInfo, +/// [(i2c::DeviceId::new(c_str!("ncv6336")), ()),] +/// ); +/// +/// struct MyDriver; +/// +/// impl i2c::Driver for MyDriver { +/// type IdInfo = (); +/// const OF_ID_TABLE: Option> = Some(&OF_ID_TABLE); +/// const I2C_ID_TABLE: Option> = Some(&I2C_ID_TABLE); +/// +/// fn probe(_client: &mut i2c::Client, +/// id_info: Option<&Self::IdInfo>) -> Result>> { +/// Ok(KBox::new(Self, GFP_KERNEL)?.into()) +/// } +/// } +///``` +pub trait Driver { + /// The type holding information about each device id supported by the driver. + // TODO: Use associated_type_defaults once stabilized: + // type IdInfo: 'static = (); + type IdInfo: 'static; + + /// An optional table of I2C device ids supported by the driver. + const I2C_ID_TABLE: Option>; + + /// An optional table of OF device ids supported by the driver. + const OF_ID_TABLE: Option>; + + /// I2C driver probe. + /// + /// Called when a new I2C client is added or discovered. + fn probe(client: &mut Client, id_info: Option<&Self::IdInfo>) -> Result>>; +} + +/// An I2C Client. +/// +/// # Invariants +/// +/// `Client` holds a valid reference of `ARef` whose underlying `struct device` is a +/// member of a `struct i2c_client`. +#[derive(Clone)] +pub struct Client(ARef); + +impl Client { + /// Convert a raw kernel device into a `Client` + /// + /// # Safety + /// + /// `dev` must be an `Aref` whose underlying `bindings::device` is a member of a + /// `bindings::i2c_client`. + unsafe fn from_dev(dev: ARef) -> Self { + Self(dev) + } + + /// Returns the raw `struct i2c_client`. + pub fn as_raw(&self) -> *mut bindings::i2c_client { + // SAFETY: By the type invariant `self.0.as_raw` is a pointer to the `struct device` + // embedded in `struct i2c_client`. + unsafe { container_of!(self.0.as_raw(), bindings::i2c_client, dev) }.cast_mut() + } +} + +impl AsRef for Client { + fn as_ref(&self) -> &Device { + &self.0 + } +} + +/// Declares a kernel module that exposes a single I2C driver. +/// +/// # Examples +/// +/// ```ignore +/// kernel::module_i2c_driver! { +/// type: MyDriver, +/// name: "Module name", +/// author: "Author name", +/// description: "Description", +/// license: "GPL v2", +/// } +/// ``` +#[macro_export] +macro_rules! module_i2c_driver { + ($($f:tt)*) => { + $crate::module_driver!(, $crate::i2c::Adapter, { $($f)* }); + }; +} + +/// Create an I2C `IdTable` with an "alias" for modpost. +/// +/// # Examples +/// +/// ``` +/// use kernel::{c_str, i2c}; +/// +/// kernel::i2c_device_table!( +/// I2C_ID_TABLE, +/// MODULE_I2C_ID_TABLE, +/// u32, +/// [(i2c::DeviceId::new(c_str!("ncv6336")), 0x6336),] +/// ); +/// ``` +#[macro_export] +macro_rules! i2c_device_table { + ($table_name:ident, $module_table_name:ident, $id_info_type: ty, $table_data: expr) => { + const $table_name: $crate::device_id::IdArray< + $crate::i2c::DeviceId, + $id_info_type, + { $table_data.len() }, + > = $crate::device_id::IdArray::new($table_data); + + $crate::module_device_table!("i2c", $module_table_name, $table_name); + }; +} diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 7fb9858966e8457611d5868783000844ba640db9..71ef7df94302b689be665676a36bd5c2e6effff3 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -48,6 +48,8 @@ #[cfg(CONFIG_RUST_FW_LOADER_ABSTRACTIONS)] pub mod firmware; pub mod fs; +#[cfg(CONFIG_I2C)] +pub mod i2c; pub mod init; pub mod ioctl; pub mod jump_label; From patchwork Wed Dec 18 23:36:32 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Fabien Parent X-Patchwork-Id: 852107 Received: from mail-pl1-f173.google.com (mail-pl1-f173.google.com [209.85.214.173]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 64D1F1FDE0E; Wed, 18 Dec 2024 23:37:22 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.173 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1734565045; cv=none; b=M2SfkB3YnWo9I/wnB+1WyE5hTZmUOf68BwW145r0rqUrXina53StkBJ3TBSNcbLgCGsua0649r38IT0yAiLfoHbHqKZP/3vNKfkf1TWgI3a/0OSXpjhte7/zNTDeicIeqPFB9V7pDb2fGk6oV9lV72StbrmN5m7HnDKVqMNYwxE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1734565045; c=relaxed/simple; bh=j3wc2EEDIkSt7Jt4Tu4hX3JgRdd8DStHRuD6IWi4vaQ=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=r17bzvcNSMRMPKFG4DWHEWSWMqP1sbgi3EvraS8Sb2WYJZDWzejL8TB8arrN57vrERbZwhJjcJ21QixcWu+TxvZNH103cGwfcpEXA1Yw9GUt70o1lYUYstCRgi69S18NHxiBvyjHGSaBMBOXHse9SSarMyYrH3Vy4bvAwGbs+T4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=Z5Rd4rcq; arc=none smtp.client-ip=209.85.214.173 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="Z5Rd4rcq" Received: by mail-pl1-f173.google.com with SMTP id d9443c01a7336-21675fd60feso2537965ad.2; Wed, 18 Dec 2024 15:37:22 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1734565042; x=1735169842; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=ockGlkCd98WlE+xo+Tpb4P4fffRELdWNwnS7jE1fxFQ=; b=Z5Rd4rcqYuS64WP9/p0DttLy33ElCmJ0gniq8w1v2qpGQqj/1lH4WovH+QgxPpBgS7 LtAVTywsd70sHbfLgmOeXnzCjBM+smER03caRycl3uNvVr7hV7AChTamEj9XvvPZECPB h8ERNz11qmE4O/Dcrj+CHdHe9NPfKfqlHDmbST0T9ElY3DR6wVukaCnGvQADlYMLBA8J 66ID5pZRg6pPdCt1Lk+v5vCuotG+7i6RnSozOErH2K/IsEiH4UoNARJ2vnca8WJ2iUW6 XlEjl6RDy3JjqQAWr6ENX5Wx/8xdq4B9DJWQtRHXCTxbgk37sjwmDNeqVqU6ui3b6tz6 zsug== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1734565042; x=1735169842; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=ockGlkCd98WlE+xo+Tpb4P4fffRELdWNwnS7jE1fxFQ=; b=n1GCBqdh07lQQ+XfAWrfOlIoNZpzIkZ531dNbvYkj2VGc2hi0oR8/Z5yl1USvDik/6 6aHQ4VvPDuRyeAM3fID4R7vgj2W92VeNFEA/tgTevddgfF+hgW61n1hRDr7q2HNzDUTi E6QC9t2wm05bxX22Hm6tD6jGjj5I6e0jLk9M7xhT8zOV6DlzLMTPlKcTDkb0O/CxYq0q hHWC+hmeokKw8kH6yM1Wo5x10Q2RHYiDThKCaR81/ybgeg3iNf5LI4sUIF0gQPdyzP03 44kasYrp6xqJIOweFbdKg4tUZM4xWZKKHoXOwJ44yADvvCM2OQD19W3KwR5i5Lyhd5pz l5pg== X-Forwarded-Encrypted: i=1; AJvYcCVyPfQzXEXQlEpIkvnFqyqP3D02AEhmwwkYKBfZEIB07vbbmbOQY9OUGLm26nvDP8Ske4A+HTvA8f6uwl+b@vger.kernel.org, AJvYcCWmDYOeXj+h5Zaj0lO/lgSgDFfZpwAORB1rsn6Op+1yMyiEr0gXFr8wxviJuPnHehh5RgCCKxSy/eyP@vger.kernel.org, AJvYcCXFwxiNVfTkNu6AbWbymWb38uuNDSVe0dUFbYwIKadkgDlIBcvVSQkymfUiPXPpf/KflQXjsypWebBe9kAG2Cw=@vger.kernel.org, AJvYcCXrc1fCEy3PbWbUiTsHL+X34k3gAaVpL93DNBbtSFGAhHaNH4VXU4kwLYa6BrXjG3cg2uEwgcLBbXX79mQV@vger.kernel.org X-Gm-Message-State: AOJu0YwsGDlcgyfgLPOZ8gJe1XR8hLhSDD7De538ZUjPYhf+elgBhw3M g8gvOWRAkpKpEhOG0lMOdPf+ijWGz5rua1dBd9FuIuERH5FHPzjx X-Gm-Gg: ASbGnctF/9f+WQtzoNjhZeg6PXXi91dmq7ZkAF3wGP7U8yk9qP26QAXhwoJ57jdIdDb OvHT7OE9bNynI8g93yW/g7cBQOAG3ixy3R9s//Izh9IR/460Iw9cRv+/nlNt6HLYvUdQJby/Vi8 fA4ZXFwU8EvsKkaUPeZVpcbZpfyvTTSQesTmLa76OxmgToQf2Cy5ka1Nm0h3gGNE3djqB5+qXrJ eL7I4KBXSLGyab0KO1Vjq0Bt0AINSAjn88+Pi7AsEeucQ4UgttGkJxb X-Google-Smtp-Source: AGHT+IHYH3KVwnRTvYRwZC7AJ0r+S6sHw+o4bGhziQYszB3LQZl42HyGhMJEzegvZl87Cbw65ACw6w== X-Received: by 2002:a17:902:ecd1:b0:215:5625:885b with SMTP id d9443c01a7336-218d726d4a4mr58689935ad.52.1734565041440; Wed, 18 Dec 2024 15:37:21 -0800 (PST) Received: from [127.0.1.1] ([2001:569:fcea:600:2e06:283e:5daa:4d0f]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-219dc9f6967sm802335ad.214.2024.12.18.15.37.19 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 18 Dec 2024 15:37:20 -0800 (PST) From: Fabien Parent Date: Wed, 18 Dec 2024 15:36:32 -0800 Subject: [PATCH 2/9] rust: add abstraction for regmap Precedence: bulk X-Mailing-List: linux-arm-msm@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20241218-ncv6336-v1-2-b8d973747f7a@gmail.com> References: <20241218-ncv6336-v1-0-b8d973747f7a@gmail.com> In-Reply-To: <20241218-ncv6336-v1-0-b8d973747f7a@gmail.com> To: Rob Herring , Saravana Kannan , Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Greg Kroah-Hartman , "Rafael J. Wysocki" , Wolfram Sang , Mark Brown , Liam Girdwood , Krzysztof Kozlowski , Conor Dooley , Bjorn Andersson , Konrad Dybcio , Fabien Parent Cc: devicetree@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-i2c@vger.kernel.org, linux-arm-msm@vger.kernel.org, vinod.koul@linaro.org, Fabien Parent X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=42675; i=parent.f@gmail.com; h=from:subject:message-id; bh=JJ70W7lDM4S7VasavE0jv8kh6nau/IdZ3LdUVUbKmtA=; b=LS0tLS1CRUdJTiBQR1AgTUVTU0FHRS0tLS0tCgpvd0o0bkFGdEFwTDlrQTBEQUFvQmlKc2d4Q kw5cktRQnl5WmlBR2RqWEtOOFk1bHhYUjdJYmlIdDhXckQrSGRaCk9YUm9tTVBjd3pYNGtvYUNl eUF0Rm9rQ013UUFBUW9BSFJZaEJONk0wZElMeXpzd3JmRnNxWWliSU1RUy9heWsKQlFKblkxeWp BQW9KRUlpYklNUVMvYXlrdzJRUC9pc29iVFcwVWo2V1VDbFlid0NSQ3k5bThMOC9RKzBtdG9SbA p6bjJlRUJNeVdtQlYxVjBYbHRkamtvRkVLYUllYmZ1aHFvYnU1TUQzRGVTR1JrSzhRanlNd1FhU HIxL0F5MVVOCjJlSitIT2Z5QTltSDBWRDc2V2NNTFRITFMxR0h3TWQ0MmlsVjd4ekZZOTVGdUZ0 MVRzN1lQaWc0b2VPNC9vTUQKekxIQWdnV3V1WFlqdFJOeU1JNlhjeVFqQ1BlS2NSQnJjZU8vQ2M 4NUxDUlE2eUhHazZUV0l0UmUrZjBMRTdQTgo3eitOamEyaFlUQ2pDRkg4QXFCeTZ0dlMvLzkvRG dMblJtekxvZTh0ZFVVdVlITEQxYXJUUGs2V0NDNlp6WFZECnBEWFZzS2xMbXRUSFdKd2cveFBxR XFHWmRzZEE5SkV3K2NPWVdocktuTEswbjBSYlVxeHBvMzBCVjRta0QvSTcKZE9YQml1dUJaODRk ejFDb0NDL2VKdGFST0gzR2ZIZVVoM0Q1T1F6YmUyVFh2ZVpRWnU1akx5amNZK1VwUDN2QgpjRUN rNThYcExySjJ1UkZUaDl2OXI4T2hHVzJhZE9QSzlTdHVabDFqRStjUmdOY3RUNVZiRjBZc3pQYk NBTEV6CjF2SkV0MkNxWkJyeURQcmxZdDUwemtBSW4xbzZoTW45OXV5M2xHSzhMVW04YUxTS2VVb mEyeXFxbXp3SjFsOSsKaFNxL1hFOXhWQXc4bTZ3MHlQRjlqeWU4SVFWQ0wxeUNtelUxM0dUVHhl Q0lBTUtPb2ZqVkpVdGY3bFlUVUw4UwpsazRUTmQ3U3JVZkV0Tk1LSUltcnpoeTF3L3RvNENqZ0Z LblJpZmxsUC84K3gvQXJpQVN5dGsxd1MwelFEdTFpCnI0eXJQdW9GTXJZdzVnPT0KPXlGMlkKLS 0tLS1FTkQgUEdQIE1FU1NBR0UtLS0tLQo= X-Developer-Key: i=parent.f@gmail.com; a=openpgp; fpr=07BB034F9E8E3AF0DF4CF7607AE864A1E16FA383 From: Fabien Parent This regmap abstraction is implemented using only the regmap_field APIs. This abstraction tries to bring some type-safety features and ease of use by enhancing the regmap API through extensive macro use to generate code. The abstraction is bringing only a small subset of all the features provided by regmap by only supporting the most vital field from `struct regmap_config`. This abstraction is used by the Regulator abstraction as well as the following driver: https://github.com/Fabo/linux/blob/b4/ncv6336/drivers/regulator/ncv6336_regulator.rs Signed-off-by: Fabien Parent --- MAINTAINERS | 1 + rust/bindings/bindings_helper.h | 1 + rust/helpers/helpers.c | 1 + rust/helpers/regmap.c | 48 ++ rust/kernel/lib.rs | 2 + rust/kernel/regmap.rs | 1043 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 1096 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 961fe4ed39605bf489d1d9e473f47bccb692ff14..acb3942eb1b66ec2bc09ac50f51c2054b7b45355 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -19790,6 +19790,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap.git F: Documentation/devicetree/bindings/regmap/ F: drivers/base/regmap/ F: include/linux/regmap.h +F: rust/kernel/regmap.rs REMOTE PROCESSOR (REMOTEPROC) SUBSYSTEM M: Bjorn Andersson diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index a882efb90bfc27960ef1fd5f2dc8cc40533a1c27..48d2b91b34067e7e9ee9c64c2e42681e988e9aad 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c index 630e903f516ee14a51f46ff0bcc68e8f9a64021a..f78371d1932939821ecc5f57b065bd63b8bc9dee 100644 --- a/rust/helpers/helpers.c +++ b/rust/helpers/helpers.c @@ -27,6 +27,7 @@ #include "rbtree.c" #include "rcu.c" #include "refcount.c" +#include "regmap.c" #include "security.c" #include "signal.c" #include "slab.c" diff --git a/rust/helpers/regmap.c b/rust/helpers/regmap.c new file mode 100644 index 0000000000000000000000000000000000000000..2426e563df339a9c7c9c52a72f9982eea5a0ed29 --- /dev/null +++ b/rust/helpers/regmap.c @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include + +#if IS_BUILTIN(CONFIG_REGMAP_I2C) +struct regmap *rust_helper_regmap_init_i2c(struct i2c_client *i2c, + const struct regmap_config *config) +{ + return regmap_init_i2c(i2c, config); +} +#endif + +int rust_helper_regmap_field_write(struct regmap_field *field, unsigned int val) +{ + return regmap_field_write(field, val); +} + +int rust_helper_regmap_field_force_write(struct regmap_field *field, + unsigned int val) +{ + return regmap_field_force_write(field, val); +} + +int rust_helper_regmap_field_update_bits(struct regmap_field *field, + unsigned int mask, unsigned int val) +{ + return regmap_field_update_bits(field, mask, val); +} + +int rust_helper_regmap_field_set_bits(struct regmap_field *field, + unsigned int bits) +{ + return regmap_field_set_bits(field, bits); +} + +int rust_helper_regmap_field_clear_bits(struct regmap_field *field, + unsigned int bits) +{ + return regmap_field_clear_bits(field, bits); +} + +int rust_helper_regmap_field_force_update_bits(struct regmap_field *field, + unsigned int mask, + unsigned int val) +{ + return regmap_field_force_update_bits(field, mask, val); +} diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 71ef7df94302b689be665676a36bd5c2e6effff3..456e979724d1079045cb157086ff2b2ed0fcca3b 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -66,6 +66,8 @@ pub mod prelude; pub mod print; pub mod rbtree; +#[cfg(CONFIG_REGMAP)] +pub mod regmap; pub mod revocable; pub mod security; pub mod seq_file; diff --git a/rust/kernel/regmap.rs b/rust/kernel/regmap.rs new file mode 100644 index 0000000000000000000000000000000000000000..232fe93df769eee97966703e0ba92c969b8f506e --- /dev/null +++ b/rust/kernel/regmap.rs @@ -0,0 +1,1043 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Register map access API. +//! +//! C header: [`include/linux/regmap.h`](srctree/include/linux/regmap.h) +//! +//! # Examples +//! +//! ```ignore +//! regmap::define_regmap_field_descs!(FIELD_DESCS, { +//! (pid, 0x3, READ, { value => raw([7:0], ro) }), +//! (limconf, 0x16, RW, { +//! rearm => bit(0, rw), +//! rststatus => bit(1, rw), +//! tpwth => enum([5:4], rw, { +//! Temp83C = 0x0, +//! Temp94C = 0x1, +//! Temp105C = 0x2, +//! Temp116C = 0x3, +//! }), +//! }) +//! }); +//! +//! fn probe(client: &mut i2c::Client) -> Result { +//! let config = regmap::Config::::new(8, 8) +//! .with_max_register(0x16) +//! .with_cache_type(regmap::CacheType::RbTree); +//! let regmap = regmap::Regmap::init_i2c(client, &config); +//! let mut fields = regmap.alloc_fields(&FIELD_DESCS)?; +//! +//! dev_info!(client.as_ref(), "PID: {:#x}", pid::value::read(&mut fields)?); +//! } +//! ``` + +use crate::{ + bindings, + error::{code::*, to_result, Error, Result}, + macros::paste, + sync::Arc, +}; +#[cfg(CONFIG_REGMAP_I2C = "y")] +use crate::{error::from_err_ptr, i2c}; +use core::{marker::PhantomData, ptr::NonNull}; + +/// Type of caching +#[repr(u32)] +pub enum CacheType { + /// Don't cache anything + None = bindings::regcache_type_REGCACHE_NONE, + /// Use RbTree caching + RbTree = bindings::regcache_type_REGCACHE_RBTREE, + /// Use Flat caching + Flat = bindings::regcache_type_REGCACHE_FLAT, + /// Use Maple caching + Maple = bindings::regcache_type_REGCACHE_MAPLE, +} + +/// Register map +/// +/// Note for Rust abstractions using Regmap: +/// Regmap C structure does not implement reference count, so in order to keep the abstractions +/// safe it is essential to keep a `Arc` instance whenever the associated C API is holding +/// on the `struct regmap` pointer. +/// +/// # Invariants +/// +/// * `self.0` is valid, non-zero, and the memory is owned by `self`. +/// * This abstraction does not allow to disable regmap locking. +pub struct Regmap(NonNull); + +impl Regmap { + #[cfg(CONFIG_REGMAP_I2C = "y")] + /// Initialize a [`Regmap`] instance for an `i2c` client. + pub fn init_i2c(i2c: &i2c::Client, config: &Config) -> Result { + // SAFETY: Type invariants guarantee that `i2c.as_raw` is valid and non-null and + // the Config type invariant guarantee that `config.raw` always contains valid data. + let regmap = from_err_ptr(unsafe { bindings::regmap_init_i2c(i2c.as_raw(), &config.raw) })?; + + Ok(Regmap(NonNull::new(regmap).ok_or(EINVAL)?)) + } + + /// Return the raw pointer of this regmap. + pub fn as_raw(&self) -> *mut bindings::regmap { + self.0.as_ptr() + } +} + +impl Drop for Regmap { + fn drop(&mut self) { + // SAFETY: By the type invariant, `self.as_raw` is a valid pointer and it can be freed + // because we own the memory. + unsafe { bindings::regmap_exit(self.as_raw()) } + } +} + +// SAFETY: The type invariants guarantee that the memory of `bindings::regmap` is owned and +// guarantee that the C API is using locked accesses. +unsafe impl Send for Regmap {} + +/// Field Descriptors +/// +/// FieldDescriptors can be created by calling the [`define_regmap_field_descs`] macro. +/// +/// # Examples +/// +/// ```ignore +/// use kernel::regmap::{define_regmap_field_descs, Fields}; +/// +/// define_regmap_field_descs!(DESCS, { +/// (pid, 0x3, READ, { value => raw([7:0], ro) }) +/// }); +/// +/// struct Registrations { +/// fields: Fields<{ DESCS.len() }>, +/// } +/// ``` +pub struct FieldDescs([bindings::reg_field; N]); + +impl FieldDescs { + // macro use only + #[doc(hidden)] + pub const fn new(fields: [bindings::reg_field; N]) -> Self { + Self(fields) + } + + /// Number of fields being held by `FieldDescs` + /// + /// This function can be used to retrieve the number of fields that were + /// created when calling [`define_regmap_field_descs`]. + #[allow(clippy::len_without_is_empty)] + pub const fn len(&self) -> usize { + N + } +} + +/// Regmap fields +/// +/// # Invariants +/// +/// `self.fields` array is garanteed to contains valid and non-null pointers. +/// `self.fields[0]` memory is owned by `Fields`. +/// `self.fields[*]` values cannot be modified. +pub struct Fields { + fields: [NonNull; N], + + // Each regmap_field hold a pointer to the `struct regmap` instance, so we need to keep a copy + // of the wrapper around. + _regmap: Arc, +} +impl Fields { + /// Allocate regmap [`Fields`] + /// + /// This function allocate regmap fields from the `reg_fields` descriptors + pub fn new(regmap: &Arc, descs: &'static FieldDescs) -> Result { + let mut fields = [NonNull::::dangling(); N]; + // SAFETY: + // * [`Regmap`] type invariants guarantee that `Regmap::as_raw` returns a valid pointer. + // * `FieldDescs::` is guaranteed to hold a valid array of size N. + to_result(unsafe { + bindings::regmap_field_bulk_alloc( + regmap.as_raw(), + fields.as_mut_ptr().cast(), + descs.0.as_ptr().cast(), + descs.0.len() as i32, + ) + })?; + + Ok(Fields { + fields, + _regmap: regmap.clone(), + }) + } + + /// Get field `index` + pub fn index(&mut self, index: usize) -> *mut bindings::regmap_field { + self.fields[index].as_ptr() + } + + // macro use only + #[doc(hidden)] + pub fn read(&mut self, index: usize) -> Result { + let mut val = 0; + + // Make sure we don't panic if the index is out of bound. + if index >= N { + return Err(EINVAL); + } + + // SAFETY: By the type invariants, we are garanteed that all fields entries point + // to valid and initialized values, hence it is safe to make this FFI call. + let ret = unsafe { bindings::regmap_field_read(self.fields[index].as_ptr(), &mut val) }; + if ret < 0 { + return Err(Error::from_errno(ret)); + } + + Ok(val) + } +} + +impl Drop for Fields { + fn drop(&mut self) { + // SAFETY: Per type invariant, `self.fields[0].as_mut` is garanteed to be valid and + // are owned by `Fields`. + unsafe { bindings::regmap_field_bulk_free(core::ptr::from_mut(self.fields[0].as_mut())) } + } +} + +// SAFETY: The type invariants guarantee that we own the `struct regmap_field` data and that they +// cannot be modified after allocation, and _regmap is Send, so it is safe for `Fields` to be Send. +unsafe impl Send for Fields {} + +macro_rules! config_with { + ($(#[$meta:meta])* $name:ident: $type:ty) => { + config_with!($(#[$meta])* $name: $type, $name); + }; + + ($(#[$meta:meta])* $name:ident: $type:ty, $e:expr) => { + paste! { + $(#[$meta])* + pub const fn [](mut self, $name: $type) -> Self { + self.raw.$name = $e; + self + } + } + }; +} + +// macro use only +#[doc(hidden)] +pub trait ConfigOps { + fn is_readable_reg(reg: u32) -> bool; + fn is_writeable_reg(reg: u32) -> bool; + fn is_volatile_reg(reg: u32) -> bool; + fn is_precious_reg(reg: u32) -> bool; +} + +/// Regmap Configuration +/// +/// # Invariants +/// +/// `self.raw` always contain valid data. +pub struct Config { + raw: bindings::regmap_config, + _phantom: PhantomData, +} +impl Config { + /// Create a new regmap Config + pub const fn new(reg_bits: i32, val_bits: i32) -> Self { + // SAFETY: FFI type is valid to be zero-initialized. + let mut cfg: bindings::regmap_config = unsafe { core::mem::zeroed() }; + + cfg.reg_bits = reg_bits; + cfg.val_bits = val_bits; + cfg.writeable_reg = Some(Self::writeable_reg_callback); + cfg.readable_reg = Some(Self::readable_reg_callback); + cfg.volatile_reg = Some(Self::volatile_reg_callback); + cfg.precious_reg = Some(Self::precious_reg_callback); + + Self { + raw: cfg, + _phantom: PhantomData, + } + } + + config_with!( + /// Specifies the maximum valid register address. + max_register: u32 + ); + + config_with!( + /// Type of caching being performed. + cache_type: CacheType, cache_type as _ + ); + + /// # Safety + /// + /// `_dev` must be a non-null and valid `struct device` pointer. + unsafe extern "C" fn writeable_reg_callback(_dev: *mut bindings::device, reg: u32) -> bool { + T::is_writeable_reg(reg) + } + + /// # Safety + /// + /// `_dev` must be a non-null and valid `struct device` pointer. + unsafe extern "C" fn readable_reg_callback(_dev: *mut bindings::device, reg: u32) -> bool { + T::is_readable_reg(reg) + } + + /// # Safety + /// + /// `_dev` must be a non-null and valid `struct device` pointer. + unsafe extern "C" fn volatile_reg_callback(_dev: *mut bindings::device, reg: u32) -> bool { + T::is_volatile_reg(reg) + } + + /// # Safety + /// + /// `_dev` must be a non-null and valid `struct device` pointer. + unsafe extern "C" fn precious_reg_callback(_dev: *mut bindings::device, reg: u32) -> bool { + T::is_precious_reg(reg) + } +} + +/// Definitions describing how registers can be accessed. +pub mod access { + /// Register can be read from. + pub const READ: u32 = 0b000001; + /// Register can be written to. + pub const WRITE: u32 = 0b000010; + /// Register should not be read outside of a call from the driver. + pub const PRECIOUS: u32 = 0b000100; + /// Register value can't be cached. + pub const VOLATILE: u32 = 0b001000; + + /// Register can be read from and written to. + pub const RW: u32 = READ | WRITE; +} + +// macro use only +#[doc(hidden)] +#[macro_export] +macro_rules! regmap_check_access { + ($type:ident, $access:expr, $reg:ident, $addr:literal) => { + if kernel::regmap::access::$type & $access > 0 && $reg == $addr { + return true; + } + }; +} +// macro use only +#[doc(hidden)] +pub use regmap_check_access; + +/// Common operations for all field types +pub trait FieldCommonOps { + /// Get the Mask for the field + fn mask() -> u32; +} + +/// Read operations for fields with `bit` type +pub trait BitFieldReadOps { + /// Returns whether the bit is set + fn is_set(fields: &mut Fields) -> Result; +} + +/// Write operations for fields with `bit` type +pub trait BitFieldWriteOps { + /// Set the bit + fn set(fields: &mut Fields) -> Result; + + /// Force set the bit + fn force_set(fields: &mut Fields) -> Result; + + /// Clear the bit + fn clear(fields: &mut Fields) -> Result; + + /// Force clear the bit + fn force_clear(fields: &mut Fields) -> Result; +} + +/// Read operations for fields with `enum` type +pub trait EnumFieldReadOps { + #[doc(hidden)] + /// Underlying enum type reprensenting the field values + type EnumType; + + /// Read the field + fn read(fields: &mut Fields) -> Result; +} + +/// Write operations for fields with `enum` type +pub trait EnumFieldWriteOps { + #[doc(hidden)] + /// Underlying enum type reprensenting the field values + type EnumType; + + /// Write the field + fn write(fields: &mut Fields, val: Self::EnumType) -> Result; + + /// Force write the field + fn force_write(fields: &mut Fields, val: Self::EnumType) -> Result; +} + +/// Read operations for fields with `raw` type +pub trait RawFieldReadOps { + /// Read the field + fn read(fields: &mut Fields) -> Result; + + /// Test the field bits + fn test_bits(fields: &mut Fields, bits: core::ffi::c_uint) -> Result; +} + +/// Write operations for fields with `raw` type +pub trait RawFieldWriteOps { + /// Write the field + fn write(fields: &mut Fields, val: core::ffi::c_uint) -> Result; + + /// Force write the field + fn force_write(fields: &mut Fields, val: core::ffi::c_uint) -> Result; + + /// Update the field using a mask + fn update_bits( + fields: &mut Fields, + mask: core::ffi::c_uint, + val: core::ffi::c_uint, + ) -> Result; + + /// Force update the field using a mask + fn force_update_bits( + fields: &mut Fields, + mask: core::ffi::c_uint, + val: core::ffi::c_uint, + ) -> Result; + + /// Set field bits + fn set_bits(fields: &mut Fields, bits: core::ffi::c_uint) -> Result; + + /// Clear the field bits + fn clear_bits(fields: &mut Fields, bits: core::ffi::c_uint) -> Result; +} + +/// Bit field +/// +/// `bit` should be use when a feature is implemented through reading or writing a single bit of +/// a register. +/// +/// See [`BitFieldReadOps`] and [`BitFieldWriteOps`] for operations available.. +/// +/// # Syntax +/// +/// `bit(index, access)` +/// +/// where +/// * `index`: bit index starting from 0 +/// * `access`: access of the bit with the following possible values: +/// - `ro`: read-only ([`BitFieldReadOps`] gets implemented) +/// - `wo`: write-only ([`BitFieldWriteOps`] gets implemented) +/// - `rw`: read and write (both [`BitFieldReadOps`] and [`BitFieldWriteOps`] gets +/// implemented) +/// +/// # Examples +/// +/// ```ignore +/// regmap::define_regmap_field_descs!(FIELD_DESCS, { +/// (command, 0x14, RW, { +/// vselgt => bit(0, rw), +/// pwmvsel1 => bit(6, rw), +/// pwmvsel0 => bit(7, rw), +/// }) +/// }); +/// +/// command::pwmvsel0::set(&mut fields); +/// command::pwmvsel0::is_set(&mut fields); +/// command::pwmvsel0::clear(&mut fields); +/// ``` +#[macro_export] +macro_rules! regmap_field_bit { + ($field_name:ident, $access: expr, $reg:literal, $pos:literal, rw) => { + kernel::static_assert!($access & kernel::regmap::access::RW == kernel::regmap::access::RW); + + $crate::regmap_field_bit!($field_name, $reg, $pos, reserved); + $crate::regmap_field_bit!($field_name, _ro); + $crate::regmap_field_bit!($field_name, _wo); + }; + + ($field_name:ident, $access: expr, $reg:literal, $pos:literal, ro) => { + kernel::static_assert!( + $access & kernel::regmap::access::READ == kernel::regmap::access::READ + ); + + $crate::regmap_field_bit!($field_name, $reg, $pos, reserved); + $crate::regmap_field_bit!($field_name, _ro); + }; + + ($field_name:ident, $access: expr, $reg:literal, $pos:literal, wo) => { + kernel::static_assert!( + $access & kernel::regmap::access::WRITE == kernel::regmap::access::WRITE + ); + + $crate::regmap_field_bit!($field_name, $reg, $pos, reserved); + $crate::regmap_field_bit!($field_name, _wo); + }; + + ($field_name:ident, $reg:literal, $pos:literal, reserved) => { + kernel::macros::paste! { + struct [<_Bit $pos >]; + } + + impl $field_name { + pub(crate) const fn reg_field() -> bindings::reg_field { + bindings::reg_field { + reg: $reg, + lsb: $pos, + msb: $pos + 1, + id_offset: 0, + id_size: 0, + } + } + + #[allow(dead_code)] + pub(crate) const fn mask() -> u32 { + kernel::genmask!($pos, $pos) as _ + } + } + }; + + ($field_name:ident, _ro) => { + impl super::BitFieldReadOps for $field_name { + fn is_set(fields: &mut regmap::Fields) -> Result { + let field = fields.index(Self::id() as usize); + let mut val: core::ffi::c_uint = 0; + // SAFETY: `Fields` guarantee that anything returned from `Fields::index` is valid + // and non-null, hence it is safe to perform the FFI function call. + kernel::error::to_result(unsafe { bindings::regmap_field_read(field, &mut val) })?; + Ok(val == 1) + } + } + }; + + ($field_name:ident, _wo) => { + impl super::BitFieldWriteOps for $field_name { + fn set(fields: &mut regmap::Fields) -> Result { + let field = fields.index(Self::id() as usize); + // SAFETY: `Fields` guarantee that anything returned from `Fields::index` is valid + // and non-null, hence it is safe to perform the FFI function call. + kernel::error::to_result(unsafe { bindings::regmap_field_write(field, 1) }) + } + + fn force_set(fields: &mut regmap::Fields) -> Result { + let field = fields.index(Self::id() as usize); + // SAFETY: `Fields` guarantee that anything returned from `Fields::index` is valid + // and non-null, hence it is safe to perform the FFI function call. + kernel::error::to_result(unsafe { bindings::regmap_field_force_write(field, 1) }) + } + + fn clear(fields: &mut regmap::Fields) -> Result { + let field = fields.index(Self::id() as usize); + // SAFETY: `Fields` guarantee that anything returned from `Fields::index` is valid + // and non-null, hence it is safe to perform the FFI function call. + kernel::error::to_result(unsafe { bindings::regmap_field_write(field, 0) }) + } + + fn force_clear(fields: &mut regmap::Fields) -> Result { + let field = fields.index(Self::id() as usize); + // SAFETY: `Fields` guarantee that anything returned from `Fields::index` is valid + // and non-null, hence it is safe to perform the FFI function call. + kernel::error::to_result(unsafe { bindings::regmap_field_force_write(field, 0) }) + } + } + }; +} + +/// Enum field +/// +/// `enum` should be used when a series of contineous bits represent possible values that can be +/// enumerated. +/// `enum` fields provide type-safety and preventing to write into the fields incorrect values. +/// +/// See [`EnumFieldReadOps`] and [`EnumFieldWriteOps`] for operations available.. +/// +/// # Syntax +/// +/// `enum(bits_range, access, { variant_definitions })` +/// +/// where +/// * `bits_range`: bit used to store the data. +/// * `access`: access of the bits with the following possible values: +/// - `ro`: read-only ([`EnumFieldReadOps`] gets implemented) +/// - `wo`: write-only ([`EnumFieldWriteOps`] gets implemented) +/// - `rw`: read and write (both [`EnumFieldReadOps`] and [`EnumFieldWriteOps`] gets +/// implemented) +/// * `variant_definitions`: list of all the enum variants using the syntax: `VariantName = Value,`. +/// +/// # Examples +/// +/// ```ignore +/// regmap::define_regmap_field_descs!(FIELD_DESCS, { +/// (limconf, 0x16, RW, { +/// ipeak => enum([7:6], rw, { +/// Peak3p5A = 0x0, +/// Peak4p0A = 0x1, +/// Peak4p5A = 0x2, +/// Peak5p0A = 0x3, +/// }), +/// }) +/// }); +/// +/// limconf::ipeak::write(&mut fields, limconf::ipeak::Peak4p0A); +/// limconf::ipeak::read(&mut fields); +/// ``` +#[macro_export] +macro_rules! regmap_field_enum { + ($field_name:ident, $access: expr, $reg:literal, [$msb:literal:$lsb:literal], ro, { + $($k:ident = $v:literal,)+ }) => { + kernel::static_assert!( + $access & kernel::regmap::access::READ == kernel::regmap::access::READ + ); + + $crate::regmap_field_enum!($field_name, $reg, [$msb:$lsb], reserved, { $($k = $v,)+ }); + $crate::regmap_field_enum!($field_name, _ro); + }; + + ($field_name:ident, $access: expr, $reg:literal, [$msb:literal:$lsb:literal], rw, { + $($k:ident = $v:literal,)+ }) => { + kernel::static_assert!($access & kernel::regmap::access::RW == kernel::regmap::access::RW); + + $crate::regmap_field_enum!($field_name, $reg, [$msb:$lsb], reserved, { $($k = $v,)+ }); + $crate::regmap_field_enum!($field_name, _ro); + $crate::regmap_field_enum!($field_name, _wo); + }; + + ($field_name:ident, $access: expr, $reg:literal, [$msb:literal:$lsb:literal], wo, { + $($k:ident = $v:literal,)+ }) => { + kernel::static_assert!( + $access & kernel::regmap::access::WRITE == kernel::regmap::access::WRITE + ); + + $crate::regmap_field_enum!($field_name, $reg, [$msb:$lsb], reserved, { $($k = $v,)+ }); + $crate::regmap_field_enum!($field_name, _wo); + }; + + ($field_name:ident, $reg:literal, [$msb:literal:$lsb:literal], reserved, { + $($k:ident = $v:literal,)+ }) => { + kernel::macros::paste! { + #[repr(u32)] + #[allow(non_camel_case_types)] + pub(crate) enum [<$field_name _enum>] { + $($k = $v,)+ + } + + impl TryFrom for [<$field_name _enum>] { + type Error = kernel::error::Error; + + fn try_from(raw_value: core::ffi::c_uint) -> Result { + match raw_value { + $($v => Ok(Self::$k),)+ + _ => Err(kernel::error::code::EINVAL), + } + } + } + + impl $field_name { + pub(crate) const fn reg_field() -> bindings::reg_field { + bindings::reg_field { + reg: $reg, + lsb: $lsb, + msb: $msb, + id_offset: 0, + id_size: 0, + } + } + + #[allow(dead_code)] + pub(crate) const fn mask() -> u32 { + kernel::genmask!($msb, $lsb) as _ + } + } + } + }; + + ($field_name:ident, _ro) => { + impl super::EnumFieldReadOps for $field_name { + type EnumType = kernel::macros::paste! {[<$field_name _enum>]}; + + fn read(fields: &mut regmap::Fields) -> Result { + Self::EnumType::try_from(fields.read(Self::id() as usize)?) + } + } + }; + + ($field_name:ident, _wo) => { + impl super::EnumFieldWriteOps for $field_name { + type EnumType = kernel::macros::paste! {[<$field_name _enum>]}; + + fn write( + fields: &mut regmap::Fields, + val: Self::EnumType + ) -> Result { + let field = fields.index(Self::id() as usize); + // SAFETY: `Fields` guarantee that anything returned from `Fields::index` is valid + // and non-null, hence it is safe to perform the FFI function call. + let ret = unsafe { bindings::regmap_field_write(field, val as _) }; + kernel::error::to_result(ret) + } + + fn force_write( + fields: &mut regmap::Fields, + val: Self::EnumType + ) -> Result { + let field = fields.index(Self::id() as usize); + // SAFETY: `Fields` guarantee that anything returned from `Fields::index` is valid + // and non-null, hence it is safe to perform the FFI function call. + let ret = unsafe { bindings::regmap_field_force_write(field, val as _) }; + kernel::error::to_result(ret) + } + } + }; +} + +/// Raw field +/// +/// `raw` should be used when bits cannot be represented by any other field types. It provides +/// raw access to the register bits. +/// +/// # Syntax +/// +/// `raw(bits_range, access)` +/// +/// where +/// * `bits_range`: bits used to store the data. +/// * `access`: access of the bit with the following possible values: +/// - `ro`: read-only ([`RawFieldReadOps`] gets implemented) +/// - `wo`: write-only ([`RawFieldWriteOps`] gets implemented) +/// - `rw`: read and write (both [`RawFieldReadOps`] and [`RawFieldWriteOps`] gets +/// implemented) +/// +/// # Examples +/// +/// ```ignore +/// regmap::define_regmap_field_descs!(FIELD_DESCS, { +/// (pid, 0x3, READ, { value => raw([7:0], ro) }), +/// (progvsel1, 0x10, RW, { +/// voutvsel1 => raw([6:0], rw), +/// }) +/// }); +/// +/// pid::value::read(&mut fields); +/// progvsel1::voutvsel1::write(&mut fields, 0x42); +/// ``` +#[macro_export] +macro_rules! regmap_field_raw { + ($field_name:ident, $access: expr, $reg:literal, [$msb:literal:$lsb:literal], rw) => { + kernel::static_assert!($access & kernel::regmap::access::RW == kernel::regmap::access::RW); + + $crate::regmap_field_raw!($field_name, $reg, [$msb:$lsb], reserved); + $crate::regmap_field_raw!($field_name, $reg, [$msb:$lsb], _ro); + $crate::regmap_field_raw!($field_name, $reg, [$msb:$lsb], _wo); + }; + + ($field_name:ident, $access: expr, $reg:literal, [$msb:literal:$lsb:literal], ro) => { + kernel::static_assert!( + $access & kernel::regmap::access::READ == kernel::regmap::access::READ + ); + + $crate::regmap_field_raw!($field_name, $reg, [$msb:$lsb], reserved); + $crate::regmap_field_raw!($field_name, $reg, [$msb:$lsb], _ro); + }; + + ($field_name:ident, $access: expr, $reg:literal, [$msb:literal:$lsb:literal], wo) => { + kernel::static_assert!( + $access & kernel::regmap::access::WRITE == kernel::regmap::access::WRITE + ); + + $crate::regmap_field_raw!($field_name, $reg, [$msb:$lsb], reserved); + $crate::regmap_field_raw!($field_name, $reg, [$msb:$lsb], _wo); + }; + + ($field_name:ident, $reg:literal, [$msb:literal:$lsb:literal], reserved) => { + impl $field_name { + pub(crate) const fn reg_field() -> bindings::reg_field { + bindings::reg_field { + reg: $reg, + lsb: $lsb, + msb: $msb, + id_offset: 0, + id_size: 0, + } + } + + #[allow(dead_code)] + pub(crate) const fn mask() -> u32 { + kernel::genmask!($msb, $lsb) as _ + } + } + }; + + ($field_name:ident, $reg:literal, [$msb:literal:$lsb:literal], _ro) => { + impl super::RawFieldReadOps for $field_name { + fn read(fields: &mut regmap::Fields) -> Result { + fields.read(Self::id() as usize) + } + + fn test_bits( + fields: &mut regmap::Fields, + bits: core::ffi::c_uint, + ) -> Result { + let field = fields.index(Self::id() as usize); + // SAFETY: `Fields` guarantee that anything returned from `Fields::index` is valid + // and non-null, hence it is safe to perform the FFI function call. + kernel::error::to_result(unsafe { bindings::regmap_field_test_bits(field, bits) }) + } + } + }; + + ($field_name:ident, $reg:literal, [$msb:literal:$lsb:literal], _wo) => { + impl super::RawFieldWriteOps for $field_name { + fn write( + fields: &mut regmap::Fields, + val: core::ffi::c_uint, + ) -> Result { + let field = fields.index(Self::id() as usize); + // SAFETY: `Fields` guarantee that anything returned from `Fields::index` is valid + // and non-null, hence it is safe to perform the FFI function call. + kernel::error::to_result(unsafe { bindings::regmap_field_write(field, val as _) }) + } + + fn force_write( + fields: &mut regmap::Fields, + val: core::ffi::c_uint, + ) -> Result { + let field = fields.index(Self::id() as usize); + // SAFETY: `Fields` guarantee that anything returned from `Fields::index` is valid + // and non-null, hence it is safe to perform the FFI function call. + kernel::error::to_result(unsafe { + bindings::regmap_field_force_write(field, val as _) + }) + } + + fn update_bits( + fields: &mut regmap::Fields, + mask: core::ffi::c_uint, + val: core::ffi::c_uint, + ) -> Result { + let field = fields.index(Self::id() as usize); + // SAFETY: `Fields` guarantee that anything returned from `Fields::index` is valid + // and non-null, hence it is safe to perform the FFI function call. + kernel::error::to_result(unsafe { + bindings::regmap_field_update_bits(field, mask, val) + }) + } + + fn force_update_bits( + fields: &mut regmap::Fields, + mask: core::ffi::c_uint, + val: core::ffi::c_uint, + ) -> Result { + let field = fields.index(Self::id() as usize); + // SAFETY: `Fields` guarantee that anything returned from `Fields::index` is valid + // and non-null, hence it is safe to perform the FFI function call. + kernel::error::to_result(unsafe { + bindings::regmap_field_force_update_bits(field, mask, val) + }) + } + + fn set_bits( + fields: &mut regmap::Fields, + bits: core::ffi::c_uint, + ) -> Result { + let field = fields.index(Self::id() as usize); + // SAFETY: `Fields` guarantee that anything returned from `Fields::index` is valid + // and non-null, hence it is safe to perform the FFI function call. + kernel::error::to_result(unsafe { bindings::regmap_field_set_bits(field, bits) }) + } + + fn clear_bits( + fields: &mut regmap::Fields, + bits: core::ffi::c_uint, + ) -> Result { + let field = fields.index(Self::id() as usize); + // SAFETY: `Fields` guarantee that anything returned from `Fields::index` is valid + // and non-null, hence it is safe to perform the FFI function call. + kernel::error::to_result(unsafe { bindings::regmap_field_clear_bits(field, bits) }) + } + } + }; +} + +// macro use only +#[doc(hidden)] +#[macro_export] +macro_rules! regmap_fields { + ($type:ident, $reg:ident, $access:expr, $name:ident, $($t:tt)*) => { + kernel::macros::paste! { + #[allow(non_camel_case_types)] + pub(crate) struct $name; + + impl $name { + #[allow(dead_code)] + pub(crate) const fn id() -> super::Fields { + super::Fields::[<$reg _ $name>] + } + } + + $crate::[]!($name, $access, $($t)*); + } + }; +} + +// macro use only +#[doc(hidden)] +#[macro_export] +macro_rules! regmap_reg_field { + ($reg_name:ident, $field_name:ident) => { + register::$reg_name::$field_name::reg_field() + }; +} + +// macro use only +#[doc(hidden)] +#[macro_export] +macro_rules! regmap_count_fields { + () => { 0usize }; + ($type:ident $($rhs:ident)*) => { 1 + $crate::regmap_count_fields!($($rhs)*) }; +} + +/// Define regmap field descriptors +/// +/// # Syntax +/// +/// ```ignore +/// define_regmap_field_desc!(VAR_NAME, { , [, ...] }); +/// ``` +/// +/// where `VAR_NAME`: symbol under which the regmap [`Fields`] are available. +/// +/// register_definition: +/// ```ignore +/// (name, address, access_permission, { , [, ...] }) +/// ``` +/// where +/// +/// * name: symbol under which this field will be available +/// * address: register address +/// * access_permission: [`access`] permission of the register +/// +/// field_definition: +/// ```ignore +/// field_name => (...), +/// ``` +/// +/// where `field_name` is the symbol under which the field will be accessible. +/// +/// The following ``s are available: +/// * [bit](`regmap_field_bit`) +/// * [enum](`regmap_field_enum`) +/// * [raw](`regmap_field_raw`) +/// +/// # Examples +/// +/// ```ignore +/// regmap::define_regmap_field_descs!(FIELD_DESCS, { +/// (pid, 0x3, READ, { value => raw([7:0], ro) }), +/// (limconf, 0x16, RW, { +/// rearm => bit(0, rw), +/// rststatus => bit(1, rw), +/// tpwth => enum([5:4], rw, { +/// Temp83C = 0x0, +/// Temp94C = 0x1, +/// Temp105C = 0x2, +/// Temp116C = 0x3, +/// }), +/// }) +/// }); +/// ``` +#[macro_export] +macro_rules! define_regmap_field_descs { + ($name:ident, { + $(( + $reg_name:ident, $reg_addr:literal, $access:expr, { + $($field_name:ident => $type:ident($($x:tt),*)),* $(,)? + } + )),+ + }) => { + mod register { + use kernel::regmap::{ + access::*, + BitFieldReadOps, BitFieldWriteOps, + ConfigOps, + EnumFieldReadOps, EnumFieldWriteOps, + RawFieldReadOps, RawFieldWriteOps + }; + + kernel::macros::paste! { + $( + pub(crate) mod $reg_name { + use kernel::{bindings, error::{Result}, regmap::{self, access::*}}; + $( + $crate::regmap_fields!($type, $reg_name, $access, $field_name, + $reg_addr, $($x),*); + )* + + #[allow(dead_code)] + pub(crate) const fn addr() -> u32 { + $reg_addr + } + } + )+ + + #[repr(u32)] + #[allow(non_camel_case_types)] + pub(crate) enum Fields { + $($( + [<$reg_name _ $field_name>], + )*)+ + } + + pub(crate) struct AccessOps; + impl ConfigOps for AccessOps { + fn is_readable_reg(reg: u32) -> bool { + $( + kernel::regmap::regmap_check_access!(READ, $access, reg, $reg_addr); + )+ + + false + } + + fn is_writeable_reg(reg: u32) -> bool { + $( + kernel::regmap::regmap_check_access!(WRITE, $access, reg, $reg_addr); + )+ + + false + } + + fn is_volatile_reg(reg: u32) -> bool { + $( + kernel::regmap::regmap_check_access!(VOLATILE, $access, reg, $reg_addr); + )+ + + false + } + + fn is_precious_reg(reg: u32) -> bool { + $( + kernel::regmap::regmap_check_access!(PRECIOUS, $access, reg, $reg_addr); + )+ + + false + } + } + } + } + + const $name: regmap::FieldDescs<{$crate::regmap_count_fields!($($($type)*)+)}> = + regmap::FieldDescs::new([ + $( + $( + $crate::regmap_reg_field!($reg_name, $field_name) + ),* + ),+ + ]); + }; +} +pub use define_regmap_field_descs; From patchwork Wed Dec 18 23:36:33 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Fabien Parent X-Patchwork-Id: 851768 Received: from mail-pl1-f182.google.com (mail-pl1-f182.google.com [209.85.214.182]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 815C41FDE2F; Wed, 18 Dec 2024 23:37:24 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.182 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1734565046; cv=none; b=tWB73yeTtWMdiYZU+mqCiJ15C0/RwUfkkv5K8M26pV0RAYVbUpZCRIBHD8d8rVI8Uv4Rzlz/BqQi7flwjYy6rtXrwnsxdufGdNXEbCu2cVm7WxHiZ0L7dPgMj2u0dppuP0QW31CQvG11LtFxI8iJXCe7SvIgGz/GBm593IqplOQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1734565046; c=relaxed/simple; bh=NsdvfP9WuPfbdehzdnnHkfOaZjm7jFfiAr29jiutkkM=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=CmXAhy2pZX71lBRaAWbu989qeg+JQ7Ys5DaGf8NABfUtkom5jOVXpCi29rRf3IrPvmi4IhV3OxUjRvSr7Pj92tAL2X3LlYtQY8HTOUjALYrO/IdZGRviwf362dmS+JTiAmw/fCdaRDzG6XjIyAhdb1ki9X1/Q2sNax03lAfE5kQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=VkHhOFvt; arc=none smtp.client-ip=209.85.214.182 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="VkHhOFvt" Received: by mail-pl1-f182.google.com with SMTP id d9443c01a7336-2163b0c09afso2234055ad.0; Wed, 18 Dec 2024 15:37:24 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1734565044; x=1735169844; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=9P/lUP2PRS4x/+lQSUCJv9QrZZ4hdzqovCNaqVm/erI=; b=VkHhOFvtsIASR5tR5256MvEWgPNb9V4/hNsKZikl7Y4xXm8VXzrk5haEB3NTWgJseI EXDNe1C9xnAGY5KMKj2PJpyPLXhKNHFKVYukC8Hk7snxnLKoEkA6o+1RJK8cOmEkUWm/ l+Pd80iUs1UR69aAeVXEDtywoqlEcjT1JW9xEnIU803FxhG3UY+5xKODQ+NZi47Nf8hQ DNnazpPpnyIx3MWS4Mim2//ZwZlqGojDBqLmweC+uX1FSvq88z/QYQlbqG/sIvuiOkqF 6EImdTcik7KxzJW+/G0LO+tX9fB9Y6Szho8BMV3+6B9ZbMpjZwBmFAcNjF70Yv9TmFsm nEeg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1734565044; x=1735169844; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=9P/lUP2PRS4x/+lQSUCJv9QrZZ4hdzqovCNaqVm/erI=; b=NltHkA+c/lDL4+gqlPnwfPSi7LLQZAvVbMk32ZcnCaxFCTb4rKS7uTZFJVMYdaFZQq hK4JwXY70aTkRgZpx3CMWSMAgY1yQGxi6fYZfGljbUvdiqGsIzmJb64sBhhCVPUPhbNh Guib2TaA76WKt4CtNsmcWWz0XxoPSDTF72v+JIcBllJGbY6itZsk9/z/llmMoFZ6ODmB J5jMz5RmaQEFOUpDxeWPMZQPo91jjIzJlSwHUP0aRV/71oes+cxoXYTFF9wFfrfS8JtD vqoEEkWAaofCkCv2rO3udlB3R9mC/uhyODZ8qTPYSQYVh3KUrPkB6FX9Z2pIoqLdEma+ FJUQ== X-Forwarded-Encrypted: i=1; AJvYcCU1uFziu4TbN72kuGtqT2Cgoh0jQykB5Mx2+sSKRvfF9EEJDWHLfTL1VgSJhDkt0jEOTO9SvcHW4t8hNbSN@vger.kernel.org, AJvYcCVCndBonKs6/xq/vH5b5GsML+ycF1Dc2p0ngXt3pAvxCUh9ZXPxlGAQbziBsCgxeIGKmSk0b5Dw7kyj@vger.kernel.org, AJvYcCWhorU7mzRNSRciUe1hEwu2gOMmubYGURHZ69/atJk+0KoogIcgl1Q4ZviPdwjnXuXUfhDPdgphQrQzXwFH@vger.kernel.org, AJvYcCXDlYVmjR12BkKszTCE7ngsxWy/k/uF1mrZ2bWRV7cE9tVIZ0ySpYRF+V+Uh9j6rpwpaDXWQslB6dxOKmobrqg=@vger.kernel.org X-Gm-Message-State: AOJu0Ywdc3UuCXPHxjUdHIGJppsAsvivC0iOVk6LCXoSEiw3lkM/lt5y a2FlRlqx/cLwx+RJHxFepJK+IxlCCip5MFX4qX/+BILAgDw1b5lp X-Gm-Gg: ASbGncs5xnAoUB/TvAQdf7srPB5ky3kVJ6KCRxRPDcwoLQIX1sapoHUWaZY7R9iNpIR 7hClrDsd7oMGZVMmCQBujiaXfq2G/E5sO10aypDfZWq5Tk7V9/zE3S66kwRkiemfxgVCGQxkyEG wB1vNzHcSEWLBTnVplrCFIsvKgRHmc5wu7ijyPmT2+WquVs0eN4QIv2Oqoge//CPDTAxe86ayGv PaQ+Xp0TkvEcOKgs93NdmCitJRgdKP3h8+Fr29b/xRO7apUc7sZkQwq X-Google-Smtp-Source: AGHT+IGfZAqlhj0audMtxGa1Hunn/puU2OflMnZRL01UUIPkYNN49aM+qmpVt7uyRf0pK9RvPlUfGg== X-Received: by 2002:a17:902:cec7:b0:215:781a:9183 with SMTP id d9443c01a7336-218d7269af1mr77074055ad.38.1734565043733; Wed, 18 Dec 2024 15:37:23 -0800 (PST) Received: from [127.0.1.1] ([2001:569:fcea:600:2e06:283e:5daa:4d0f]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-219dc9f6967sm802335ad.214.2024.12.18.15.37.21 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 18 Dec 2024 15:37:22 -0800 (PST) From: Fabien Parent Date: Wed, 18 Dec 2024 15:36:33 -0800 Subject: [PATCH 3/9] rust: error: add declaration for ENOTRECOVERABLE error Precedence: bulk X-Mailing-List: linux-arm-msm@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20241218-ncv6336-v1-3-b8d973747f7a@gmail.com> References: <20241218-ncv6336-v1-0-b8d973747f7a@gmail.com> In-Reply-To: <20241218-ncv6336-v1-0-b8d973747f7a@gmail.com> To: Rob Herring , Saravana Kannan , Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Greg Kroah-Hartman , "Rafael J. Wysocki" , Wolfram Sang , Mark Brown , Liam Girdwood , Krzysztof Kozlowski , Conor Dooley , Bjorn Andersson , Konrad Dybcio , Fabien Parent Cc: devicetree@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-i2c@vger.kernel.org, linux-arm-msm@vger.kernel.org, vinod.koul@linaro.org, Fabien Parent X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=907; i=parent.f@gmail.com; h=from:subject:message-id; bh=vynuSFjbYPtMJhL+ID8YRu8L0WeE0IXWsYbO1p8vUJg=; b=LS0tLS1CRUdJTiBQR1AgTUVTU0FHRS0tLS0tCgpvd0o0bkFGdEFwTDlrQTBEQUFvQmlKc2d4Q kw5cktRQnl5WmlBR2RqWEtUaDR0bFM0bm9HUVFtSTFkR0YvZE5UCk1HR0h0bmZoVXFHQ0N2SkQw VmhDdW9rQ013UUFBUW9BSFJZaEJONk0wZElMeXpzd3JmRnNxWWliSU1RUy9heWsKQlFKblkxeWt BQW9KRUlpYklNUVMvYXlrNjdnUUFNWGw5R2xzR2lJRjc0Wm9jbzdldTdsN0hIT0hWbG9ibVpXMg phQ2ZqT2NoYUZLUFo3dHlUSCs2dko4L1NzZnpDRlljZW5wUlBMQzdsNDZhcHdiT24yVFRlbGFwb kE2ZDQvc0hXCm8yK0lvQ01Db0xOa1pzVXFzWndCVlpaS3pwMGpGeGc5aVU1cHQ2N29jL0RzcXpr MDZGR0ErSzQyU3BTNUQ3SlEKbXR4aHlpcCtQc2l5TGJtZ0x5eWw2UUpyYUhxTmxvMGdGUG5tUHB YUmdPVHNub2JtOWlUZ0ZVWU03aE5qYWJocwpjOFV2MVE3RGZHNGU1eGpvaitaQVdrVUdUT0FUdU 1INEloWjVJKzNHeUFIeWh0SDNjYWxZOTU3aG5yN1lva1dDCjJpWTcrMjQzcHhodGttd3M1SGVmV zg5dVNuSkVzNGxFRkc5WDdrWFZMc1dhd3RqeTdxbnNRN0FheUJ3Rm1nWmgKSmU4UitRQkEwaFpX eVREdTZDSmZDYytuVlF1TVpHdE1OdytMZVhlekVLbzRPckRETTA3OTUyOGEwVEFPb0FUTgoycjQ 1cGtOdXpuV3h5RTNpdjNGNnkzWi9IZitqdlJzMVZkMWtuR0pIbEszZHEwZEZUQ3JHb2JOK00xTk FmWHIrCjVySWR2L0xOc2xWbERsQ0dFb3FBeE52QUptZ05DVGQ4SHhmeUkzMFVsbHEzazdJbzZMV 2VHZFA2OGNxbGRQTkUKb3E4UDU4TnNwdjBtWHZSMGlWcUQzdEZ2L1RvclR3Yk1mZTFyN2gxamlX aGNvMFI3OUQ2eEl6WCtsOWtKMHBraQp4WEtkb2R6UmNOL2dFTVFEMkVNR3VUWWNxdkxoMUVqa2V yVkFYY0RkeG9sZjdSZDhxTVNtODdidFIvZS83V1hsCkJJcG5yTks5RWkwNU93PT0KPXFZYnUKLS 0tLS1FTkQgUEdQIE1FU1NBR0UtLS0tLQo= X-Developer-Key: i=parent.f@gmail.com; a=openpgp; fpr=07BB034F9E8E3AF0DF4CF7607AE864A1E16FA383 From: Fabien Parent Add a missing errno for ENOTRECOVERABLE. ENOTRECOVERABLE is returned by get_voltage{_sel} from the Regulator driver API when a voltage cannot be read at bootup and hasn't been set yet. Signed-off-by: Fabien Parent --- rust/kernel/error.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/rust/kernel/error.rs b/rust/kernel/error.rs index 52c5024324474fc1306047f3fd7516f0023d0313..19c8287032631ee8c4afd3c9c3f1e0709591ba3d 100644 --- a/rust/kernel/error.rs +++ b/rust/kernel/error.rs @@ -82,6 +82,7 @@ macro_rules! declare_err { declare_err!(EIOCBQUEUED, "iocb queued, will get completion event."); declare_err!(ERECALLCONFLICT, "Conflict with recalled state."); declare_err!(ENOGRACE, "NFS file lock reclaim refused."); + declare_err!(ENOTRECOVERABLE, "State not recoverable."); } /// Generic integer kernel error. From patchwork Wed Dec 18 23:36:34 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Fabien Parent X-Patchwork-Id: 852106 Received: from mail-pl1-f181.google.com (mail-pl1-f181.google.com [209.85.214.181]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 852C71FDE29; Wed, 18 Dec 2024 23:37:26 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.181 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1734565048; cv=none; b=q2NQzfmvSH2PEb4wUr1pJkcfBF/asCIdQNyKK9huH1xYW1ebTAvFEXiqPLh+7r2j2xDR1xNu/Hnt7k1qGcjhQii6Yz/09JuCJ/c4glL87tdsKoC8y6PBuxFIIUdFMgq1mvumwdP2Kiq/2Ewt3wDa+PUISeSTBTCBgohbi+BPExE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1734565048; c=relaxed/simple; bh=jg2oRp5e5fuog+zg79Tz0wrUxR488MRr97JufU3GLxU=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=XS0VTxwB7bfeYNQVnBgKFFz+nFipZMxiFvS8B0xhs56bFCoJiTjRBPeBvhb9FKjS5uvQg6FcDtKvHnGI03lZZotBuDxJvWO4EUvmxt70KnJ5piN++lQPZsEGviGP2yCC9JTwN7/cPSRR30/DNlBEMByxoWQAPppu8ce0WHRlKM8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=II061yV3; arc=none smtp.client-ip=209.85.214.181 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="II061yV3" Received: by mail-pl1-f181.google.com with SMTP id d9443c01a7336-2164b662090so1805535ad.1; Wed, 18 Dec 2024 15:37:26 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1734565046; x=1735169846; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=12KHJUczqWRitS9k61VKlbaTiRVMkGTV7QU9FFZ0F4Y=; b=II061yV30WJGrbZRWMMqSgSFchY61Sry9HtVbDiGL6InG98zIugXAhCrK//xAhv2vZ Znnyqwm0CIXC2olxjd5m9aMQoB6QsizUt9k9EHwO1CDQj5moCT0pqr2cmetxV2bMf9NL TtASnrb4ltyyJL5slcWsQo/Tqquvzf5YvHgvcB5a5YP3U3W1XcpZk9ywwhcPjAXJn/yg xDDnaDCgryPulQaQtvanD4x+OpoiFvvOGf5+63bQA4iojugdtiiqY8O0cSDx2uAxMUwx hZ4ONf5+MMJaqau8i75WwM/4GtRoSAtLMqMQNu74vPlhmwGEAAY8aWRy47aL7RWReNcd NKvA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1734565046; x=1735169846; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=12KHJUczqWRitS9k61VKlbaTiRVMkGTV7QU9FFZ0F4Y=; b=b9r3bIoyWVGPaVQ5htPmbl+JDcWs0sRD4IxVdwzroy0sohVoLEDomRCrpr7V84L6ib MEB/U0Y6KTd+Ppy7L+g/9zUc6jFkQfHooxquLaBtmqix/+iWv3Tx0T2lShuukWgCD9Va FO1boR7AYZUMzJl/5fmINuJgKy7qdd/tMmAwTPl8usns/oF8Q6AA+FcXWuqA/3nslHoQ UB8QABnrZaFWC+Q4cQVSB1Uo8LjW9/nzr5Iv7AWl0ukdARh0nl/dm2lXsz0KDx07sv7U VY0sZEfNy5kKN9UZ21Sm1KHN8627oU10ghN1Y0CfIRQiy/BRmzYPPKluPUwwYEYCOtl/ jyAw== X-Forwarded-Encrypted: i=1; AJvYcCWszyX8WWcBOnA1AHzZqNluBrm+MMVCy4z+VkClZ+7aLNJml8g5FSpClHIPCn0g2ODEQK6y53b0lIhQ@vger.kernel.org, AJvYcCX7HiOMrvBiXeVBm7hkWyTrp/FiiFxzEXzMBrU0DxJUoeEQ8G+fSAe3Xk527ZvbXADE4fKgDez+vCIfMo1l@vger.kernel.org, AJvYcCXWKpwLcYn5snPq1fCDAmcSgkn5T2GjUKDpsAEc3TzmAOQoOe8gwtXD1BIn4gIcbrHKspQaEJYXqYj+UmZENns=@vger.kernel.org, AJvYcCXxTwFG2dg/QArB8otjQ9RI/1uJ+9N6jCLJbH4x8+ygQjkszyQRJdmdDaLaZ0mvDhtAzDX13vLbcq0RbgTe@vger.kernel.org X-Gm-Message-State: AOJu0YxOAKRu0X96i6lUwHYdkl6+lIqj+DoDPrsdeeViyHCBtAIRP50c gOiPWFpXvQMXg2M4Ovss99zXdhfWjvEJnt6wJXiXz6ilvluHXl82evMsoHK6i8U= X-Gm-Gg: ASbGnctel6q/NVak86SlG6RmS9loiXFJA1e+KAK1XZMgfCGTmz974FnatQS5O3nKMNU ntYafehZp7EWSL9PJjbnuknwco4lOzHutHyIbuoVP6VTq8SscmDry/S8ZEd09dgn1nSRxPl4kOc vmii4gjFOpC4RmAT+CUzkkX2L0BWrF5f7xsXrMLHK0P4nyTobmok0U3wnIIn9pp9GQ0Q3f11sOx c5mXjGTa48xbl2Oj6OAiWonNrW79G2z6a/PMQEYmk7pdVDHPYTO4WaJ X-Google-Smtp-Source: AGHT+IH9mSleE//GIh6Qq7FHtStK+3iupDiRh9ncyKsC553vYaM8qlvEWdk9TswOWc4efLT878WwmQ== X-Received: by 2002:a17:903:2bce:b0:216:7ee9:2227 with SMTP id d9443c01a7336-218d724d328mr69209335ad.36.1734565045836; Wed, 18 Dec 2024 15:37:25 -0800 (PST) Received: from [127.0.1.1] ([2001:569:fcea:600:2e06:283e:5daa:4d0f]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-219dc9f6967sm802335ad.214.2024.12.18.15.37.23 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 18 Dec 2024 15:37:24 -0800 (PST) From: Fabien Parent Date: Wed, 18 Dec 2024 15:36:34 -0800 Subject: [PATCH 4/9] rust: regulator: add abstraction for Regulator's modes Precedence: bulk X-Mailing-List: linux-arm-msm@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20241218-ncv6336-v1-4-b8d973747f7a@gmail.com> References: <20241218-ncv6336-v1-0-b8d973747f7a@gmail.com> In-Reply-To: <20241218-ncv6336-v1-0-b8d973747f7a@gmail.com> To: Rob Herring , Saravana Kannan , Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Greg Kroah-Hartman , "Rafael J. Wysocki" , Wolfram Sang , Mark Brown , Liam Girdwood , Krzysztof Kozlowski , Conor Dooley , Bjorn Andersson , Konrad Dybcio , Fabien Parent Cc: devicetree@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-i2c@vger.kernel.org, linux-arm-msm@vger.kernel.org, vinod.koul@linaro.org, Fabien Parent X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=3505; i=parent.f@gmail.com; h=from:subject:message-id; bh=P1S4uQEgJR2OofcG/5ihA0BvuOtU7xhJ4vJHZDYeWxc=; b=LS0tLS1CRUdJTiBQR1AgTUVTU0FHRS0tLS0tCgpvd0o0bkFGdEFwTDlrQTBEQUFvQmlKc2d4Q kw5cktRQnl5WmlBR2RqWEtVcldDRGpYdXlsZXNoUHh5QzM5TjQyCmxyOHNjQWQyU0R3bjI1UFpP RXhkbTRrQ013UUFBUW9BSFJZaEJONk0wZElMeXpzd3JmRnNxWWliSU1RUy9heWsKQlFKblkxeWx BQW9KRUlpYklNUVMvYXlrVDk0UC8zSm55Z2Q2SU1PYmkxa25teTAvZGROWnFhOGEwb3BqZ2NweA prT3BHS2Nna0pNV1FsWGlQZlRNb3RUSWg4d0VGSDhwU01kdWUwaHVwYU82b3NkekRQRFU3U2Z4T Tlyem1PQW94CmNFTWpWVVNRT0NQWmtnRjNYSUFHaS9hOGJ0NWo2OTlpNGhtSVpDZU4xMnp3MXJG MDBNOFB2OWhvc1VnM2ljNWwKaGxDcll0cUx2cTVaYUFRd3Qvb3N2NjFwU0JoZG41djEzTEVFcUZ aY2NnekwvVHBuakQxUFpYaUJ0Z1hqYVhoUwp5SWd4cjZuK3RJN2lwSTh0RnlWS3U2Ni9SdnRDRE hvczE4cTMxNExOeTBKSDlIU2ZlemJDMVdjVGUxWFJiSk5UCkN1allrRlJzMjVRUUtZU2svQ0V2U jhFdzVmSFpzeC83TTNIeHRWV0lOUWxxUHptb3IyajdOZXRmK0lJREZOU00KdjFEb205T3BkMVNP N0NQWU45UWx2UWIrLzdaYWtCbGRWMmROQytick1mRkFUdFFTdlFuL2k2SW9FY0xucjFEYQpLTm1 KMjE1MDdOR3pockNjR253Z0FBcUVEeVNqU3JsUFJzN3F1VDEwdVh5KzdEYm9xd2xuUmhTSTBuZ3 U4SkdhClZCK1p3MHl4cVd2RWFaMmR4dmtLOTkzMWVGVUovNndyUlRodXRGT3p1dDl2bUxJOXBMR FBLdC83S2VpUEdUdzcKZjh0NU1SVUV0S2xYZ1ZCOHllRFVHUXhGbGxCNzV1cjJtY09IZ2FON1pm Yi9tOFFLWTQ0QTgveTBBR0dEMnZ6Nwp3QlA0VHdjM01Xd0MvenBmd1N2QXJYWkJlRW41UGhidGh FS0d5dnpyYlJaSHhsVEVzTTFvQTZKdEFCb3hhQjVqCkR5QmV4dU9nVUo0d2NnPT0KPU1BNmgKLS 0tLS1FTkQgUEdQIE1FU1NBR0UtLS0tLQo= X-Developer-Key: i=parent.f@gmail.com; a=openpgp; fpr=07BB034F9E8E3AF0DF4CF7607AE864A1E16FA383 From: Fabien Parent The type regulator::Mode is used by both the regulator consumer abstraction and the regulator driver abstraction. This commits adds a shared abstraction for it. Signed-off-by: Fabien Parent --- MAINTAINERS | 1 + rust/bindings/bindings_helper.h | 1 + rust/kernel/lib.rs | 2 ++ rust/kernel/regulator.rs | 42 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 46 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index acb3942eb1b66ec2bc09ac50f51c2054b7b45355..90c231f0aa7381aa8d206fb94c5d1f013dfcae41 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -25159,6 +25159,7 @@ F: Documentation/power/regulator/ F: drivers/regulator/ F: include/dt-bindings/regulator/ F: include/linux/regulator/ +F: rust/kernel/regulator.rs K: regulator_get_optional VOLTAGE AND CURRENT REGULATOR IRQ HELPERS diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index 48d2b91b34067e7e9ee9c64c2e42681e988e9aad..b18d772bc3a0e78d749cc9e5ae81a4237a57f8c5 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 456e979724d1079045cb157086ff2b2ed0fcca3b..3aa36648e9571e305a89f5d1353c0dd44e136384 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -68,6 +68,8 @@ pub mod rbtree; #[cfg(CONFIG_REGMAP)] pub mod regmap; +#[cfg(CONFIG_REGULATOR)] +pub mod regulator; pub mod revocable; pub mod security; pub mod seq_file; diff --git a/rust/kernel/regulator.rs b/rust/kernel/regulator.rs new file mode 100644 index 0000000000000000000000000000000000000000..d695ac955193efcfda62770784a92d70d606b93d --- /dev/null +++ b/rust/kernel/regulator.rs @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! SoC Regulators + +use crate::{ + bindings, + error::{code::*, Error, Result}, +}; + +/// Regulators operating modes +#[derive(Copy, Clone)] +#[repr(u32)] +pub enum Mode { + /// Invalid mode + Invalid = bindings::REGULATOR_MODE_INVALID, + /// Regulator can handle fast changes in it's load + Fast = bindings::REGULATOR_MODE_FAST, + /// Normal regulator power supply mode + Normal = bindings::REGULATOR_MODE_NORMAL, + /// Regulator runs in a more efficient mode for light loads + Idle = bindings::REGULATOR_MODE_IDLE, + /// Regulator runs in the most efficient mode for very light loads + Standby = bindings::REGULATOR_MODE_STANDBY, +} + +impl TryFrom for Mode { + type Error = Error; + + /// Convert a mode represented as an unsigned integer into its Rust enum equivalent + /// + /// If the integer does not match any of the [`Mode`], then [`EINVAL`] is returned + fn try_from(mode: core::ffi::c_uint) -> Result { + match mode { + bindings::REGULATOR_MODE_FAST => Ok(Self::Fast), + bindings::REGULATOR_MODE_NORMAL => Ok(Self::Normal), + bindings::REGULATOR_MODE_IDLE => Ok(Self::Idle), + bindings::REGULATOR_MODE_STANDBY => Ok(Self::Standby), + bindings::REGULATOR_MODE_INVALID => Ok(Self::Invalid), + _ => Err(EINVAL), + } + } +} From patchwork Wed Dec 18 23:36:35 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Fabien Parent X-Patchwork-Id: 851767 Received: from mail-pl1-f173.google.com (mail-pl1-f173.google.com [209.85.214.173]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 0745E1FF1C8; Wed, 18 Dec 2024 23:37:28 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.173 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1734565051; cv=none; b=mYBgcWVANxgl01Z0EBxH0kGx3XuhuxXK1Ota9HHBwOa4qdO+bTqfqBi7ZarTh8blPBHJch7yUtGSoi1UC7849wJs0uKY1bwbRwTGhuUkKFEMxiP2VZ8q9v7x8e94NTADh1daPTbWFN7rYMsnwRKeCigquuH/Uc78YAl4YViLowQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1734565051; c=relaxed/simple; bh=JAfbjxBIY8Nq4rIVbaojRV+WEdf2qBL4PSC8ODV2X0Q=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=iyWDTkVxZLR1qm6gxcE8rz0/0JwXlsGCfiTiqxw6Pemp/tTVQpuLZTAEVYWSQjzTyo3ZHqIuKwsqba4Jd/1rzHKz+wdZWU5GJH8JkpIxbSJQq62IVoc5RKWymZ6caD3YPOTAsgFzsmn8Mn7sddhhhox67cA9kFjRV8FW3EWzju8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=BO61Cmln; arc=none smtp.client-ip=209.85.214.173 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="BO61Cmln" Received: by mail-pl1-f173.google.com with SMTP id d9443c01a7336-216728b1836so2060805ad.0; Wed, 18 Dec 2024 15:37:28 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1734565048; x=1735169848; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=tl+g5tdx7rxmvzKspLWFkF4LAJ0SjMm1pdSmLUZ7lgI=; b=BO61CmlnCVrdFf00dNyhfET2EfvBXBkwONq3/HPTOAX9fhdaz9L/7xvegtfY/YDDJM nMgtnxnx/4MuOy7talh85Gd42HSsEIkRL8tbuw36pxEX9Lv9J6pQSDm2yeviJo46pQvw aLlVcCuIh2tpLORsjlClRJRc1KxPTyYY5ZIpxrBkJTWPr/23NkBe1ZEbVHOQx1IhxoAZ 9lzjQhtpllWF3RL7xwLF5MjJ7dd5hsE0pGSpf5WxhOWvOurYaRh57+ixH2/EHF6axP65 uy18gq5/C04KbOo4ZkSv1zq5UO9PsqMInbupBDTMe53rsLD2z2DvO8BmxDsVZCnXWGon LzEg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1734565048; x=1735169848; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=tl+g5tdx7rxmvzKspLWFkF4LAJ0SjMm1pdSmLUZ7lgI=; b=Y+vcTiNCGrL2ImypUS4z3FKTnPW1asLs9dOBpbP54/aksAADuKi5dLWuh4/aK8VH44 cCNU7Vy/vCkl0vxemEkBds+qmfjCZvAPH7ATCR2isBazPZJX6e8PkVsleKdIsMaPHNOo CYxcENgPhd/Xj8SmOk5USXl79M4akDFdzB5GB5mi4UYhxFh2t/oodYNRA1OcMcyMjMFs EBLI/xrQQd4RkKfJqvoSCXXtzMVkiB0X2AR17mLltafnfEuQHo0vBqGEks/MQMDzWBLu j21ULsu+A9iVaqFP9WC+JtbttZZQmIPq8wlh8WAM2j3g2G7Yr/HYnGm5x+7yNJk0RvWe lh+Q== X-Forwarded-Encrypted: i=1; AJvYcCUvzZg9CD8ATzPNRT0db9cG4IIaxeGpMsDS6zPt48mWmgPvnHMvCKamtrnf12gGSKfPWAu/Hen4PwFi5LAf@vger.kernel.org, AJvYcCVt4nsTetxepY70NqDaUoWFlbwo5ykwhmU8u+RerYYfu6ZhT6LoCeNFA5pK/WZMSCTEomP0dSGHcpVQ@vger.kernel.org, AJvYcCWtveDQ+1+ZDoIy0BLJQ0Qdkr1fRPO1OyZ/pGlUE9U4/tZ6FtusZpsPjd5Q6ClZhgLVQty4TmpyJqP/67MA1rY=@vger.kernel.org, AJvYcCX+UU2m1rtEwhVo5dgrmM0mu7jlPmney6Tb3UE1Y1VBh5NIqM2yg3AscNeR9RIj73jM4Npwx6wc481D0znY@vger.kernel.org X-Gm-Message-State: AOJu0YxULn3sDuG3LSMoK6PsSdaKI2ZtVVSJEFVXOhPleXo/wfXcpJNR N6J7a8m6WfqHIFfpprh6yiNuZmobBC/doPT0czP0TUluExVtqQkZ X-Gm-Gg: ASbGncuBmcZLTsS/KDGgSuhxtvQSH2P4JPmWie6xRiA8CuKGmKxsfE0JOoX6pEaYZ8Y 031KgBZVcL5XUHL8BrLuy1/DVw2TkBH33rwN7bRGBs0BqMgpzyMfiQoGDdjRsL8m3xubE7d8ZB+ 61628eeDVk0shYrA3sr9Pk34A6pRSgnyEGjZhitmQ1b6DdjcOMVI1Wivy1omqQWQhPXiyN2LSnd lo21gMctbfqPjAK35M8OeN8IlsrBPsaSiALCclJzU459jOmzWOxVUi+ X-Google-Smtp-Source: AGHT+IGsgZowloX2sSekJiZwr5HUa7XJGyd9XeMnodxAPqgzxE3diRw7PiA2qjjYWejh/G7dEWQHhw== X-Received: by 2002:a17:902:ce8b:b0:216:6f1a:1c81 with SMTP id d9443c01a7336-218d6fc3a38mr64676395ad.2.1734565047899; Wed, 18 Dec 2024 15:37:27 -0800 (PST) Received: from [127.0.1.1] ([2001:569:fcea:600:2e06:283e:5daa:4d0f]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-219dc9f6967sm802335ad.214.2024.12.18.15.37.25 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 18 Dec 2024 15:37:26 -0800 (PST) From: Fabien Parent Date: Wed, 18 Dec 2024 15:36:35 -0800 Subject: [PATCH 5/9] rust: regulator: add Regulator Driver abstraction Precedence: bulk X-Mailing-List: linux-arm-msm@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20241218-ncv6336-v1-5-b8d973747f7a@gmail.com> References: <20241218-ncv6336-v1-0-b8d973747f7a@gmail.com> In-Reply-To: <20241218-ncv6336-v1-0-b8d973747f7a@gmail.com> To: Rob Herring , Saravana Kannan , Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Greg Kroah-Hartman , "Rafael J. Wysocki" , Wolfram Sang , Mark Brown , Liam Girdwood , Krzysztof Kozlowski , Conor Dooley , Bjorn Andersson , Konrad Dybcio , Fabien Parent Cc: devicetree@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-i2c@vger.kernel.org, linux-arm-msm@vger.kernel.org, vinod.koul@linaro.org, Fabien Parent X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=31897; i=parent.f@gmail.com; h=from:subject:message-id; bh=OsmHu/e69v+xfLx7ce6mgWFJpVCdUWuT+k1hKoJNLVg=; b=LS0tLS1CRUdJTiBQR1AgTUVTU0FHRS0tLS0tCgpvd0o0bkFGdEFwTDlrQTBEQUFvQmlKc2d4Q kw5cktRQnl5WmlBR2RqWEtiVEFyUFF4SFdUYkNlTTBPNkVyTG9KCjBtejJLMXV0WXkxWlpuNzlG V09rellrQ013UUFBUW9BSFJZaEJONk0wZElMeXpzd3JmRnNxWWliSU1RUy9heWsKQlFKblkxeW1 BQW9KRUlpYklNUVMvYXlrZGRnUC8zb0lsalZwdEs3TzBwdnRLcEhFd0F2Z2NISXhwd29DeDFYNw pnSzBwbm9MRnQ0RXdhc2lOeHZqa0FoYlgra2x5ZmVQT2VSR095YWdVd3BLMEZHelZVWXBGUXplM HBkL2tGOXF1CmpFeEYxQUxqMFJ1TllLMmtaaXVHbDhTQ240TnlZTGlvYitUM1B4cFRQS1JXWWx3 VlBPMTg1cVJrM29HdDJwTHoKQzNsVU8vcU0ySlU1MVYvT1d4TFN2bTVKUDRIVjcvTUhxeVhwcjl xU3BpZHJDcjVXRjViOXVyZUZyVDZRSlhneAorajRMSVZEWWJBVHlCT0dHR1o5TUpKKzlSNXhrVX ZCVk5leGp4ZThRWUQxR0pqWndCZVFhUklNOXJYUElub25GClcxTDBDc2xrVGR0MlNHOStYL1F6V 1JKeWQ2bEtRWVR1cnBDNFpxWTZURTg4U1R5NHFTOW5QY25Va0oyWW5GaXoKRTNGRVA3aFVsTnVU ZHNzbS9jMEVwUkJRUS9yako2MGZqa1pvMjV4bkp0dHQxSjZWc1JQaDdYK0V4SVlXemVuNwo2elB ZUTFGWDRzM3JIZnZHenJzWlgySkRsVGl2d3lqYm1CSmdDUmZzWS9SWWs5cTk0VTd2NjFPaEwzY2 dzbjZyCnFpRDZTZEd4dFlPTzV5ZkhCL3ZCbUxyaGMyUm9wbTFXL0t3K2tuMnZEdEJ4YUF1VnBoc 09TanpBYzRPNEZxclcKMlNjbFdWOVdpRkQ4bWlGQlh6WUNHMlVQZDhtbFp3Y3pmY3RaUzBvZTFY Njh6Z29UMTY0UEV2bWVtODFTN2h3cwo1NW9WdnFlbFFndlRlMFIxdS9jd3cyZEo5ZGYrRmd3YXB 1UHZ1RXpiRW1mOS9UbTltdVJHVllhdjhScExhQk11CmZzTldQSDFLYjk0dzl3PT0KPW9qWEoKLS 0tLS1FTkQgUEdQIE1FU1NBR0UtLS0tLQo= X-Developer-Key: i=parent.f@gmail.com; a=openpgp; fpr=07BB034F9E8E3AF0DF4CF7607AE864A1E16FA383 From: Fabien Parent This commit adds a Rust abstraction to write Regulator drivers. Only the features used by the NCV6336 driver were added to this abstraction. Signed-off-by: Fabien Parent --- MAINTAINERS | 1 + rust/bindings/bindings_helper.h | 1 + rust/kernel/regulator.rs | 4 +- rust/kernel/regulator/driver.rs | 850 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 855 insertions(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 90c231f0aa7381aa8d206fb94c5d1f013dfcae41..87da43251bf0f20d2b5831345778ead592c407dc 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -25160,6 +25160,7 @@ F: drivers/regulator/ F: include/dt-bindings/regulator/ F: include/linux/regulator/ F: rust/kernel/regulator.rs +F: rust/kernel/regulator/ K: regulator_get_optional VOLTAGE AND CURRENT REGULATOR IRQ HELPERS diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index b18d772bc3a0e78d749cc9e5ae81a4237a57f8c5..124129daea73c143c919d05814fc02bb4460ddfd 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include diff --git a/rust/kernel/regulator.rs b/rust/kernel/regulator.rs index d695ac955193efcfda62770784a92d70d606b93d..bd8202fe5702b944201e76553b9496e1d42cb429 100644 --- a/rust/kernel/regulator.rs +++ b/rust/kernel/regulator.rs @@ -2,12 +2,14 @@ //! SoC Regulators +pub mod driver; + use crate::{ bindings, error::{code::*, Error, Result}, }; -/// Regulators operating modes +/// [`driver::Device`] operating modes #[derive(Copy, Clone)] #[repr(u32)] pub enum Mode { diff --git a/rust/kernel/regulator/driver.rs b/rust/kernel/regulator/driver.rs new file mode 100644 index 0000000000000000000000000000000000000000..8079ea28fd5bf7b6871a0b1d2cea7a6fffcb43ca --- /dev/null +++ b/rust/kernel/regulator/driver.rs @@ -0,0 +1,850 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! SoC Device Driver Interface +//! +//! C header: [`include/linux/regulator/driver.h`](srctree/include/linux/regulator/driver.h) +//! +//! # Examples +//! +//! ``` +//! use kernel::regulator::driver::{Config, Desc, Device, Driver, Type}; +//! +//! static DESC: Desc = +//! Desc::new::(kernel::c_str!("my-regulator-driver"), Type::Voltage); +//! +//! struct MyDeviceDriver; +//! +//! #[vtable] +//! impl Driver for MyDeviceDriver { +//! type Data = (); +//! +//! // Implement supported `Driver`'s operations here. +//! +//! // Example: +//! fn is_enabled(reg: &mut Device) -> Result { +//! Ok(true) +//! } +//! } +//! +//! impl MyDeviceDriver { +//! fn probe(dev: &mut kernel::device::Device) { +//! let _ = Device::register(dev, &DESC, Config::<::Data>::new(dev, ())); +//! } +//! } +//! ``` + +use crate::{ + device, + error::{code::*, from_err_ptr, from_result, Error, Result}, + macros::vtable, + regulator::Mode, + str::CStr, + types::ForeignOwnable, + ThisModule, +}; +use core::{marker::PhantomData, mem::ManuallyDrop, ptr::NonNull}; + +/// [`Device`]'s status +#[derive(Eq, PartialEq)] +pub enum Status { + /// Device is off + Off, + /// Device is on + On, + /// Device is in an error state + Error, + /// Device is on and in Fast mode + Fast, + /// Device is on and in Normal mode + Normal, + /// Device is on and in Idle mode + Idle, + /// Device is on and in Standby mode + Standby, + /// Device is enabled but not regulating + Bypass, + /// Device is any other status + Undefined, +} + +impl TryFrom for Status { + type Error = Error; + + fn try_from(status: core::ffi::c_uint) -> Result { + match status { + bindings::regulator_status_REGULATOR_STATUS_OFF => Ok(Self::Off), + bindings::regulator_status_REGULATOR_STATUS_ON => Ok(Self::On), + bindings::regulator_status_REGULATOR_STATUS_ERROR => Ok(Self::Error), + bindings::regulator_status_REGULATOR_STATUS_FAST => Ok(Self::Fast), + bindings::regulator_status_REGULATOR_STATUS_NORMAL => Ok(Self::Normal), + bindings::regulator_status_REGULATOR_STATUS_IDLE => Ok(Self::Idle), + bindings::regulator_status_REGULATOR_STATUS_STANDBY => Ok(Self::Standby), + bindings::regulator_status_REGULATOR_STATUS_BYPASS => Ok(Self::Bypass), + bindings::regulator_status_REGULATOR_STATUS_UNDEFINED => Ok(Self::Undefined), + _ => Err(EINVAL), + } + } +} + +impl From for Status { + fn from(mode: Mode) -> Self { + // SAFETY: `regulator_mode_to_status` is a `pure function` that is only doing integer + // to integer conversion, hence this function call is safe. + let status = unsafe { bindings::regulator_mode_to_status(mode as _) }; + + if status < 0 { + Self::Undefined + } else { + Self::try_from(status as core::ffi::c_uint).unwrap_or(Self::Undefined) + } + } +} + +/// [`Device`]'s operations +#[vtable] +pub trait Driver { + /// User data that will be accessible to all operations + type Data: ForeignOwnable + Send + Sync; + + /// Return one of the supported voltages, in microvolt; zero if the selector indicates a + /// voltage that is unusable by the system; or negative errno. Selectors range from zero to one + /// less than the number of voltages supported by the system. + fn list_voltage(_rdev: &mut Device, _selector: u32) -> Result { + Err(ENOTSUPP) + } + + /// Set the voltage for the regulator within the range specified. The driver should select the + /// voltage closest to `min_uv`. + fn set_voltage(_rdev: &mut Device, _min_uv: i32, _max_uv: i32) -> Result { + Err(ENOTSUPP) + } + + /// Set the voltage for the regulator using the specified selector. + fn set_voltage_sel(_rdev: &mut Device, _selector: u32) -> Result { + Err(ENOTSUPP) + } + + /// Convert a voltage into a selector. + fn map_voltage(_rdev: &mut Device, _min_uv: i32, _max_uv: i32) -> Result { + Err(ENOTSUPP) + } + + /// Get the currently configured voltage for the regulator; Returns + /// [`ENOTRECOVERABLE`] if the regulator can't be read at bootup and hasn't been + /// set yet. + fn get_voltage(_rdev: &mut Device) -> Result { + Err(ENOTSUPP) + } + + /// Get the currently configured voltage selector for the regulator; Returns + /// [`ENOTRECOVERABLE`] if the regulator can't be read at bootup and hasn't been + /// set yet. + fn get_voltage_sel(_rdev: &mut Device) -> Result { + Err(ENOTSUPP) + } + + /// Configure a limit for a current-limited regulator. + /// + /// The driver should select the current closest to `max_ua`. + fn set_current_limit(_rdev: &mut Device, _min_ua: i32, _max_ua: i32) -> Result { + Err(ENOTSUPP) + } + + /// Get the configured limit for a current-limited regulator. + fn get_current_limit(_rdev: &mut Device) -> Result { + Err(ENOTSUPP) + } + + /// Enable or disable the active discharge of the regulator. + fn set_active_discharge(_rdev: &mut Device, _enable: bool) -> Result { + Err(ENOTSUPP) + } + + /// Configure the regulator as enabled. + fn enable(_rdev: &mut Device) -> Result { + Err(ENOTSUPP) + } + + /// Configure the regulator as disabled. + fn disable(_rdev: &mut Device) -> Result { + Err(ENOTSUPP) + } + + /// Returns enablement state of the regulator. + fn is_enabled(_rdev: &mut Device) -> Result { + Err(ENOTSUPP) + } + + /// Set the configured operating [`Mode`] for the regulator. + fn set_mode(_rdev: &mut Device, _mode: Mode) -> Result { + Err(ENOTSUPP) + } + + /// Get the configured operating [`Mode`] for the regulator. + fn get_mode(_rdev: &mut Device) -> Mode { + Mode::Invalid + } + + /// Report the regulator [`Status`]. + fn get_status(_rdev: &mut Device) -> Result { + Err(ENOTSUPP) + } + + /// Set the voltage for the regaultor when the system is suspended. + fn set_suspend_voltage(_rdev: &mut Device, _uv: i32) -> Result { + Err(ENOTSUPP) + } + + /// Mark the regulator as enabled when the system is suspended. + fn set_suspend_enable(_rdev: &mut Device) -> Result { + Err(ENOTSUPP) + } + + /// Mark the regulator as disabled when the system is suspended. + fn set_suspend_disable(_rdev: &mut Device) -> Result { + Err(ENOTSUPP) + } + + /// Set the operating mode for the regulator when the system is suspended. + fn set_suspend_mode(_rdev: &mut Device, _mode: Mode) -> Result { + Err(ENOTSUPP) + } +} + +/// [`Device`]'s descriptor +/// +/// # Examples +/// +/// ``` +/// use kernel::{ +/// c_str, +/// device, +/// regulator::driver::{Config, Desc, Device, Driver, Type}, +/// types::ForeignOwnable, +/// }; +/// +/// struct MyDeviceDriver; +/// +/// #[vtable] +/// impl Driver for MyDeviceDriver { +/// type Data = (); +/// } +/// +/// static BUCK_DESC: Desc = Desc::new::(c_str!("my_driver"), Type::Voltage) +/// .with_of_match(c_str!("buck")) +/// .with_enable(0x24, 0x1, 0x1, 0); +/// +/// fn example(dev: &mut device::Device, mut config: Config<::Data>) { +/// let _ = Device::register(dev, &BUCK_DESC, config); +/// } +/// ``` +/// +/// # Invariants +/// +/// `self.0` has always valid data. +pub struct Desc(bindings::regulator_desc); +impl Desc { + /// Create a new [`Device`] descriptor + pub const fn new(name: &'static CStr, reg_type: Type) -> Self { + // SAFETY: `bindings::regulator_desc" is safe to initialize with 0s. + let mut desc: bindings::regulator_desc = unsafe { core::mem::zeroed() }; + desc.name = name.as_char_ptr(); + desc.type_ = match reg_type { + Type::Voltage => bindings::regulator_type_REGULATOR_VOLTAGE, + Type::Current => bindings::regulator_type_REGULATOR_CURRENT, + }; + desc.ops = Adapter::::build(); + Self(desc) + } + + /// Setup the register address, mask, and {en,dis}able values + pub const fn with_enable(mut self, reg: u32, mask: u32, en_val: u32, dis_val: u32) -> Self { + self.0.enable_reg = reg; + self.0.enable_mask = mask; + self.0.enable_val = en_val; + self.0.disable_val = dis_val; + self + } + + /// Setup the register address, mask, and {en,dis}able values. {En,Dis}able values are + /// inverted, i.e. `dis_val` will be use to enable the regulator while `en_val` will be used + /// to disable the regulator. + pub const fn with_inverted_enable( + mut self, + reg: u32, + mask: u32, + en_val: u32, + dis_val: u32, + ) -> Self { + self.0.enable_is_inverted = true; + self.with_enable(reg, mask, en_val, dis_val) + } + + /// Setup the active discharge regiter address, mask, on/off values. + pub const fn with_active_discharge(mut self, reg: u32, mask: u32, on: u32, off: u32) -> Self { + self.0.active_discharge_on = on; + self.0.active_discharge_off = off; + self.0.active_discharge_reg = reg; + self.0.active_discharge_mask = mask; + self + } + + /// Setup the current selection register address, mask, and current table + pub const fn with_csel(mut self, reg: u32, mask: u32, table: &'static [u32]) -> Self { + self.0.csel_reg = reg; + self.0.csel_mask = mask; + self.0.curr_table = table.as_ptr(); + self + } + + /// Voltages are a linear mapping + pub const fn with_linear_mapping( + mut self, + reg: u32, + mask: u32, + min_uv: u32, + uv_step: u32, + n_voltages: u32, + linear_min_sel: u32, + ) -> Self { + self.0.vsel_reg = reg; + self.0.vsel_mask = mask; + self.0.n_voltages = n_voltages; + self.0.min_uV = min_uv; + self.0.uV_step = uv_step; + self.0.linear_min_sel = linear_min_sel; + self + } + + /// Set the regulator owner + pub const fn with_owner(mut self, owner: &'static ThisModule) -> Self { + self.0.owner = owner.as_ptr(); + self + } + + /// Set the name used to identify the regulator in the DT. + pub const fn with_of_match(mut self, of_match: &'static CStr) -> Self { + self.0.of_match = of_match.as_char_ptr(); + self + } +} + +// SAFETY: `Desc` cannot be modified after its declaration and owns its data, hence it is safe +// to share references between threads. +unsafe impl Sync for Desc {} + +/// [`Device`]'s Config +/// +/// # Examples +/// +/// ``` +/// use kernel::regulator::driver::Config; +/// # use kernel::regulator::driver::{Desc, Device}; +/// # use kernel::{device, sync::Arc}; +/// +/// struct DriverData(u32); +/// +/// # fn probe(dev: &device::Device, desc: &'static Desc) -> Result { +/// let config = Config::>::new(dev, Arc::new(DriverData(128), GFP_KERNEL)?); +/// let reg = Device::register(dev, desc, config)?; +/// # Ok(()) +/// # } +/// ``` +/// +/// # Invariants +/// +/// `self.cfg` always hold valid data. +pub struct Config { + cfg: bindings::regulator_config, + data: T, +} + +impl Config { + /// Create a [`Device`] config. + pub fn new(dev: &device::Device, data: T) -> Self { + Self { + cfg: bindings::regulator_config { + dev: dev.as_raw(), + ..Default::default() + }, + data, + } + } +} + +/// Regulator device +/// +/// Abstraction for `struct regulator_dev`. +/// +/// # Invariants +/// +/// * `self.rdev` is valid and non-null. +/// * [`Self`] has owns `self.rdev` memory allocation. +/// * [`Self`] has owns memory of type `T` that can be retrieved through `rdev_get_drvdata`. +pub struct Device { + rdev: NonNull, + _data_type: PhantomData, +} + +impl Device { + /// # Safety + /// + /// `rdev` must be valid and non-null. + unsafe fn from_raw(rdev: *mut bindings::regulator_dev) -> ManuallyDrop { + ManuallyDrop::new(Self { + // SAFETY: The caller of `Self::from_raw` must garantee that `rdev` is non-null and + // valid.. + rdev: unsafe { NonNull::new_unchecked(rdev) }, + _data_type: PhantomData::, + }) + } + + /// register a Regulator driver + pub fn register( + dev: &device::Device, + desc: &'static Desc, + mut config: Config, + ) -> Result { + config.cfg.driver_data = config.data.into_foreign() as _; + + // SAFETY: By the type invariants, we know that `dev.as_ref().as_raw()` is always + // valid and non-null, and the descriptor and config are guaranteed to be valid values, + // hence it is safe to perform the FFI call. + let rdev = from_err_ptr(unsafe { + bindings::regulator_register(dev.as_raw(), &desc.0, &config.cfg) + })?; + + Ok(Self { + rdev: NonNull::new(rdev).ok_or(EINVAL)?, + _data_type: PhantomData::, + }) + } + + /// List voltages when the regulator is using linear mapping + pub fn list_voltage_linear(&self, selector: u32) -> Result { + // SAFETY: By the type invariants, we know that `self.rdev` is always valid and non-null. + // The C function is safe to call with any selector values. + let ret = unsafe { bindings::regulator_list_voltage_linear(self.rdev.as_ptr(), selector) }; + if ret < 0 { + return Err(Error::from_errno(ret)); + } + Ok(ret) + } + + /// Get regulator's name + pub fn get_name(&self) -> &'static CStr { + // SAFETY: By the type invariants, we know that `self.rdev` is always valid and non-null. + // The C function is guaranteed to return a valid string. + unsafe { CStr::from_char_ptr(bindings::rdev_get_name(self.rdev.as_ptr())) } + } + + /// Get regulator's ID + pub fn get_id(&self) -> i32 { + // SAFETY: By the type invariants, we know that `self.rdev` is always valid and non-null. + unsafe { bindings::rdev_get_id(self.rdev.as_ptr()) } + } + + /// Retrieve driver data associated to `self` + pub fn data(&self) -> T::Borrowed<'_> { + // SAFETY: By the type invariants, we know that `self.rdev` is always valid and non-null. + unsafe { T::borrow(bindings::rdev_get_drvdata(self.rdev.as_ptr())) } + } +} + +impl Drop for Device { + fn drop(&mut self) { + // SAFETY: The type invariants guarantee that `self.rdev` is valid and non-null, + // so it is safe to perform the FFI call. + unsafe { bindings::regulator_unregister(self.rdev.as_ptr()) }; + + // SAFETY: The type invariants garuantee that `self.rdev` is valid and non-null, and + // that `rdev_get_drvdata` is valid memory of type `T` stored there by calling + // `T::into_foreign`. + unsafe { T::from_foreign(bindings::rdev_get_drvdata(self.rdev.as_ptr())) }; + } +} + +// SAFETY: `Device` has sole ownership of `self.rdev` and is never read outside of the C +// implementation. It is safe to use it from any thread. +unsafe impl Send for Device {} + +// SAFETY: It is OK to access `Device` through shared references from other threads because +// the C code is insuring proper synchronization of `self.rdev`. +unsafe impl Sync for Device {} + +/// [`Device`] type +pub enum Type { + /// Voltage regulator + Voltage, + /// Current regulator + Current, +} + +pub(crate) struct Adapter(PhantomData); + +impl Adapter { + /// # Safety + /// + /// `rdev` must be non-null and valid. + unsafe extern "C" fn list_voltage_callback( + rdev: *mut bindings::regulator_dev, + selector: core::ffi::c_uint, + ) -> core::ffi::c_int { + // SAFETY: Per this function safety requirements, `rdev` is non-null and valid. + let mut rdev = unsafe { Device::from_raw(rdev) }; + from_result(|| T::list_voltage(&mut rdev, selector)) + } + + /// # Safety + /// + /// `rdev` and `selector` must be non-null and valid. + unsafe extern "C" fn set_voltage_callback( + rdev: *mut bindings::regulator_dev, + min_uv: core::ffi::c_int, + max_uv: core::ffi::c_int, + selector: *mut core::ffi::c_uint, + ) -> core::ffi::c_int { + // SAFETY: Per this function safety requirements, `rdev` is non-null and valid. + let mut rdev = unsafe { Device::from_raw(rdev) }; + match T::set_voltage(&mut rdev, min_uv, max_uv) { + Ok(v) => { + // SAFETY: Per this function safety requirements, `rdev` is non-null and valid. + unsafe { *selector = v as _ }; + 0 + } + Err(e) => e.to_errno(), + } + } + + /// # Safety + /// + /// `rdev` must be non-null and valid. + unsafe extern "C" fn map_voltage_callback( + rdev: *mut bindings::regulator_dev, + min_uv: core::ffi::c_int, + max_uv: core::ffi::c_int, + ) -> core::ffi::c_int { + // SAFETY: Per this function safety requirements, `rdev` is non-null and valid. + let mut rdev = unsafe { Device::from_raw(rdev) }; + from_result(|| T::map_voltage(&mut rdev, min_uv, max_uv)) + } + + /// # Safety + /// + /// `rdev` must be non-null and valid. + unsafe extern "C" fn set_voltage_sel_callback( + rdev: *mut bindings::regulator_dev, + selector: core::ffi::c_uint, + ) -> core::ffi::c_int { + // SAFETY: Per this function safety requirements, `rdev` is non-null and valid. + let mut rdev = unsafe { Device::from_raw(rdev) }; + from_result(|| { + T::set_voltage_sel(&mut rdev, selector)?; + Ok(0) + }) + } + + /// # Safety + /// + /// `rdev` must be non-null and valid. + unsafe extern "C" fn get_voltage_callback( + rdev: *mut bindings::regulator_dev, + ) -> core::ffi::c_int { + // SAFETY: Per this function safety requirements, `rdev` is non-null and valid. + let mut rdev = unsafe { Device::from_raw(rdev) }; + from_result(|| T::get_voltage(&mut rdev)) + } + + /// # Safety + /// + /// `rdev` must be non-null and valid. + unsafe extern "C" fn get_voltage_sel_callback( + rdev: *mut bindings::regulator_dev, + ) -> core::ffi::c_int { + // SAFETY: Per this function safety requirements, `rdev` is non-null and valid. + let mut rdev = unsafe { Device::from_raw(rdev) }; + from_result(|| T::get_voltage_sel(&mut rdev)) + } + + /// # Safety + /// + /// `rdev` must be non-null and valid. + unsafe extern "C" fn set_current_limit_callback( + rdev: *mut bindings::regulator_dev, + min_ua: core::ffi::c_int, + max_ua: core::ffi::c_int, + ) -> core::ffi::c_int { + // SAFETY: Per this function safety requirements, `rdev` is non-null and valid. + let mut rdev = unsafe { Device::from_raw(rdev) }; + from_result(|| { + T::set_current_limit(&mut rdev, min_ua, max_ua)?; + Ok(0) + }) + } + + /// # Safety + /// + /// `rdev` must be non-null and valid. + unsafe extern "C" fn get_current_limit_callback( + rdev: *mut bindings::regulator_dev, + ) -> core::ffi::c_int { + // SAFETY: Per this function safety requirements, `rdev` is non-null and valid. + let mut rdev = unsafe { Device::from_raw(rdev) }; + from_result(|| T::get_current_limit(&mut rdev)) + } + + /// # Safety + /// + /// `rdev` must be non-null and valid. + unsafe extern "C" fn set_active_discharge_callback( + rdev: *mut bindings::regulator_dev, + enable: bool, + ) -> core::ffi::c_int { + // SAFETY: Per this function safety requirements, `rdev` is non-null and valid. + let mut rdev = unsafe { Device::from_raw(rdev) }; + from_result(|| { + T::set_active_discharge(&mut rdev, enable)?; + Ok(0) + }) + } + + /// # Safety + /// + /// `rdev` must be non-null and valid. + unsafe extern "C" fn enable_callback(rdev: *mut bindings::regulator_dev) -> core::ffi::c_int { + // SAFETY: Per this function safety requirements, `rdev` is non-null and valid. + let mut rdev = unsafe { Device::from_raw(rdev) }; + from_result(|| { + T::enable(&mut rdev)?; + Ok(0) + }) + } + + /// # Safety + /// + /// `rdev` must be non-null and valid. + unsafe extern "C" fn disable_callback(rdev: *mut bindings::regulator_dev) -> core::ffi::c_int { + // SAFETY: Per this function safety requirements, `rdev` is non-null and valid. + let mut rdev = unsafe { Device::from_raw(rdev) }; + from_result(|| { + T::disable(&mut rdev)?; + Ok(0) + }) + } + + /// # Safety + /// + /// `rdev` must be non-null and valid. + unsafe extern "C" fn is_enabled_callback( + rdev: *mut bindings::regulator_dev, + ) -> core::ffi::c_int { + // SAFETY: Per this function safety requirements, `rdev` is non-null and valid. + let mut rdev = unsafe { Device::from_raw(rdev) }; + from_result(|| { + T::is_enabled(&mut rdev)?; + Ok(0) + }) + } + + /// # Safety + /// + /// `rdev` must be non-null and valid. + unsafe extern "C" fn set_mode_callback( + rdev: *mut bindings::regulator_dev, + mode: core::ffi::c_uint, + ) -> core::ffi::c_int { + // SAFETY: Per this function safety requirements, `rdev` is non-null and valid. + let mut rdev = unsafe { Device::from_raw(rdev) }; + from_result(|| { + let mode = Mode::try_from(mode).unwrap_or(Mode::Invalid); + T::set_mode(&mut rdev, mode)?; + Ok(0) + }) + } + + /// # Safety + /// + /// `rdev` must be non-null and valid. + unsafe extern "C" fn get_mode_callback( + rdev: *mut bindings::regulator_dev, + ) -> core::ffi::c_uint { + // SAFETY: Per this function safety requirements, `rdev` is non-null and valid. + let mut rdev = unsafe { Device::from_raw(rdev) }; + T::get_mode(&mut rdev) as _ + } + + /// # Safety + /// + /// `rdev` must be non-null and valid. + unsafe extern "C" fn get_status_callback( + rdev: *mut bindings::regulator_dev, + ) -> core::ffi::c_int { + // SAFETY: Per this function safety requirements, `rdev` is non-null and valid. + let mut rdev = unsafe { Device::from_raw(rdev) }; + from_result(|| Ok(T::get_status(&mut rdev)? as _)) + } + + /// # Safety + /// + /// `rdev` must be non-null and valid. + unsafe extern "C" fn set_suspend_voltage_callback( + rdev: *mut bindings::regulator_dev, + uv: core::ffi::c_int, + ) -> core::ffi::c_int { + // SAFETY: Per this function safety requirements, `rdev` is non-null and valid. + let mut rdev = unsafe { Device::from_raw(rdev) }; + from_result(|| { + T::set_suspend_voltage(&mut rdev, uv)?; + Ok(0) + }) + } + + /// # Safety + /// + /// `rdev` must be non-null and valid. + unsafe extern "C" fn set_suspend_enable_callback( + rdev: *mut bindings::regulator_dev, + ) -> core::ffi::c_int { + // SAFETY: Per this function safety requirements, `rdev` is non-null and valid. + let mut rdev = unsafe { Device::from_raw(rdev) }; + from_result(|| { + T::set_suspend_enable(&mut rdev)?; + Ok(0) + }) + } + + /// # Safety + /// + /// `rdev` must be non-null and valid. + unsafe extern "C" fn set_suspend_disable_callback( + rdev: *mut bindings::regulator_dev, + ) -> core::ffi::c_int { + // SAFETY: Per this function safety requirements, `rdev` is non-null and valid. + let mut rdev = unsafe { Device::from_raw(rdev) }; + from_result(|| { + T::set_suspend_disable(&mut rdev)?; + Ok(0) + }) + } + + /// # Safety + /// + /// `rdev` must be non-null and valid. + unsafe extern "C" fn set_suspend_mode_callback( + rdev: *mut bindings::regulator_dev, + mode: core::ffi::c_uint, + ) -> core::ffi::c_int { + // SAFETY: Per this function safety requirements, `rdev` is non-null and valid. + let mut rdev = unsafe { Device::from_raw(rdev) }; + from_result(|| { + let mode = Mode::try_from(mode).unwrap_or(Mode::Invalid); + T::set_suspend_mode(&mut rdev, mode)?; + Ok(0) + }) + } + + const VTABLE: bindings::regulator_ops = bindings::regulator_ops { + list_voltage: if T::HAS_LIST_VOLTAGE { + Some(Adapter::::list_voltage_callback) + } else { + None + }, + set_voltage: if T::HAS_SET_VOLTAGE { + Some(Adapter::::set_voltage_callback) + } else { + None + }, + map_voltage: if T::HAS_MAP_VOLTAGE { + Some(Adapter::::map_voltage_callback) + } else { + None + }, + set_voltage_sel: if T::HAS_SET_VOLTAGE_SEL { + Some(Adapter::::set_voltage_sel_callback) + } else { + None + }, + get_voltage: if T::HAS_GET_VOLTAGE { + Some(Adapter::::get_voltage_callback) + } else { + None + }, + get_voltage_sel: if T::HAS_GET_VOLTAGE_SEL { + Some(Adapter::::get_voltage_sel_callback) + } else { + None + }, + set_current_limit: if T::HAS_SET_CURRENT_LIMIT { + Some(Adapter::::set_current_limit_callback) + } else { + None + }, + get_current_limit: if T::HAS_GET_CURRENT_LIMIT { + Some(Adapter::::get_current_limit_callback) + } else { + None + }, + set_active_discharge: if T::HAS_SET_ACTIVE_DISCHARGE { + Some(Adapter::::set_active_discharge_callback) + } else { + None + }, + enable: if T::HAS_ENABLE { + Some(Adapter::::enable_callback) + } else { + None + }, + disable: if T::HAS_DISABLE { + Some(Adapter::::disable_callback) + } else { + None + }, + is_enabled: if T::HAS_IS_ENABLED { + Some(Adapter::::is_enabled_callback) + } else { + None + }, + set_mode: if T::HAS_SET_MODE { + Some(Adapter::::set_mode_callback) + } else { + None + }, + get_mode: if T::HAS_GET_MODE { + Some(Adapter::::get_mode_callback) + } else { + None + }, + get_status: if T::HAS_GET_STATUS { + Some(Adapter::::get_status_callback) + } else { + None + }, + set_suspend_voltage: if T::HAS_SET_SUSPEND_VOLTAGE { + Some(Adapter::::set_suspend_voltage_callback) + } else { + None + }, + set_suspend_enable: if T::HAS_SET_SUSPEND_ENABLE { + Some(Adapter::::set_suspend_enable_callback) + } else { + None + }, + set_suspend_disable: if T::HAS_SET_SUSPEND_DISABLE { + Some(Adapter::::set_suspend_disable_callback) + } else { + None + }, + set_suspend_mode: if T::HAS_SET_SUSPEND_MODE { + Some(Adapter::::set_suspend_mode_callback) + } else { + None + }, + // SAFETY: The rest is zeroed out to initialize `struct regulator_ops`, + // sets `Option<&F>` to be `None`. + ..unsafe { core::mem::zeroed() } + }; + + const fn build() -> &'static bindings::regulator_ops { + &Self::VTABLE + } +} From patchwork Wed Dec 18 23:36:36 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Fabien Parent X-Patchwork-Id: 852105 Received: from mail-pl1-f174.google.com (mail-pl1-f174.google.com [209.85.214.174]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C1A581FF601; Wed, 18 Dec 2024 23:37:30 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.174 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1734565052; cv=none; b=MeTakeK3VrozZT5MAVze2vZjLG6zQkkzUBLTrTG+ynzHTeObwMpnUnkFa47lWpWHFHDqFX6dDa3xQi7M1pM6/DUsF7OtWUxpKM9A4uFq4XzCkssi0FzzsF/ceSnBtRBeC020wYh3y7fOHiD13VKpIu4R+dOGtfcRWCo2+rRrCn4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1734565052; c=relaxed/simple; bh=Fh2CSoolKTOXoGPkldUPoU6C9epmAg/wwigUZCOyxXY=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=TtwVDZAPu2/IDGhiHfXhCkH1609clZfXc+jSKW38QgtzpGZJ3ag8ZSXNsTjzcMwGb4ov/NNCPtLaECG7dwD/sNUzbyZpyRUVDRv0r5aHq/TgufnSeYO8Xh1xw5bdU4Q7/RRtuMy0+ruKqUpDv3/EJjC83bYcIhoVwB5UHGqdvrc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=nSXAtWoa; arc=none smtp.client-ip=209.85.214.174 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="nSXAtWoa" Received: by mail-pl1-f174.google.com with SMTP id d9443c01a7336-21675fd60feso2538745ad.2; Wed, 18 Dec 2024 15:37:30 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1734565050; x=1735169850; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=8wN7HbnCS89pWKKsi3ngBvHoXn6Ip6orbk5X0jvS7fU=; b=nSXAtWoaFjdTNWYB4qzNAv1FwYDds0I+Sq83g99PZSKgc2U+u7y16sG+I8wiVwWfLt /9h3KbYlFCVPFsABNadWebVUvHlswYE3wJfdP3RYgLHDS58YzuwyuvwxkgQyf3C39ANO d7Ml7ME4K7Uv2X6JjfwFRXm8cCOdhAfuu6RbBK8tsmxW7ujZHujTv+5fb1Gn4zZD99FK AJobq7QQ/Ma3+Q83KhOnzOCZPd6Cn/Pj1bk5QKw+pwyrGhe+1wMy7Ryl72d/1ZnxWVJQ EdQ3i9v2EnkR0avtCtrAyiZciM0vRDZyt7oPpLxxpCKv2AWLvwOo729PbVA0mD26wZzf OB7g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1734565050; x=1735169850; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=8wN7HbnCS89pWKKsi3ngBvHoXn6Ip6orbk5X0jvS7fU=; b=COiT8ADGU92gKu+RxxmAchRAI3Wfnd6kO0XZjuP2QfQR2ONrRNTCurx+CwgE4dvOIO TZivCn3jQw0zpWFbI592GtaLe1SWh5kGXH7OfRkdM8JcClu6IK4/lfL3MP1d8/K4vEgM 8YmTUjS7QdfWWTOzCjhWBHuMici80mjagag8CZbjvafhkxmEx54NRquhgsvYHlyNbefq c+xEcaMQug5162GGMnj5S+4l8k0hYKhMsMXuGCw7yOq/1XNrCDH+3/MItASit7IrV2N/ 0bqRf7p5HG3M18e7wboxoFUxDLqbwuhYgDoBCU0ex2hG9nVNkA5UWZEqKu/iOHQxUlHG Mn5A== X-Forwarded-Encrypted: i=1; AJvYcCVJFHmcxZcE4oQHgKsAwX3SMRhJ1Q6noUwSHhpNCpwu6wG+fydzZcEHelNQuD3oN/dc++QRvO1b9+7C5cPlqgw=@vger.kernel.org, AJvYcCVQ+qOz9h4qLbRczCx8PCrmkP3fKvGNbNzmrjxMHTfcW6vTsSzw/GoYWiPLnxAfHRG+RGKTQxADEnTs+kA1@vger.kernel.org, AJvYcCVgSzUG8PSVBO/eJ7y0I2poNUKn2XPi1rjg9kzQQVtpHFxS8KlO7HgxKtwaAgDkRX5UL8NX1n6uxa4up9Iw@vger.kernel.org, AJvYcCXcvrJrTpTxQ8VkMEA+2tflYx0bGH107+mk3y3Kt1T9T1YDdl5Yw242D3jBIFZXtKsCS+dePlXZrpDQ@vger.kernel.org X-Gm-Message-State: AOJu0YxNC8O9iD0VlBgyI+NJE+DkVlHSgyXH5oBM9+G5JAGJ09Br4+eI 4yygbA2SLdLdT1CwNvHZIhF4W+Jn8YReoBETRbYfkOkkELDvrT3O X-Gm-Gg: ASbGncvp5qtaeRTlliaMR6e62lHFfBtgWuTaG9EiFDYz7apwti4pgfAqVWU4FKyZmXa PuJWXsR3Vi6Odu2CYqkAg6RFwhmF3c8yVa1WqfJ44hafMdT+KsBwUxs8lrgYujqTGHm+ysxBYQj hp33KN9fOT2dsRheuuFM7IyEykQIF84ju2D8rkqtXfGL0eCwICfyHfxmqA9Rvly5gF+f+/Ticj1 RbVGuNfL7CDw7ohZZ7PdhJAIPVkPOyXoBNven0Z21Vxz08vArNG4BZi X-Google-Smtp-Source: AGHT+IE1ZKhghi3VMTfaw6MZhoWc6MdtkU4Axg6ZEskM2qO36+BshJ8EHDAigxhQEPUAhCQGDSIl5g== X-Received: by 2002:a17:903:2a8c:b0:219:d28a:ca23 with SMTP id d9443c01a7336-219d28acce5mr51876255ad.36.1734565050036; Wed, 18 Dec 2024 15:37:30 -0800 (PST) Received: from [127.0.1.1] ([2001:569:fcea:600:2e06:283e:5daa:4d0f]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-219dc9f6967sm802335ad.214.2024.12.18.15.37.28 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 18 Dec 2024 15:37:28 -0800 (PST) From: Fabien Parent Date: Wed, 18 Dec 2024 15:36:36 -0800 Subject: [PATCH 6/9] rust: regulator: add support for regmap Precedence: bulk X-Mailing-List: linux-arm-msm@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20241218-ncv6336-v1-6-b8d973747f7a@gmail.com> References: <20241218-ncv6336-v1-0-b8d973747f7a@gmail.com> In-Reply-To: <20241218-ncv6336-v1-0-b8d973747f7a@gmail.com> To: Rob Herring , Saravana Kannan , Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Greg Kroah-Hartman , "Rafael J. Wysocki" , Wolfram Sang , Mark Brown , Liam Girdwood , Krzysztof Kozlowski , Conor Dooley , Bjorn Andersson , Konrad Dybcio , Fabien Parent Cc: devicetree@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-i2c@vger.kernel.org, linux-arm-msm@vger.kernel.org, vinod.koul@linaro.org, Fabien Parent X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=8661; i=parent.f@gmail.com; h=from:subject:message-id; bh=qRS9r1n2/HjY8gk2eXA0ISjuhS69yAMbdqsctAPosAU=; b=LS0tLS1CRUdJTiBQR1AgTUVTU0FHRS0tLS0tCgpvd0o0bkFGdEFwTDlrQTBEQUFvQmlKc2d4Q kw5cktRQnl5WmlBR2RqWEtlV2wyVG1YTHU5UU1EeTllWXplbmhQClNsN1gyQ25vb1JtRWR2UTFG MS9VTG9rQ013UUFBUW9BSFJZaEJONk0wZElMeXpzd3JmRnNxWWliSU1RUy9heWsKQlFKblkxeW5 BQW9KRUlpYklNUVMvYXlrdXFVUUFLSklObTFxNGtVTXVrZnRoWi9qQ3hMTTU4MjFXNXJxRElOUg pwamQvamJpRURZZmZneTFHdHJZb1VQbVNiVDNDY0ZUdFQ0cy9CZmJsOGJLMldTNysrcFlWS2pJU Fd5dWZhVUJrCkpOUVVXNnBTNWpjMjI5WENranlMR1BZNjFBN2RYSmpJWVpDNU91YzBpNTBrUXB6 ajlNZ204WFJENlVCaU1xOWYKbDZwVXZjK3l3am5CdjlzSlZoRVJSdmI0R3RlWmpxSHZUZXV6bnJ ZUnB4OGlPSHoxVzB2aEl1clpkNFdjY3hhbApJWG1uSlY1elZsUzNiMzZPZm9aV2gvVFRJSzZuTi 92OXZ0UldXUG1HZDg5QjhlRWo5YzZJZDFKZFBpVEV0TnN5CklTOXZhY0RndHBzUHc3V2VvM0p1S CtVTWRSVlhhV05udXVhaUp0ZU5idjRtd0RGa3ljQmVMZEw0eFdYWW9YalIKMXlyOFFzZUowdXkz NFVpTzlPbmkvalhWaUVtVmc0U3lnVWNOc1FBanZkNWd1dWZmU1RiSVQxbmZLODVKWEh5dApRUzN ZVGlVZTB4cFQyUXJMTXlpMHJyR1l3NmZiS28zT0wxeDM0UUtJd2ZFbk1TbWhhY04vaXMzMnBxMn JJc3BoCkxpdmpsSmFaQUxKZFMzSEFaYzh1ZlRDbHhHMG9kUTJpck0wUHJSenlMMkxwK0pibnNPZ EwvZUw1NC9TTEhUeWkKanV3OW1ZNFFXYUtmdjduUnRmSGVJQ0FxaWVTNDBEVzdXQjFPUFVlMjN0 d1J0WjA3ZXoyYWpnbUFLY2d5MkhwdQpEZTFtdFVjMDVwL3d5S1ZyS0RkMld4QzJZVmVKbUhrOTg zbmJBR0ZBblZMSEx4OUt3VGhCcE1tZXNYekFpM0tFCk5uamtCemRuL1owM0ZnPT0KPS9vb2kKLS 0tLS1FTkQgUEdQIE1FU1NBR0UtLS0tLQo= X-Developer-Key: i=parent.f@gmail.com; a=openpgp; fpr=07BB034F9E8E3AF0DF4CF7607AE864A1E16FA383 From: Fabien Parent The regulator API offer many helpers to help simplifies drivers that use the regmap API. This commit adds partial support for it, only the function needed by the NCV6336 driver were added. Signed-off-by: Fabien Parent --- rust/kernel/regulator/driver.rs | 141 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 141 insertions(+) diff --git a/rust/kernel/regulator/driver.rs b/rust/kernel/regulator/driver.rs index 8079ea28fd5bf7b6871a0b1d2cea7a6fffcb43ca..e79e93122b094e5e086780f18ecdac5105d07153 100644 --- a/rust/kernel/regulator/driver.rs +++ b/rust/kernel/regulator/driver.rs @@ -37,13 +37,27 @@ device, error::{code::*, from_err_ptr, from_result, Error, Result}, macros::vtable, + private::Sealed, regulator::Mode, str::CStr, + sync::Arc, types::ForeignOwnable, ThisModule, }; +#[cfg(CONFIG_REGMAP)] +use crate::{error::to_result, regmap::Regmap}; use core::{marker::PhantomData, mem::ManuallyDrop, ptr::NonNull}; +#[cfg(not(CONFIG_REGMAP))] +struct Regmap; + +#[cfg(not(CONFIG_REGMAP))] +impl Regmap { + pub fn as_raw(&self) -> *mut bindings::regmap { + core::ptr::null_mut() + } +} + /// [`Device`]'s status #[derive(Eq, PartialEq)] pub enum Status { @@ -357,6 +371,7 @@ unsafe impl Sync for Desc {} pub struct Config { cfg: bindings::regulator_config, data: T, + regmap: Option>, } impl Config { @@ -368,8 +383,16 @@ pub fn new(dev: &device::Device, data: T) -> Self { ..Default::default() }, data, + regmap: None, } } + + /// Assign a regmap device to the config + #[cfg(CONFIG_REGMAP)] + pub fn with_regmap(mut self, regmap: Arc) -> Self { + self.regmap = Some(regmap); + self + } } /// Regulator device @@ -384,6 +407,9 @@ pub fn new(dev: &device::Device, data: T) -> Self { pub struct Device { rdev: NonNull, _data_type: PhantomData, + // The C regmap API does not keep reference count. Keep a reference to the regmap pointer that + // is shared to the C regulator API. + _regmap: Option>, } impl Device { @@ -396,6 +422,7 @@ unsafe fn from_raw(rdev: *mut bindings::regulator_dev) -> ManuallyDrop { // valid.. rdev: unsafe { NonNull::new_unchecked(rdev) }, _data_type: PhantomData::, + _regmap: None, }) } @@ -407,6 +434,11 @@ pub fn register( ) -> Result { config.cfg.driver_data = config.data.into_foreign() as _; + let regmap = config.regmap.take(); + if let Some(regmap) = ®map { + config.cfg.regmap = regmap.as_raw() as _; + }; + // SAFETY: By the type invariants, we know that `dev.as_ref().as_raw()` is always // valid and non-null, and the descriptor and config are guaranteed to be valid values, // hence it is safe to perform the FFI call. @@ -417,6 +449,7 @@ pub fn register( Ok(Self { rdev: NonNull::new(rdev).ok_or(EINVAL)?, _data_type: PhantomData::, + _regmap: regmap, }) } @@ -472,6 +505,114 @@ unsafe impl Send for Device {} // the C code is insuring proper synchronization of `self.rdev`. unsafe impl Sync for Device {} +impl Sealed for Device {} + +/// Helper functions to implement some of the [`Driver`] trait methods using [`Regmap`]. +/// +/// This trait is implemented by [`Device`] and is Sealed to prevent +/// to be implemented by anyone else. +#[cfg(CONFIG_REGMAP)] +pub trait RegmapHelpers: Sealed { + /// Implementation of [`Driver::get_voltage_sel`] using [`Regmap`]. + fn get_voltage_sel_regmap(&self) -> Result; + /// Implementation of [`Driver::set_voltage_sel`] using [`Regmap`]. + fn set_voltage_sel_regmap(&self, sel: u32) -> Result; + + /// Implementation of [`Driver::is_enabled`] using [`Regmap`]. + /// + /// [`Desc::with_enable`] or [`Desc::with_inverted_enable`] must have been called + /// to setup the fields required by regmap. + fn is_enabled_regmap(&self) -> Result; + /// Implementation of [`Driver::enable`] using [`Regmap`]. + /// + /// [`Desc::with_enable`] or [`Desc::with_inverted_enable`] must have been called + /// to setup the fields required by regmap. + fn enable_regmap(&self) -> Result; + /// Implementation of [`Driver::disable`] using [`Regmap`]. + /// + /// [`Desc::with_enable`] or [`Desc::with_inverted_enable`] must have been called + /// to setup the fields required by regmap. + fn disable_regmap(&self) -> Result; + + /// Implementation of [`Driver::set_active_discharge`] using [`Regmap`]. + /// + /// [`Desc::with_active_discharge`] must have been called to setup the fields required + /// by regmap. + fn set_active_discharge_regmap(&self, enable: bool) -> Result; + + /// Implementation of [`Driver::set_current_limit`] using [`Regmap`]. + fn set_current_limit_regmap(&self, min_ua: i32, max_ua: i32) -> Result; + /// Implementation of [`Driver::get_current_limit`] using [`Regmap`]. + fn get_current_limit_regmap(&self) -> Result; +} + +#[cfg(CONFIG_REGMAP)] +impl RegmapHelpers for Device { + fn get_voltage_sel_regmap(&self) -> Result { + // SAFETY: The type invariants guarantee that `self.rdev` is valid and non-null, + // so it is safe to perform the FFI call. + let ret = unsafe { bindings::regulator_get_voltage_sel_regmap(self.rdev.as_ptr()) }; + if ret < 0 { + return Err(Error::from_errno(ret)); + } + Ok(ret) + } + + fn set_voltage_sel_regmap(&self, sel: u32) -> Result { + // SAFETY: The type invariants guarantee that `self.rdev` is valid and non-null, + // so it is safe to perform the FFI call. + to_result(unsafe { bindings::regulator_set_voltage_sel_regmap(self.rdev.as_ptr(), sel) }) + } + + fn is_enabled_regmap(&self) -> Result { + // SAFETY: The type invariants guarantee that `self.rdev` is valid and non-null, + // so it is safe to perform the FFI call. + let ret = unsafe { bindings::regulator_is_enabled_regmap(self.rdev.as_ptr()) }; + if ret < 0 { + return Err(Error::from_errno(ret)); + } + Ok(ret > 0) + } + + fn enable_regmap(&self) -> Result { + // SAFETY: The type invariants guarantee that `self.rdev` is valid and non-null, + // so it is safe to perform the FFI call. + to_result(unsafe { bindings::regulator_enable_regmap(self.rdev.as_ptr()) }) + } + + fn disable_regmap(&self) -> Result { + // SAFETY: The type invariants guarantee that `self.rdev` is valid and non-null, + // so it is safe to perform the FFI call. + to_result(unsafe { bindings::regulator_disable_regmap(self.rdev.as_ptr()) }) + } + + fn set_active_discharge_regmap(&self, enable: bool) -> Result { + // SAFETY: The type invariants guarantee that `self.rdev` is valid and non-null, + // so it is safe to perform the FFI call. + to_result(unsafe { + bindings::regulator_set_active_discharge_regmap(self.rdev.as_ptr(), enable) + }) + } + + fn set_current_limit_regmap(&self, min_ua: i32, max_ua: i32) -> Result { + // SAFETY: The type invariants guarantee that `self.rdev` is valid and non-null, + // so it is safe to perform the FFI call. + to_result(unsafe { + bindings::regulator_set_current_limit_regmap(self.rdev.as_ptr(), min_ua, max_ua) + }) + } + + fn get_current_limit_regmap(&self) -> Result { + // SAFETY: The type invariants guarantee that `self.rdev` is valid and non-null, + // so it is safe to perform the FFI call. + let ret = unsafe { bindings::regulator_get_current_limit_regmap(self.rdev.as_ptr()) }; + if ret < 0 { + return Err(Error::from_errno(ret)); + } + Ok(ret) + } +} + /// [`Device`] type pub enum Type { /// Voltage regulator From patchwork Wed Dec 18 23:36:37 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Fabien Parent X-Patchwork-Id: 851766 Received: from mail-pl1-f178.google.com (mail-pl1-f178.google.com [209.85.214.178]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B474C1FDE29; Wed, 18 Dec 2024 23:37:32 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.178 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1734565054; cv=none; b=uBW6pbswzo1AALANqC5HaEpi6AnIskflevuCoEYWk5/YWtEAcJuYdMXdebgVLRY8Wapl8l5Ml2kMboH//VUMPHWu05SsCUQEO5ULBDhhjcYCGniMLm/ZPe8Zp/I+NksmqIvk8oD0p9s+KRHk1zpKW2N+B25LvWl48OcC4Xejdbg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1734565054; c=relaxed/simple; bh=t2TsMiP3ilVlrTWn1B/PYvmpFe1OKGm02kYl5hvd16o=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=Vh7ennABYqjEp/B8pJJIPoMF35y5oovMQQVET3GN+g4huCqVdsHXaBW3p5eTLwPB/KfLj/YARt0iRMLFlXw9YzqFFA9bKeThA/d5uFRCYN8oUjsZVmiCp4PXwBlz7YCy59jWfKP6tvtfHdO1NNgKIPZu8L3YaztsNya8rdrqaDY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=XSo6p10t; arc=none smtp.client-ip=209.85.214.178 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="XSo6p10t" Received: by mail-pl1-f178.google.com with SMTP id d9443c01a7336-21670dce0a7so2627735ad.1; Wed, 18 Dec 2024 15:37:32 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1734565052; x=1735169852; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=MwKgzZr86c7oRTHczyCJoBnZeP8Mi7cizIcfC7/y0QE=; b=XSo6p10t91g5ySCuLaj+Ufz6kt6gR8ovg6xb6d0qtYtRYptc8gRGuHLU4qA8CoLTxD fHa0i8BmzNm7gsPUV5MCTXaqNrsf7YebL8LvWaCP3oephDp5zJTg4IceTd02ieqGU7TD 6Tj/f+o3Z9N2dL6p94sAXiVaP3CrhsejzfwkPD0ODMNWGZTx1+N1rZ0wMviEfErUQytZ oOnKeNeJykMM0Wq6MBsPx7NZShgRQD3nh3b163bho0rY29N5xwMdZ0RahXm+bsmsv38a xO0xicOfrjReUGsYqv4wmt0I32nJ+/Drafc1Eg1Mv+JPICqwQmzlMMGGRHE+5oo6qtwM Jl8g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1734565052; x=1735169852; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=MwKgzZr86c7oRTHczyCJoBnZeP8Mi7cizIcfC7/y0QE=; b=nqlH/2mbsk2Yl0tC+zplMopHhCOENHXNVsIRVYsEi/TUEmZD9272j1F+95DC+WEjK2 OQJCisUbzVWyohG5yv9BHmGK8gm9c83jvBn3yMeMHZ1ynIqRnYroQN5vaUyDxEr/8cCf uoDcPSszNOJuxziLUx8t5YCJfCX6ZUmQjbBh8mRvyvGBOzOh0wJVOGrWmHpOgAKYTiPj uWnk5BIDEfdsnMA1MYmRgPaVJMnLUgSbf4zJyVdI86whHj7yOkGyONIQmGpgS+xv8evY Z1cflYF2SRg6Nj+cEd3uK72tW2Uh/q9F/i8qEi69cTkT7uYKUGgpQbO9GcTiWoaOSk1h pD7w== X-Forwarded-Encrypted: i=1; AJvYcCVx1hld+HvzBFVpIkZ4dqRBumOV5iWdkQWyk/JZbi8TVmOQmS9mytByc2XiipIovkfJgl6yx8HYpSjr@vger.kernel.org, AJvYcCW2iQglCqozzDAJGLANp9sqPX50USp0w+Na/LbwGLNQ5nkexSYcvKDt2H7kcV6UID811gk5bg3mTXYEwLnp@vger.kernel.org, AJvYcCWkAH3Qa4kt97Ue/iBixerkQntGr+FgEFIzQhlK4Mxv2z7VTz8EBDPjGm4dgQGRzNPD8jIliL63+rWxg9NxRuo=@vger.kernel.org, AJvYcCXjetXlse6oMYDK/huyceNX8dGAb/ezrLZywC4wqEzt73WFv16cVYYqFOXk4CQqKC1ytZzRU1lFHRzE6D55@vger.kernel.org X-Gm-Message-State: AOJu0YzOLrnvLqak+sS0GH0UBxMX3nX6pPnb2r1akbdYAvzarYCwITix CG2hZmphsodL7c3YDhmY6Nm6Jbsv4wrNR3QEGjb39YgXap2mBN1p X-Gm-Gg: ASbGncsuoUnzuIcA9jAClC2y413tysJ+7mmOt1z8vo2QtRyaprXhKi2GUdvWWLjpA1P bhnmzGt6EAePUYD2cILUVvZDKBF+KL+jTEiyes7GpgjrEbb4A1U15BVhuTrEwWmoV0EnYQbP4fI aIS+lKtmllcKwZqp0d/p8081RXZjPR9sOaAUXVEWnwuW7l8K+38CM2gYnQ7/Fz7PsteyEEITKIt kLZX045Ha8sh/ZNv/kPkd8Mo1EtgvyxgFGGZnLmYl0pRoRr6aznHuSn X-Google-Smtp-Source: AGHT+IEXFMesvXJhtouY2pEYJ1RzXyDqbP8BeiasyhxSyJNIN4LRKtgRxxtfBkdvFwD8b4B4U9eHUw== X-Received: by 2002:a17:902:d2ca:b0:215:8695:ef91 with SMTP id d9443c01a7336-219d965c6c8mr18160005ad.6.1734565051971; Wed, 18 Dec 2024 15:37:31 -0800 (PST) Received: from [127.0.1.1] ([2001:569:fcea:600:2e06:283e:5daa:4d0f]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-219dc9f6967sm802335ad.214.2024.12.18.15.37.30 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 18 Dec 2024 15:37:31 -0800 (PST) From: Fabien Parent Date: Wed, 18 Dec 2024 15:36:37 -0800 Subject: [PATCH 7/9] dt-bindings: regulator: add binding for ncv6336 regulator Precedence: bulk X-Mailing-List: linux-arm-msm@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20241218-ncv6336-v1-7-b8d973747f7a@gmail.com> References: <20241218-ncv6336-v1-0-b8d973747f7a@gmail.com> In-Reply-To: <20241218-ncv6336-v1-0-b8d973747f7a@gmail.com> To: Rob Herring , Saravana Kannan , Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Greg Kroah-Hartman , "Rafael J. Wysocki" , Wolfram Sang , Mark Brown , Liam Girdwood , Krzysztof Kozlowski , Conor Dooley , Bjorn Andersson , Konrad Dybcio , Fabien Parent Cc: devicetree@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-i2c@vger.kernel.org, linux-arm-msm@vger.kernel.org, vinod.koul@linaro.org, Fabien Parent X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=1743; i=parent.f@gmail.com; h=from:subject:message-id; bh=ggkzqiAnE/aO71fqKd9R2qFTZ9vozMA5CH3AStZDGIU=; b=LS0tLS1CRUdJTiBQR1AgTUVTU0FHRS0tLS0tCgpvd0o0bkFGdEFwTDlrQTBEQUFvQmlKc2d4Q kw5cktRQnl5WmlBR2RqWEtoQkhva1UrTDJTS1ZBakR6ckswbXYwCjZ0Nnh1QWhmeER3ZWZnTmdP WFYvRklrQ013UUFBUW9BSFJZaEJONk0wZElMeXpzd3JmRnNxWWliSU1RUy9heWsKQlFKblkxeW9 BQW9KRUlpYklNUVMvYXlrM05VUC9qaWhsTjBZOHlEeEZqaUozTHNMS3ZsMk94R3NCSkxYOHFORw o0SzhMaWJ1RTQyT1FZemY0aEowcS90MTZSK0dlU1ZJNXI3YTlFWkR4ZGF4Sk50cVNDakthWFJyS jNyL21nL0wxCkJNb1JDZ25IZDlzWDFuamMxZmZvbERXR21uaGdlRDg4bEcwNjNFUnRpMDBiSUlO aUxSL0YrZ0cwQTJ5ZDFWRmQKcEl0VDlMQXNDbGlGeGtyT0tXZWpKQzFUVklUK0Frd2hQZmMwUnR 2ZCs5eWRENHMrZzE4c3JMdTNyYUZVTUs1QwpVcXBxMnNFc041a29yNXNvSkVKTkVzMDRZN2IzSk xxK1JPeWdrYUV2SzhGSlVOS3ByVXduZXA4UnA5VGRzNXJDCmRGZW5vdVpkRXM3cXBFaXQ4OHlYV W04czk0MmhnSlo5TTVHcFdSNjJodVo1S050MWUwYlE4T3NPUW0xT1NYc3AKb2NZem9tMkZUL05V eUYwVEtCdDYzbTFVa3NkdEZnT2taQ2ZpeUgrS0daQ3dZWVJYbDVqTlhZcmJsR3pVRU9nLwpBNFd 3TU5pUGY3aXZ5MWs2VVc5d2MrQ0w4Tk9mSUdGVDVkQXFVRDFLdVhRNnJSSitwNFR6Q0lkMENqZF NDQSs5CnY5WFZNRGNkdzFWVHNNaTYyTk9GS0R1R0Ura0FJYThnVEdITmYveVVsYysrV09SalJCa UIvbHNnWW9TMWhQZ2oKRGxrMTBDOGxheTdoL2RrcUd1c2lpWFo5L3l0ODZwRkNnWm1tSlBaZlUv dnhnVjhmN0tWZzBmR2tLWjd3K3o2aAp5byt1eU1mbTA4TFM5Z3MxTVFzQ3RqMm9HWG9FU3drcnJ ZcFBaWmRmVTdOZCs2MUJ5M3QzdHVSVTRSK0N5NjJYClE1SXEvS2VvWExZc2JRPT0KPXdKdGUKLS 0tLS1FTkQgUEdQIE1FU1NBR0UtLS0tLQo= X-Developer-Key: i=parent.f@gmail.com; a=openpgp; fpr=07BB034F9E8E3AF0DF4CF7607AE864A1E16FA383 From: Fabien Parent Add binding documentation for the Onsemi NCV6336 regulator. Signed-off-by: Fabien Parent --- .../bindings/regulator/onnn,ncv6336.yaml | 54 ++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/Documentation/devicetree/bindings/regulator/onnn,ncv6336.yaml b/Documentation/devicetree/bindings/regulator/onnn,ncv6336.yaml new file mode 100644 index 0000000000000000000000000000000000000000..c69d126cab33668febe18e77bb34bd4bef52c993 --- /dev/null +++ b/Documentation/devicetree/bindings/regulator/onnn,ncv6336.yaml @@ -0,0 +1,54 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/regulator/onnn,ncv6336.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Onsemi NCV6336 Buck converter + +maintainers: + - Fabien Parent + +description: | + The NCV6336 is an I2C programmable BUCK (step-down) converter. + It is designed for mobile power applications. + +properties: + $nodename: + pattern: "regulator@[0-9a-f]{2}" + + compatible: + const: onnn,ncv6336 + + reg: + maxItems: 1 + + buck: + description: buck regulator description + type: object + $ref: regulator.yaml# + unevaluatedProperties: false + +required: + - compatible + - reg + - buck + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + regulator@1c { + compatible = "onnn,ncv6336"; + reg = <0x1c>; + + buck { + regulator-name = "ncv6336,buck"; + }; + }; + }; +... From patchwork Wed Dec 18 23:36:38 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Fabien Parent X-Patchwork-Id: 852104 Received: from mail-pl1-f176.google.com (mail-pl1-f176.google.com [209.85.214.176]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 535762036FB; Wed, 18 Dec 2024 23:37:35 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.176 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1734565057; cv=none; b=BGJO0IN0lwC0WsrAIoNj/bmg+TDk6VEEpILeKzdgo9k52dlgxiGOmi37j55JFSo3m5vbV85VUOGfqeEXFQZ1096AXtOtmJS+wh1tPsMlyFuo4e9A4l6wyS0KpPCqilxm6HE3uHPu66R1XU9HECLPqlVS1BdbgLHrH4HOWTj5ris= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1734565057; c=relaxed/simple; bh=TMU9VZ6PTqivckI1JKN5Ey2ahEr1d5gq+kwxzzwrZms=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=ePGUOTE1GWSWmEOM/jQTKqe+A+l1TcgcIluWywM5vaf/NlzMzAPZlvkP3hD3iCj1hsGYK1PKMcXkgM01koxXdND0q7tivlpAwDc5KZYQgYM5jwaBWzOWuyo263yw8gpzVo/l+GuUSt/+EkMpyZ5RE8cT6d1raOUu0HSD26V6rhc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=Qhj/Eg0i; arc=none smtp.client-ip=209.85.214.176 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="Qhj/Eg0i" Received: by mail-pl1-f176.google.com with SMTP id d9443c01a7336-2166651f752so2478125ad.3; Wed, 18 Dec 2024 15:37:35 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1734565055; x=1735169855; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=hd/lAaOH2L0N9wnqk4iURGlewLV+M/XaIIsQzSkZkqM=; b=Qhj/Eg0iaNt79GqkTjc1SPoir9rJK8ih3icDDVJhOIS0Pk9oQQ8nb4s1hbPhGKrBPs weo6XOimWpDTXlOzFZlAp5ufFx/YIRjAsJ/qfiDleeS0WrNDmo2yvAeiYeSZk3YdFkRT akY7qQgYeV8X0b9zlvNE/yK+w859YoEuG+IhtrB8SsmShJ9sJc+edKleh7YGpd05av9p r69h9sQ2AZ3FAH6rFDgW2c2cJBRv+Ex9AFgAewyCbhAZORuzEf0wA9GDsqirzgAH3Oh+ 0a4kY2/f5lkMf45kWy02ThR8L5XaPEOz2veSSlbsMmToDo+mv4ebFmss+1FjomS4pG/F +hMA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1734565055; x=1735169855; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=hd/lAaOH2L0N9wnqk4iURGlewLV+M/XaIIsQzSkZkqM=; b=pbgBs74PoCdR92JEXOs+NTZNxPksFSA0k8nts4rPbJBW7c6+CBsMdtuv8tjeArK/mV m8HUCju4QOk77aG4+Pv6LcSdrpJXA9MgDOsSqIwbY20ucSBT3yJRiYnInJtoA+cdBfxl f/XMYODm9xE6ORopXVMOfPYpGKFvHnYkQteYhUzbpTIAkmgGK41SQuJ/AM7ADsZIDI9b Y/hg2+BFI9xmn2ZIntGmX3a7Quu+UetCfwNJOSoRgI9kHsgh4VAB7+kDZWxB0PDUQWiZ aedLMQECHIKK704bC3yLhDNS1UnhGW5f3WPbEl6Zyhaltv1GLU2mPnoHLO5t7oxmZqYB z/Fw== X-Forwarded-Encrypted: i=1; AJvYcCULGIyDwSvuutF7hEGYKsRoUsttNaXbKsReNyiGrUHBYFZ+DCd1hKdaauDntQZGQlJxOOVJ8fqjoXWB@vger.kernel.org, AJvYcCURB2OogKJF4NYqOq7L0fE7JenYggMHsOVVT6jHpHpaKMuby1grlhemk8d2F++QbePAypSBWq5ud+FELqXf@vger.kernel.org, AJvYcCUlwFZDUmuj/9ZLLomws5QpPI6tp4Tyzv6gtLe8JLRGml1MKBMj2guLJqNRNkXC49zvjf2E928mNsTNigQ+@vger.kernel.org, AJvYcCXzLvncpw5Bd81WD/RbXT6cQlKjtnT2AJavZyVojpnhNPaIetN5e6rND7ZiRE3V83wMtBKHBJMWT3mqB8QQSM4=@vger.kernel.org X-Gm-Message-State: AOJu0YxKnTvFgcgzIbsNUvxJjPExBv/vg0qlycM4nE/ML+B7oLfxpbHC aF1vizH/gbf9zK1b/+For9nYSl2E7gbulmzddlb5r3N27L2QRsPh X-Gm-Gg: ASbGnct7D11kvEvgjzO4tA4RokcJP3tAWpDIEnbLqAsmL67XccCgjMsbWF6tXMGMhAN eagqy/XvdacYovoHDNzjKr/o5wvumqIH7wZpe7JFHXRML49fJ09SWHgs0+83BWWYQfzGk04xyQW j/H6W9tyjVtja69OT4H3x0H9ch98VaAE7ZRX9uqfMqxV8/wr0YJ/yHCqJvoxq/Vuu5teuVt/P1f hXs//MOzfPSbCRsIXWb50QEZ1x4csuHJdhCG0H0Rwef/m88FwBSEtUl X-Google-Smtp-Source: AGHT+IF+WtFTEtPNMBwgZfofryr+jcualMK5NtwUVd+BMo91Vigzb5En+7ZipV2RAxZZ7XVsyY7e7w== X-Received: by 2002:a17:902:d2c5:b0:216:48f4:4f20 with SMTP id d9443c01a7336-218d70dc236mr67176745ad.16.1734565054640; Wed, 18 Dec 2024 15:37:34 -0800 (PST) Received: from [127.0.1.1] ([2001:569:fcea:600:2e06:283e:5daa:4d0f]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-219dc9f6967sm802335ad.214.2024.12.18.15.37.32 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 18 Dec 2024 15:37:33 -0800 (PST) From: Fabien Parent Date: Wed, 18 Dec 2024 15:36:38 -0800 Subject: [PATCH 8/9] regulator: add driver for ncv6336 regulator Precedence: bulk X-Mailing-List: linux-arm-msm@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20241218-ncv6336-v1-8-b8d973747f7a@gmail.com> References: <20241218-ncv6336-v1-0-b8d973747f7a@gmail.com> In-Reply-To: <20241218-ncv6336-v1-0-b8d973747f7a@gmail.com> To: Rob Herring , Saravana Kannan , Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Greg Kroah-Hartman , "Rafael J. Wysocki" , Wolfram Sang , Mark Brown , Liam Girdwood , Krzysztof Kozlowski , Conor Dooley , Bjorn Andersson , Konrad Dybcio , Fabien Parent Cc: devicetree@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-i2c@vger.kernel.org, linux-arm-msm@vger.kernel.org, vinod.koul@linaro.org, Fabien Parent X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=9527; i=parent.f@gmail.com; h=from:subject:message-id; bh=vtb7NM/ARwqwFSM2X0HpnsIhUiEzaB5FxGPUahmOA/4=; b=LS0tLS1CRUdJTiBQR1AgTUVTU0FHRS0tLS0tCgpvd0o0bkFGdEFwTDlrQTBEQUFvQmlKc2d4Q kw5cktRQnl5WmlBR2RqWEtsT0lORjRKUHRneUsyUk0zYzFON0FQCkZBait6S2hIMkNTRVdmUS9s N3pjRDRrQ013UUFBUW9BSFJZaEJONk0wZElMeXpzd3JmRnNxWWliSU1RUy9heWsKQlFKblkxeXB BQW9KRUlpYklNUVMvYXlreE80UC9SRkNkNFBTQkNpTjRCTHFPRzgyNk5nT2N5VmRpZFhJc0VObQ p1NVZ4TGM2MFR2RTJQenNaWmNLMllGRzZwSDNRa3FKTHRqaHFDMStWWmZOOVF1bTVwc3FrL3kvV 1diTjNpalpEClV6SitGTkFpaFRFaWJMSU5SWFo5VFJXOFFUeGlvVWJYMUNqUFNhYURFTDRNOFdw dzcxYnR0Z0Nmc2o4c1A4YkoKVTRocGtmMWlyY01MdWJXaDZOeWJHWWdBbG5teExmc1hMQU84ZFV sU2duL2JIUENTeHpyZTRpZGdFZ2ZGZ0RvbgpTeDBtMFZsRHZlWm5sc0d5d2p3aHpEU01TcnRFWE JjbDNmRWx3R3hmdDRVbTBRSGVoOGlhbUEwRmt4NEVjOE5HCnFjcjlTaDhZSGNmWDVwMnhKMkcyR jFheXlPL2t0NFBya0hleXo1bzJlTmREc2U2RTlWbXRQVTdoWjZDSVE3MWEKbWg3TlRmNU54dUlG UytXOUdWVGY3MDJDalEwS25HR0FJbTc2QW04cUc0dTFhVmM0WUNxaGhBUEtNVWRiUC9uVQpiRk5 xNDlqVUd4VjdERHRXVWkyVElhd2hZSFB0a3d3cUgxQUJXUmY1d3FmeVIvNXJUbTZCVzE4ZmRkOV FCRzBvClR2RkhERWgvd1BYMVkxVm5XU25YMG5DTXVkSmV1SzRaa2pHZ2dHeTBzQzhYYjNLUEM1M jYyRng5Rld4aWlDd0QKKytkN0tRMEdvc3MrZ0lJb1c1QjNJRzQ4c0cwY29OQk1Sc3U2b1RZN1NZ alJWNW5tTGRjaGR4WjZobzRWcVd0NAptK09YaHFxUnpoeWV4OWpNeThuVi9MS0xoQU4zSlBxYTc 1b0tSTHU3amZNZWxMbmRXWUQvMGcva0wwMTlNcSsyCmRoUWNBZkMzWFNVa1ZBPT0KPTB5S1QKLS 0tLS1FTkQgUEdQIE1FU1NBR0UtLS0tLQo= X-Developer-Key: i=parent.f@gmail.com; a=openpgp; fpr=07BB034F9E8E3AF0DF4CF7607AE864A1E16FA383 From: Fabien Parent This commit adds support for the Onsemi NCV6336 buck down converter. Signed-off-by: Fabien Parent --- drivers/regulator/Kconfig | 7 + drivers/regulator/Makefile | 1 + drivers/regulator/ncv6336_regulator.rs | 241 +++++++++++++++++++++++++++++++++ 3 files changed, 249 insertions(+) diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 39297f7d8177193e51c99bc2b360c6d9936e62fe..7254a6e1d7539b147b7ba00ebcb6fd92eb6b2616 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -952,6 +952,13 @@ config REGULATOR_MTK_DVFSRC of Mediatek. It allows for voting on regulator state between multiple users. +config REGULATOR_NCV6336 + tristate "Onsemi NCV6336 regulator driver" + depends on RUST && OF && I2C=y + select REGMAP_I2C + help + Say y here to support the Onsemi NCV6336 buck converter. + config REGULATOR_PALMAS tristate "TI Palmas PMIC Regulators" depends on MFD_PALMAS diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 3d5a803dce8a0556ba9557fa069c6e37593b3c69..0309a78b820cc85883c0c129801ab713e08e4391 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -113,6 +113,7 @@ obj-$(CONFIG_REGULATOR_MT6370) += mt6370-regulator.o obj-$(CONFIG_REGULATOR_MT6380) += mt6380-regulator.o obj-$(CONFIG_REGULATOR_MT6397) += mt6397-regulator.o obj-$(CONFIG_REGULATOR_MTK_DVFSRC) += mtk-dvfsrc-regulator.o +obj-$(CONFIG_REGULATOR_NCV6336) += ncv6336_regulator.o obj-$(CONFIG_REGULATOR_QCOM_LABIBB) += qcom-labibb-regulator.o obj-$(CONFIG_REGULATOR_QCOM_PM8008) += qcom-pm8008-regulator.o obj-$(CONFIG_REGULATOR_QCOM_REFGEN) += qcom-refgen-regulator.o diff --git a/drivers/regulator/ncv6336_regulator.rs b/drivers/regulator/ncv6336_regulator.rs new file mode 100644 index 0000000000000000000000000000000000000000..7efd7630792b68fb353ed1b1634980def9e326a1 --- /dev/null +++ b/drivers/regulator/ncv6336_regulator.rs @@ -0,0 +1,241 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Driver for the Onsemi Buck Converter NCV6336 +//! +//! Datasheet: https://www.onsemi.com/pdf/datasheet/ncv6336bm-d.pdf + +use kernel::{ + c_str, i2c, of, + prelude::*, + regmap::{self, BitFieldReadOps, BitFieldWriteOps, RawFieldWriteOps}, + regulator::{ + driver::{Config, Desc, Device, Driver, RegmapHelpers, Status, Type}, + Mode, + }, + sync::{new_mutex, Arc, Mutex}, +}; +use register::*; + +kernel::module_i2c_driver! { + type: Ncv6336, + name: "ncv6336", + author: "Fabien Parent ", + license: "GPL", +} + +kernel::i2c_device_table!( + I2C_ID_TABLE, + MODULE_I2C_ID_TABLE, + ::IdInfo, + [(i2c::DeviceId::new(c_str!("ncv6336")), ()),] +); + +kernel::of_device_table!( + OF_ID_TABLE, + MODULE_OF_ID_TABLE, + ::IdInfo, + [(of::DeviceId::new(c_str!("onnn,ncv6336")), ()),] +); + +regmap::define_regmap_field_descs!(FIELD_DESCS, { + (pid, 0x3, READ, { value => raw([7:0], ro) }), + (rid, 0x4, READ, { value => raw([7:0], ro) }), + (fid, 0x5, READ, { value => raw([7:0], ro) }), + (progvsel1, 0x10, RW, { + voutvsel1 => raw([6:0], rw), + envsel1 => bit(7, rw), + }), + (progvsel0, 0x11, RW, { + voutvsel0 => raw([6:0], rw), + envsel0 => bit(7, rw), + }), + (pgood, 0x12, RW, { dischg => bit(4, rw) }), + (command, 0x14, RW, { + vselgt => bit(0, rw), + pwmvsel1 => bit(6, rw), + pwmvsel0 => bit(7, rw), + }), + (limconf, 0x16, RW, { + rearm => bit(0, rw), + rststatus => bit(1, rw), + tpwth => enum([5:4], rw, { + Temp83C = 0x0, + Temp94C = 0x1, + Temp105C = 0x2, + Temp116C = 0x3, + }), + ipeak => enum([7:6], rw, { + Peak3p5A = 0x0, + Peak4p0A = 0x1, + Peak4p5A = 0x2, + Peak5p0A = 0x3, + }), + }) +}); + +static NCV6336_DESC: Desc = Desc::new::(c_str!("ncv6336"), Type::Voltage) + .with_owner(&THIS_MODULE) + .with_of_match(c_str!("buck")) + .with_active_discharge( + pgood::addr(), + pgood::dischg::mask(), + pgood::dischg::mask(), + 0, + ) + .with_csel( + limconf::addr(), + limconf::ipeak::mask(), + &[3_500_000, 4_000_000, 4_500_000, 5_000_000], + ) + .with_enable( + progvsel0::addr(), + progvsel0::envsel0::mask(), + progvsel0::envsel0::mask(), + 0, + ) + .with_linear_mapping( + progvsel0::addr(), + progvsel0::voutvsel0::mask(), + 600_000, + 6250, + 128, + 0, + ); + +struct Ncv6336RegulatorData { + fields: regmap::Fields<{ FIELD_DESCS.len() }>, +} + +struct Ncv6336(#[expect(dead_code)] Device<::Data>); + +impl i2c::Driver for Ncv6336 { + type IdInfo = (); + + const I2C_ID_TABLE: Option> = Some(&I2C_ID_TABLE); + const OF_ID_TABLE: Option> = Some(&OF_ID_TABLE); + + fn probe(client: &mut i2c::Client, _id_info: Option<&Self::IdInfo>) -> Result>> { + let config = regmap::Config::::new(8, 8) + .with_max_register(0x16) + .with_cache_type(regmap::CacheType::RbTree); + let regmap = Arc::new(regmap::Regmap::init_i2c(client, &config)?, GFP_KERNEL)?; + let fields = regmap::Fields::new(®map, &FIELD_DESCS)?; + + let data = Arc::pin_init(new_mutex!(Ncv6336RegulatorData { fields }), GFP_KERNEL)?; + let config = Config::new(client.as_ref(), data.clone()).with_regmap(regmap.clone()); + let regulator = Device::register(client.as_ref(), &NCV6336_DESC, config)?; + + let drvdata = KBox::new(Self(regulator), GFP_KERNEL)?; + + Ok(drvdata.into()) + } +} + +#[vtable] +impl Driver for Ncv6336 { + type Data = Arc>; + + fn list_voltage(reg: &mut Device, selector: u32) -> Result { + reg.list_voltage_linear(selector) + } + + fn enable(reg: &mut Device) -> Result { + reg.enable_regmap() + } + + fn disable(reg: &mut Device) -> Result { + reg.disable_regmap() + } + + fn is_enabled(reg: &mut Device) -> Result { + reg.is_enabled_regmap() + } + + fn set_active_discharge(reg: &mut Device, enable: bool) -> Result { + reg.set_active_discharge_regmap(enable) + } + + fn set_current_limit(reg: &mut Device, min_ua: i32, max_ua: i32) -> Result { + reg.set_current_limit_regmap(min_ua, max_ua) + } + + fn get_current_limit(reg: &mut Device) -> Result { + reg.get_current_limit_regmap() + } + + fn set_voltage_sel(reg: &mut Device, selector: u32) -> Result { + reg.set_voltage_sel_regmap(selector) + } + + fn get_voltage_sel(reg: &mut Device) -> Result { + reg.get_voltage_sel_regmap() + } + + fn set_mode(reg: &mut Device, mode: Mode) -> Result { + let data = reg.data(); + let fields = &mut data.lock().fields; + + match mode { + Mode::Normal => command::pwmvsel0::clear(fields), + Mode::Fast => command::pwmvsel0::set(fields), + _ => Err(ENOTSUPP), + } + } + + fn get_mode(reg: &mut Device) -> Mode { + let data = reg.data(); + let fields = &mut data.lock().fields; + + match command::pwmvsel0::is_set(fields) { + Ok(true) => Mode::Fast, + Ok(false) => Mode::Normal, + Err(_) => Mode::Invalid, + } + } + + fn get_status(reg: &mut Device) -> Result { + if !Self::is_enabled(reg)? { + return Ok(Status::Off); + } + + Ok(Self::get_mode(reg).into()) + } + + fn set_suspend_voltage(reg: &mut Device, uv: i32) -> Result { + let data = reg.data(); + let fields = &mut data.lock().fields; + + let quot = (uv - 600000) / 6250; + let rem = (uv - 600000) % 6250; + let selector = if rem > 0 { quot + 1 } else { quot }; + + progvsel1::voutvsel1::write(fields, selector as _) + } + + fn set_suspend_enable(reg: &mut Device) -> Result { + let data = reg.data(); + let fields = &mut data.lock().fields; + + progvsel1::envsel1::set(fields)?; + command::vselgt::clear(fields) + } + + fn set_suspend_disable(reg: &mut Device) -> Result { + let data = reg.data(); + let fields = &mut data.lock().fields; + + progvsel1::envsel1::clear(fields)?; + command::vselgt::set(fields) + } + + fn set_suspend_mode(reg: &mut Device, mode: Mode) -> Result { + let data = reg.data(); + let fields = &mut data.lock().fields; + + match mode { + Mode::Normal => command::pwmvsel1::clear(fields), + Mode::Fast => command::pwmvsel1::set(fields), + _ => Err(ENOTSUPP), + } + } +} From patchwork Wed Dec 18 23:36:39 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Fabien Parent X-Patchwork-Id: 851765 Received: from mail-pl1-f174.google.com (mail-pl1-f174.google.com [209.85.214.174]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 47FA7204F66; Wed, 18 Dec 2024 23:37:37 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.174 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1734565058; cv=none; b=ZWa72RSFlEdiEhe140nQl4v2e1LyOk72wSIA4q6JFaqJZ21udtWmUK20Ad4XV6znfjcvqeZ4sLKYL/uSSdAmTfiip/23hhBEohnj1H66PgAYqegQFosSfYjO+Beumh//oki5wjpxGthew4qzFlMc2jJ3vHa68Xq1H/dLsZN3JS8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1734565058; c=relaxed/simple; bh=eZUgIXh4Dafp5SjQXe3JmOhBWmOQQGNy2uNWoRlXbQc=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=rU/wkFqQtKl75dIr+9FhGMeRqfhYayfBEgozfNFHl59qVxvdO0A748aCjiar9eXjya9y3iZJTIhDoixzwmgZ9csZ//ZTTyzqctneQ6kgJfLxlMX9Tbzlqr9g75e4Y8kfvNOOipxKBErjQn04r9TwUgJyeKGSfe+hHmtpchk4rho= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=ILJ/EcO2; arc=none smtp.client-ip=209.85.214.174 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="ILJ/EcO2" Received: by mail-pl1-f174.google.com with SMTP id d9443c01a7336-216426b0865so2170875ad.0; Wed, 18 Dec 2024 15:37:37 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1734565056; x=1735169856; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=h2p+Bn+O63LERCuE6dGd6gVg3sXhL98osLDUQM697Sg=; b=ILJ/EcO2Y5BLl/RoAUabqnW76Ct7JSvRn2V3/vehQG4PjmkgQTWdf6DdJF6XxzfmR9 qT7RPXeNgzWQkYiLpJ9pYruUWaY0geFTeM7lSgCNzM/r8HAnrJcdVUHQ7cvFFch6mBZa xezxNvDVrrfrQhhht1rRtkxrky40Vb1dmoc5g6Bzl0zOru20sl/oJnHFPyMEbGXbEg+K gwarLydx7iQcjKtzgVqK8TVPF71D5eOSXRi8SwGPIkoJ+s4ZCRPi3ukdW0Ogtc2a0j1x NaXUoiqDNGVkWs2hUPCQEd3pUTValJTfdVqxmksXiSzlbXJEf7deBGAIa4OuoRY7Vs7+ SsMA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1734565056; x=1735169856; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=h2p+Bn+O63LERCuE6dGd6gVg3sXhL98osLDUQM697Sg=; b=fdAuEvP1arumZpBLatz42WK+HTadRAE9nIIoZ2gx34pUTwZV11Ndg+Fib9ieDq+0G0 1gUySxSDaEwNiBy5BPJZW2AybXSpPFqaN9pkoxKKr74Dot5WfozlGDzptiM21SkGY8ho +S3bfHcN2x7mE4l6PtOHdpKM3KGa4N8SevNXHW3S0IlyJ11SYFpd99jo6Dm7RjQ3hJfR jziy8sxT5PmBrXaC2mTZXb4DwdQQ4RtT+HCpfsmA1q2z5C+wwFKqz0ItWthUl1ssWZo+ blxwwCGT03IDElSnWUlgf3JfPrZ41WgNVoqMWG7BoDxqMHyTWx3hoF3PrJnaA9H1N+3A 5iQw== X-Forwarded-Encrypted: i=1; AJvYcCVGHVLolDKSD5NdC/OpM9sRmxiVXom6Zrsju9N9b9aWtA2ckm9DKK0Tvx6ckJAwzx/W23RI+gARevKu@vger.kernel.org, AJvYcCVjB50JSG8sMbrb9/IEGHOczSIsoOG4iZwuEQrwg97mnt3Ig9l64wT+GvHKT3UkP5vqVaFkWZsJLobheCiA@vger.kernel.org, AJvYcCVsF12TcgniougzEi4j78W6NhPf9hlpl7q29+6+VDfvSThrH/sMSTI8fdzFPCupeerTy3JV6sZDPBXbafEbxBU=@vger.kernel.org, AJvYcCXDDwqF0XJBKHxqzMi8TI3AzO9yKPr/fgzVR/LCv0ECgwFPqBS1TPBI9R3qZ2AoNXXBlkm5fgLxvtng4yer@vger.kernel.org X-Gm-Message-State: AOJu0YwpGY5P92PqDCxiOeWTeyMHMMTmSwvLnXU5PYpzhgZK+oeec66J zBHtKorh/rRXuC/ncY7rNfZ0tI9eeEJPCjEQbabuxq8fT+MdWRaA X-Gm-Gg: ASbGncvr0mLow9apz2RJDUx6efyMEsfIFLERdNfhdZkvf46nBUCCfErUYxge70NZJFn vggZJ+QQMPQc2AzP9xcPxJFqKV+4xvqpOTQs2NDKSYRJ/IpRp+tgEfr0ysw73hlfCje0jv4JVOm tW6QAOpcr7H6zFfreXyJynvrK10S3czeUjcjRonO1iPJm/B9BbjmTVbZdO1E9pm9Q9Y9pEEKbba dyPErxyfb+pmsvljk9OT0Wge4xb8PRzoDNUqymc00E2sMzhcG50d9Lb X-Google-Smtp-Source: AGHT+IHSZdbhBBo2sF8tqN4LT+Oi0B4GGcc3Vh9M1Y2KXAp+XnvVO/Rtbaui7owIt/uIhqw2619amg== X-Received: by 2002:a17:902:ea11:b0:216:2f7f:ff69 with SMTP id d9443c01a7336-219d966ea2bmr21730945ad.5.1734565056516; Wed, 18 Dec 2024 15:37:36 -0800 (PST) Received: from [127.0.1.1] ([2001:569:fcea:600:2e06:283e:5daa:4d0f]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-219dc9f6967sm802335ad.214.2024.12.18.15.37.34 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 18 Dec 2024 15:37:35 -0800 (PST) From: Fabien Parent Date: Wed, 18 Dec 2024 15:36:39 -0800 Subject: [PATCH 9/9] arm64: dts: qcom: apq8039-t2: add node for ncv6336 regulator Precedence: bulk X-Mailing-List: linux-arm-msm@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20241218-ncv6336-v1-9-b8d973747f7a@gmail.com> References: <20241218-ncv6336-v1-0-b8d973747f7a@gmail.com> In-Reply-To: <20241218-ncv6336-v1-0-b8d973747f7a@gmail.com> To: Rob Herring , Saravana Kannan , Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Greg Kroah-Hartman , "Rafael J. Wysocki" , Wolfram Sang , Mark Brown , Liam Girdwood , Krzysztof Kozlowski , Conor Dooley , Bjorn Andersson , Konrad Dybcio , Fabien Parent Cc: devicetree@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-i2c@vger.kernel.org, linux-arm-msm@vger.kernel.org, vinod.koul@linaro.org, Fabien Parent X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=1251; i=parent.f@gmail.com; h=from:subject:message-id; bh=HWjtZtg1pmveHn6OiavIms18l8HI9Sa8dLce2gBDllY=; b=LS0tLS1CRUdJTiBQR1AgTUVTU0FHRS0tLS0tCgpvd0o0bkFGdEFwTDlrQTBEQUFvQmlKc2d4Q kw5cktRQnl5WmlBR2RqWEtwQTJvQ0gvSmYydG1KWGZDWGx2SGo2CkY1VnJSNWNRbG56NFV0NGJw TU5PWTRrQ013UUFBUW9BSFJZaEJONk0wZElMeXpzd3JmRnNxWWliSU1RUy9heWsKQlFKblkxeXF BQW9KRUlpYklNUVMvYXlrdHQwUC9SWG1lTHU1L29wcHNxWHR1blJXUFVQeVpFZlBYVnlmbDRNaw paQnRFRzBDcU1RZTdqMk5qVVAxS25hWHFtQTgvVS9mMzRKSGNwSjBKTVhxV2ZxY3Z6dmxNRXh0c jZIK3BKM045CnFuUmt5KzBpUFNiL0lNUUZQVktWMVUwSHN6VWl3MVVtbTNNWlVTenBWQjBPSm9U OC9MODlEdHNzUVFCenZxaWsKanJLc2ZSV1MwNzlpL1NJdm9uc1JqOFZFY2lpY3FrbW5maERLYWo 2a1d0MFE2aTcvY0daQ2QvWVZndWFyYlNKOQpFcm8zbndrVEI0SVkrSWpGWWtQbkFhUGJUaVJpV1 lSYlQ4TjhIaDRjdEFKa2hSM1pzOFNkZ05jRUtwWEFjN1ZlCmFjelNXSC80VXNCby9mQ0U5d0szV mgvclZSTDlxdndBbWRCY2ZYMWVNZ2tMZWYrQWt6ZXNiWFpVUVR1L21aanoKZThNTk1IS0JYYytl d3pzYnlDd2UrWVJEcE8reEt0SVRpcFFvRFpPS1pQbEptaURJZ2RSRkMzeHJDaHROdy9mdwphNnB ReUFBS0RLWVl1QktrMjk3M1JmOFRQM0xPS1FJZ0NLRU1haXRQV2NzWWFKdjgrSkRhVU5aOWEzSj A3VDVnCjc2eXRvOS9qYW02dVNreWlqckpSYW11YUVRRjYwUTNvUzlQSTd5NnVWREFEdXEwZFZTe GZkb2ZBM0NCN1pyOTMKSDY3REVZWHErNG9CY2kwdlVmUTV5Mm5WVG10bDZuN080UHRJRTltNFk1 RHRXT2lOTXhCS05jdFVPSjlvRG1mNQo1bTRXUHJVWkEyQjI5TUZpVDU0dEhKb05yR1ZKUHBBVWh HZWNHcVBPQ0tFeXJvL2FWZm41UHhQS3ZZdXBPN3pkCmoySWIzSjMvZU9BbHRnPT0KPWNkTDkKLS 0tLS1FTkQgUEdQIE1FU1NBR0UtLS0tLQo= X-Developer-Key: i=parent.f@gmail.com; a=openpgp; fpr=07BB034F9E8E3AF0DF4CF7607AE864A1E16FA383 From: Fabien Parent CPR is using the power rail provided by the ncv6336 buck regulator on the apq8039 t2 board. This commit adds the required regulator. Signed-off-by: Fabien Parent --- arch/arm64/boot/dts/qcom/apq8039-t2.dts | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/apq8039-t2.dts b/arch/arm64/boot/dts/qcom/apq8039-t2.dts index 4f82bb668616f942d65f59a6f418cf38f404df32..2da5b7d01521520a6814b76c02329be3bdedb4fd 100644 --- a/arch/arm64/boot/dts/qcom/apq8039-t2.dts +++ b/arch/arm64/boot/dts/qcom/apq8039-t2.dts @@ -111,6 +111,23 @@ typec_ep: endpoint { }; }; +&blsp_i2c4 { + status = "okay"; + + regulator@1c { + compatible = "onnn,ncv6336"; + reg = <0x1c>; + pinctrl-0 = <&ncv6336_vsel>; + pinctrl-names = "default"; + + buck { + regulator-name = "ncv6336,buck"; + regulator-min-microvolt = <600000>; + regulator-max-microvolt = <1393750>; + }; + }; +}; + &blsp_i2c5 { status = "okay"; }; @@ -371,6 +388,12 @@ typec_irq: typec-irq-state { pins = "gpio107"; bias-pull-up; }; + + ncv6336_vsel: ncv6336-state { + function = "gpio"; + pins = "gpio111"; + output-low; + }; }; &usb {