* [PATCH v8 05/10] rust: drm: set fops.owner from driver module pointer
From: Alvin Sun @ 2026-07-13 6:45 UTC (permalink / raw)
To: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Luis Chamberlain, Petr Pavlu, Daniel Gomez,
Sami Tolvanen, Aaron Tomlin, Greg Kroah-Hartman,
Rafael J. Wysocki, David Airlie, Simona Vetter, Daniel Almeida,
Arnd Bergmann, Brendan Higgins, David Gow, Rae Moar, Breno Leitao,
Jens Axboe, Dave Ertman, Leon Romanovsky, Igor Korotin,
FUJITA Tomonori, Bjorn Helgaas, Krzysztof Wilczyński,
Arve Hjønnevåg, Todd Kjos, Christian Brauner,
Carlos Llamas
Cc: rust-for-linux, linux-modules, driver-core, dri-devel, nova-gpu,
linux-kselftest, kunit-dev, linux-block, linux-kernel, netdev,
linux-pci, Alvin Sun
In-Reply-To: <20260713-fix-fops-owner-v8-0-2495cfa82d47@linux.dev>
Change `create_fops()` to accept an owner module pointer instead of
hardcoding `null_mut()`, ensuring the kernel correctly tracks the
module owning the DRM device's file operations.
Assisted-by: opencode:glm-5.2
Reviewed-by: Andreas Hindborg <a.hindborg@kernel.org>
Reviewed-by: Gary Guo <gary@garyguo.net>
Acked-by: Danilo Krummrich <dakr@kernel.org>
Signed-off-by: Alvin Sun <alvin.sun@linux.dev>
---
rust/kernel/drm/device.rs | 3 ++-
rust/kernel/drm/gem/mod.rs | 4 ++--
2 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/rust/kernel/drm/device.rs b/rust/kernel/drm/device.rs
index 403fc35353c74..d92cacb665366 100644
--- a/rust/kernel/drm/device.rs
+++ b/rust/kernel/drm/device.rs
@@ -111,7 +111,8 @@ impl<T: drm::Driver> Device<T> {
fops: &Self::GEM_FOPS,
};
- const GEM_FOPS: bindings::file_operations = drm::gem::create_fops();
+ const GEM_FOPS: bindings::file_operations =
+ drm::gem::create_fops(crate::module::this_module::<T::OwnerModule>().as_ptr());
/// Create a new `drm::Device` for a `drm::Driver`.
pub fn new(dev: &device::Device, data: impl PinInit<T::Data, Error>) -> Result<ARef<Self>> {
diff --git a/rust/kernel/drm/gem/mod.rs b/rust/kernel/drm/gem/mod.rs
index 01b5bd47a3332..9a203efc59116 100644
--- a/rust/kernel/drm/gem/mod.rs
+++ b/rust/kernel/drm/gem/mod.rs
@@ -357,10 +357,10 @@ impl<T: DriverObject> AllocImpl for Object<T> {
};
}
-pub(super) const fn create_fops() -> bindings::file_operations {
+pub(super) const fn create_fops(owner: *mut bindings::module) -> bindings::file_operations {
let mut fops: bindings::file_operations = pin_init::zeroed();
- fops.owner = core::ptr::null_mut();
+ fops.owner = owner;
fops.open = Some(bindings::drm_open);
fops.release = Some(bindings::drm_release);
fops.unlocked_ioctl = Some(bindings::drm_ioctl);
--
2.43.0
^ permalink raw reply related
* [PATCH v8 06/10] rust: miscdevice: set fops.owner from driver module pointer
From: Alvin Sun @ 2026-07-13 6:45 UTC (permalink / raw)
To: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Luis Chamberlain, Petr Pavlu, Daniel Gomez,
Sami Tolvanen, Aaron Tomlin, Greg Kroah-Hartman,
Rafael J. Wysocki, David Airlie, Simona Vetter, Daniel Almeida,
Arnd Bergmann, Brendan Higgins, David Gow, Rae Moar, Breno Leitao,
Jens Axboe, Dave Ertman, Leon Romanovsky, Igor Korotin,
FUJITA Tomonori, Bjorn Helgaas, Krzysztof Wilczyński,
Arve Hjønnevåg, Todd Kjos, Christian Brauner,
Carlos Llamas
Cc: rust-for-linux, linux-modules, driver-core, dri-devel, nova-gpu,
linux-kselftest, kunit-dev, linux-block, linux-kernel, netdev,
linux-pci, Alvin Sun
In-Reply-To: <20260713-fix-fops-owner-v8-0-2495cfa82d47@linux.dev>
Set the miscdevice fops owner field from the driver module pointer
via the `this_module::<T::OwnerModule>()` helper, instead of
defaulting to null.
Assisted-by: opencode:glm-5.2
Reviewed-by: Andreas Hindborg <a.hindborg@kernel.org>
Reviewed-by: Gary Guo <gary@garyguo.net>
Acked-by: Danilo Krummrich <dakr@kernel.org>
Signed-off-by: Alvin Sun <alvin.sun@linux.dev>
---
rust/kernel/miscdevice.rs | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/rust/kernel/miscdevice.rs b/rust/kernel/miscdevice.rs
index 83ce50def5ac9..2a4329f98614e 100644
--- a/rust/kernel/miscdevice.rs
+++ b/rust/kernel/miscdevice.rs
@@ -24,12 +24,13 @@
IovIterSource, //
},
mm::virt::VmaNew,
+ module::this_module,
prelude::*,
seq_file::SeqFile,
types::{
ForeignOwnable,
Opaque, //
- },
+ }, //
};
use core::marker::PhantomData;
@@ -430,6 +431,7 @@ impl<T: MiscDevice> MiscdeviceVTable<T> {
} else {
None
},
+ owner: this_module::<T::OwnerModule>().as_ptr(),
..pin_init::zeroed()
};
--
2.43.0
^ permalink raw reply related
* [PATCH v8 08/10] rust: binder: use `LocalModule` for `THIS_MODULE`
From: Alvin Sun @ 2026-07-13 6:45 UTC (permalink / raw)
To: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Luis Chamberlain, Petr Pavlu, Daniel Gomez,
Sami Tolvanen, Aaron Tomlin, Greg Kroah-Hartman,
Rafael J. Wysocki, David Airlie, Simona Vetter, Daniel Almeida,
Arnd Bergmann, Brendan Higgins, David Gow, Rae Moar, Breno Leitao,
Jens Axboe, Dave Ertman, Leon Romanovsky, Igor Korotin,
FUJITA Tomonori, Bjorn Helgaas, Krzysztof Wilczyński,
Arve Hjønnevåg, Todd Kjos, Christian Brauner,
Carlos Llamas
Cc: rust-for-linux, linux-modules, driver-core, dri-devel, nova-gpu,
linux-kselftest, kunit-dev, linux-block, linux-kernel, netdev,
linux-pci, Alvin Sun
In-Reply-To: <20260713-fix-fops-owner-v8-0-2495cfa82d47@linux.dev>
Replace the `THIS_MODULE` static reference in the binder fops with
`this_module::<LocalModule>()`, consistent with the move of
`THIS_MODULE` into the `ModuleMetadata` trait.
Assisted-by: opencode:glm-5.2
Reviewed-by: Gary Guo <gary@garyguo.net>
Acked-by: Danilo Krummrich <dakr@kernel.org>
Signed-off-by: Alvin Sun <alvin.sun@linux.dev>
---
drivers/android/binder/rust_binder_main.rs | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/android/binder/rust_binder_main.rs b/drivers/android/binder/rust_binder_main.rs
index dc1941cd2407b..d6ceebbd5f94e 100644
--- a/drivers/android/binder/rust_binder_main.rs
+++ b/drivers/android/binder/rust_binder_main.rs
@@ -17,6 +17,7 @@
bindings::{self, seq_file},
fs::File,
list::{ListArc, ListArcSafe, ListLinksSelfPtr, TryNewListArc},
+ module::this_module,
prelude::*,
seq_file::SeqFile,
seq_print,
@@ -318,7 +319,7 @@ unsafe impl<T> Sync for AssertSync<T> {}
let zeroed_ops = unsafe { core::mem::MaybeUninit::zeroed().assume_init() };
let ops = kernel::bindings::file_operations {
- owner: THIS_MODULE.as_ptr(),
+ owner: this_module::<LocalModule>().as_ptr(),
poll: Some(rust_binder_poll),
unlocked_ioctl: Some(rust_binder_ioctl),
compat_ioctl: bindings::compat_ptr_ioctl,
--
2.43.0
^ permalink raw reply related
* [PATCH v8 04/10] rust: macros: auto-insert OwnerModule in #[vtable]
From: Alvin Sun @ 2026-07-13 6:45 UTC (permalink / raw)
To: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Luis Chamberlain, Petr Pavlu, Daniel Gomez,
Sami Tolvanen, Aaron Tomlin, Greg Kroah-Hartman,
Rafael J. Wysocki, David Airlie, Simona Vetter, Daniel Almeida,
Arnd Bergmann, Brendan Higgins, David Gow, Rae Moar, Breno Leitao,
Jens Axboe, Dave Ertman, Leon Romanovsky, Igor Korotin,
FUJITA Tomonori, Bjorn Helgaas, Krzysztof Wilczyński,
Arve Hjønnevåg, Todd Kjos, Christian Brauner,
Carlos Llamas
Cc: rust-for-linux, linux-modules, driver-core, dri-devel, nova-gpu,
linux-kselftest, kunit-dev, linux-block, linux-kernel, netdev,
linux-pci, Alvin Sun
In-Reply-To: <20260713-fix-fops-owner-v8-0-2495cfa82d47@linux.dev>
Auto-add `type OwnerModule: ::kernel::ModuleMetadata;` as a required
associated type on the trait side if not already defined, and
auto-insert `type OwnerModule = crate::LocalModule;` on the impl side
if not explicitly provided, eliminating the need to manually declare
and implement `OwnerModule` in every vtable trait and impl.
Assisted-by: opencode:glm-5.2
Reviewed-by: Andreas Hindborg <a.hindborg@kernel.org>
Suggested-by: Gary Guo <gary@garyguo.net>
Link: https://lore.kernel.org/all/DIMMWHUOLPSH.13JFRHDKDQJGO@garyguo.net
Reviewed-by: Gary Guo <gary@garyguo.net>
Acked-by: Danilo Krummrich <dakr@kernel.org>
Signed-off-by: Alvin Sun <alvin.sun@linux.dev>
---
rust/macros/lib.rs | 6 ++++++
rust/macros/vtable.rs | 41 ++++++++++++++++++++++++++++++++++++-----
2 files changed, 42 insertions(+), 5 deletions(-)
diff --git a/rust/macros/lib.rs b/rust/macros/lib.rs
index 2cfd59e0f9e7c..bc7ded353c5ca 100644
--- a/rust/macros/lib.rs
+++ b/rust/macros/lib.rs
@@ -176,6 +176,12 @@ pub fn module(input: TokenStream) -> TokenStream {
///
/// This macro should not be used when all functions are required.
///
+/// Additionally, this macro automatically handles the `OwnerModule`
+/// associated type: on the trait side, `type OwnerModule: ModuleMetadata;`
+/// is added as a required associated type if not already defined; on the
+/// impl side, `type OwnerModule = LocalModule;` is automatically inserted
+/// if not explicitly defined.
+///
/// # Examples
///
/// ```
diff --git a/rust/macros/vtable.rs b/rust/macros/vtable.rs
index c6510b0c4ea1d..be9a5ed8abe5e 100644
--- a/rust/macros/vtable.rs
+++ b/rust/macros/vtable.rs
@@ -30,6 +30,22 @@ fn handle_trait(mut item: ItemTrait) -> Result<ItemTrait> {
const USE_VTABLE_ATTR: ();
});
+ // Add `type OwnerModule: ModuleMetadata` as a required associated type if
+ // the trait does not already define it.
+ if !item
+ .items
+ .iter()
+ .any(|i| matches!(i, TraitItem::Type(t) if t.ident == "OwnerModule"))
+ {
+ gen_items.push(parse_quote! {
+ /// The module implementing this vtable trait.
+ ///
+ /// Automatically set to `crate::LocalModule` by the `#[vtable]`
+ /// impl macro.
+ type OwnerModule: ::kernel::ModuleMetadata;
+ });
+ }
+
for item in &item.items {
if let TraitItem::Fn(fn_item) = item {
let name = &fn_item.sig.ident;
@@ -57,12 +73,18 @@ fn handle_trait(mut item: ItemTrait) -> Result<ItemTrait> {
fn handle_impl(mut item: ItemImpl) -> Result<ItemImpl> {
let mut gen_items = Vec::new();
- let mut defined_consts = HashSet::new();
+ let mut defined_items = HashSet::new();
- // Iterate over all user-defined constants to gather any possible explicit overrides.
+ // Iterate over all user-defined items to gather any possible explicit overrides.
for item in &item.items {
- if let ImplItem::Const(const_item) = item {
- defined_consts.insert(const_item.ident.clone());
+ match item {
+ ImplItem::Const(const_item) => {
+ defined_items.insert(const_item.ident.clone());
+ }
+ ImplItem::Type(type_item) => {
+ defined_items.insert(type_item.ident.clone());
+ }
+ _ => {}
}
}
@@ -70,6 +92,15 @@ fn handle_impl(mut item: ItemImpl) -> Result<ItemImpl> {
const USE_VTABLE_ATTR: () = ();
});
+ // Auto-insert `type OwnerModule = crate::LocalModule` if not explicitly defined.
+ // `crate::LocalModule` resolves to the real module type (via `module!`) or a
+ // dummy fallback in non-module contexts (e.g., doctests).
+ if !defined_items.contains(&parse_quote!(OwnerModule)) {
+ gen_items.push(parse_quote! {
+ type OwnerModule = crate::LocalModule;
+ });
+ }
+
for item in &item.items {
if let ImplItem::Fn(fn_item) = item {
let name = &fn_item.sig.ident;
@@ -78,7 +109,7 @@ fn handle_impl(mut item: ItemImpl) -> Result<ItemImpl> {
name.span(),
);
// Skip if it's declared already -- this allows user override.
- if defined_consts.contains(&gen_const_name) {
+ if defined_items.contains(&gen_const_name) {
continue;
}
let cfg_attrs = crate::helpers::gather_cfg_attrs(&fn_item.attrs);
--
2.43.0
^ permalink raw reply related
* [PATCH v8 07/10] rust: configfs: use `LocalModule` for `THIS_MODULE`
From: Alvin Sun @ 2026-07-13 6:45 UTC (permalink / raw)
To: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Luis Chamberlain, Petr Pavlu, Daniel Gomez,
Sami Tolvanen, Aaron Tomlin, Greg Kroah-Hartman,
Rafael J. Wysocki, David Airlie, Simona Vetter, Daniel Almeida,
Arnd Bergmann, Brendan Higgins, David Gow, Rae Moar, Breno Leitao,
Jens Axboe, Dave Ertman, Leon Romanovsky, Igor Korotin,
FUJITA Tomonori, Bjorn Helgaas, Krzysztof Wilczyński,
Arve Hjønnevåg, Todd Kjos, Christian Brauner,
Carlos Llamas
Cc: rust-for-linux, linux-modules, driver-core, dri-devel, nova-gpu,
linux-kselftest, kunit-dev, linux-block, linux-kernel, netdev,
linux-pci, Alvin Sun
In-Reply-To: <20260713-fix-fops-owner-v8-0-2495cfa82d47@linux.dev>
Replace the `THIS_MODULE` static reference in the `configfs_attrs!`
macro with `this_module::<LocalModule>()`, and update
rnull to import `LocalModule` instead of `THIS_MODULE`, consistent
with the move of `THIS_MODULE` into the `ModuleMetadata` trait.
Assisted-by: opencode:glm-5.2
Reviewed-by: Andreas Hindborg <a.hindborg@kernel.org>
Acked-by: Danilo Krummrich <dakr@kernel.org>
Signed-off-by: Alvin Sun <alvin.sun@linux.dev>
---
drivers/block/rnull/configfs.rs | 5 +----
rust/kernel/configfs.rs | 9 ++++++---
2 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/drivers/block/rnull/configfs.rs b/drivers/block/rnull/configfs.rs
index c10a55fc58948..9b28be2150933 100644
--- a/drivers/block/rnull/configfs.rs
+++ b/drivers/block/rnull/configfs.rs
@@ -1,9 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
-use super::{
- NullBlkDevice,
- THIS_MODULE, //
-};
+use super::NullBlkDevice;
use kernel::{
block::mq::gen_disk::{
GenDisk,
diff --git a/rust/kernel/configfs.rs b/rust/kernel/configfs.rs
index 2339c6467325d..cd082b83e9e74 100644
--- a/rust/kernel/configfs.rs
+++ b/rust/kernel/configfs.rs
@@ -875,13 +875,14 @@ fn as_ptr(&self) -> *const bindings::config_item_type {
/// configfs::Subsystem<Configuration>,
/// Configuration
/// >::new_with_child_ctor::<N,Child>(
-/// &THIS_MODULE,
+/// ::kernel::module::this_module::<crate::LocalModule>(),
/// &CONFIGURATION_ATTRS
/// );
///
/// &CONFIGURATION_TPE
/// }
/// ```
+#[allow(clippy::crate_in_macro_def)]
#[macro_export]
macro_rules! configfs_attrs {
(
@@ -1021,7 +1022,8 @@ macro_rules! configfs_attrs {
static [< $data:upper _TPE >] : $crate::configfs::ItemType<$container, $data> =
$crate::configfs::ItemType::<$container, $data>::new::<N>(
- &THIS_MODULE, &[<$ data:upper _ATTRS >]
+ $crate::module::this_module::<crate::LocalModule>(),
+ &[<$ data:upper _ATTRS >]
);
)?
@@ -1030,7 +1032,8 @@ macro_rules! configfs_attrs {
$crate::configfs::ItemType<$container, $data> =
$crate::configfs::ItemType::<$container, $data>::
new_with_child_ctor::<N, $child>(
- &THIS_MODULE, &[<$ data:upper _ATTRS >]
+ $crate::module::this_module::<crate::LocalModule>(),
+ &[<$ data:upper _ATTRS >]
);
)?
--
2.43.0
^ permalink raw reply related
* [PATCH v8 09/10] rust: macros: remove `THIS_MODULE` static from `module!`
From: Alvin Sun @ 2026-07-13 6:45 UTC (permalink / raw)
To: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Luis Chamberlain, Petr Pavlu, Daniel Gomez,
Sami Tolvanen, Aaron Tomlin, Greg Kroah-Hartman,
Rafael J. Wysocki, David Airlie, Simona Vetter, Daniel Almeida,
Arnd Bergmann, Brendan Higgins, David Gow, Rae Moar, Breno Leitao,
Jens Axboe, Dave Ertman, Leon Romanovsky, Igor Korotin,
FUJITA Tomonori, Bjorn Helgaas, Krzysztof Wilczyński,
Arve Hjønnevåg, Todd Kjos, Christian Brauner,
Carlos Llamas
Cc: rust-for-linux, linux-modules, driver-core, dri-devel, nova-gpu,
linux-kselftest, kunit-dev, linux-block, linux-kernel, netdev,
linux-pci, Alvin Sun
In-Reply-To: <20260713-fix-fops-owner-v8-0-2495cfa82d47@linux.dev>
All users have been migrated to `ModuleMetadata::THIS_MODULE` const or
`this_module::<LocalModule>()` helper. The `static THIS_MODULE`
generated by the `module!` macro is no longer referenced anywhere,
so remove it to avoid having two sources of the same `ThisModule`
pointer.
Assisted-by: opencode:glm-5.2
Reviewed-by: Andreas Hindborg <a.hindborg@kernel.org>
Reviewed-by: Gary Guo <gary@garyguo.net>
Acked-by: Danilo Krummrich <dakr@kernel.org>
Signed-off-by: Alvin Sun <alvin.sun@linux.dev>
---
rust/macros/module.rs | 16 ----------------
1 file changed, 16 deletions(-)
diff --git a/rust/macros/module.rs b/rust/macros/module.rs
index aa9a618d5d19e..23b6a1b456b80 100644
--- a/rust/macros/module.rs
+++ b/rust/macros/module.rs
@@ -497,22 +497,6 @@ pub(crate) fn module(info: ModuleInfo) -> Result<TokenStream> {
/// Used by the printing macros, e.g. [`info!`].
const __LOG_PREFIX: &[u8] = #name_cstr.to_bytes_with_nul();
- // SAFETY: `__this_module` is constructed by the kernel at load time and will not be
- // freed until the module is unloaded.
- #[cfg(MODULE)]
- static THIS_MODULE: ::kernel::ThisModule = unsafe {
- extern "C" {
- static __this_module: ::kernel::types::Opaque<::kernel::bindings::module>;
- };
-
- ::kernel::ThisModule::from_ptr(__this_module.get())
- };
-
- #[cfg(not(MODULE))]
- static THIS_MODULE: ::kernel::ThisModule = unsafe {
- ::kernel::ThisModule::from_ptr(::core::ptr::null_mut())
- };
-
/// The `LocalModule` type is the type of the module created by `module!`,
/// `module_pci_driver!`, `module_platform_driver!`, etc.
type LocalModule = #type_;
--
2.43.0
^ permalink raw reply related
* [PATCH v8 10/10] rust: module: update MAINTAINERS to cover module.rs
From: Alvin Sun @ 2026-07-13 6:45 UTC (permalink / raw)
To: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Luis Chamberlain, Petr Pavlu, Daniel Gomez,
Sami Tolvanen, Aaron Tomlin, Greg Kroah-Hartman,
Rafael J. Wysocki, David Airlie, Simona Vetter, Daniel Almeida,
Arnd Bergmann, Brendan Higgins, David Gow, Rae Moar, Breno Leitao,
Jens Axboe, Dave Ertman, Leon Romanovsky, Igor Korotin,
FUJITA Tomonori, Bjorn Helgaas, Krzysztof Wilczyński,
Arve Hjønnevåg, Todd Kjos, Christian Brauner,
Carlos Llamas
Cc: rust-for-linux, linux-modules, driver-core, dri-devel, nova-gpu,
linux-kselftest, kunit-dev, linux-block, linux-kernel, netdev,
linux-pci, Alvin Sun
In-Reply-To: <20260713-fix-fops-owner-v8-0-2495cfa82d47@linux.dev>
Module types now live in `rust/kernel/module.rs` alongside
`rust/kernel/module_param.rs`. Update the MODULE SUPPORT file pattern
from `rust/kernel/module_param.rs` to `rust/kernel/module*.rs` so both
files are covered.
Cc: Petr Pavlu <petr.pavlu@suse.com>
Assisted-by: opencode:glm-5.2
Link: https://lore.kernel.org/rust-for-linux/8ea21b29-9baf-4926-a16f-7d21c5a1a1b8@suse.com
Signed-off-by: Alvin Sun <alvin.sun@linux.dev>
---
MAINTAINERS | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/MAINTAINERS b/MAINTAINERS
index e035a3be797c4..74733de3e41ee 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -17984,7 +17984,7 @@ F: include/linux/module*.h
F: kernel/module/
F: lib/test_kmod.c
F: lib/tests/module/
-F: rust/kernel/module_param.rs
+F: rust/kernel/module*.rs
F: rust/macros/module.rs
F: scripts/module*
F: tools/testing/selftests/kmod/
--
2.43.0
^ permalink raw reply related
* Re: [PATCH] vhost/net: Fill virtio_net_hdr GSO/csum metadata on RX
From: Michael S. Tsirkin @ 2026-07-13 7:03 UTC (permalink / raw)
To: weimin xiong; +Cc: jasowang, virtualization, netdev, xiongweimin
In-Reply-To: <20260713010442.331109-1-15927021679@163.com>
On Mon, Jul 13, 2026 at 09:04:42AM +0800, weimin xiong wrote:
> From: xiongweimin <xiongweimin@kylinos.cn>
>
> When VHOST_NET_F_VIRTIO_NET_HDR is set, vhost supplies virtio_net_hdr to
> the guest but previously always wrote a zeroed header (GSO_NONE). Guests
> that rely on GUEST_TSO*/GUEST_CSUM therefore never saw offload metadata.
Right. Question is why are you using VHOST_NET_F_VIRTIO_NET_HDR?
>
> Peek the socket skb before recvmsg and populate the header with
> virtio_net_hdr_from_skb(). Also advertise the corresponding guest offload
> feature bits from VHOST_GET_FEATURES.
>
> TX TSO toward backends without IFF_VNET_HDR is intentionally left for a
> follow-up series.
>
> Signed-off-by: xiongweimin <xiongweimin@kylinos.cn>
> Cc: Michael S. Tsirkin <mst@redhat.com>
> Cc: Jason Wang <jasowang@redhat.com>
> Cc: virtualization@vger.kernel.org
> Cc: netdev@vger.kernel.org
> ---
>
> --- a/drivers/vhost/net.c
> +++ b/drivers/vhost/net.c
> @@ -73,6 +73,10 @@
> VHOST_FEATURES,
> VHOST_NET_F_VIRTIO_NET_HDR,
> VIRTIO_NET_F_MRG_RXBUF,
> + VIRTIO_NET_F_GUEST_CSUM,
> + VIRTIO_NET_F_GUEST_TSO4,
> + VIRTIO_NET_F_GUEST_TSO6,
> + VIRTIO_NET_F_GUEST_ECN,
> VIRTIO_F_ACCESS_PLATFORM,
> VIRTIO_F_RING_RESET,
> VIRTIO_F_IN_ORDER,
> @@ -644,7 +648,7 @@
> static size_t init_iov_iter(struct vhost_virtqueue *vq, struct iov_iter *iter,
> size_t hdr_size, int out)
> {
> - /* Skip header. TODO: support TSO. */
> + /* Skip guest virtio_net_hdr; TX TSO handled in a follow-up. */
> size_t len = iov_length(vq->iov, out);
>
> iov_iter_init(iter, ITER_SOURCE, vq->iov, out, len);
> @@ -1025,6 +1029,35 @@
> return len;
> }
>
> +/*
> + * When VHOST_NET_F_VIRTIO_NET_HDR is set, vhost supplies virtio_net_hdr.
> + * Populate GSO/checksum metadata from the socket skb so guests that
> + * negotiated GUEST_TSO*/GUEST_CSUM receive correct offload information.
> + */
this is a wrong type of multiline comment. this file follows net
convention:
/* AAA
* BBB
*/
> +static int vhost_net_hdr_from_sock(struct vhost_virtqueue *vq, struct sock *sk,
> + struct virtio_net_hdr *hdr)
> +{
> + struct sk_buff *skb;
> + unsigned long flags;
> + int vlan_hlen = 0;
> + int ret;
> +
> + spin_lock_irqsave(&sk->sk_receive_queue.lock, flags);
> + skb = skb_peek(&sk->sk_receive_queue);
> + if (!skb) {
> + spin_unlock_irqrestore(&sk->sk_receive_queue.lock, flags);
> + memset(hdr, 0, sizeof(*hdr));
> + hdr->gso_type = VIRTIO_NET_HDR_GSO_NONE;
> + return 0;
> + }
> + if (skb_vlan_tag_present(skb))
> + vlan_hlen = VLAN_HLEN;
> + ret = virtio_net_hdr_from_skb(skb, hdr, vhost_is_little_endian(vq),
> + true, vlan_hlen);
> + spin_unlock_irqrestore(&sk->sk_receive_queue.lock, flags);
> + return ret;
> +}
This means the header will be wrong if something consumes
the skb after we drop the lock, no?
That's why in the end we put the header filling logic
in tun, it can avoid races there.
> +
> static int vhost_net_rx_peek_head_len(struct vhost_net *net, struct sock *sk,
> bool *busyloop_intr, unsigned int *count)
> {
> @@ -1239,10 +1272,18 @@
> /* We don't need to be notified again. */
> iov_iter_init(&msg.msg_iter, ITER_DEST, vq->iov, in, vhost_len);
> fixup = msg.msg_iter;
> - if (unlikely((vhost_hlen))) {
> - /* We will supply the header ourselves
> - * TODO: support TSO.
> + if (unlikely(vhost_hlen)) {
> + /*
> + * Build virtio_net_hdr from the socket skb before
> + * recvmsg consumes it. Skip for ptr_ring backends
> + * where the skb is not on sk_receive_queue.
> */
> + if (!nvq->rx_ring &&
> + vhost_net_hdr_from_sock(vq, sock->sk, &hdr)) {
> + vq_err(vq, "Failed to build vnet_hdr from skb\n");
> + vhost_discard_vq_desc(vq, headcount, ndesc);
> + continue;
> + }
> iov_iter_advance(&msg.msg_iter, vhost_hlen);
> }
> err = sock->ops->recvmsg(sock, &msg,
> @@ -1270,7 +1311,6 @@
> */
> iov_iter_advance(&fixup, sizeof(hdr));
> }
> - /* TODO: Should check and handle checksum. */
>
> num_buffers = cpu_to_vhost16(vq, headcount);
> if (likely(set_num_buffers) &&
^ permalink raw reply
* Re: [PATCH net-next v4 2/3] ptp: Add driver for R-Car Gen4
From: Uwe Kleine-König @ 2026-07-13 7:09 UTC (permalink / raw)
To: Niklas Söderlund
Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Geert Uytterhoeven, Magnus Damm, Richard Cochran, Andrew Lunn,
DavidS. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
linux-renesas-soc, devicetree, linux-kernel, netdev
In-Reply-To: <20260702125525.2230427-3-niklas.soderlund+renesas@ragnatech.se>
[-- Attachment #1: Type: text/plain, Size: 254 bytes --]
Hello,
On Thu, Jul 02, 2026 at 02:55:24PM +0200, Niklas Söderlund wrote:
> +#include <linux/mod_devicetable.h>
Please don't add new users for this header file. Only use those
<linux/device-id/*.h> that you actually need (if any).
Thanks
Uwe
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply
* [PATCH net-next v2 00/10] net: dsa: microchip: add PTP support for KSZ8463
From: Bastien Curutchet (Schneider Electric) @ 2026-07-13 7:11 UTC (permalink / raw)
To: Woojung Huh, UNGLinuxDriver, Andrew Lunn, Vladimir Oltean,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Richard Cochran, Russell King, Simon Horman, Maxime Chevallier
Cc: Pascal Eberhard, Miquèl Raynal, Thomas Petazzoni, netdev,
linux-kernel, Bastien Curutchet (Schneider Electric)
Hi all,
This series adds PTP support for the KSZ8463.
The KSZ8463 differs quite a lot from other KSZ switches supporting PTP:
it has a different interrupt logic and a different 'PTP engine'.
This second iteration addresses most of the Sashiko comments of the v1.
In [1] I said I would re-order the patches to please Sashiko but in fact
it would lead to 'unused function' warnings so I finally decided to keep
this order.
Patches 1 to 4 add interrupt support for the KSZ8463
Patches 5 to 10 add the PTP support for the KSZ8463
[1]: https://lore.kernel.org/r/20260709-ksz-new-ptp-v1-0-344f02fe739e@bootlin.com
Signed-off-by: Bastien Curutchet (Schneider Electric) <bastien.curutchet@bootlin.com>
---
Changes in v2:
- PATCH 1:
- Modify the commit log to explain why IRQ setup isn't done on KSZ8463
- PATCH 4:
- Fix the error code path in ksz8463_ptp_port_irq_setup().
- Change the IRQ request/free orders to register/unregister the top level
handler first/last
- PATCH 8:
- Free the skb if its linearization fails
- Downgrade ip_summed to CHECKSUM_NONE when clearing the reserved
field of the PTP header
- PATCH 10:
- Ensure the skb is linearized before accessing the PTP header
- Link to v1: https://lore.kernel.org/r/20260709-ksz-new-ptp-v1-0-344f02fe739e@bootlin.com
---
Bastien Curutchet (Schneider Electric) (10):
net: dsa: microchip: implement ksz8463_setup()
net: dsa: microchip: split ksz8_config_cpu_port()
net: dsa: microchip: allow the use of other IRQ operations.
net: dsa: microchip: add PTP interrupt handling for KSZ8463
net: dsa: microchip: adapt port offset for KSZ8463's PTP register
net: dsa: tag_ksz: move the KSZ8795 tag handling below ksz_xmit_timestamp()
net: dsa: tag_ksz: share code for KSZ8795 and KSZ9893 xmit operations
net: dsa: microchip: add KSZ8463 tail tag handling
net: dsa: microchip: explicitly enable detection of L2 PTP frames
net: dsa: microchip: add two-steps PTP support for KSZ8463
drivers/net/dsa/microchip/ksz8.c | 311 +++++++++++++++++++++++++++-----
drivers/net/dsa/microchip/ksz8_reg.h | 1 +
drivers/net/dsa/microchip/ksz_common.c | 15 +-
drivers/net/dsa/microchip/ksz_common.h | 6 +
drivers/net/dsa/microchip/ksz_ptp.c | 266 ++++++++++++++++++++++++++-
drivers/net/dsa/microchip/ksz_ptp.h | 16 ++
drivers/net/dsa/microchip/ksz_ptp_reg.h | 10 +
include/net/dsa.h | 2 +
net/dsa/tag_ksz.c | 232 +++++++++++++++---------
9 files changed, 721 insertions(+), 138 deletions(-)
---
base-commit: a6cfd2762eb18cde4eb34906599ffa7b07c2ed60
change-id: 20260708-ksz-new-ptp-f98db30a8a4f
Best regards,
--
Bastien Curutchet (Schneider Electric) <bastien.curutchet@bootlin.com>
^ permalink raw reply
* [PATCH net-next v2 01/10] net: dsa: microchip: implement ksz8463_setup()
From: Bastien Curutchet (Schneider Electric) @ 2026-07-13 7:11 UTC (permalink / raw)
To: Woojung Huh, UNGLinuxDriver, Andrew Lunn, Vladimir Oltean,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Richard Cochran, Russell King, Simon Horman, Maxime Chevallier
Cc: Pascal Eberhard, Miquèl Raynal, Thomas Petazzoni, netdev,
linux-kernel, Bastien Curutchet (Schneider Electric)
In-Reply-To: <20260713-ksz-new-ptp-v2-0-7209c05058ef@bootlin.com>
KSZ8463 uses the ksz8_setup() as setup() callback for its DSA
operations. Its behavior is quite different than other KSZ8 switches,
especially its interrupt scheme.
Remove from the ksz8_setup()/ksz8_reset_switch() everything that is
ksz8463-related.
Create a dedicated ksz8463_setup() and a ksz8463_reset_switch() function.
This new ksz8463_setup() is widely inspired from ksz8_setup, it has
following differences:
- it doesn't configure drive strength (not supported on KSZ8463)
- it uses the ksz8463_reset_switch()
- it doesn't call ksz8_handle_global_errata() (the handled errata only
affects the KSZ87xx variant)
- it doesn't configure IRQs. Note that ksz8_setup()'s IRQ initialization
doesn't work for the KSZ8463 anyway. Proper support for it comes in
upcoming patches.
Remove the teardown implementation from the KSZ8463 operations. Since
PTP and interrupts aren't setup, the common ksz_teardown() wouldn't do
anything anyway.
Signed-off-by: Bastien Curutchet (Schneider Electric) <bastien.curutchet@bootlin.com>
---
drivers/net/dsa/microchip/ksz8.c | 126 +++++++++++++++++++++++++++++++++++----
1 file changed, 114 insertions(+), 12 deletions(-)
diff --git a/drivers/net/dsa/microchip/ksz8.c b/drivers/net/dsa/microchip/ksz8.c
index c4c769028a20..5ed63f425013 100644
--- a/drivers/net/dsa/microchip/ksz8.c
+++ b/drivers/net/dsa/microchip/ksz8.c
@@ -181,6 +181,14 @@ static int ksz8_pme_pwrite8(struct ksz_device *dev, int port, int offset, u8 dat
return ksz8_ind_write8(dev, table, (u8)(offset), data);
}
+static int ksz8463_reset_switch(struct ksz_device *dev)
+{
+ ksz_cfg(dev, KSZ8463_REG_SW_RESET, KSZ8463_GLOBAL_SOFTWARE_RESET, true);
+ ksz_cfg(dev, KSZ8463_REG_SW_RESET, KSZ8463_GLOBAL_SOFTWARE_RESET,
+ false);
+ return 0;
+}
+
static int ksz8_reset_switch(struct ksz_device *dev)
{
if (ksz_is_ksz88x3(dev)) {
@@ -189,11 +197,6 @@ static int ksz8_reset_switch(struct ksz_device *dev)
KSZ8863_GLOBAL_SOFTWARE_RESET | KSZ8863_PCS_RESET, true);
ksz_cfg(dev, KSZ8863_REG_SW_RESET,
KSZ8863_GLOBAL_SOFTWARE_RESET | KSZ8863_PCS_RESET, false);
- } else if (ksz_is_ksz8463(dev)) {
- ksz_cfg(dev, KSZ8463_REG_SW_RESET,
- KSZ8463_GLOBAL_SOFTWARE_RESET, true);
- ksz_cfg(dev, KSZ8463_REG_SW_RESET,
- KSZ8463_GLOBAL_SOFTWARE_RESET, false);
} else {
/* reset switch */
ksz_write8(dev, REG_POWER_MANAGEMENT_1,
@@ -2300,6 +2303,110 @@ static void ksz88xx_r_mib_stats64(struct ksz_device *dev, int port)
spin_unlock(&mib->stats64_lock);
}
+static int ksz8463_setup(struct dsa_switch *ds)
+{
+ struct ksz_device *dev = ds->priv;
+ u16 storm_mask, storm_rate;
+ struct ksz_port *p;
+ const u16 *regs;
+ int i, ret;
+
+ regs = dev->info->regs;
+
+ dev->vlan_cache = devm_kcalloc(dev->dev, sizeof(struct vlan_table),
+ dev->info->num_vlans, GFP_KERNEL);
+ if (!dev->vlan_cache)
+ return -ENOMEM;
+
+ ret = ksz8463_reset_switch(dev);
+ if (ret) {
+ dev_err(ds->dev, "failed to reset switch\n");
+ return ret;
+ }
+
+ /* set broadcast storm protection 10% rate */
+ storm_mask = BROADCAST_STORM_RATE;
+ storm_rate = (BROADCAST_STORM_VALUE * BROADCAST_STORM_PROT_RATE) / 100;
+ storm_mask = swab16(storm_mask);
+ storm_rate = swab16(storm_rate);
+ regmap_update_bits(ksz_regmap_16(dev), regs[S_BROADCAST_CTRL],
+ storm_mask, storm_rate);
+
+ ksz8_config_cpu_port(ds);
+
+ ksz8_enable_stp_addr(dev);
+
+ ds->num_tx_queues = dev->info->num_tx_queues;
+
+ regmap_update_bits(ksz_regmap_8(dev), regs[S_MULTICAST_CTRL],
+ MULTICAST_STORM_DISABLE, MULTICAST_STORM_DISABLE);
+
+ ksz_init_mib_timer(dev);
+
+ ds->configure_vlan_while_not_filtering = false;
+ ds->dscp_prio_mapping_is_global = true;
+ ds->mtu_enforcement_ingress = true;
+
+ /* We rely on software untagging on the CPU port, so that we
+ * can support both tagged and untagged VLANs
+ */
+ ds->untag_bridge_pvid = true;
+
+ /* VLAN filtering is partly controlled by the global VLAN
+ * Enable flag
+ */
+ ds->vlan_filtering_is_global = true;
+
+ /* Enable automatic fast aging when link changed detected. */
+ ksz_cfg(dev, S_LINK_AGING_CTRL, SW_LINK_AUTO_AGING, true);
+
+ /* Enable aggressive back off algorithm in half duplex mode. */
+ ret = ksz_rmw8(dev, REG_SW_CTRL_1, SW_AGGR_BACKOFF, SW_AGGR_BACKOFF);
+ if (ret)
+ return ret;
+
+ /*
+ * Make sure unicast VLAN boundary is set as default and
+ * enable no excessive collision drop.
+ */
+ ret = ksz_rmw8(dev, REG_SW_CTRL_2,
+ UNICAST_VLAN_BOUNDARY | NO_EXC_COLLISION_DROP,
+ UNICAST_VLAN_BOUNDARY | NO_EXC_COLLISION_DROP);
+ if (ret)
+ return ret;
+
+ ksz_cfg(dev, S_REPLACE_VID_CTRL, SW_REPLACE_VID, false);
+
+ ksz_cfg(dev, S_MIRROR_CTRL, SW_MIRROR_RX_TX, false);
+
+ for (i = 0; i < (dev->info->num_vlans / 4); i++)
+ ksz8_r_vlan_entries(dev, i);
+
+ /* Start with learning disabled on standalone user ports, and enabled
+ * on the CPU port. In lack of other finer mechanisms, learning on the
+ * CPU port will avoid flooding bridge local addresses on the network
+ * in some cases.
+ */
+ p = &dev->ports[dev->cpu_port];
+ p->learning = true;
+
+ ret = ksz_mdio_register(dev);
+ if (ret < 0) {
+ dev_err(dev->dev, "failed to register the mdio");
+ return ret;
+ }
+
+ ret = ksz_dcb_init(dev);
+ if (ret)
+ return ret;
+
+ /* start switch */
+ regmap_update_bits(ksz_regmap_8(dev), regs[S_START_CTRL],
+ SW_START, SW_START);
+
+ return 0;
+}
+
/**
* ksz88x3_drive_strength_write() - Set the drive strength configuration for
* KSZ8863 compatible chip variants.
@@ -2445,10 +2552,6 @@ static int ksz8_setup(struct dsa_switch *ds)
/* set broadcast storm protection 10% rate */
storm_mask = BROADCAST_STORM_RATE;
storm_rate = (BROADCAST_STORM_VALUE * BROADCAST_STORM_PROT_RATE) / 100;
- if (ksz_is_ksz8463(dev)) {
- storm_mask = swab16(storm_mask);
- storm_rate = swab16(storm_rate);
- }
regmap_update_bits(ksz_regmap_16(dev), regs[S_BROADCAST_CTRL],
storm_mask, storm_rate);
@@ -2499,7 +2602,7 @@ static int ksz8_setup(struct dsa_switch *ds)
ksz_cfg(dev, S_MIRROR_CTRL, SW_MIRROR_RX_TX, false);
- if (!ksz_is_ksz88x3(dev) && !ksz_is_ksz8463(dev))
+ if (!ksz_is_ksz88x3(dev))
ksz_cfg(dev, REG_SW_CTRL_19, SW_INS_TAG_ENABLE, true);
for (i = 0; i < (dev->info->num_vlans / 4); i++)
@@ -2889,8 +2992,7 @@ const struct ksz_dev_ops ksz88xx_dev_ops = {
const struct dsa_switch_ops ksz8463_switch_ops = {
.get_tag_protocol = ksz8463_get_tag_protocol,
.connect_tag_protocol = ksz8463_connect_tag_protocol,
- .setup = ksz8_setup,
- .teardown = ksz_teardown,
+ .setup = ksz8463_setup,
.phy_read = ksz8463_phy_read16,
.phy_write = ksz8463_phy_write16,
.phylink_get_caps = ksz8_phylink_get_caps,
--
2.54.0
^ permalink raw reply related
* [PATCH net-next v2 02/10] net: dsa: microchip: split ksz8_config_cpu_port()
From: Bastien Curutchet (Schneider Electric) @ 2026-07-13 7:11 UTC (permalink / raw)
To: Woojung Huh, UNGLinuxDriver, Andrew Lunn, Vladimir Oltean,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Richard Cochran, Russell King, Simon Horman, Maxime Chevallier
Cc: Pascal Eberhard, Miquèl Raynal, Thomas Petazzoni, netdev,
linux-kernel, Bastien Curutchet (Schneider Electric)
In-Reply-To: <20260713-ksz-new-ptp-v2-0-7209c05058ef@bootlin.com>
ksz8_config_cpu_port() is only called twice, once by ksz8_setup() and
once by ksz8463_setup(). It contains a ksz8463 branch that could be
avoided in the ksz8_setup() case and a ksz87xx/ksz88xx branches that
could be avoided in ksz8463_setup() case.
Create ksz8463_config_cpu_port() that only handles the ksz8463 case and
remove the ksz8463 specificities from the common ksz8_config_cpu_port().
Signed-off-by: Bastien Curutchet (Schneider Electric) <bastien.curutchet@bootlin.com>
---
drivers/net/dsa/microchip/ksz8.c | 73 +++++++++++++++++++++++++---------------
1 file changed, 45 insertions(+), 28 deletions(-)
diff --git a/drivers/net/dsa/microchip/ksz8.c b/drivers/net/dsa/microchip/ksz8.c
index 5ed63f425013..3bbca6f9cfc5 100644
--- a/drivers/net/dsa/microchip/ksz8.c
+++ b/drivers/net/dsa/microchip/ksz8.c
@@ -2029,6 +2029,50 @@ static void ksz88x3_config_rmii_clk(struct ksz_device *dev)
KSZ88X3_PORT3_RMII_CLK_INTERNAL, rmii_clk_internal);
}
+static void ksz8463_config_cpu_port(struct dsa_switch *ds)
+{
+ struct ksz_device *dev = ds->priv;
+ struct ksz_port *p;
+ u8 fiber_ports = 0;
+ const u32 *masks;
+ const u16 *regs;
+ int i;
+
+ masks = dev->info->masks;
+ regs = dev->info->regs;
+
+ ksz_cfg(dev, regs[S_TAIL_TAG_CTRL], masks[SW_TAIL_TAG_ENABLE], true);
+
+ ksz8_port_setup(dev, dev->cpu_port, true);
+
+ for (i = 0; i < dev->phy_port_cnt; i++)
+ ksz_port_stp_state_set(ds, i, BR_STATE_DISABLED);
+
+ for (i = 0; i < dev->phy_port_cnt; i++) {
+ p = &dev->ports[i];
+ ksz_port_cfg(dev, i, regs[P_STP_CTRL], PORT_FORCE_FLOW_CTRL,
+ p->fiber);
+ if (p->fiber)
+ fiber_ports |= (1 << i);
+ }
+
+ /* Setup fiber ports. */
+ if (fiber_ports) {
+ fiber_ports &= 3;
+ regmap_update_bits(ksz_regmap_16(dev), KSZ8463_REG_CFG_CTRL,
+ fiber_ports << PORT_COPPER_MODE_S,
+ 0);
+ regmap_update_bits(ksz_regmap_16(dev), KSZ8463_REG_DSP_CTRL_6,
+ COPPER_RECEIVE_ADJUSTMENT, 0);
+ }
+
+ /* Turn off PTP function as the switch enables it by default */
+ regmap_update_bits(ksz_regmap_16(dev), KSZ8463_PTP_MSG_CONF1,
+ PTP_ENABLE, 0);
+ regmap_update_bits(ksz_regmap_16(dev), KSZ8463_PTP_CLK_CTRL,
+ PTP_CLK_ENABLE, 0);
+}
+
static void ksz8_config_cpu_port(struct dsa_switch *ds)
{
struct ksz_device *dev = ds->priv;
@@ -2036,7 +2080,6 @@ static void ksz8_config_cpu_port(struct dsa_switch *ds)
const u32 *masks;
const u16 *regs;
u8 remote;
- u8 fiber_ports = 0;
int i;
masks = dev->info->masks;
@@ -2067,32 +2110,6 @@ static void ksz8_config_cpu_port(struct dsa_switch *ds)
else
ksz_port_cfg(dev, i, regs[P_STP_CTRL],
PORT_FORCE_FLOW_CTRL, false);
- if (p->fiber)
- fiber_ports |= (1 << i);
- }
- if (ksz_is_ksz8463(dev)) {
- /* Setup fiber ports. */
- if (fiber_ports) {
- fiber_ports &= 3;
- regmap_update_bits(ksz_regmap_16(dev),
- KSZ8463_REG_CFG_CTRL,
- fiber_ports << PORT_COPPER_MODE_S,
- 0);
- regmap_update_bits(ksz_regmap_16(dev),
- KSZ8463_REG_DSP_CTRL_6,
- COPPER_RECEIVE_ADJUSTMENT, 0);
- }
-
- /* Turn off PTP function as the switch's proprietary way of
- * handling timestamp is not supported in current Linux PTP
- * stack implementation.
- */
- regmap_update_bits(ksz_regmap_16(dev),
- KSZ8463_PTP_MSG_CONF1,
- PTP_ENABLE, 0);
- regmap_update_bits(ksz_regmap_16(dev),
- KSZ8463_PTP_CLK_CTRL,
- PTP_CLK_ENABLE, 0);
}
}
@@ -2332,7 +2349,7 @@ static int ksz8463_setup(struct dsa_switch *ds)
regmap_update_bits(ksz_regmap_16(dev), regs[S_BROADCAST_CTRL],
storm_mask, storm_rate);
- ksz8_config_cpu_port(ds);
+ ksz8463_config_cpu_port(ds);
ksz8_enable_stp_addr(dev);
--
2.54.0
^ permalink raw reply related
* [PATCH net-next v2 03/10] net: dsa: microchip: allow the use of other IRQ operations.
From: Bastien Curutchet (Schneider Electric) @ 2026-07-13 7:11 UTC (permalink / raw)
To: Woojung Huh, UNGLinuxDriver, Andrew Lunn, Vladimir Oltean,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Richard Cochran, Russell King, Simon Horman, Maxime Chevallier
Cc: Pascal Eberhard, Miquèl Raynal, Thomas Petazzoni, netdev,
linux-kernel, Bastien Curutchet (Schneider Electric)
In-Reply-To: <20260713-ksz-new-ptp-v2-0-7209c05058ef@bootlin.com>
The IRQ setup uses an hardcoded set of IRQ operations. These operations
don't fit with the KSZ8463 which has an inverted bit logic (it uses an
'enable irq' register instead of a 'mask irq' one) and 16-bits registers.
Take the IRQ domain operations as input of ksz_irq_common_setup() to
allow KSZ8463 to use the already existing setup with its own set of IRQ
operations.
Expose ksz_irq_common_setup() and ksz_irq_bus_lock/unlock() so they can
be used by ksz8.c.
Signed-off-by: Bastien Curutchet (Schneider Electric) <bastien.curutchet@bootlin.com>
---
drivers/net/dsa/microchip/ksz_common.c | 15 ++++++++-------
drivers/net/dsa/microchip/ksz_common.h | 5 +++++
2 files changed, 13 insertions(+), 7 deletions(-)
diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c
index 67ab6ddb9e53..ff1be540b546 100644
--- a/drivers/net/dsa/microchip/ksz_common.c
+++ b/drivers/net/dsa/microchip/ksz_common.c
@@ -2419,14 +2419,14 @@ static void ksz_irq_unmask(struct irq_data *d)
kirq->masked &= ~BIT(d->hwirq);
}
-static void ksz_irq_bus_lock(struct irq_data *d)
+void ksz_irq_bus_lock(struct irq_data *d)
{
struct ksz_irq *kirq = irq_data_get_irq_chip_data(d);
mutex_lock(&kirq->dev->lock_irq);
}
-static void ksz_irq_bus_sync_unlock(struct irq_data *d)
+void ksz_irq_bus_sync_unlock(struct irq_data *d)
{
struct ksz_irq *kirq = irq_data_get_irq_chip_data(d);
struct ksz_device *dev = kirq->dev;
@@ -2504,14 +2504,15 @@ static irqreturn_t ksz_irq_thread_fn(int irq, void *dev_id)
return (nhandled > 0 ? IRQ_HANDLED : IRQ_NONE);
}
-static int ksz_irq_common_setup(struct ksz_device *dev, struct ksz_irq *kirq)
+int ksz_irq_common_setup(struct ksz_device *dev, struct ksz_irq *kirq,
+ const struct irq_domain_ops *ops)
{
int ret, n;
kirq->dev = dev;
- kirq->domain = irq_domain_create_simple(dev_fwnode(dev->dev), kirq->nirqs, 0,
- &ksz_irq_domain_ops, kirq);
+ kirq->domain = irq_domain_create_simple(dev_fwnode(dev->dev),
+ kirq->nirqs, 0, ops, kirq);
if (!kirq->domain)
return -ENOMEM;
@@ -2543,7 +2544,7 @@ int ksz_girq_setup(struct ksz_device *dev)
girq->irq_num = dev->irq;
- return ksz_irq_common_setup(dev, girq);
+ return ksz_irq_common_setup(dev, girq, &ksz_irq_domain_ops);
}
int ksz_pirq_setup(struct ksz_device *dev, u8 p)
@@ -2560,7 +2561,7 @@ int ksz_pirq_setup(struct ksz_device *dev, u8 p)
if (!pirq->irq_num)
return -EINVAL;
- return ksz_irq_common_setup(dev, pirq);
+ return ksz_irq_common_setup(dev, pirq, &ksz_irq_domain_ops);
}
void ksz_teardown(struct dsa_switch *ds)
diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h
index acaf70e6f393..0f2abb22ca91 100644
--- a/drivers/net/dsa/microchip/ksz_common.h
+++ b/drivers/net/dsa/microchip/ksz_common.h
@@ -8,6 +8,7 @@
#define __KSZ_COMMON_H
#include <linux/etherdevice.h>
+#include <linux/irqdomain.h>
#include <linux/kernel.h>
#include <linux/mutex.h>
#include <linux/pcs/pcs-xpcs.h>
@@ -508,6 +509,10 @@ int ksz_sw_mdio_write(struct mii_bus *bus, int addr, int regnum, u16 val);
int ksz_parent_mdio_read(struct mii_bus *bus, int addr, int regnum);
int ksz_parent_mdio_write(struct mii_bus *bus, int addr, int regnum, u16 val);
int ksz_mdio_register(struct ksz_device *dev);
+void ksz_irq_bus_lock(struct irq_data *d);
+void ksz_irq_bus_sync_unlock(struct irq_data *d);
+int ksz_irq_common_setup(struct ksz_device *dev, struct ksz_irq *kirq,
+ const struct irq_domain_ops *ops);
int ksz_pirq_setup(struct ksz_device *dev, u8 p);
int ksz_girq_setup(struct ksz_device *dev);
void ksz_irq_free(struct ksz_irq *kirq);
--
2.54.0
^ permalink raw reply related
* [PATCH net-next v2 05/10] net: dsa: microchip: adapt port offset for KSZ8463's PTP register
From: Bastien Curutchet (Schneider Electric) @ 2026-07-13 7:11 UTC (permalink / raw)
To: Woojung Huh, UNGLinuxDriver, Andrew Lunn, Vladimir Oltean,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Richard Cochran, Russell King, Simon Horman, Maxime Chevallier
Cc: Pascal Eberhard, Miquèl Raynal, Thomas Petazzoni, netdev,
linux-kernel, Bastien Curutchet (Schneider Electric)
In-Reply-To: <20260713-ksz-new-ptp-v2-0-7209c05058ef@bootlin.com>
In KSZ8463 register's layout, the offset between port 1 and port 2
registers isn't the same in the generic control register area than in
the PTP register area. The get_port_addr() always uses the same offset
so it doesn't work when it's used to access PTP registers.
Adapt the port offset in get_port_addr() when the accessed register is
in the PTP area.
Signed-off-by: Bastien Curutchet (Schneider Electric) <bastien.curutchet@bootlin.com>
---
drivers/net/dsa/microchip/ksz8.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/net/dsa/microchip/ksz8.c b/drivers/net/dsa/microchip/ksz8.c
index c099a7005808..5e5bfc5cae2d 100644
--- a/drivers/net/dsa/microchip/ksz8.c
+++ b/drivers/net/dsa/microchip/ksz8.c
@@ -2831,6 +2831,9 @@ static u32 ksz8_get_port_addr(int port, int offset)
static u32 ksz8463_get_port_addr(int port, int offset)
{
+ if (offset >= KSZ8463_PTP_CLK_CTRL)
+ return offset + 0x20 * port;
+
return offset + 0x18 * port;
}
--
2.54.0
^ permalink raw reply related
* [PATCH net-next v2 04/10] net: dsa: microchip: add PTP interrupt handling for KSZ8463
From: Bastien Curutchet (Schneider Electric) @ 2026-07-13 7:11 UTC (permalink / raw)
To: Woojung Huh, UNGLinuxDriver, Andrew Lunn, Vladimir Oltean,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Richard Cochran, Russell King, Simon Horman, Maxime Chevallier
Cc: Pascal Eberhard, Miquèl Raynal, Thomas Petazzoni, netdev,
linux-kernel, Bastien Curutchet (Schneider Electric)
In-Reply-To: <20260713-ksz-new-ptp-v2-0-7209c05058ef@bootlin.com>
KSZ8463 PTP interrupts aren't handled by the driver.
The interrupt layout in KSZ8463 has nothing to do with the other
switches:
- Its global interrupt enable register is 16-bits long and follow an
'enable' logic, instead of a 'mask' one
- all the interrupts of all ports are grouped into one status register
while others have one interrupt register per port
- xdelay_req and pdresp timestamps share one single interrupt bit on the
KSZ8463 while each of them has its own interrupt bit on other switches
Create a KSZ8463-specific set of interrupt domain operations to handle
the global IRQ layer. To limit code duplication, it uses the same
interrupt handler than the other switches. Since other switches have
8-bits registers, only the high-byte of the interrupt status/enable
registers are used. This high-byte is where the PTP interrupts are
located. The low-byte contains the wake-up detection interrupts so if at
some points these interrupts are needed we'll need a bit of rework here.
Create KSZ8463-specific functions to setup the PTP interrupts. The
created IRQ domain is tied to the first port of the KSZ8463. Again,
the same PTP interrupt handler than the others switches is used.
Implement the teardown callback to release the interrupts.
Signed-off-by: Bastien Curutchet (Schneider Electric) <bastien.curutchet@bootlin.com>
---
drivers/net/dsa/microchip/ksz8.c | 93 ++++++++++++++++++++++-
drivers/net/dsa/microchip/ksz_ptp.c | 131 ++++++++++++++++++++++++++++++++
drivers/net/dsa/microchip/ksz_ptp.h | 9 +++
drivers/net/dsa/microchip/ksz_ptp_reg.h | 6 ++
4 files changed, 237 insertions(+), 2 deletions(-)
diff --git a/drivers/net/dsa/microchip/ksz8.c b/drivers/net/dsa/microchip/ksz8.c
index 3bbca6f9cfc5..c099a7005808 100644
--- a/drivers/net/dsa/microchip/ksz8.c
+++ b/drivers/net/dsa/microchip/ksz8.c
@@ -36,6 +36,13 @@
#include "ksz8_reg.h"
#include "ksz8.h"
+/*
+ * We use only the high-byte (so odd addresses) of the 16-bits registers to fit
+ * in the common IRQ framework
+ */
+#define KSZ8463_REG_ISR 0x191
+#define KSZ8463_REG_IER 0x193
+
/* ksz88x3_drive_strengths - Drive strength mapping for KSZ8863, KSZ8873, ..
* variants.
* This values are documented in KSZ8873 and KSZ8863 datasheets.
@@ -181,6 +188,58 @@ static int ksz8_pme_pwrite8(struct ksz_device *dev, int port, int offset, u8 dat
return ksz8_ind_write8(dev, table, (u8)(offset), data);
}
+static void ksz8463_irq_mask(struct irq_data *d)
+{
+ struct ksz_irq *kirq = irq_data_get_irq_chip_data(d);
+
+ kirq->masked &= ~BIT(d->hwirq);
+}
+
+static void ksz8463_irq_unmask(struct irq_data *d)
+{
+ struct ksz_irq *kirq = irq_data_get_irq_chip_data(d);
+
+ kirq->masked |= BIT(d->hwirq);
+}
+
+static const struct irq_chip ksz8463_irq_chip = {
+ .name = "ksz8463-irq",
+ .irq_mask = ksz8463_irq_mask,
+ .irq_unmask = ksz8463_irq_unmask,
+ .irq_bus_lock = ksz_irq_bus_lock,
+ .irq_bus_sync_unlock = ksz_irq_bus_sync_unlock,
+};
+
+static int ksz8463_irq_domain_map(struct irq_domain *d,
+ unsigned int irq, irq_hw_number_t hwirq)
+{
+ irq_set_chip_data(irq, d->host_data);
+ irq_set_chip_and_handler(irq, &ksz8463_irq_chip, handle_level_irq);
+ irq_set_noprobe(irq);
+
+ return 0;
+}
+
+static const struct irq_domain_ops ksz8463_irq_domain_ops = {
+ .map = ksz8463_irq_domain_map,
+ .xlate = irq_domain_xlate_twocell,
+};
+
+static int ksz8463_girq_setup(struct ksz_device *dev)
+{
+ struct ksz_irq *girq = &dev->girq;
+
+ girq->nirqs = 8;
+ girq->reg_mask = KSZ8463_REG_IER;
+ girq->reg_status = KSZ8463_REG_ISR;
+ girq->masked = 0;
+ snprintf(girq->name, sizeof(girq->name), "ksz8463-girq");
+
+ girq->irq_num = dev->irq;
+
+ return ksz_irq_common_setup(dev, girq, &ksz8463_irq_domain_ops);
+}
+
static int ksz8463_reset_switch(struct ksz_device *dev)
{
ksz_cfg(dev, KSZ8463_REG_SW_RESET, KSZ8463_GLOBAL_SOFTWARE_RESET, true);
@@ -2407,21 +2466,50 @@ static int ksz8463_setup(struct dsa_switch *ds)
p = &dev->ports[dev->cpu_port];
p->learning = true;
+ if (dev->irq > 0) {
+ ret = ksz8463_girq_setup(dev);
+ if (ret)
+ return ret;
+
+ ret = ksz8463_ptp_irq_setup(ds);
+ if (ret)
+ goto free_girq;
+ }
+
ret = ksz_mdio_register(dev);
if (ret < 0) {
dev_err(dev->dev, "failed to register the mdio");
- return ret;
+ goto free_ptp_irq;
}
ret = ksz_dcb_init(dev);
if (ret)
- return ret;
+ goto free_ptp_irq;
/* start switch */
regmap_update_bits(ksz_regmap_8(dev), regs[S_START_CTRL],
SW_START, SW_START);
return 0;
+
+free_ptp_irq:
+ if (dev->irq > 0)
+ ksz8463_ptp_irq_free(ds);
+free_girq:
+ if (dev->irq > 0)
+ ksz_irq_free(&dev->girq);
+
+ return ret;
+}
+
+static void ksz8463_teardown(struct dsa_switch *ds)
+{
+ struct ksz_device *dev = ds->priv;
+
+ if (dev->irq > 0) {
+ ksz8463_ptp_irq_free(ds);
+ ksz_irq_free(&dev->girq);
+ }
}
/**
@@ -3010,6 +3098,7 @@ const struct dsa_switch_ops ksz8463_switch_ops = {
.get_tag_protocol = ksz8463_get_tag_protocol,
.connect_tag_protocol = ksz8463_connect_tag_protocol,
.setup = ksz8463_setup,
+ .teardown = ksz8463_teardown,
.phy_read = ksz8463_phy_read16,
.phy_write = ksz8463_phy_write16,
.phylink_get_caps = ksz8_phylink_get_caps,
diff --git a/drivers/net/dsa/microchip/ksz_ptp.c b/drivers/net/dsa/microchip/ksz_ptp.c
index 8b98039320ad..89fbc1253f25 100644
--- a/drivers/net/dsa/microchip/ksz_ptp.c
+++ b/drivers/net/dsa/microchip/ksz_ptp.c
@@ -32,6 +32,15 @@
#define KSZ_PTP_INT_START 13
+/*
+ * PTP interrupt bit is the bit 12 of the 16-bits ISR/IER. But ksz_common.c only
+ * accesses the high-byte of these registers so the PTP interrupt bit becomes 4.
+ */
+#define KSZ8463_SRC_PTP_INT 4
+#define KSZ8463_PTP_PORT1_INT_START 12
+#define KSZ8463_PTP_PORT2_INT_START 14
+#define KSZ8463_PTP_INT_START KSZ8463_PTP_PORT1_INT_START
+
static int ksz_ptp_tou_gpio(struct ksz_device *dev)
{
int ret;
@@ -1129,6 +1138,128 @@ static int ksz_ptp_msg_irq_setup(struct ksz_port *port, u8 n)
return ret;
}
+static int ksz8463_ptp_port_irq_setup(struct ksz_irq *ptpirq,
+ struct ksz_port *port, int hw_irq)
+{
+ u16 ts_reg[] = {KSZ8463_REG_PORT_SYNC_TS, KSZ8463_REG_PORT_DREQ_TS};
+ static const char * const name[] = {"sync-msg", "delay-msg"};
+ const struct ksz_dev_ops *ops = port->ksz_dev->dev_ops;
+ struct ksz_ptp_irq *ptpmsg_irq;
+ int ret;
+ int i;
+
+ init_completion(&port->tstamp_msg_comp);
+
+ for (i = 0; i < 2; i++) {
+ ptpmsg_irq = &port->ptpmsg_irq[i];
+ ptpmsg_irq->num = irq_create_mapping(ptpirq->domain,
+ hw_irq + i);
+ if (!ptpmsg_irq->num) {
+ ret = -EINVAL;
+ goto release_msg_irq;
+ }
+
+ ptpmsg_irq->port = port;
+ ptpmsg_irq->ts_reg = ops->get_port_addr(port->num, ts_reg[i]);
+
+ strscpy(ptpmsg_irq->name, name[i]);
+
+ ret = request_threaded_irq(ptpmsg_irq->num, NULL,
+ ksz_ptp_msg_thread_fn, IRQF_ONESHOT,
+ ptpmsg_irq->name, ptpmsg_irq);
+ if (ret) {
+ irq_dispose_mapping(ptpmsg_irq->num);
+ goto release_msg_irq;
+ }
+ }
+
+ return 0;
+
+release_msg_irq:
+ while (i--)
+ ksz_ptp_msg_irq_free(port, i);
+
+ return ret;
+}
+
+static void ksz8463_ptp_port_irq_teardown(struct ksz_port *port)
+{
+ int i;
+
+ for (i = 0; i < 2; i++)
+ ksz_ptp_msg_irq_free(port, i);
+}
+
+int ksz8463_ptp_irq_setup(struct dsa_switch *ds)
+{
+ struct ksz_device *dev = ds->priv;
+ struct ksz_port *port1, *port2;
+ struct ksz_irq *ptpirq;
+ int ret;
+
+ port1 = &dev->ports[0];
+ port2 = &dev->ports[1];
+ ptpirq = &port1->ptpirq;
+
+ ptpirq->irq_num = irq_find_mapping(dev->girq.domain,
+ KSZ8463_SRC_PTP_INT);
+ if (!ptpirq->irq_num)
+ return -EINVAL;
+
+ ptpirq->dev = dev;
+ ptpirq->nirqs = 4;
+ ptpirq->reg_mask = KSZ8463_PTP_TS_IER;
+ ptpirq->reg_status = KSZ8463_PTP_TS_ISR;
+ ptpirq->irq0_offset = KSZ8463_PTP_INT_START;
+ snprintf(ptpirq->name, sizeof(ptpirq->name), "ptp-irq");
+
+ ptpirq->domain = irq_domain_create_linear(dev_fwnode(dev->dev),
+ ptpirq->nirqs,
+ &ksz_ptp_irq_domain_ops,
+ ptpirq);
+ if (!ptpirq->domain)
+ return -ENOMEM;
+
+ ret = ksz8463_ptp_port_irq_setup(ptpirq, port1,
+ KSZ8463_PTP_PORT1_INT_START - KSZ8463_PTP_INT_START);
+ if (ret)
+ goto release_domain;
+
+ ret = ksz8463_ptp_port_irq_setup(ptpirq, port2,
+ KSZ8463_PTP_PORT2_INT_START - KSZ8463_PTP_INT_START);
+ if (ret)
+ goto free_port1;
+
+ ret = request_threaded_irq(ptpirq->irq_num, NULL, ksz_ptp_irq_thread_fn,
+ IRQF_ONESHOT, ptpirq->name, ptpirq);
+ if (ret)
+ goto free_port2;
+
+ return 0;
+
+free_port2:
+ ksz8463_ptp_port_irq_teardown(port2);
+free_port1:
+ ksz8463_ptp_port_irq_teardown(port1);
+release_domain:
+ irq_domain_remove(ptpirq->domain);
+
+ return ret;
+}
+
+void ksz8463_ptp_irq_free(struct dsa_switch *ds)
+{
+ struct ksz_device *dev = ds->priv;
+ struct ksz_port *port1 = &dev->ports[0];
+ struct ksz_port *port2 = &dev->ports[1];
+ struct ksz_irq *ptpirq = &port1->ptpirq;
+
+ free_irq(ptpirq->irq_num, ptpirq);
+ ksz8463_ptp_port_irq_teardown(port2);
+ ksz8463_ptp_port_irq_teardown(port1);
+ irq_domain_remove(ptpirq->domain);
+}
+
int ksz_ptp_irq_setup(struct dsa_switch *ds, u8 p)
{
struct ksz_device *dev = ds->priv;
diff --git a/drivers/net/dsa/microchip/ksz_ptp.h b/drivers/net/dsa/microchip/ksz_ptp.h
index 3086e519b1b6..11408580031d 100644
--- a/drivers/net/dsa/microchip/ksz_ptp.h
+++ b/drivers/net/dsa/microchip/ksz_ptp.h
@@ -50,6 +50,8 @@ bool ksz_port_rxtstamp(struct dsa_switch *ds, int port, struct sk_buff *skb,
unsigned int type);
int ksz_ptp_irq_setup(struct dsa_switch *ds, u8 p);
void ksz_ptp_irq_free(struct dsa_switch *ds, u8 p);
+int ksz8463_ptp_irq_setup(struct dsa_switch *ds);
+void ksz8463_ptp_irq_free(struct dsa_switch *ds);
#else
@@ -72,6 +74,13 @@ static inline int ksz_ptp_irq_setup(struct dsa_switch *ds, u8 p)
static inline void ksz_ptp_irq_free(struct dsa_switch *ds, u8 p) {}
+static inline int ksz8463_ptp_irq_setup(struct dsa_switch *ds)
+{
+ return 0;
+}
+
+static inline void ksz8463_ptp_irq_free(struct dsa_switch *ds) {}
+
#define ksz_get_ts_info NULL
#define ksz_hwtstamp_get NULL
diff --git a/drivers/net/dsa/microchip/ksz_ptp_reg.h b/drivers/net/dsa/microchip/ksz_ptp_reg.h
index eab9aecb7fa8..1a669d6ee889 100644
--- a/drivers/net/dsa/microchip/ksz_ptp_reg.h
+++ b/drivers/net/dsa/microchip/ksz_ptp_reg.h
@@ -121,6 +121,12 @@
#define REG_PTP_PORT_SYNC_TS 0x0C0C
#define REG_PTP_PORT_PDRESP_TS 0x0C10
+#define KSZ8463_REG_PORT_DREQ_TS 0x0648
+#define KSZ8463_REG_PORT_SYNC_TS 0x064C
+#define KSZ8463_REG_PORT_DRESP_TS 0x0650
+#define KSZ8463_PTP_TS_ISR 0x068C
+#define KSZ8463_PTP_TS_IER 0x068E
+
#define REG_PTP_PORT_TX_INT_STATUS__2 0x0C14
#define REG_PTP_PORT_TX_INT_ENABLE__2 0x0C16
--
2.54.0
^ permalink raw reply related
* [PATCH net-next v2 06/10] net: dsa: tag_ksz: move the KSZ8795 tag handling below ksz_xmit_timestamp()
From: Bastien Curutchet (Schneider Electric) @ 2026-07-13 7:11 UTC (permalink / raw)
To: Woojung Huh, UNGLinuxDriver, Andrew Lunn, Vladimir Oltean,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Richard Cochran, Russell King, Simon Horman, Maxime Chevallier
Cc: Pascal Eberhard, Miquèl Raynal, Thomas Petazzoni, netdev,
linux-kernel, Bastien Curutchet (Schneider Electric)
In-Reply-To: <20260713-ksz-new-ptp-v2-0-7209c05058ef@bootlin.com>
Upcoming patch reduces code duplication between KSZ8795 and KSZ9893 by
introducing a common xmit() function. This rework needs the KSZ8795
handlers to be implemented below ksz_defer_xmit().
Do the move now to reduce the noise in next patch.
No functionnal change is intended in this patch.
Signed-off-by: Bastien Curutchet (Schneider Electric) <bastien.curutchet@bootlin.com>
---
net/dsa/tag_ksz.c | 132 +++++++++++++++++++++++++++---------------------------
1 file changed, 66 insertions(+), 66 deletions(-)
diff --git a/net/dsa/tag_ksz.c b/net/dsa/tag_ksz.c
index 67fa89f102e0..f58ce0f0e9e4 100644
--- a/net/dsa/tag_ksz.c
+++ b/net/dsa/tag_ksz.c
@@ -103,72 +103,6 @@ static struct sk_buff *ksz_common_rcv(struct sk_buff *skb,
return skb;
}
-/*
- * For Ingress (Host -> KSZ8795), 1 byte is added before FCS.
- * ---------------------------------------------------------------------------
- * DA(6bytes)|SA(6bytes)|....|Data(nbytes)|tag(1byte)|FCS(4bytes)
- * ---------------------------------------------------------------------------
- * tag : each bit represents port (eg, 0x01=port1, 0x02=port2, 0x10=port5)
- *
- * For Egress (KSZ8795 -> Host), 1 byte is added before FCS.
- * ---------------------------------------------------------------------------
- * DA(6bytes)|SA(6bytes)|....|Data(nbytes)|tag0(1byte)|FCS(4bytes)
- * ---------------------------------------------------------------------------
- * tag0 : zero-based value represents port
- * (eg, 0x0=port1, 0x2=port3, 0x3=port4)
- */
-
-#define KSZ8795_TAIL_TAG_EG_PORT_M GENMASK(1, 0)
-#define KSZ8795_TAIL_TAG_OVERRIDE BIT(6)
-#define KSZ8795_TAIL_TAG_LOOKUP BIT(7)
-
-static struct sk_buff *ksz8795_xmit(struct sk_buff *skb, struct net_device *dev)
-{
- struct ethhdr *hdr;
- u8 *tag;
-
- if (skb->ip_summed == CHECKSUM_PARTIAL && skb_checksum_help(skb)) {
- kfree_skb(skb);
- return NULL;
- }
-
- /* Tag encoding */
- tag = skb_put(skb, KSZ_INGRESS_TAG_LEN);
- hdr = skb_eth_hdr(skb);
-
- *tag = dsa_xmit_port_mask(skb, dev);
- if (is_link_local_ether_addr(hdr->h_dest))
- *tag |= KSZ8795_TAIL_TAG_OVERRIDE;
-
- return skb;
-}
-
-static struct sk_buff *ksz8795_rcv(struct sk_buff *skb, struct net_device *dev)
-{
- u8 *tag;
-
- if (skb_linearize(skb)) {
- kfree_skb(skb);
- return NULL;
- }
-
- tag = skb_tail_pointer(skb) - KSZ_EGRESS_TAG_LEN;
-
- return ksz_common_rcv(skb, dev, tag[0] & KSZ8795_TAIL_TAG_EG_PORT_M,
- KSZ_EGRESS_TAG_LEN);
-}
-
-static const struct dsa_device_ops ksz8795_netdev_ops = {
- .name = KSZ8795_NAME,
- .proto = DSA_TAG_PROTO_KSZ8795,
- .xmit = ksz8795_xmit,
- .rcv = ksz8795_rcv,
- .needed_tailroom = KSZ_INGRESS_TAG_LEN,
-};
-
-DSA_TAG_DRIVER(ksz8795_netdev_ops);
-MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_KSZ8795, KSZ8795_NAME);
-
/*
* For Ingress (Host -> KSZ9477), 2/6 bytes are added before FCS.
* ---------------------------------------------------------------------------
@@ -353,6 +287,72 @@ static const struct dsa_device_ops ksz9477_netdev_ops = {
DSA_TAG_DRIVER(ksz9477_netdev_ops);
MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_KSZ9477, KSZ9477_NAME);
+/*
+ * For Ingress (Host -> KSZ8795), 1 byte is added before FCS.
+ * ---------------------------------------------------------------------------
+ * DA(6bytes)|SA(6bytes)|....|Data(nbytes)|tag(1byte)|FCS(4bytes)
+ * ---------------------------------------------------------------------------
+ * tag : each bit represents port (eg, 0x01=port1, 0x02=port2, 0x10=port5)
+ *
+ * For Egress (KSZ8795 -> Host), 1 byte is added before FCS.
+ * ---------------------------------------------------------------------------
+ * DA(6bytes)|SA(6bytes)|....|Data(nbytes)|tag0(1byte)|FCS(4bytes)
+ * ---------------------------------------------------------------------------
+ * tag0 : zero-based value represents port
+ * (eg, 0x0=port1, 0x2=port3, 0x3=port4)
+ */
+
+#define KSZ8795_TAIL_TAG_EG_PORT_M GENMASK(1, 0)
+#define KSZ8795_TAIL_TAG_OVERRIDE BIT(6)
+#define KSZ8795_TAIL_TAG_LOOKUP BIT(7)
+
+static struct sk_buff *ksz8795_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct ethhdr *hdr;
+ u8 *tag;
+
+ if (skb->ip_summed == CHECKSUM_PARTIAL && skb_checksum_help(skb)) {
+ kfree_skb(skb);
+ return NULL;
+ }
+
+ /* Tag encoding */
+ tag = skb_put(skb, KSZ_INGRESS_TAG_LEN);
+ hdr = skb_eth_hdr(skb);
+
+ *tag = dsa_xmit_port_mask(skb, dev);
+ if (is_link_local_ether_addr(hdr->h_dest))
+ *tag |= KSZ8795_TAIL_TAG_OVERRIDE;
+
+ return skb;
+}
+
+static struct sk_buff *ksz8795_rcv(struct sk_buff *skb, struct net_device *dev)
+{
+ u8 *tag;
+
+ if (skb_linearize(skb)) {
+ kfree_skb(skb);
+ return NULL;
+ }
+
+ tag = skb_tail_pointer(skb) - KSZ_EGRESS_TAG_LEN;
+
+ return ksz_common_rcv(skb, dev, tag[0] & KSZ8795_TAIL_TAG_EG_PORT_M,
+ KSZ_EGRESS_TAG_LEN);
+}
+
+static const struct dsa_device_ops ksz8795_netdev_ops = {
+ .name = KSZ8795_NAME,
+ .proto = DSA_TAG_PROTO_KSZ8795,
+ .xmit = ksz8795_xmit,
+ .rcv = ksz8795_rcv,
+ .needed_tailroom = KSZ_INGRESS_TAG_LEN,
+};
+
+DSA_TAG_DRIVER(ksz8795_netdev_ops);
+MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_KSZ8795, KSZ8795_NAME);
+
#define KSZ9893_TAIL_TAG_PRIO GENMASK(4, 3)
#define KSZ9893_TAIL_TAG_OVERRIDE BIT(5)
#define KSZ9893_TAIL_TAG_LOOKUP BIT(6)
--
2.54.0
^ permalink raw reply related
* [PATCH net-next v2 08/10] net: dsa: microchip: add KSZ8463 tail tag handling
From: Bastien Curutchet (Schneider Electric) @ 2026-07-13 7:11 UTC (permalink / raw)
To: Woojung Huh, UNGLinuxDriver, Andrew Lunn, Vladimir Oltean,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Richard Cochran, Russell King, Simon Horman, Maxime Chevallier
Cc: Pascal Eberhard, Miquèl Raynal, Thomas Petazzoni, netdev,
linux-kernel, Bastien Curutchet (Schneider Electric)
In-Reply-To: <20260713-ksz-new-ptp-v2-0-7209c05058ef@bootlin.com>
KSZ8463 uses the KSZ9893 DSA TAG driver. However, the KSZ8463 doesn't
use the tail tag to convey timestamps to the host as KSZ9893 does. It
uses the reserved fields in the PTP header instead.
Add a KSZ8463-specific DSA_TAG driver to handle KSZ8463 timestamps.
There is no information in the tail tag to distinguish PTP packets from
others so use the ptp_classify_raw() helper to find the PTP packets and
extract the timestamp from their PTP headers.
Signed-off-by: Bastien Curutchet (Schneider Electric) <bastien.curutchet@bootlin.com>
---
drivers/net/dsa/microchip/ksz8.c | 4 +--
include/net/dsa.h | 2 ++
net/dsa/tag_ksz.c | 65 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 69 insertions(+), 2 deletions(-)
diff --git a/drivers/net/dsa/microchip/ksz8.c b/drivers/net/dsa/microchip/ksz8.c
index 5e5bfc5cae2d..ac9e8ef5774a 100644
--- a/drivers/net/dsa/microchip/ksz8.c
+++ b/drivers/net/dsa/microchip/ksz8.c
@@ -2966,7 +2966,7 @@ static enum dsa_tag_protocol ksz8463_get_tag_protocol(struct dsa_switch *ds,
int port,
enum dsa_tag_protocol mp)
{
- return DSA_TAG_PROTO_KSZ9893;
+ return DSA_TAG_PROTO_KSZ8463;
}
static int ksz8463_connect_tag_protocol(struct dsa_switch *ds,
@@ -2974,7 +2974,7 @@ static int ksz8463_connect_tag_protocol(struct dsa_switch *ds,
{
struct ksz_tagger_data *tagger_data;
- if (proto != DSA_TAG_PROTO_KSZ9893)
+ if (proto != DSA_TAG_PROTO_KSZ8463)
return -EPROTONOSUPPORT;
tagger_data = ksz_tagger_data(ds);
diff --git a/include/net/dsa.h b/include/net/dsa.h
index 8c16ef23cc10..6f7f5c17b532 100644
--- a/include/net/dsa.h
+++ b/include/net/dsa.h
@@ -59,6 +59,7 @@ struct tc_action;
#define DSA_TAG_PROTO_MXL_GSW1XX_VALUE 31
#define DSA_TAG_PROTO_MXL862_VALUE 32
#define DSA_TAG_PROTO_NETC_VALUE 33
+#define DSA_TAG_PROTO_KSZ8463_VALUE 34
enum dsa_tag_protocol {
DSA_TAG_PROTO_NONE = DSA_TAG_PROTO_NONE_VALUE,
@@ -95,6 +96,7 @@ enum dsa_tag_protocol {
DSA_TAG_PROTO_MXL_GSW1XX = DSA_TAG_PROTO_MXL_GSW1XX_VALUE,
DSA_TAG_PROTO_MXL862 = DSA_TAG_PROTO_MXL862_VALUE,
DSA_TAG_PROTO_NETC = DSA_TAG_PROTO_NETC_VALUE,
+ DSA_TAG_PROTO_KSZ8463 = DSA_TAG_PROTO_KSZ8463_VALUE,
};
struct dsa_switch;
diff --git a/net/dsa/tag_ksz.c b/net/dsa/tag_ksz.c
index f8b40437c5fa..477f2997c1a3 100644
--- a/net/dsa/tag_ksz.c
+++ b/net/dsa/tag_ksz.c
@@ -12,6 +12,7 @@
#include "tag.h"
+#define KSZ8463_NAME "ksz8463"
#define KSZ8795_NAME "ksz8795"
#define KSZ9477_NAME "ksz9477"
#define KSZ9893_NAME "ksz9893"
@@ -396,6 +397,69 @@ static const struct dsa_device_ops ksz9893_netdev_ops = {
DSA_TAG_DRIVER(ksz9893_netdev_ops);
MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_KSZ9893, KSZ9893_NAME);
+#define KSZ8463_TAIL_TAG_PRIO GENMASK(4, 3)
+#define KSZ8463_TAIL_TAG_EG_PORT_M GENMASK(2, 0)
+
+static struct sk_buff *ksz8463_xmit(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ u16 queue_mapping = skb_get_queue_mapping(skb);
+ u8 prio = netdev_txq_to_tc(dev, queue_mapping);
+
+ return ksz_common_xmit(skb, dev, false,
+ FIELD_PREP(KSZ8463_TAIL_TAG_PRIO, prio),
+ 0);
+}
+
+static struct sk_buff *ksz8463_rcv(struct sk_buff *skb, struct net_device *dev)
+{
+ unsigned int len = KSZ_EGRESS_TAG_LEN;
+ struct ptp_header *ptp_hdr;
+ unsigned int ptp_class;
+ unsigned int port;
+ ktime_t ts;
+ u8 *tag;
+
+ if (skb_linearize(skb)) {
+ kfree_skb(skb);
+ return NULL;
+ }
+
+ /* Tag decoding */
+ tag = skb_tail_pointer(skb) - KSZ_EGRESS_TAG_LEN;
+ port = tag[0] & KSZ8463_TAIL_TAG_EG_PORT_M;
+
+ __skb_push(skb, ETH_HLEN);
+ ptp_class = ptp_classify_raw(skb);
+ __skb_pull(skb, ETH_HLEN);
+ if (ptp_class == PTP_CLASS_NONE)
+ goto common_rcv;
+
+ ptp_hdr = ptp_parse_header(skb, ptp_class);
+ if (ptp_hdr) {
+ ts = ksz_decode_tstamp(get_unaligned_be32(&ptp_hdr->reserved2));
+ KSZ_SKB_CB(skb)->tstamp = ts;
+ ptp_hdr->reserved2 = 0;
+ skb->ip_summed = CHECKSUM_NONE;
+ }
+
+common_rcv:
+ return ksz_common_rcv(skb, dev, port, len);
+}
+
+static const struct dsa_device_ops ksz8463_netdev_ops = {
+ .name = KSZ8463_NAME,
+ .proto = DSA_TAG_PROTO_KSZ8463,
+ .xmit = ksz8463_xmit,
+ .rcv = ksz8463_rcv,
+ .connect = ksz_connect,
+ .disconnect = ksz_disconnect,
+ .needed_tailroom = KSZ_INGRESS_TAG_LEN,
+};
+
+DSA_TAG_DRIVER(ksz8463_netdev_ops);
+MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_KSZ8463, KSZ8463_NAME);
+
/* For xmit, 2/6 bytes are added before FCS.
* ---------------------------------------------------------------------------
* DA(6bytes)|SA(6bytes)|....|Data(nbytes)|ts(4bytes)|tag0(1byte)|tag1(1byte)|
@@ -468,6 +532,7 @@ DSA_TAG_DRIVER(lan937x_netdev_ops);
MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_LAN937X, LAN937X_NAME);
static struct dsa_tag_driver *dsa_tag_driver_array[] = {
+ &DSA_TAG_DRIVER_NAME(ksz8463_netdev_ops),
&DSA_TAG_DRIVER_NAME(ksz8795_netdev_ops),
&DSA_TAG_DRIVER_NAME(ksz9477_netdev_ops),
&DSA_TAG_DRIVER_NAME(ksz9893_netdev_ops),
--
2.54.0
^ permalink raw reply related
* [PATCH net-next v2 07/10] net: dsa: tag_ksz: share code for KSZ8795 and KSZ9893 xmit operations
From: Bastien Curutchet (Schneider Electric) @ 2026-07-13 7:11 UTC (permalink / raw)
To: Woojung Huh, UNGLinuxDriver, Andrew Lunn, Vladimir Oltean,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Richard Cochran, Russell King, Simon Horman, Maxime Chevallier
Cc: Pascal Eberhard, Miquèl Raynal, Thomas Petazzoni, netdev,
linux-kernel, Bastien Curutchet (Schneider Electric)
In-Reply-To: <20260713-ksz-new-ptp-v2-0-7209c05058ef@bootlin.com>
KSZ8795 and KSZ9893 have very similar tag handling in the xmit path,
leading to code duplication.
There are only two differences between the two ksz*_xmit():
- the KSZ8795 doesn't handle priorities between frames
- ksz8795_xmit() directly returns the SKB instead of calling
ksz_defer_xmit(). Yet, ksz_defer_xmit() also returns directly the SKB
if no clone is present inside the SKB. Clones are only created by the KSZ
driver when the PTP feature is enabled. Since KSZ8795 doesn't support
PTP, returning the SKB directly or ksz_defer_xmit() is the same.
The upcoming support for the KSZ8463 also requires a similar xmit().
Gather the common code from ksz8795_xmit() and ksz9893_xmit() into a new
ksz_common_xmit() function that takes three input arguments:
- do_tstamp to tell whether ksz_xmit_timestamp() should be called
- prio to give the priority tag (if any)
- override_mask to give the location of the override bit (if any)
Signed-off-by: Bastien Curutchet (Schneider Electric) <bastien.curutchet@bootlin.com>
---
net/dsa/tag_ksz.c | 73 ++++++++++++++++++++++++++-----------------------------
1 file changed, 35 insertions(+), 38 deletions(-)
diff --git a/net/dsa/tag_ksz.c b/net/dsa/tag_ksz.c
index f58ce0f0e9e4..f8b40437c5fa 100644
--- a/net/dsa/tag_ksz.c
+++ b/net/dsa/tag_ksz.c
@@ -218,6 +218,37 @@ static struct sk_buff *ksz_defer_xmit(struct dsa_port *dp, struct sk_buff *skb)
return NULL;
}
+static struct sk_buff *ksz_common_xmit(struct sk_buff *skb,
+ struct net_device *dev,
+ bool do_tstamp,
+ u8 prio,
+ u8 override_mask)
+{
+ struct dsa_port *dp = dsa_user_to_port(dev);
+ struct ethhdr *hdr;
+ u8 *tag;
+
+ if (skb->ip_summed == CHECKSUM_PARTIAL && skb_checksum_help(skb)) {
+ kfree_skb(skb);
+ return NULL;
+ }
+
+ /* Tag encoding */
+ if (do_tstamp)
+ ksz_xmit_timestamp(dp, skb);
+
+ tag = skb_put(skb, KSZ_INGRESS_TAG_LEN);
+ hdr = skb_eth_hdr(skb);
+
+ *tag = dsa_xmit_port_mask(skb, dev);
+ *tag |= prio;
+
+ if (is_link_local_ether_addr(hdr->h_dest))
+ *tag |= override_mask;
+
+ return ksz_defer_xmit(dp, skb);
+}
+
static struct sk_buff *ksz9477_xmit(struct sk_buff *skb,
struct net_device *dev)
{
@@ -308,23 +339,7 @@ MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_KSZ9477, KSZ9477_NAME);
static struct sk_buff *ksz8795_xmit(struct sk_buff *skb, struct net_device *dev)
{
- struct ethhdr *hdr;
- u8 *tag;
-
- if (skb->ip_summed == CHECKSUM_PARTIAL && skb_checksum_help(skb)) {
- kfree_skb(skb);
- return NULL;
- }
-
- /* Tag encoding */
- tag = skb_put(skb, KSZ_INGRESS_TAG_LEN);
- hdr = skb_eth_hdr(skb);
-
- *tag = dsa_xmit_port_mask(skb, dev);
- if (is_link_local_ether_addr(hdr->h_dest))
- *tag |= KSZ8795_TAIL_TAG_OVERRIDE;
-
- return skb;
+ return ksz_common_xmit(skb, dev, false, 0, KSZ8795_TAIL_TAG_OVERRIDE);
}
static struct sk_buff *ksz8795_rcv(struct sk_buff *skb, struct net_device *dev)
@@ -362,28 +377,10 @@ static struct sk_buff *ksz9893_xmit(struct sk_buff *skb,
{
u16 queue_mapping = skb_get_queue_mapping(skb);
u8 prio = netdev_txq_to_tc(dev, queue_mapping);
- struct dsa_port *dp = dsa_user_to_port(dev);
- struct ethhdr *hdr;
- u8 *tag;
-
- if (skb->ip_summed == CHECKSUM_PARTIAL && skb_checksum_help(skb)) {
- kfree_skb(skb);
- return NULL;
- }
-
- /* Tag encoding */
- ksz_xmit_timestamp(dp, skb);
-
- tag = skb_put(skb, KSZ_INGRESS_TAG_LEN);
- hdr = skb_eth_hdr(skb);
-
- *tag = dsa_xmit_port_mask(skb, dev);
- *tag |= FIELD_PREP(KSZ9893_TAIL_TAG_PRIO, prio);
- if (is_link_local_ether_addr(hdr->h_dest))
- *tag |= KSZ9893_TAIL_TAG_OVERRIDE;
-
- return ksz_defer_xmit(dp, skb);
+ return ksz_common_xmit(skb, dev, true,
+ FIELD_PREP(KSZ9893_TAIL_TAG_PRIO, prio),
+ KSZ9893_TAIL_TAG_OVERRIDE);
}
static const struct dsa_device_ops ksz9893_netdev_ops = {
--
2.54.0
^ permalink raw reply related
* [PATCH net-next v2 09/10] net: dsa: microchip: explicitly enable detection of L2 PTP frames
From: Bastien Curutchet (Schneider Electric) @ 2026-07-13 7:11 UTC (permalink / raw)
To: Woojung Huh, UNGLinuxDriver, Andrew Lunn, Vladimir Oltean,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Richard Cochran, Russell King, Simon Horman, Maxime Chevallier
Cc: Pascal Eberhard, Miquèl Raynal, Thomas Petazzoni, netdev,
linux-kernel, Bastien Curutchet (Schneider Electric)
In-Reply-To: <20260713-ksz-new-ptp-v2-0-7209c05058ef@bootlin.com>
Detection of L2 PTP frames needs to be enabled for PTP to work at the L2
layer. The bit enabling this detection is set by default on the switches
currently supported by the driver, but it is unset by default on the
KSZ8463 for which support will be added in upcoming patches.
Explicitly enable the detection of L2 PTP frames for all switches when
PTP is enabled.
Signed-off-by: Bastien Curutchet (Schneider Electric) <bastien.curutchet@bootlin.com>
---
drivers/net/dsa/microchip/ksz_ptp.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/drivers/net/dsa/microchip/ksz_ptp.c b/drivers/net/dsa/microchip/ksz_ptp.c
index 89fbc1253f25..be6b8240ac03 100644
--- a/drivers/net/dsa/microchip/ksz_ptp.c
+++ b/drivers/net/dsa/microchip/ksz_ptp.c
@@ -953,8 +953,9 @@ int ksz_ptp_clock_register(struct dsa_switch *ds)
/* Currently only P2P mode is supported. When 802_1AS bit is set, it
* forwards all PTP packets to host port and none to other ports.
*/
- ret = ksz_rmw16(dev, regs[PTP_MSG_CONF1], PTP_TC_P2P | PTP_802_1AS,
- PTP_TC_P2P | PTP_802_1AS);
+ ret = ksz_rmw16(dev, regs[PTP_MSG_CONF1],
+ PTP_TC_P2P | PTP_802_1AS | PTP_ETH_ENABLE,
+ PTP_TC_P2P | PTP_802_1AS | PTP_ETH_ENABLE);
if (ret)
return ret;
--
2.54.0
^ permalink raw reply related
* [PATCH net-next v2 10/10] net: dsa: microchip: add two-steps PTP support for KSZ8463
From: Bastien Curutchet (Schneider Electric) @ 2026-07-13 7:11 UTC (permalink / raw)
To: Woojung Huh, UNGLinuxDriver, Andrew Lunn, Vladimir Oltean,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Richard Cochran, Russell King, Simon Horman, Maxime Chevallier
Cc: Pascal Eberhard, Miquèl Raynal, Thomas Petazzoni, netdev,
linux-kernel, Bastien Curutchet (Schneider Electric)
In-Reply-To: <20260713-ksz-new-ptp-v2-0-7209c05058ef@bootlin.com>
The KSZ8463 switch supports PTP but it's not supported by the driver.
Add L2 two-step PTP support for the KSZ8463. IPv4 and IPv6 layers aren't
supported. Neither is one-step PTP. Use KSZ8463-specific implementations
of the .get_ts_info and .port_hwtstamp_set callbacks.
The pdelay_req and pdelay_resp timestamps share one interrupt bit status
while they're located in two different registers. So introduce
last_tx_is_pdelayresp to keep track of the last sent event type. This
flag is set by the xmit worker right before sending the packet and then
used in the interrupt handler to retrieve the timestamp location.
Signed-off-by: Bastien Curutchet (Schneider Electric) <bastien.curutchet@bootlin.com>
---
drivers/net/dsa/microchip/ksz8.c | 26 +++++--
drivers/net/dsa/microchip/ksz8_reg.h | 1 +
drivers/net/dsa/microchip/ksz_common.h | 1 +
drivers/net/dsa/microchip/ksz_ptp.c | 130 +++++++++++++++++++++++++++++++-
drivers/net/dsa/microchip/ksz_ptp.h | 7 ++
drivers/net/dsa/microchip/ksz_ptp_reg.h | 4 +
6 files changed, 162 insertions(+), 7 deletions(-)
diff --git a/drivers/net/dsa/microchip/ksz8.c b/drivers/net/dsa/microchip/ksz8.c
index ac9e8ef5774a..941ae9f66f70 100644
--- a/drivers/net/dsa/microchip/ksz8.c
+++ b/drivers/net/dsa/microchip/ksz8.c
@@ -242,8 +242,11 @@ static int ksz8463_girq_setup(struct ksz_device *dev)
static int ksz8463_reset_switch(struct ksz_device *dev)
{
- ksz_cfg(dev, KSZ8463_REG_SW_RESET, KSZ8463_GLOBAL_SOFTWARE_RESET, true);
- ksz_cfg(dev, KSZ8463_REG_SW_RESET, KSZ8463_GLOBAL_SOFTWARE_RESET,
+ ksz_cfg(dev, KSZ8463_REG_SW_RESET,
+ KSZ8463_GLOBAL_SOFTWARE_RESET | KSZ8463_PTP_SOFTWARE_RESET,
+ true);
+ ksz_cfg(dev, KSZ8463_REG_SW_RESET,
+ KSZ8463_GLOBAL_SOFTWARE_RESET | KSZ8463_PTP_SOFTWARE_RESET,
false);
return 0;
}
@@ -2474,17 +2477,24 @@ static int ksz8463_setup(struct dsa_switch *ds)
ret = ksz8463_ptp_irq_setup(ds);
if (ret)
goto free_girq;
+
+ ret = ksz_ptp_clock_register(ds);
+ if (ret) {
+ dev_err(dev->dev, "Failed to register PTP clock: %d\n",
+ ret);
+ goto free_ptp_irq;
+ }
}
ret = ksz_mdio_register(dev);
if (ret < 0) {
dev_err(dev->dev, "failed to register the mdio");
- goto free_ptp_irq;
+ goto ptp_clock_unregister;
}
ret = ksz_dcb_init(dev);
if (ret)
- goto free_ptp_irq;
+ goto ptp_clock_unregister;
/* start switch */
regmap_update_bits(ksz_regmap_8(dev), regs[S_START_CTRL],
@@ -2492,6 +2502,9 @@ static int ksz8463_setup(struct dsa_switch *ds)
return 0;
+ptp_clock_unregister:
+ if (dev->irq > 0)
+ ksz_ptp_clock_unregister(ds);
free_ptp_irq:
if (dev->irq > 0)
ksz8463_ptp_irq_free(ds);
@@ -2507,6 +2520,7 @@ static void ksz8463_teardown(struct dsa_switch *ds)
struct ksz_device *dev = ds->priv;
if (dev->irq > 0) {
+ ksz_ptp_clock_unregister(ds);
ksz8463_ptp_irq_free(ds);
ksz_irq_free(&dev->girq);
}
@@ -3129,9 +3143,9 @@ const struct dsa_switch_ops ksz8463_switch_ops = {
.port_max_mtu = ksz88xx_max_mtu,
.suspend = ksz_suspend,
.resume = ksz_resume,
- .get_ts_info = ksz_get_ts_info,
+ .get_ts_info = ksz8463_get_ts_info,
.port_hwtstamp_get = ksz_hwtstamp_get,
- .port_hwtstamp_set = ksz_hwtstamp_set,
+ .port_hwtstamp_set = ksz8463_hwtstamp_set,
.port_txtstamp = ksz_port_txtstamp,
.port_rxtstamp = ksz_port_rxtstamp,
.port_setup_tc = ksz8_setup_tc,
diff --git a/drivers/net/dsa/microchip/ksz8_reg.h b/drivers/net/dsa/microchip/ksz8_reg.h
index 981ab441d9b7..6bc511da1f7d 100644
--- a/drivers/net/dsa/microchip/ksz8_reg.h
+++ b/drivers/net/dsa/microchip/ksz8_reg.h
@@ -786,6 +786,7 @@
#define KSZ8463_REG_SW_RESET 0x126
#define KSZ8463_GLOBAL_SOFTWARE_RESET BIT(0)
+#define KSZ8463_PTP_SOFTWARE_RESET BIT(2)
#define KSZ8463_PTP_CLK_CTRL 0x600
diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h
index 0f2abb22ca91..cbe98494578c 100644
--- a/drivers/net/dsa/microchip/ksz_common.h
+++ b/drivers/net/dsa/microchip/ksz_common.h
@@ -194,6 +194,7 @@ struct ksz_port {
struct kernel_hwtstamp_config tstamp_config;
bool hwts_tx_en;
bool hwts_rx_en;
+ bool last_tx_is_pdelayresp;
struct ksz_irq ptpirq;
struct ksz_ptp_irq ptpmsg_irq[3];
ktime_t tstamp_msg;
diff --git a/drivers/net/dsa/microchip/ksz_ptp.c b/drivers/net/dsa/microchip/ksz_ptp.c
index be6b8240ac03..1cd2940c7bef 100644
--- a/drivers/net/dsa/microchip/ksz_ptp.c
+++ b/drivers/net/dsa/microchip/ksz_ptp.c
@@ -297,6 +297,31 @@ static int ksz_ptp_enable_mode(struct ksz_device *dev)
tag_en ? PTP_ENABLE : 0);
}
+int ksz8463_get_ts_info(struct dsa_switch *ds, int port,
+ struct kernel_ethtool_ts_info *ts)
+{
+ struct ksz_device *dev = ds->priv;
+ struct ksz_ptp_data *ptp_data;
+
+ ptp_data = &dev->ptp_data;
+
+ if (!ptp_data->clock)
+ return -ENODEV;
+
+ ts->so_timestamping = SOF_TIMESTAMPING_TX_HARDWARE |
+ SOF_TIMESTAMPING_RX_HARDWARE |
+ SOF_TIMESTAMPING_RAW_HARDWARE;
+
+ ts->tx_types = BIT(HWTSTAMP_TX_OFF) | BIT(HWTSTAMP_TX_ON);
+
+ ts->rx_filters = BIT(HWTSTAMP_FILTER_NONE) |
+ BIT(HWTSTAMP_FILTER_PTP_V2_L2_EVENT);
+
+ ts->phc_index = ptp_clock_index(ptp_data->clock);
+
+ return 0;
+}
+
/* The function is return back the capability of timestamping feature when
* requested through ethtool -T <interface> utility
*/
@@ -341,6 +366,72 @@ int ksz_hwtstamp_get(struct dsa_switch *ds, int port,
return 0;
}
+static int ksz8463_set_hwtstamp_config(struct ksz_device *dev,
+ struct ksz_port *prt,
+ struct kernel_hwtstamp_config *config)
+{
+ const u16 *regs = dev->info->regs;
+ int ret;
+
+ if (config->flags)
+ return -EINVAL;
+
+ switch (config->tx_type) {
+ case HWTSTAMP_TX_OFF:
+ prt->ptpmsg_irq[KSZ8463_SYNC_MSG].ts_en = false;
+ prt->ptpmsg_irq[KSZ8463_XDREQ_PDRES_MSG].ts_en = false;
+ prt->hwts_tx_en = false;
+ break;
+ case HWTSTAMP_TX_ON:
+ prt->ptpmsg_irq[KSZ8463_SYNC_MSG].ts_en = true;
+ prt->ptpmsg_irq[KSZ8463_XDREQ_PDRES_MSG].ts_en = true;
+ prt->hwts_tx_en = true;
+
+ ret = ksz_rmw16(dev, regs[PTP_MSG_CONF1], PTP_1STEP, 0);
+ if (ret)
+ return ret;
+
+ break;
+ default:
+ return -ERANGE;
+ }
+
+ switch (config->rx_filter) {
+ case HWTSTAMP_FILTER_NONE:
+ prt->hwts_rx_en = false;
+ break;
+ case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
+ case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
+ config->rx_filter = HWTSTAMP_FILTER_PTP_V2_L2_EVENT;
+ prt->hwts_rx_en = true;
+ break;
+ default:
+ config->rx_filter = HWTSTAMP_FILTER_NONE;
+ return -ERANGE;
+ }
+
+ return ksz_ptp_enable_mode(dev);
+}
+
+int ksz8463_hwtstamp_set(struct dsa_switch *ds, int port,
+ struct kernel_hwtstamp_config *config,
+ struct netlink_ext_ack *extack)
+{
+ struct ksz_device *dev = ds->priv;
+ struct ksz_port *prt;
+ int ret;
+
+ prt = &dev->ports[port];
+
+ ret = ksz8463_set_hwtstamp_config(dev, prt, config);
+ if (ret)
+ return ret;
+
+ prt->tstamp_config = *config;
+
+ return 0;
+}
+
static int ksz_set_hwtstamp_config(struct ksz_device *dev,
struct ksz_port *prt,
struct kernel_hwtstamp_config *config)
@@ -571,6 +662,31 @@ static void ksz_ptp_txtstamp_skb(struct ksz_device *dev,
skb_complete_tx_timestamp(skb, &hwtstamps);
}
+static void ksz8463_set_pdelayresp_flag(struct ksz_port *prt,
+ struct sk_buff *skb)
+{
+ struct ptp_header *hdr;
+ unsigned int type;
+ u8 ptp_msg_type;
+
+ if (!ksz_is_ksz8463(prt->ksz_dev))
+ return;
+
+ if (skb_linearize(skb))
+ return;
+
+ type = ptp_classify_raw(skb);
+ if (type == PTP_CLASS_NONE)
+ return;
+
+ hdr = ptp_parse_header(skb, type);
+ if (!hdr)
+ return;
+
+ ptp_msg_type = ptp_get_msgtype(hdr, type);
+ prt->last_tx_is_pdelayresp = (ptp_msg_type == PTP_MSGTYPE_PDELAY_RESP);
+}
+
void ksz_port_deferred_xmit(struct kthread_work *work)
{
struct ksz_deferred_xmit_work *xmit_work = work_to_xmit_work(work);
@@ -587,6 +703,8 @@ void ksz_port_deferred_xmit(struct kthread_work *work)
reinit_completion(&prt->tstamp_msg_comp);
+ ksz8463_set_pdelayresp_flag(prt, skb);
+
dsa_enqueue_skb(skb, skb->dev);
ksz_ptp_txtstamp_skb(dev, prt, clone);
@@ -979,7 +1097,17 @@ void ksz_ptp_clock_unregister(struct dsa_switch *ds)
static int ksz_read_ts(struct ksz_port *port, u16 reg, u32 *ts)
{
- return ksz_read32(port->ksz_dev, reg, ts);
+ u16 ts_reg = reg;
+
+ /**
+ * On KSZ8463 DREQ and DRESP timestamps share one interrupt line
+ * so we have to check the nature of the latest event sent to know
+ * where the timestamp is located
+ */
+ if (ksz_is_ksz8463(port->ksz_dev) && port->last_tx_is_pdelayresp)
+ ts_reg += KSZ8463_DRESP_TS_OFFSET;
+
+ return ksz_read32(port->ksz_dev, ts_reg, ts);
}
static irqreturn_t ksz_ptp_msg_thread_fn(int irq, void *dev_id)
diff --git a/drivers/net/dsa/microchip/ksz_ptp.h b/drivers/net/dsa/microchip/ksz_ptp.h
index 11408580031d..7067ec9bd1e6 100644
--- a/drivers/net/dsa/microchip/ksz_ptp.h
+++ b/drivers/net/dsa/microchip/ksz_ptp.h
@@ -39,11 +39,16 @@ void ksz_ptp_clock_unregister(struct dsa_switch *ds);
int ksz_get_ts_info(struct dsa_switch *ds, int port,
struct kernel_ethtool_ts_info *ts);
+int ksz8463_get_ts_info(struct dsa_switch *ds, int port,
+ struct kernel_ethtool_ts_info *ts);
int ksz_hwtstamp_get(struct dsa_switch *ds, int port,
struct kernel_hwtstamp_config *config);
int ksz_hwtstamp_set(struct dsa_switch *ds, int port,
struct kernel_hwtstamp_config *config,
struct netlink_ext_ack *extack);
+int ksz8463_hwtstamp_set(struct dsa_switch *ds, int port,
+ struct kernel_hwtstamp_config *config,
+ struct netlink_ext_ack *extack);
void ksz_port_txtstamp(struct dsa_switch *ds, int port, struct sk_buff *skb);
void ksz_port_deferred_xmit(struct kthread_work *work);
bool ksz_port_rxtstamp(struct dsa_switch *ds, int port, struct sk_buff *skb,
@@ -82,10 +87,12 @@ static inline int ksz8463_ptp_irq_setup(struct dsa_switch *ds)
static inline void ksz8463_ptp_irq_free(struct dsa_switch *ds) {}
#define ksz_get_ts_info NULL
+#define ksz8463_get_ts_info NULL
#define ksz_hwtstamp_get NULL
#define ksz_hwtstamp_set NULL
+#define ksz8463_hwtstamp_set NULL
#define ksz_port_rxtstamp NULL
diff --git a/drivers/net/dsa/microchip/ksz_ptp_reg.h b/drivers/net/dsa/microchip/ksz_ptp_reg.h
index 1a669d6ee889..65ea8577af75 100644
--- a/drivers/net/dsa/microchip/ksz_ptp_reg.h
+++ b/drivers/net/dsa/microchip/ksz_ptp_reg.h
@@ -137,4 +137,8 @@
#define KSZ_XDREQ_MSG 1
#define KSZ_PDRES_MSG 0
+#define KSZ8463_DRESP_TS_OFFSET (KSZ8463_REG_PORT_DRESP_TS - KSZ8463_REG_PORT_DREQ_TS)
+#define KSZ8463_SYNC_MSG 0
+#define KSZ8463_XDREQ_PDRES_MSG 1
+
#endif
--
2.54.0
^ permalink raw reply related
* [PATCH v4 0/4] dmaengine: xilinx_dma: MCDMA descriptor and metadata handling improvements
From: Srinivas Neeli @ 2026-07-13 7:21 UTC (permalink / raw)
To: Vinod Koul, Radhey Shyam Pandey
Cc: Frank Li, Michal Simek, Andrew Lunn, David S . Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Suraj Gupta,
Marek Vasut, Tomi Valkeinen, Alex Bereza, Folker Schwesinger,
dmaengine, netdev, linux-arm-kernel, linux-kernel, git
This series improves the Xilinx AXI DMA and MCDMA driver's descriptor
handling and metadata reporting. It fixes direction-specific descriptor
field usage, ensures completion is based on the hardware completion bit
(important with interrupt coalescing), and extends metadata handling to
expose status and sideband fields alongside APP fields.
The axienet driver is updated to derive RX frame length from the standard
dmaengine residue mechanism rather than descriptor APP fields, making it
work on designs where the AXI4-Stream status/control interface is not
present.
Changes in V4:
- Patch 1: Added Reviewed-by: Radhey Shyam Pandey.
- Patch 2: Reworded commit message to reference the AXIDMA fix it mirrors;
added Reviewed-by: Radhey Shyam Pandey.
- Patch 3: Renamed subject to "...from residue in dmaengine path";
condensed commit message; dropped Fixes tag.
- Patch 4: Restructured get_metadata_ptr() so AXIDMA is the fall-through
path (no WARN_ON_ONCE); rewrote the kernel-doc as an index table
covering AXI DMA, MCDMA S2MM and MCDMA MM2S; condensed commit message.
Changes in V3:
- Patch 1: Renamed subject, added static_assert for descriptor size,
refactored residue calculation for clarity.
- Patch 2: Added Fixes tag, expanded commit message explaining interrupt
coalescing scenario, simplified completion check logic.
- Patch 3: New patch - axienet now uses result->residue for RX length
instead of APP metadata, removing dependency on status/control stream.
- Patch 4: Complete rewrite - metadata pointer now starts at status field
(index 0) exposing status/sideband to clients; uses EOF descriptor;
removed 'chan' field from descriptor struct.
- Dropped V2 patches 4/5 (dt-bindings) and 5/5 (xferred_bytes) as the
approach changed to use standard residue mechanism.
Changes in V2:
- Rebased on the AXI DMA binding YAML conversion.
- Added xlnx,include-stscntrl-strm in the YAML binding.
- Clarified cover letter to reflect metadata behavior with and without
APP fields.
https://lore.kernel.org/all/20260309033444.3472359-1-abin.joseph@amd.com/
Srinivas Neeli (3):
dmaengine: xilinx_dma: Fix MCDMA descriptor fields based on DMA
direction
dmaengine: xilinx_dma: Move descriptors to done list based on
completion bit
net: xilinx: axienet: Derive RX frame length from residue in dmaengine
path
Suraj Gupta (1):
dmaengine: xilinx_dma: Extend metadata handling for AXI DMA and MCDMA
drivers/dma/xilinx/xilinx_dma.c | 86 ++++++++++++++++---
.../net/ethernet/xilinx/xilinx_axienet_main.c | 14 ++-
2 files changed, 77 insertions(+), 23 deletions(-)
--
2.25.1
^ permalink raw reply
* [PATCH v4 1/4] dmaengine: xilinx_dma: Fix MCDMA descriptor fields based on DMA direction
From: Srinivas Neeli @ 2026-07-13 7:21 UTC (permalink / raw)
To: Vinod Koul, Radhey Shyam Pandey
Cc: Frank Li, Michal Simek, Andrew Lunn, David S . Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Suraj Gupta,
Marek Vasut, Tomi Valkeinen, Alex Bereza, Folker Schwesinger,
dmaengine, netdev, linux-arm-kernel, linux-kernel, git
In-Reply-To: <20260713072146.45269-1-srinivas.neeli@amd.com>
The MCDMA BD format differs between memory-to-device (MM2S) and
device-to-memory (S2MM) directions, but the driver was using generic
'status' and 'sideband_status' fields for both. This led to incorrect
residue calculations when the hardware updates direction-specific fields.
Refactor the descriptor structure to use unions with direction-specific
field mappings, and update the residue calculation logic to select the
correct status field based on DMA direction.
This matches the hardware descriptor layout and fixes incorrect
residue reporting.
Fixes: 6ccd692bfb7f ("dmaengine: xilinx_dma: Add Xilinx AXI MCDMA Engine driver support")
Reviewed-by: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
Signed-off-by: Srinivas Neeli <srinivas.neeli@amd.com>
---
Changes in V4:
- Added Reviewed-by: Radhey Shyam Pandey.
Changes in V3:
- Renamed subject from "for MM2S vs S2MM" to "based on DMA direction".
- Reworded commit message for clarity.
- Added XILINX_MCDMA_BD_HW_SIZE macro and static_assert to verify
descriptor size at compile time.
- Refactored residue calculation to separate addition and subtraction
operations for better readability.
Changes in V2:
- No change.
---
drivers/dma/xilinx/xilinx_dma.c | 26 +++++++++++++++++++-------
1 file changed, 19 insertions(+), 7 deletions(-)
diff --git a/drivers/dma/xilinx/xilinx_dma.c b/drivers/dma/xilinx/xilinx_dma.c
index 98b41b8f8915..ff5b29a808e9 100644
--- a/drivers/dma/xilinx/xilinx_dma.c
+++ b/drivers/dma/xilinx/xilinx_dma.c
@@ -223,6 +223,7 @@
#define XILINX_MCDMA_IRQ_ERR_MASK BIT(7)
#define XILINX_MCDMA_BD_EOP BIT(30)
#define XILINX_MCDMA_BD_SOP BIT(31)
+#define XILINX_MCDMA_BD_HW_SIZE 64
/**
* struct xilinx_vdma_desc_hw - Hardware Descriptor
@@ -277,8 +278,10 @@ struct xilinx_axidma_desc_hw {
* @buf_addr_msb: MSB of Buffer address @0x0C
* @rsvd: Reserved field @0x10
* @control: Control Information field @0x14
- * @status: Status field @0x18
- * @sideband_status: Status of sideband signals @0x1C
+ * @mm2s_ctrl_sideband: Sideband control info for mm2s @0x18
+ * @s2mm_status: Status field for s2mm @0x18
+ * @mm2s_status: Status field for mm2s @0x1C
+ * @s2mm_sideband_status: Sideband status for s2mm @0x1C
* @app: APP Fields @0x20 - 0x30
*/
struct xilinx_aximcdma_desc_hw {
@@ -288,10 +291,17 @@ struct xilinx_aximcdma_desc_hw {
u32 buf_addr_msb;
u32 rsvd;
u32 control;
- u32 status;
- u32 sideband_status;
+ union {
+ u32 mm2s_ctrl_sideband;
+ u32 s2mm_status;
+ };
+ union {
+ u32 mm2s_status;
+ u32 s2mm_sideband_status;
+ };
u32 app[XILINX_DMA_NUM_APP_WORDS];
} __aligned(64);
+static_assert(sizeof(struct xilinx_aximcdma_desc_hw) == XILINX_MCDMA_BD_HW_SIZE);
/**
* struct xilinx_cdma_desc_hw - Hardware Descriptor
@@ -1015,9 +1025,11 @@ static u32 xilinx_dma_get_residue(struct xilinx_dma_chan *chan,
struct xilinx_aximcdma_tx_segment,
node);
aximcdma_hw = &aximcdma_seg->hw;
- residue +=
- (aximcdma_hw->control & chan->xdev->max_buffer_len) -
- (aximcdma_hw->status & chan->xdev->max_buffer_len);
+ residue += aximcdma_hw->control & chan->xdev->max_buffer_len;
+ if (chan->direction == DMA_DEV_TO_MEM)
+ residue -= aximcdma_hw->s2mm_status & chan->xdev->max_buffer_len;
+ else
+ residue -= aximcdma_hw->mm2s_status & chan->xdev->max_buffer_len;
}
}
--
2.25.1
^ permalink raw reply related
* [PATCH v4 2/4] dmaengine: xilinx_dma: Move descriptors to done list based on completion bit
From: Srinivas Neeli @ 2026-07-13 7:21 UTC (permalink / raw)
To: Vinod Koul, Radhey Shyam Pandey
Cc: Frank Li, Michal Simek, Andrew Lunn, David S . Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Suraj Gupta,
Marek Vasut, Tomi Valkeinen, Alex Bereza, Folker Schwesinger,
dmaengine, netdev, linux-arm-kernel, linux-kernel, git
In-Reply-To: <20260713072146.45269-1-srinivas.neeli@amd.com>
In AXI MCDMA, xilinx_dma_complete_descriptor() walks the channel's
active_list and unconditionally moves every entry to the done_list. The
MCDMA IOC interrupt handler invokes this function on every
interrupt-on-completion, but with interrupt coalescing (IRQThreshold > 1)
an IOC interrupt may fire after only a subset of the queued descriptors
have actually been processed by the hardware. As a result, descriptors
whose completion bit is not yet set in the BD status were being reported
as completed to client drivers.
Add a check for the descriptor completion bit before moving entries from
the active list to the done list, using the appropriate direction-
specific status field (s2mm_status for DMA_DEV_TO_MEM, mm2s_status for
DMA_MEM_TO_DEV).
This mirrors the AXIDMA fix in commit 7bcdaa658102 ("dmaengine:
xilinx_dma: Freeup active list based on descriptor completion bit").
Fixes: 6ccd692bfb7f ("dmaengine: xilinx_dma: Add Xilinx AXI MCDMA Engine driver support")
Reviewed-by: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
Signed-off-by: Srinivas Neeli <srinivas.neeli@amd.com>
---
Changes in V4:
- Reworded commit message to reference the AXIDMA fix it mirrors
(commit 7bcdaa658102).
- Added Reviewed-by: Radhey Shyam Pandey.
Changes in V3:
- Added Fixes tag.
- Expanded commit message to explain the interrupt coalescing scenario
and why the has_sg guard is omitted for MCDMA.
- Changed local variable from 'bool completed' to 'u32 status' for
cleaner status field access.
- Simplified completion check logic.
Changes in V2:
- No change.
---
drivers/dma/xilinx/xilinx_dma.c | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/drivers/dma/xilinx/xilinx_dma.c b/drivers/dma/xilinx/xilinx_dma.c
index ff5b29a808e9..1b5b00f08c5f 100644
--- a/drivers/dma/xilinx/xilinx_dma.c
+++ b/drivers/dma/xilinx/xilinx_dma.c
@@ -1784,6 +1784,17 @@ static void xilinx_dma_complete_descriptor(struct xilinx_dma_chan *chan)
struct xilinx_axidma_tx_segment, node);
if (!(seg->hw.status & XILINX_DMA_BD_COMP_MASK) && chan->has_sg)
break;
+ } else if (chan->xdev->dma_config->dmatype == XDMA_TYPE_AXIMCDMA) {
+ struct xilinx_aximcdma_tx_segment *seg;
+ u32 status;
+
+ seg = list_last_entry(&desc->segments,
+ struct xilinx_aximcdma_tx_segment,
+ node);
+ status = (chan->direction == DMA_DEV_TO_MEM) ?
+ seg->hw.s2mm_status : seg->hw.mm2s_status;
+ if (!(status & XILINX_DMA_BD_COMP_MASK))
+ break;
}
if (chan->has_sg && chan->xdev->dma_config->dmatype !=
XDMA_TYPE_VDMA)
--
2.25.1
^ permalink raw reply related
* [PATCH v4 4/4] dmaengine: xilinx_dma: Extend metadata handling for AXI DMA and MCDMA
From: Srinivas Neeli @ 2026-07-13 7:21 UTC (permalink / raw)
To: Vinod Koul, Radhey Shyam Pandey
Cc: Frank Li, Michal Simek, Andrew Lunn, David S . Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Suraj Gupta,
Marek Vasut, Tomi Valkeinen, Alex Bereza, Folker Schwesinger,
dmaengine, netdev, linux-arm-kernel, linux-kernel, git
In-Reply-To: <20260713072146.45269-1-srinivas.neeli@amd.com>
From: Suraj Gupta <suraj.gupta2@amd.com>
xilinx_dma_get_metadata_ptr() exposed only the descriptor APP fields.
Each descriptor also carries a status word, and AXI MCDMA carries an
AXI4-Stream sideband word holding TID, TDEST and TUSER that clients may
need. Return a pointer to the status word so clients can read the status,
the sideband and the APP fields together. The exact index layout is
documented at the function.
Take the pointer from the End-Of-Frame descriptor, where the hardware
writes these fields. For AXI DMA the pointer now starts at the status word
of the EOF descriptor instead of the APP fields of the first descriptor,
and the payload grows from 20 to 24 bytes. No in-tree consumer is affected,
since axienet reads the RX frame length from result->residue rather than
the APP fields.
Read xlnx,axistream-connected for MCDMA as well, and attach metadata_ops
in xilinx_mcdma_prep_slave_sg() when an AXI Stream interface is present,
so MCDMA clients use the metadata API the same way as AXI DMA clients.
Signed-off-by: Suraj Gupta <suraj.gupta2@amd.com>
Co-developed-by: Srinivas Neeli <srinivas.neeli@amd.com>
Signed-off-by: Srinivas Neeli <srinivas.neeli@amd.com>
---
Changes in V4:
- Restructured xilinx_dma_get_metadata_ptr(): AXIDMA is now the
fall-through path instead of a separate branch guarded by
WARN_ON_ONCE()/ERR_PTR().
- Rewrote the kernel-doc as an index table covering AXI DMA, MCDMA S2MM
and MCDMA MM2S, and documented that the pointer and payload length are
the same for both MCDMA directions.
- Added an inline comment explaining the union aliasing.
- Condensed the commit message.
Changes in V3:
- Renamed subject to include "AXI DMA and MCDMA" (was "AXI MCDMA" only).
- Complete rewrite of commit message and implementation.
- Metadata pointer now returns status field at index 0 instead of APP
fields, exposing status and sideband information to clients.
- Changed from list_first_entry to list_last_entry to return the EOF
descriptor where hardware writes status and APP fields.
- Added explicit handling for both AXIDMA and MCDMA types with proper
payload length calculation.
- Added WARN_ON_ONCE for unsupported DMA types.
- Removed the 'chan' field from struct xilinx_dma_tx_descriptor (was
added in V2) as it's no longer needed; channel is obtained from
tx->chan instead.
- Dropped V2 patches 4/5 (dt-bindings xlnx,include-stscntrl-strm) and
5/5 (xferred_bytes support) as the approach changed to use residue.
Changes in V2:
- Added support for MCDMA metadata handling alongside AXIDMA.
- Added 'chan' field to struct xilinx_dma_tx_descriptor.
---
drivers/dma/xilinx/xilinx_dma.c | 49 ++++++++++++++++++++++++++++-----
1 file changed, 42 insertions(+), 7 deletions(-)
diff --git a/drivers/dma/xilinx/xilinx_dma.c b/drivers/dma/xilinx/xilinx_dma.c
index 1b5b00f08c5f..2be95f0ba3ea 100644
--- a/drivers/dma/xilinx/xilinx_dma.c
+++ b/drivers/dma/xilinx/xilinx_dma.c
@@ -651,18 +651,49 @@ static inline void xilinx_aximcdma_buf(struct xilinx_dma_chan *chan,
* @tx: async transaction descriptor
* @payload_len: metadata payload length
* @max_len: metadata max length
- * Return: The app field pointer.
+ *
+ * The hardware writes the status, sideband and APP fields into the last
+ * (End-Of-Frame) descriptor. These words are contiguous, so a client reads
+ * them by index from the returned pointer:
+ *
+ * AXI DMA: [0] status, [1..] app
+ * AXI MCDMA (S2MM): [0] status, [1] sideband (TID/TDEST/TUSER), [2..] app
+ * AXI MCDMA (MM2S): [0] ctrl sideband, [1] status, [2..] app
+ *
+ * For MCDMA the pointer and payload length are the same in both directions
+ * because the union members overlay the same descriptor words.
+ *
+ * Return: Pointer to the first metadata word.
*/
static void *xilinx_dma_get_metadata_ptr(struct dma_async_tx_descriptor *tx,
size_t *payload_len, size_t *max_len)
{
struct xilinx_dma_tx_descriptor *desc = to_dma_tx_descriptor(tx);
- struct xilinx_axidma_tx_segment *seg;
+ struct xilinx_dma_chan *chan = to_xilinx_chan(tx->chan);
+
+ if (chan->xdev->dma_config->dmatype == XDMA_TYPE_AXIMCDMA) {
+ struct xilinx_aximcdma_tx_segment *seg =
+ list_last_entry(&desc->segments,
+ struct xilinx_aximcdma_tx_segment, node);
- *max_len = *payload_len = sizeof(u32) * XILINX_DMA_NUM_APP_WORDS;
- seg = list_first_entry(&desc->segments,
- struct xilinx_axidma_tx_segment, node);
- return seg->hw.app;
+ /*
+ * The union members overlay the same words, so one pointer and
+ * length cover both directions (see the layout above).
+ */
+ *max_len = *payload_len = sizeof(seg->hw.s2mm_status) +
+ sizeof(seg->hw.s2mm_sideband_status) +
+ sizeof(seg->hw.app);
+ return &seg->hw.s2mm_status;
+ }
+
+ /* Only AXIDMA and MCDMA attach metadata_ops, so this is AXIDMA. */
+ struct xilinx_axidma_tx_segment *seg =
+ list_last_entry(&desc->segments,
+ struct xilinx_axidma_tx_segment, node);
+
+ *max_len = *payload_len = sizeof(seg->hw.status) +
+ sizeof(seg->hw.app);
+ return &seg->hw.status;
}
static struct dma_descriptor_metadata_ops xilinx_dma_metadata_ops = {
@@ -2639,6 +2670,9 @@ xilinx_mcdma_prep_slave_sg(struct dma_chan *dchan, struct scatterlist *sgl,
segment->hw.control |= XILINX_MCDMA_BD_EOP;
}
+ if (chan->xdev->has_axistream_connected)
+ desc->async_tx.metadata_ops = &xilinx_dma_metadata_ops;
+
return &desc->async_tx;
error:
@@ -3287,7 +3321,8 @@ static int xilinx_dma_probe(struct platform_device *pdev)
dma_set_max_seg_size(xdev->dev, xdev->max_buffer_len);
- if (xdev->dma_config->dmatype == XDMA_TYPE_AXIDMA) {
+ if (xdev->dma_config->dmatype == XDMA_TYPE_AXIDMA ||
+ xdev->dma_config->dmatype == XDMA_TYPE_AXIMCDMA) {
xdev->has_axistream_connected =
of_property_read_bool(node, "xlnx,axistream-connected");
}
--
2.25.1
^ permalink raw reply related
* [PATCH v4 3/4] net: xilinx: axienet: Derive RX frame length from residue in dmaengine path
From: Srinivas Neeli @ 2026-07-13 7:21 UTC (permalink / raw)
To: Vinod Koul, Radhey Shyam Pandey
Cc: Frank Li, Michal Simek, Andrew Lunn, David S . Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Suraj Gupta,
Marek Vasut, Tomi Valkeinen, Alex Bereza, Folker Schwesinger,
dmaengine, netdev, linux-arm-kernel, linux-kernel, git
In-Reply-To: <20260713072146.45269-1-srinivas.neeli@amd.com>
The dmaengine RX path derived the received frame length from the descriptor
APP metadata. That only works when the optional AXI4-Stream status/control
interface is present, because the hardware populates the APP fields solely
when that interface is enabled. On designs without it the length read back
is invalid.
The AXI DMA engine already reports how many bytes it wrote into the buffer
through the standard dmaengine residue mechanism. Compute the RX frame
length as the posted buffer length minus result->residue, which is
independent of the status/control interface and correct across all designs,
including multi-descriptor frames where the residue is summed over the
chain.
Drop the descriptor metadata lookup, which was only used for this purpose.
Detect a failed transfer from dmaengine_result.result instead of the
metadata pointer return value, and remove the now unused LEN_APP macro.
The transmit path is unaffected. It still passes APP metadata for checksum
offload and derives its length from the skb.
Signed-off-by: Srinivas Neeli <srinivas.neeli@amd.com>
---
Changes in V4:
- Renamed subject to "Derive RX frame length from residue in dmaengine
path".
- Condensed the commit message.
- Dropped the Fixes tag.
Changes in V3:
- New patch in this series.
- This patch enables axienet to work on designs where the AXI4-Stream
status/control interface is not present. By using the standard
dmaengine residue mechanism, the driver no longer depends on APP
fields being populated by hardware.
- This approach replaces the V2 xferred_bytes mechanism (V2 patch 5/5),
making the dt-bindings patch (V2 patch 4/5) for xlnx,include-stscntrl-strm
also unnecessary. Both V2 patches are dropped in this series.
---
drivers/net/ethernet/xilinx/xilinx_axienet_main.c | 14 +++++---------
1 file changed, 5 insertions(+), 9 deletions(-)
diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
index fcf517069d16..67d1b8e91d68 100644
--- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
+++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
@@ -53,7 +53,6 @@
#define TX_BD_NUM_MAX 4096
#define RX_BD_NUM_MAX 4096
#define DMA_NUM_APP_WORDS 5
-#define LEN_APP 4
#define RX_BUF_NUM_DEFAULT 128
/* Must be shorter than length of ethtool_drvinfo.driver field to fit */
@@ -1159,29 +1158,26 @@ axienet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
static void axienet_dma_rx_cb(void *data, const struct dmaengine_result *result)
{
struct skbuf_dma_descriptor *skbuf_dma;
- size_t meta_len, meta_max_len, rx_len;
struct axienet_local *lp = data;
struct sk_buff *skb;
- u32 *app_metadata;
+ size_t rx_len;
int i;
skbuf_dma = axienet_get_rx_desc(lp, lp->rx_ring_tail++);
skb = skbuf_dma->skb;
- app_metadata = dmaengine_desc_get_metadata_ptr(skbuf_dma->desc, &meta_len,
- &meta_max_len);
dma_unmap_single(lp->dev, skbuf_dma->dma_address, lp->max_frm_size,
DMA_FROM_DEVICE);
- if (IS_ERR(app_metadata)) {
+ if (result->result != DMA_TRANS_NOERROR) {
if (net_ratelimit())
- netdev_err(lp->ndev, "Failed to get RX metadata pointer\n");
+ netdev_err(lp->ndev, "RX DMA transfer failed\n");
dev_kfree_skb_any(skb);
lp->ndev->stats.rx_dropped++;
goto rx_submit;
}
- /* TODO: Derive app word index programmatically */
- rx_len = (app_metadata[LEN_APP] & 0xFFFF);
+ /* Actual length = posted buffer length - residue. */
+ rx_len = lp->max_frm_size - result->residue;
skb_put(skb, rx_len);
skb->protocol = eth_type_trans(skb, lp->ndev);
skb->ip_summed = CHECKSUM_NONE;
--
2.25.1
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox