* [PATCH v2 01/11] MAINTAINERS: add an entry for media maintainers profile
From: Mauro Carvalho Chehab @ 2026-04-17 6:11 UTC (permalink / raw)
To: Jonathan Corbet, Linux Doc Mailing List
Cc: Mauro Carvalho Chehab, linux-kernel, linux-riscv, workflows,
Dan Williams, Mauro Carvalho Chehab, Randy Dunlap
In-Reply-To: <cover.1776405189.git.mchehab+huawei@kernel.org>
The media subsystem has a maintainers entry profile, but its entry
is missing at MAINTAINERS.
Add it.
Acked-by: Randy Dunlap <rdunlap@infradead.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
Message-ID: <5af4aa6a716228eea4d59dc26b97d642e1e7d419.1776176108.git.mchehab+huawei@kernel.org>
---
MAINTAINERS | 1 +
1 file changed, 1 insertion(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index f0b106a4dd96..620219e48f98 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -16115,6 +16115,7 @@ S: Maintained
W: https://linuxtv.org
Q: http://patchwork.kernel.org/project/linux-media/list/
T: git git://linuxtv.org/media.git
+P: Documentation/driver-api/media/maintainer-entry-profile.rst
F: Documentation/admin-guide/media/
F: Documentation/devicetree/bindings/media/
F: Documentation/driver-api/media/
--
2.53.0
^ permalink raw reply related
* [PATCH v2 03/11] docs: maintainers_include: auto-generate maintainer profile TOC
From: Mauro Carvalho Chehab @ 2026-04-17 6:11 UTC (permalink / raw)
To: Jonathan Corbet, Linux Doc Mailing List, Mauro Carvalho Chehab
Cc: Mauro Carvalho Chehab, linux-kernel, linux-riscv, workflows,
Dan Williams, Randy Dunlap, Shuah Khan
In-Reply-To: <cover.1776405189.git.mchehab+huawei@kernel.org>
Add a feature to allow auto-generating media entry profiles from the
corresponding field inside MAINTAINERS file(s).
Suggested-by: Dan Williams <djbw@kernel.org>
Closes: https://lore.kernel.org/linux-doc/69dd6299440be_147c801005b@djbw-dev.notmuch/
Acked-by: Dan Williams <djbw@kernel.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
Message-ID: <4e9512a3d05942c98361d06d60a118d7c78762b6.1776176108.git.mchehab+huawei@kernel.org>
---
Documentation/sphinx/maintainers_include.py | 93 +++++++++++++++++----
1 file changed, 76 insertions(+), 17 deletions(-)
diff --git a/Documentation/sphinx/maintainers_include.py b/Documentation/sphinx/maintainers_include.py
index 519ad18685b2..1dac83bf1a65 100755
--- a/Documentation/sphinx/maintainers_include.py
+++ b/Documentation/sphinx/maintainers_include.py
@@ -21,6 +21,8 @@ import sys
import re
import os.path
+from textwrap import indent
+
from docutils import statemachine
from docutils.parsers.rst import Directive
from docutils.parsers.rst.directives.misc import Include
@@ -30,20 +32,11 @@ def ErrorString(exc): # Shamelessly stolen from docutils
__version__ = '1.0'
-def setup(app):
- app.add_directive("maintainers-include", MaintainersInclude)
- return dict(
- version = __version__,
- parallel_read_safe = True,
- parallel_write_safe = True
- )
+class MaintainersParser:
+ """Parse MAINTAINERS file(s) content"""
-class MaintainersInclude(Include):
- """MaintainersInclude (``maintainers-include``) directive"""
- required_arguments = 0
-
- def parse_maintainers(self, path):
- """Parse all the MAINTAINERS lines into ReST for human-readability"""
+ def __init__(self, base_path, path):
+ self.profiles = list()
result = list()
result.append(".. _maintainers:")
@@ -78,6 +71,12 @@ class MaintainersInclude(Include):
# Drop needless input whitespace.
line = line.rstrip()
+ match = re.match(r"P:\s*(Documentation/\S+)\.rst", line)
+ if match:
+ fname = os.path.relpath(match.group(1), base_path)
+ if fname not in self.profiles:
+ self.profiles.append(fname)
+
# Linkify all non-wildcard refs to ReST files in Documentation/.
pat = r'(Documentation/([^\s\?\*]*)\.rst)'
m = re.search(pat, line)
@@ -165,12 +164,23 @@ class MaintainersInclude(Include):
for separated in field_content.split('\n'):
result.append(separated)
- output = "\n".join(result)
+ self.output = "\n".join(result)
+
+ # Create a TOC class
+
+class MaintainersInclude(Include):
+ """MaintainersInclude (``maintainers-include``) directive"""
+ required_arguments = 0
+
+ def emit(self, base_path, path):
+ """Parse all the MAINTAINERS lines into ReST for human-readability"""
+
+ output = MaintainersParser(base_path, path).output
+
# For debugging the pre-rendered results...
#print(output, file=open("/tmp/MAINTAINERS.rst", "w"))
- self.state_machine.insert_input(
- statemachine.string2lines(output), path)
+ self.state_machine.insert_input(statemachine.string2lines(output), path)
def run(self):
"""Include the MAINTAINERS file as part of this reST file."""
@@ -186,12 +196,61 @@ class MaintainersInclude(Include):
# Append "MAINTAINERS"
path = os.path.join(path, "MAINTAINERS")
+ base_path = os.path.dirname(self.state.document.document.current_source)
try:
self.state.document.settings.record_dependencies.add(path)
- lines = self.parse_maintainers(path)
+ lines = self.emit(base_path, path)
except IOError as error:
raise self.severe('Problems with "%s" directive path:\n%s.' %
(self.name, ErrorString(error)))
return []
+
+class MaintainersProfile(Include):
+ required_arguments = 0
+
+ def emit(self, base_path, path):
+ """Parse all the MAINTAINERS lines looking for profile entries"""
+
+ profiles = MaintainersParser(base_path, path).profiles
+
+ output = ".. toctree::\n"
+ output += " :maxdepth: 2\n\n"
+ output += indent("\n".join(profiles), " ")
+
+ self.state_machine.insert_input(statemachine.string2lines(output), path)
+
+ def run(self):
+ """Include the MAINTAINERS file as part of this reST file."""
+ if not self.state.document.settings.file_insertion_enabled:
+ raise self.warning('"%s" directive disabled.' % self.name)
+
+ # Walk up source path directories to find Documentation/../
+ path = self.state_machine.document.attributes['source']
+ path = os.path.realpath(path)
+ tail = path
+ while tail != "Documentation" and tail != "":
+ (path, tail) = os.path.split(path)
+
+ # Append "MAINTAINERS"
+ path = os.path.join(path, "MAINTAINERS")
+ base_path = os.path.dirname(self.state.document.document.current_source)
+
+ try:
+ self.state.document.settings.record_dependencies.add(path)
+ lines = self.emit(base_path, path)
+ except IOError as error:
+ raise self.severe('Problems with "%s" directive path:\n%s.' %
+ (self.name, ErrorString(error)))
+
+ return []
+
+def setup(app):
+ app.add_directive("maintainers-include", MaintainersInclude)
+ app.add_directive("maintainers-profile-toc", MaintainersProfile)
+ return dict(
+ version = __version__,
+ parallel_read_safe = True,
+ parallel_write_safe = True
+ )
--
2.53.0
^ permalink raw reply related
* [PATCH v2 02/11] MAINTAINERS: add maintainer-tip.rst to X86
From: Mauro Carvalho Chehab @ 2026-04-17 6:11 UTC (permalink / raw)
To: Jonathan Corbet, Linux Doc Mailing List
Cc: Mauro Carvalho Chehab, linux-kernel, linux-riscv, workflows,
Dan Williams, Mauro Carvalho Chehab, Randy Dunlap
In-Reply-To: <cover.1776405189.git.mchehab+huawei@kernel.org>
The X86 subsystem has a maintainers entry profile, but its entry
is missing at MAINTAINERS.
Add it.
Acked-by: Randy Dunlap <rdunlap@infradead.org>
Acked-by: Dan Williams <djbw@kernel.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
Message-ID: <970434c647aa1e1e9a81c87b4d5fed934d4018a7.1776176108.git.mchehab+huawei@kernel.org>
---
MAINTAINERS | 1 +
1 file changed, 1 insertion(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 620219e48f98..a85fcae5f56e 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -28560,6 +28560,7 @@ M: Ingo Molnar <mingo@redhat.com>
M: Borislav Petkov <bp@alien8.de>
M: Dave Hansen <dave.hansen@linux.intel.com>
M: x86@kernel.org
+P: Documentation/process/maintainer-tip.rst
R: "H. Peter Anvin" <hpa@zytor.com>
L: linux-kernel@vger.kernel.org
S: Maintained
--
2.53.0
^ permalink raw reply related
* [PATCH v2 05/11] docs: maintainers_include: use a better title for profiles
From: Mauro Carvalho Chehab @ 2026-04-17 6:11 UTC (permalink / raw)
To: Jonathan Corbet, Linux Doc Mailing List, Mauro Carvalho Chehab
Cc: Mauro Carvalho Chehab, linux-kernel, linux-riscv, workflows,
Dan Williams, Randy Dunlap, Shuah Khan
In-Reply-To: <cover.1776405189.git.mchehab+huawei@kernel.org>
As we're picking the name of the subsystem from MAINTAINERS,
also use its subsystem name for the titles.
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
---
Documentation/sphinx/maintainers_include.py | 18 +++++++++++++++---
1 file changed, 15 insertions(+), 3 deletions(-)
diff --git a/Documentation/sphinx/maintainers_include.py b/Documentation/sphinx/maintainers_include.py
index 1dac83bf1a65..cf428db7599c 100755
--- a/Documentation/sphinx/maintainers_include.py
+++ b/Documentation/sphinx/maintainers_include.py
@@ -36,7 +36,7 @@ class MaintainersParser:
"""Parse MAINTAINERS file(s) content"""
def __init__(self, base_path, path):
- self.profiles = list()
+ self.profiles = {}
result = list()
result.append(".. _maintainers:")
@@ -54,6 +54,7 @@ class MaintainersParser:
prev = None
field_prev = ""
field_content = ""
+ subsystem_name = None
for line in open(path):
# Have we reached the end of the preformatted Descriptions text?
@@ -75,7 +76,10 @@ class MaintainersParser:
if match:
fname = os.path.relpath(match.group(1), base_path)
if fname not in self.profiles:
- self.profiles.append(fname)
+ if self.profiles.get(fname) is None:
+ self.profiles[fname] = subsystem_name
+ else:
+ self.profiles[fname] += f", {subsystem_name}"
# Linkify all non-wildcard refs to ReST files in Documentation/.
pat = r'(Documentation/([^\s\?\*]*)\.rst)'
@@ -112,6 +116,8 @@ class MaintainersParser:
output = field_content + "\n\n"
field_content = ""
+ subsystem_name = line.title()
+
# Collapse whitespace in subsystem name.
heading = re.sub(r"\s+", " ", line)
output = output + "%s\n%s" % (heading, "~" * len(heading))
@@ -217,7 +223,13 @@ class MaintainersProfile(Include):
output = ".. toctree::\n"
output += " :maxdepth: 2\n\n"
- output += indent("\n".join(profiles), " ")
+
+ items = sorted(profiles.items(), key=lambda kv: (kv[1] or "", kv[0]))
+ for fname, profile in items:
+ if profile:
+ output += f" {profile} <{fname}>\n"
+ else:
+ output += f" {fname}\n"
self.state_machine.insert_input(statemachine.string2lines(output), path)
--
2.53.0
^ permalink raw reply related
* [PATCH v2 00/11] Auto-generate maintainer profile entries
From: Mauro Carvalho Chehab @ 2026-04-17 6:11 UTC (permalink / raw)
To: Albert Ou, Jonathan Corbet, Mauro Carvalho Chehab, Palmer Dabbelt,
Paul Walmsley
Cc: Mauro Carvalho Chehab, linux-doc, linux-kernel, linux-riscv,
workflows, Alexandre Ghiti, Shuah Khan, Randy Dunlap,
Dan Williams
Hi Jon,
This patch series change the way maintainer entry profile links
are added to the documentation. Instead of having an entry for
each of them at an ReST file, get them from MAINTAINERS content.
That should likely make easier to maintain, as there will be a single
point to place all such profiles.
The output is a per-subsystem sorted (*) series of links shown as a
list like this:
- Arm And Arm64 Soc Sub-Architectures (Common Parts)
- Arm/Samsung S3C, S5P And Exynos Arm Architectures
- Arm/Tesla Fsd Soc Support
...
- Xfs Filesystem
Please notice that the series is doing one logical change per patch.
I could have merged some changes altogether, but I opted doing it
in small steps to help reviews. If you prefer, feel free to merge
maintainers_include changes on merge.
There is one interesting side effect of this series: there is no
need to add rst files containing profiles inside a TOC tree: Just
creating the file anywhere inside Documentation and adding a P entry
is enough. Adding them to a TOC won't hurt.
Reported-by: Randy Dunlap <rdunlap@infradead.org>
Suggested-by: Dan Williams <djbw@kernel.org>
Closes: https://lore.kernel.org/linux-doc/69dd6299440be_147c801005b@djbw-dev.notmuch/
(*) At the end, I opted to use sorted(), just to ensure it, even
knowing that MAINTAINER entries are supposed to be sorted, as
the cost of sorting ~20 already-sorted entries is negligible.
---
v2:
- I placed the to MAINTAINERS changes at the beginning.
- fix a bug when O=DOCS is used;
- proper handle glob "P" entries (just in case, no profiles use it ATM);
- when SPHINXDIRS=process, instead of producing warnings, point to
entries at https://docs.kernel.org;
- MAINTAINERS parsing now happens just once;
- The output won't be numered for entries inside numered TOC trees;
- TOC tree is now hidden;
- instead of display a TOC tree, it shows a list of profiles,
ordered and named after file system name taken from MAINTAINERS file;
- At the output list, both https and file profiles are shown the same
way.
Mauro Carvalho Chehab (11):
MAINTAINERS: add an entry for media maintainers profile
MAINTAINERS: add maintainer-tip.rst to X86
docs: maintainers_include: auto-generate maintainer profile TOC
docs: auto-generate maintainer entry profile links
docs: maintainers_include: use a better title for profiles
docs: maintainers_include: add external profile URLs
docs: maintainers_include: preserve names for files under process/
docs: maintainers_include: Only show main entry for profiles
docs: maintainers_include: improve its output
docs: maintainers_include: fix support for O=dir
docs: maintainers_include: parse MAINTAINERS just once
.../maintainer/maintainer-entry-profile.rst | 24 +--
.../process/maintainer-handbooks.rst | 17 +-
Documentation/sphinx/maintainers_include.py | 161 +++++++++++++++---
MAINTAINERS | 2 +
4 files changed, 150 insertions(+), 54 deletions(-)
--
2.53.0
^ permalink raw reply
* [PATCH v2 09/11] docs: maintainers_include: improve its output
From: Mauro Carvalho Chehab @ 2026-04-17 6:11 UTC (permalink / raw)
To: Jonathan Corbet, Linux Doc Mailing List, Mauro Carvalho Chehab
Cc: Mauro Carvalho Chehab, linux-kernel, linux-riscv, workflows,
Dan Williams, Randy Dunlap, Shuah Khan
In-Reply-To: <cover.1776405189.git.mchehab+huawei@kernel.org>
There are three "types" of profiles:
1. Profiles already included inside subsystem-specific documentation.
This is the most common case;
2. Profiles that are hosted externally;
3. Profiles that are at the same location as maintainer-handbooks.rst.
For (3), we need to create a TOC, as they don't exist elsewhere.
Change the logic to create TOC just for (3), prepending the
content of maintainer-handbooks with a sorted entry of all types,
before the TOC.
With such change, we can have an unique sorted list of profiles,
having the subsystem names used there listed.
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
---
Documentation/sphinx/maintainers_include.py | 76 +++++++++++----------
1 file changed, 40 insertions(+), 36 deletions(-)
diff --git a/Documentation/sphinx/maintainers_include.py b/Documentation/sphinx/maintainers_include.py
index 7ab921820612..5413c1350bba 100755
--- a/Documentation/sphinx/maintainers_include.py
+++ b/Documentation/sphinx/maintainers_include.py
@@ -21,7 +21,7 @@ import sys
import re
import os.path
-from textwrap import indent
+from glob import glob
from docutils import statemachine
from docutils.parsers.rst import Directive
@@ -36,8 +36,8 @@ class MaintainersParser:
"""Parse MAINTAINERS file(s) content"""
def __init__(self, base_path, path):
- self.profiles = {}
- self.profile_urls = {}
+ self.profile_toc = set()
+ self.profile_entries = {}
result = list()
result.append(".. _maintainers:")
@@ -73,26 +73,24 @@ class MaintainersParser:
# Drop needless input whitespace.
line = line.rstrip()
+ #
+ # Handle profile entries - either as files or as https refs
+ #
match = re.match(r"P:\s*(Documentation/\S+)\.rst", line)
if match:
- fname = os.path.relpath(match.group(1), base_path)
- if fname.startswith("../"):
- if self.profiles.get(fname) is None:
- self.profiles[fname] = subsystem_name
- else:
- self.profiles[fname] += f", {subsystem_name}"
+ entry = os.path.relpath(match.group(1), base_path)
+ if "*" in entry:
+ for e in glob(entry):
+ self.profile_toc.add(e)
+ self.profile_entries[subsystem_name] = e
else:
- self.profiles[fname] = None
-
- match = re.match(r"P:\s*(https?://.*)", line)
- if match:
- url = match.group(1).strip()
- if url not in self.profile_urls:
- if self.profile_urls.get(url) is None:
- self.profile_urls[url] = subsystem_name
- else:
- self.profile_urls[url] += f", {subsystem_name}"
-
+ self.profile_toc.add(entry)
+ self.profile_entries[subsystem_name] = entry
+ else:
+ match = re.match(r"P:\s*(https?://.*)", line)
+ if match:
+ entry = match.group(1).strip()
+ self.profile_entries[subsystem_name] = entry
# Linkify all non-wildcard refs to ReST files in Documentation/.
pat = r'(Documentation/([^\s\?\*]*)\.rst)'
@@ -234,26 +232,32 @@ class MaintainersProfile(Include):
maint = MaintainersParser(base_path, path)
- output = ".. toctree::\n"
- output += " :maxdepth: 1\n\n"
+ #
+ # Produce a list with all maintainer profiles, sorted by subsystem name
+ #
+ output = ""
- items = sorted(maint.profiles.items(),
- key=lambda kv: (kv[1] or "", kv[0]))
- for fname, profile in items:
- if profile:
- output += f" {profile} <{fname}>\n"
+ for profile, entry in maint.profile_entries.items():
+ if entry.startswith("http"):
+ if profile:
+ output += f"- `{profile} <{entry}>`_\n"
+ else:
+ output += f"- `<{entry}>_`\n"
else:
- output += f" {fname}\n"
+ if profile:
+ output += f"- :doc:`{profile} <{entry}>`\n"
+ else:
+ output += f"- :doc:`<{entry}>`\n"
- output += "\n**External profiles**\n\n"
+ #
+ # Create a hidden TOC table with all profiles. That allows adding
+ # profiles without needing to add them on any index.rst file.
+ #
+ output += "\n.. toctree::\n"
+ output += " :hidden:\n\n"
- items = sorted(maint.profile_urls.items(),
- key=lambda kv: (kv[1] or "", kv[0]))
- for url, profile in items:
- if profile:
- output += f"- {profile} <{url}>\n"
- else:
- output += f"- {url}\n"
+ for fname in maint.profile_toc:
+ output += f" {fname}\n"
output += "\n"
--
2.53.0
^ permalink raw reply related
* [PATCH v2 04/11] docs: auto-generate maintainer entry profile links
From: Mauro Carvalho Chehab @ 2026-04-17 6:11 UTC (permalink / raw)
To: Jonathan Corbet, Linux Doc Mailing List
Cc: Mauro Carvalho Chehab, linux-kernel, linux-riscv, workflows,
Albert Ou, Alexandre Ghiti, Dan Williams, Mauro Carvalho Chehab,
Palmer Dabbelt, Paul Walmsley, Randy Dunlap, Shuah Khan
In-Reply-To: <cover.1776405189.git.mchehab+huawei@kernel.org>
Instead of manually creating a TOC tree for them, use the new
tag to auto-generate its TOC.
Co-developed-by: Dan Williams <djbw@kernel.org>
Signed-off-by: Dan Williams <djbw@kernel.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
Message-ID: <9228f77b0339b8e5dea4a201ab6d4feb30cef5c2.1776176108.git.mchehab+huawei@kernel.org>
---
.../maintainer/maintainer-entry-profile.rst | 24 ++++---------------
.../process/maintainer-handbooks.rst | 19 ++++++++-------
2 files changed, 14 insertions(+), 29 deletions(-)
diff --git a/Documentation/maintainer/maintainer-entry-profile.rst b/Documentation/maintainer/maintainer-entry-profile.rst
index 6020d188e13d..58e2af333692 100644
--- a/Documentation/maintainer/maintainer-entry-profile.rst
+++ b/Documentation/maintainer/maintainer-entry-profile.rst
@@ -92,24 +92,8 @@ full series, or privately send a reminder email. This section might also
list how review works for this code area and methods to get feedback
that are not directly from the maintainer.
-Existing profiles
------------------
+Maintainer Handbooks
+--------------------
-For now, existing maintainer profiles are listed here; we will likely want
-to do something different in the near future.
-
-.. toctree::
- :maxdepth: 1
-
- ../doc-guide/maintainer-profile
- ../nvdimm/maintainer-entry-profile
- ../arch/riscv/patch-acceptance
- ../process/maintainer-soc
- ../process/maintainer-soc-clean-dts
- ../driver-api/media/maintainer-entry-profile
- ../process/maintainer-netdev
- ../driver-api/vfio-pci-device-specific-driver-acceptance
- ../nvme/feature-and-quirk-policy
- ../filesystems/nfs/nfsd-maintainer-entry-profile
- ../filesystems/xfs/xfs-maintainer-entry-profile
- ../mm/damon/maintainer-profile
+For examples of other subsystem handbooks see
+Documentation/process/maintainer-handbooks.rst.
diff --git a/Documentation/process/maintainer-handbooks.rst b/Documentation/process/maintainer-handbooks.rst
index 3d72ad25fc6a..531985a0fae8 100644
--- a/Documentation/process/maintainer-handbooks.rst
+++ b/Documentation/process/maintainer-handbooks.rst
@@ -7,14 +7,15 @@ The purpose of this document is to provide subsystem specific information
which is supplementary to the general development process handbook
:ref:`Documentation/process <development_process_main>`.
+For developers, see below for all the known subsystem specific guides.
+If the subsystem you are contributing to does not have a guide listed
+here, it is fair to seek clarification of questions raised in
+Documentation/maintainer/maintainer-entry-profile.rst.
+
+For maintainers, consider documenting additional requirements and
+expectations if submissions routinely overlook specific submission
+criteria. See Documentation/maintainer/maintainer-entry-profile.rst.
+
Contents:
-.. toctree::
- :numbered:
- :maxdepth: 2
-
- maintainer-netdev
- maintainer-soc
- maintainer-soc-clean-dts
- maintainer-tip
- maintainer-kvm-x86
+.. maintainers-profile-toc::
--
2.53.0
^ permalink raw reply related
* [PATCH v2 06/11] docs: maintainers_include: add external profile URLs
From: Mauro Carvalho Chehab @ 2026-04-17 6:11 UTC (permalink / raw)
To: Jonathan Corbet, Linux Doc Mailing List, Mauro Carvalho Chehab
Cc: Mauro Carvalho Chehab, linux-kernel, linux-riscv, workflows,
Dan Williams, Randy Dunlap, Shuah Khan
In-Reply-To: <cover.1776405189.git.mchehab+huawei@kernel.org>
Some subsystem profiles are maintained elsewhere. Add them to
the output.
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
---
Documentation/sphinx/maintainers_include.py | 28 +++++++++++++++++++--
1 file changed, 26 insertions(+), 2 deletions(-)
diff --git a/Documentation/sphinx/maintainers_include.py b/Documentation/sphinx/maintainers_include.py
index cf428db7599c..f1b8d4b00c2a 100755
--- a/Documentation/sphinx/maintainers_include.py
+++ b/Documentation/sphinx/maintainers_include.py
@@ -37,6 +37,7 @@ class MaintainersParser:
def __init__(self, base_path, path):
self.profiles = {}
+ self.profile_urls = {}
result = list()
result.append(".. _maintainers:")
@@ -81,6 +82,16 @@ class MaintainersParser:
else:
self.profiles[fname] += f", {subsystem_name}"
+ match = re.match(r"P:\s*(https?://.*)", line)
+ if match:
+ url = match.group(1).strip()
+ if url not in self.profile_urls:
+ if self.profile_urls.get(url) is None:
+ self.profile_urls[url] = subsystem_name
+ else:
+ self.profile_urls[url] += f", {subsystem_name}"
+
+
# Linkify all non-wildcard refs to ReST files in Documentation/.
pat = r'(Documentation/([^\s\?\*]*)\.rst)'
m = re.search(pat, line)
@@ -219,18 +230,31 @@ class MaintainersProfile(Include):
def emit(self, base_path, path):
"""Parse all the MAINTAINERS lines looking for profile entries"""
- profiles = MaintainersParser(base_path, path).profiles
+ maint = MaintainersParser(base_path, path)
output = ".. toctree::\n"
output += " :maxdepth: 2\n\n"
- items = sorted(profiles.items(), key=lambda kv: (kv[1] or "", kv[0]))
+ items = sorted(maint.profiles.items(),
+ key=lambda kv: (kv[1] or "", kv[0]))
for fname, profile in items:
if profile:
output += f" {profile} <{fname}>\n"
else:
output += f" {fname}\n"
+ output += "\n**External profiles**\n\n"
+
+ items = sorted(maint.profile_urls.items(),
+ key=lambda kv: (kv[1] or "", kv[0]))
+ for url, profile in items:
+ if profile:
+ output += f"- {profile} <{url}>\n"
+ else:
+ output += f"- {url}\n"
+
+ output += "\n"
+
self.state_machine.insert_input(statemachine.string2lines(output), path)
def run(self):
--
2.53.0
^ permalink raw reply related
* [PATCH v2 11/11] docs: maintainers_include: parse MAINTAINERS just once
From: Mauro Carvalho Chehab @ 2026-04-17 6:11 UTC (permalink / raw)
To: Jonathan Corbet, Linux Doc Mailing List, Mauro Carvalho Chehab
Cc: Mauro Carvalho Chehab, linux-kernel, linux-riscv, workflows,
Dan Williams, Randy Dunlap, Shuah Khan
In-Reply-To: <cover.1776405189.git.mchehab+huawei@kernel.org>
Change the logic to parse MAINTAINERS file content just once,
while still allowing using it multiple times.
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
---
Documentation/sphinx/maintainers_include.py | 61 +++++++--------------
1 file changed, 21 insertions(+), 40 deletions(-)
diff --git a/Documentation/sphinx/maintainers_include.py b/Documentation/sphinx/maintainers_include.py
index ae52e8198750..436e7ac42ffc 100755
--- a/Documentation/sphinx/maintainers_include.py
+++ b/Documentation/sphinx/maintainers_include.py
@@ -37,14 +37,13 @@ def ErrorString(exc): # Shamelessly stolen from docutils
__version__ = '1.0'
-app_dir = "."
+maint_parser = None
class MaintainersParser:
"""Parse MAINTAINERS file(s) content"""
- def __init__(self, path):
- global app_dir
-
+ def __init__(self, app_dir, path):
+ self.path = path
self.profile_toc = set()
self.profile_entries = {}
@@ -67,7 +66,6 @@ class MaintainersParser:
subsystem_name = None
base_dir, doc_dir, sphinx_dir = app_dir.partition("Documentation")
- print("BASE DIR", base_dir)
for line in open(path):
# Have we reached the end of the preformatted Descriptions text?
@@ -105,8 +103,6 @@ class MaintainersParser:
else:
entry = "/" + entry
- print(f"{name}: entry: {entry} FULL: {full_name} path: {path}")
-
if "*" in entry:
for e in glob(entry):
self.profile_toc.add(e)
@@ -217,14 +213,17 @@ class MaintainersInclude(Include):
"""MaintainersInclude (``maintainers-include``) directive"""
required_arguments = 0
- def emit(self, path):
+ def emit(self):
"""Parse all the MAINTAINERS lines into ReST for human-readability"""
+ global maint_parser
- output = MaintainersParser(path).output
+ path = maint_parser.path
+ output = maint_parser.output
# For debugging the pre-rendered results...
#print(output, file=open("/tmp/MAINTAINERS.rst", "w"))
+ self.state.document.settings.record_dependencies.add(path)
self.state_machine.insert_input(statemachine.string2lines(output), path)
def run(self):
@@ -232,19 +231,8 @@ class MaintainersInclude(Include):
if not self.state.document.settings.file_insertion_enabled:
raise self.warning('"%s" directive disabled.' % self.name)
- # Walk up source path directories to find Documentation/../
- path = self.state_machine.document.attributes['source']
- path = os.path.realpath(path)
- tail = path
- while tail != "Documentation" and tail != "":
- (path, tail) = os.path.split(path)
-
- # Append "MAINTAINERS"
- path = os.path.join(path, "MAINTAINERS")
-
try:
- self.state.document.settings.record_dependencies.add(path)
- lines = self.emit(path)
+ lines = self.emit()
except IOError as error:
raise self.severe('Problems with "%s" directive path:\n%s.' %
(self.name, ErrorString(error)))
@@ -254,16 +242,17 @@ class MaintainersInclude(Include):
class MaintainersProfile(Include):
required_arguments = 0
- def emit(self, path):
+ def emit(self):
"""Parse all the MAINTAINERS lines looking for profile entries"""
+ global maint_parser
- maint = MaintainersParser(path)
+ path = maint_parser.path
#
# Produce a list with all maintainer profiles, sorted by subsystem name
#
output = ""
- for profile, entry in sorted(maint.profile_entries.items()):
+ for profile, entry in sorted(maint_parser.profile_entries.items()):
if entry.startswith("http"):
output += f"- `{profile} <{entry}>`_\n"
else:
@@ -276,13 +265,12 @@ class MaintainersProfile(Include):
output += "\n.. toctree::\n"
output += " :hidden:\n\n"
- for fname in maint.profile_toc:
+ for fname in maint_parser.profile_toc:
output += f" {fname}\n"
output += "\n"
- print(output)
-
+ self.state.document.settings.record_dependencies.add(path)
self.state_machine.insert_input(statemachine.string2lines(output), path)
def run(self):
@@ -290,19 +278,8 @@ class MaintainersProfile(Include):
if not self.state.document.settings.file_insertion_enabled:
raise self.warning('"%s" directive disabled.' % self.name)
- # Walk up source path directories to find Documentation/../
- path = self.state_machine.document.attributes['source']
- path = os.path.realpath(path)
- tail = path
- while tail != "Documentation" and tail != "":
- (path, tail) = os.path.split(path)
-
- # Append "MAINTAINERS"
- path = os.path.join(path, "MAINTAINERS")
-
try:
- self.state.document.settings.record_dependencies.add(path)
- lines = self.emit(path)
+ lines = self.emit()
except IOError as error:
raise self.severe('Problems with "%s" directive path:\n%s.' %
(self.name, ErrorString(error)))
@@ -310,13 +287,17 @@ class MaintainersProfile(Include):
return []
def setup(app):
- global app_dir
+ global maint_parser
#
# NOTE: we're using os.fspath() here because of a Sphinx warning:
# RemovedInSphinx90Warning: Sphinx 9 will drop support for representing paths as strings. Use "pathlib.Path" or "os.fspath" instead.
#
app_dir = os.fspath(app.srcdir)
+ srctree = os.path.abspath(os.environ["srctree"])
+ path = os.path.join(srctree, "MAINTAINERS")
+
+ maint_parser = MaintainersParser(app_dir, path)
app.add_directive("maintainers-include", MaintainersInclude)
app.add_directive("maintainers-profile-toc", MaintainersProfile)
--
2.53.0
^ permalink raw reply related
* [PATCH v2 08/11] docs: maintainers_include: Only show main entry for profiles
From: Mauro Carvalho Chehab @ 2026-04-17 6:11 UTC (permalink / raw)
To: Jonathan Corbet, Linux Doc Mailing List
Cc: Mauro Carvalho Chehab, linux-kernel, linux-riscv, workflows,
Dan Williams, Mauro Carvalho Chehab, Randy Dunlap, Shuah Khan
In-Reply-To: <cover.1776405189.git.mchehab+huawei@kernel.org>
Instead of showing as a "Contents:" with 2 identation levels,
drop its title and show profiles as a list of entries.
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
---
Documentation/process/maintainer-handbooks.rst | 2 --
Documentation/sphinx/maintainers_include.py | 2 +-
2 files changed, 1 insertion(+), 3 deletions(-)
diff --git a/Documentation/process/maintainer-handbooks.rst b/Documentation/process/maintainer-handbooks.rst
index 531985a0fae8..3821e78aefc0 100644
--- a/Documentation/process/maintainer-handbooks.rst
+++ b/Documentation/process/maintainer-handbooks.rst
@@ -16,6 +16,4 @@ For maintainers, consider documenting additional requirements and
expectations if submissions routinely overlook specific submission
criteria. See Documentation/maintainer/maintainer-entry-profile.rst.
-Contents:
-
.. maintainers-profile-toc::
diff --git a/Documentation/sphinx/maintainers_include.py b/Documentation/sphinx/maintainers_include.py
index 948746b998a3..7ab921820612 100755
--- a/Documentation/sphinx/maintainers_include.py
+++ b/Documentation/sphinx/maintainers_include.py
@@ -235,7 +235,7 @@ class MaintainersProfile(Include):
maint = MaintainersParser(base_path, path)
output = ".. toctree::\n"
- output += " :maxdepth: 2\n\n"
+ output += " :maxdepth: 1\n\n"
items = sorted(maint.profiles.items(),
key=lambda kv: (kv[1] or "", kv[0]))
--
2.53.0
^ permalink raw reply related
* [PATCH v2 07/11] docs: maintainers_include: preserve names for files under process/
From: Mauro Carvalho Chehab @ 2026-04-17 6:11 UTC (permalink / raw)
To: Jonathan Corbet, Linux Doc Mailing List, Mauro Carvalho Chehab
Cc: Mauro Carvalho Chehab, linux-kernel, linux-riscv, workflows,
Dan Williams, Randy Dunlap, Shuah Khan
In-Reply-To: <cover.1776405189.git.mchehab+huawei@kernel.org>
When a maintainer's profile is stored outside process, they're
already included on some other book and the name of the filesystem
may not be there. That's why the logic picks the name from the
subsystem's name.
However, files directly placed together with maintainers-handbooks.rst
(e.g. under Documentation/process/) is a different history: those
aren't placed anywhere, so we can keep using their own names,
letting Sphinx do his thing.
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
---
Documentation/sphinx/maintainers_include.py | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/Documentation/sphinx/maintainers_include.py b/Documentation/sphinx/maintainers_include.py
index f1b8d4b00c2a..948746b998a3 100755
--- a/Documentation/sphinx/maintainers_include.py
+++ b/Documentation/sphinx/maintainers_include.py
@@ -76,11 +76,13 @@ class MaintainersParser:
match = re.match(r"P:\s*(Documentation/\S+)\.rst", line)
if match:
fname = os.path.relpath(match.group(1), base_path)
- if fname not in self.profiles:
+ if fname.startswith("../"):
if self.profiles.get(fname) is None:
self.profiles[fname] = subsystem_name
else:
self.profiles[fname] += f", {subsystem_name}"
+ else:
+ self.profiles[fname] = None
match = re.match(r"P:\s*(https?://.*)", line)
if match:
--
2.53.0
^ permalink raw reply related
* [PATCH v2 10/11] docs: maintainers_include: fix support for O=dir
From: Mauro Carvalho Chehab @ 2026-04-17 6:11 UTC (permalink / raw)
To: Jonathan Corbet, Linux Doc Mailing List, Mauro Carvalho Chehab
Cc: Mauro Carvalho Chehab, linux-kernel, linux-riscv, workflows,
Dan Williams, Randy Dunlap, Shuah Khan
In-Reply-To: <cover.1776405189.git.mchehab+huawei@kernel.org>
os.path.relpath() will do the wrong thing with O=dir, as the build
system uses "cd <dir>" internally.
Solve it by using app.srcdir, which, on normal cases, point to
Documentation/, or, when SPHINXDIRS=process, it will be set with
Documentation/process.
While here, remove a dead code while writing maintainer profiles,
as now all entries should have both profile and entry.
Reported-by: Randy Dunlap <rdunlap@infradead.org>
Closes: https://lore.kernel.org/linux-doc/88335220-3527-4b1f-9500-417f7ebb7a02@infradead.org/T/#m6854cbd8d30e2c5d3e8c4173bae1c3d6922ff970
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
---
Documentation/sphinx/maintainers_include.py | 71 +++++++++++++++------
1 file changed, 50 insertions(+), 21 deletions(-)
diff --git a/Documentation/sphinx/maintainers_include.py b/Documentation/sphinx/maintainers_include.py
index 5413c1350bba..ae52e8198750 100755
--- a/Documentation/sphinx/maintainers_include.py
+++ b/Documentation/sphinx/maintainers_include.py
@@ -27,15 +27,24 @@ from docutils import statemachine
from docutils.parsers.rst import Directive
from docutils.parsers.rst.directives.misc import Include
+#
+# Base URL for intersphinx-like links to maintainer profiles
+#
+KERNELDOC_URL = "https://docs.kernel.org/"
+
def ErrorString(exc): # Shamelessly stolen from docutils
return f'{exc.__class__.__name}: {exc}'
__version__ = '1.0'
+app_dir = "."
+
class MaintainersParser:
"""Parse MAINTAINERS file(s) content"""
- def __init__(self, base_path, path):
+ def __init__(self, path):
+ global app_dir
+
self.profile_toc = set()
self.profile_entries = {}
@@ -57,6 +66,9 @@ class MaintainersParser:
field_content = ""
subsystem_name = None
+ base_dir, doc_dir, sphinx_dir = app_dir.partition("Documentation")
+ print("BASE DIR", base_dir)
+
for line in open(path):
# Have we reached the end of the preformatted Descriptions text?
if descriptions and line.startswith('Maintainers'):
@@ -76,9 +88,25 @@ class MaintainersParser:
#
# Handle profile entries - either as files or as https refs
#
- match = re.match(r"P:\s*(Documentation/\S+)\.rst", line)
+ match = re.match(rf"P:\s*({doc_dir})(/\S+)\.rst", line)
if match:
- entry = os.path.relpath(match.group(1), base_path)
+ name = "".join(match.groups())
+ entry = os.path.relpath(base_dir + name, app_dir)
+
+ full_name = os.path.join(base_dir, name)
+ path = os.path.relpath(full_name, app_dir)
+ #
+ # When SPHINXDIRS is used, it will try to reference files
+ # outside srctree, causing warnings. To avoid that, point
+ # to the latest official documentation
+ #
+ if path.startswith("../"):
+ entry = KERNELDOC_URL + match.group(2) + ".html"
+ else:
+ entry = "/" + entry
+
+ print(f"{name}: entry: {entry} FULL: {full_name} path: {path}")
+
if "*" in entry:
for e in glob(entry):
self.profile_toc.add(e)
@@ -189,10 +217,10 @@ class MaintainersInclude(Include):
"""MaintainersInclude (``maintainers-include``) directive"""
required_arguments = 0
- def emit(self, base_path, path):
+ def emit(self, path):
"""Parse all the MAINTAINERS lines into ReST for human-readability"""
- output = MaintainersParser(base_path, path).output
+ output = MaintainersParser(path).output
# For debugging the pre-rendered results...
#print(output, file=open("/tmp/MAINTAINERS.rst", "w"))
@@ -213,11 +241,10 @@ class MaintainersInclude(Include):
# Append "MAINTAINERS"
path = os.path.join(path, "MAINTAINERS")
- base_path = os.path.dirname(self.state.document.document.current_source)
try:
self.state.document.settings.record_dependencies.add(path)
- lines = self.emit(base_path, path)
+ lines = self.emit(path)
except IOError as error:
raise self.severe('Problems with "%s" directive path:\n%s.' %
(self.name, ErrorString(error)))
@@ -227,27 +254,20 @@ class MaintainersInclude(Include):
class MaintainersProfile(Include):
required_arguments = 0
- def emit(self, base_path, path):
+ def emit(self, path):
"""Parse all the MAINTAINERS lines looking for profile entries"""
- maint = MaintainersParser(base_path, path)
+ maint = MaintainersParser(path)
#
# Produce a list with all maintainer profiles, sorted by subsystem name
#
output = ""
-
- for profile, entry in maint.profile_entries.items():
+ for profile, entry in sorted(maint.profile_entries.items()):
if entry.startswith("http"):
- if profile:
- output += f"- `{profile} <{entry}>`_\n"
- else:
- output += f"- `<{entry}>_`\n"
+ output += f"- `{profile} <{entry}>`_\n"
else:
- if profile:
- output += f"- :doc:`{profile} <{entry}>`\n"
- else:
- output += f"- :doc:`<{entry}>`\n"
+ output += f"- :doc:`{profile} <{entry}>`\n"
#
# Create a hidden TOC table with all profiles. That allows adding
@@ -261,6 +281,8 @@ class MaintainersProfile(Include):
output += "\n"
+ print(output)
+
self.state_machine.insert_input(statemachine.string2lines(output), path)
def run(self):
@@ -277,11 +299,10 @@ class MaintainersProfile(Include):
# Append "MAINTAINERS"
path = os.path.join(path, "MAINTAINERS")
- base_path = os.path.dirname(self.state.document.document.current_source)
try:
self.state.document.settings.record_dependencies.add(path)
- lines = self.emit(base_path, path)
+ lines = self.emit(path)
except IOError as error:
raise self.severe('Problems with "%s" directive path:\n%s.' %
(self.name, ErrorString(error)))
@@ -289,6 +310,14 @@ class MaintainersProfile(Include):
return []
def setup(app):
+ global app_dir
+
+ #
+ # NOTE: we're using os.fspath() here because of a Sphinx warning:
+ # RemovedInSphinx90Warning: Sphinx 9 will drop support for representing paths as strings. Use "pathlib.Path" or "os.fspath" instead.
+ #
+ app_dir = os.fspath(app.srcdir)
+
app.add_directive("maintainers-include", MaintainersInclude)
app.add_directive("maintainers-profile-toc", MaintainersProfile)
return dict(
--
2.53.0
^ permalink raw reply related
* Re: [PATCH 0/8] Auto-generate maintainer profile entries
From: Mauro Carvalho Chehab @ 2026-04-17 6:18 UTC (permalink / raw)
To: Randy Dunlap
Cc: Albert Ou, Jonathan Corbet, Mauro Carvalho Chehab, Palmer Dabbelt,
Paul Walmsley, linux-doc, linux-kernel, linux-riscv, workflows,
Alexandre Ghiti, Shuah Khan, Dan Williams
In-Reply-To: <c325d85e-98d2-4e35-b7e7-7bb4d6ee77aa@infradead.org>
On Thu, 16 Apr 2026 14:32:04 -0700
Randy Dunlap <rdunlap@infradead.org> wrote:
>
>
> On 4/16/26 1:00 AM, Mauro Carvalho Chehab wrote:
> > On Wed, 15 Apr 2026 13:41:16 -0700
> > Randy Dunlap <rdunlap@infradead.org> wrote:
> >
> >> Hi Mauro,
> >>
> >> Thanks for tackling this issue.
> >>
> >> On 4/15/26 1:52 AM, Mauro Carvalho Chehab wrote:
> >>> Date: Tue, 14 Apr 2026 16:29:03 +0200
> >>> From: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
> >>> To: Albert Ou <aou@eecs.berkeley.edu>, Jonathan Corbet <corbet@lwn.net>, Dan Williams <djbw@kernel.org>, Mauro Carvalho Chehab <mchehab@kernel.org>, Palmer Dabbelt <palmer@dabbelt.com>, Paul Walmsley <pjw@kernel.org>
> >>> Cc: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>, Randy Dunlap <rdunlap@infradead.org>, linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, linux-riscv@lists.infradead.org, workflows@vger.kernel.org, Alexandre Ghiti <alex@ghiti.fr>, Shuah Khan <skhan@linuxfoundation.org>
> >>> Message-ID: <cover.1776176108.git.mchehab+huawei@kernel.org>
> >>>
> >>> Hi Dan/Jon,
> >>>
> >>> This patch series change the way maintainer entry profile links
> >>> are added to the documentation. Instead of having an entry for
> >>> each of them at an ReST file, get them from MAINTAINERS content.
> >>>
> >>> That should likely make easier to maintain, as there will be a single
> >>> point to place all such profiles.
> >>>
> >>> On this version, I added Dan's text to patch 4.
> >>>
> >>> I also added a couple of other patches to improve its output. While
> >>> I could have them merged at the first patch, I opted to make them
> >>> separate, as, in case of problems or needed changes, it would be
> >>> easier to revert or modify the corresponding logic. Also, it should
> >>> be better to review, in case one wants some changes there.
> >>>
> >>> The main changes against RFC are:
> >>>
> >>> - now, the TOC will be presented with 1 depth identation level,
> >>> meaning that it would look like a list;
> >>> - for files outside Documentation/process, it will use the name of
> >>> the subsystem with title capitalization for the name of the
> >>> profile entry;
> >>> - the logic also parses and produces a list of profiles that are
> >>> maintained elsewhere, picking its http/https link;
> >>> - entries are now better sorted: first by subsystem name, then
> >>> by its name.
> >>>
> >>> Suggested-by: Dan Williams <djbw@kernel.org>
> >>> Closes: https://lore.kernel.org/linux-doc/69dd6299440be_147c801005b@djbw-dev.notmuch/
> >>>
> >>> Mauro Carvalho Chehab (8):
> >>> docs: maintainers_include: auto-generate maintainer profile TOC
> >>> MAINTAINERS: add an entry for media maintainers profile
> >>> MAINTAINERS: add maintainer-tip.rst to X86
> >>> docs: auto-generate maintainer entry profile links
> >>> docs: maintainers_include: use a better title for profiles
> >>> docs: maintainers_include: add external profile URLs
> >>> docs: maintainers_include: preserve names for files under process/
> >>> docs: maintainers_include: Only show main entry for profiles
> >>>
> >>> .../maintainer/maintainer-entry-profile.rst | 24 +---
> >>> .../process/maintainer-handbooks.rst | 17 ++-
> >>> Documentation/sphinx/maintainers_include.py | 131 +++++++++++++++---
> >>> MAINTAINERS | 2 +
> >>> 4 files changed, 128 insertions(+), 46 deletions(-)
> >>
> >> When building htmldocs with O=DOCS, I get a bunch of warnings.
> >> I tested against today's linux-next tree.
> >>
> >> The 'make O=DOCS htmldocs' warnings are (subset of all warnings):
> >>
> >> linux-next/MAINTAINERS:38: WARNING: toctree contains reference to nonexisting document 'DOCS/Documentation/process/maintainer-kvm-x86' [toc.not_readable]
> >> linux-next/MAINTAINERS:38: WARNING: toctree contains reference to nonexisting document 'DOCS/Documentation/filesystems/xfs/xfs-maintainer-entry-profile' [toc.not_readable]
> >> linux-next/MAINTAINERS:38: WARNING: toctree contains reference to nonexisting document 'DOCS/Documentation/process/maintainer-soc-clean-dts' [toc.not_readable]
> >> linux-next/MAINTAINERS:38: WARNING: toctree contains reference to nonexisting document 'DOCS/Documentation/process/maintainer-netdev' [toc.not_readable]
> >> linux-next/MAINTAINERS:38: WARNING: toctree contains reference to nonexisting document 'DOCS/Documentation/process/maintainer-tip' [toc.not_readable]
> >>
> >> linux-next/Documentation/filesystems/nfs/nfsd-maintainer-entry-profile.rst: WARNING: document isn't included in any toctree [toc.not_included]
> >> linux-next/Documentation/process/maintainer-kvm-x86.rst: WARNING: document isn't included in any toctree [toc.not_included]
> >> linux-next/Documentation/process/maintainer-netdev.rst: WARNING: document isn't included in any toctree [toc.not_included]
> >> linux-next/Documentation/process/maintainer-soc.rst: WARNING: document isn't included in any toctree [toc.not_included]
> >> linux-next/Documentation/process/maintainer-soc-clean-dts.rst: WARNING: document isn't included in any toctree [toc.not_included]
> >> linux-next/Documentation/process/maintainer-tip.rst: WARNING: document isn't included in any toctree [toc.not_included]
> >>
> >> linux-next/MAINTAINERS:1: WARNING: unknown document: '../../DOCS/Documentation/process/maintainer-soc' [ref.doc]
> >> linux-next/MAINTAINERS:2: WARNING: unknown document: '../../DOCS/Documentation/process/maintainer-soc-clean-dts' [ref.doc]
> >> linux-next/MAINTAINERS:3: WARNING: unknown document: '../../DOCS/Documentation/process/maintainer-soc-clean-dts' [ref.doc]
> >> linux-next/MAINTAINERS:5: WARNING: unknown document: '../../DOCS/Documentation/process/maintainer-tip' [ref.doc]
> >> linux-next/MAINTAINERS:6: WARNING: unknown document: '../../DOCS/Documentation/process/maintainer-tip' [ref.doc]
> >
> > Heh, os.path.relpath() does the wrong thing here.
> >
> > The enclosed patch should handle it better.
> >
> > Thanks,
> > Mauro
> >
> > [PATCH] docs: maintainers_include: fix support for O=dir
> >
> > os.path.relpath() will do the wrong thing with O=dir, as the build
> > system uses "cd <dir>" internally.
> >
> > Solve it by using app.srcdir, which, on normal cases, point to
> > Documentation/, or, when SPHINXDIRS=process, it will be set with
> > Documentation/process.
> >
> > While here, remove a dead code while writing maintainer profiles,
> > as now all entries should have both profile and entry.
> >
> > Reported-by: Randy Dunlap <rdunlap@infradead.org>
> > Closes: https://lore.kernel.org/linux-doc/88335220-3527-4b1f-9500-417f7ebb7a02@infradead.org/T/#m6854cbd8d30e2c5d3e8c4173bae1c3d6922ff970
> > Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
> >
> > diff --git a/Documentation/sphinx/maintainers_include.py b/Documentation/sphinx/maintainers_include.py
> > index 5413c1350bba..fff9bdd55a56 100755
> > --- a/Documentation/sphinx/maintainers_include.py
> > +++ b/Documentation/sphinx/maintainers_include.py
> > @@ -27,15 +27,24 @@ from docutils import statemachine
> > from docutils.parsers.rst import Directive
> > from docutils.parsers.rst.directives.misc import Include
> >
> > +#
> > +# Base URL for intersphinx-like links to maintainer profiles
> > +#
> > +KERNELDOC_URL = "https://docs.kernel.org/"
> > +
> > def ErrorString(exc): # Shamelessly stolen from docutils
> > return f'{exc.__class__.__name}: {exc}'
> >
> > __version__ = '1.0'
> >
> > +base_dir = "."
> > +
> > class MaintainersParser:
> > """Parse MAINTAINERS file(s) content"""
> >
> > - def __init__(self, base_path, path):
> > + def __init__(self, path):
> > + global base_dir
> > +
> > self.profile_toc = set()
> > self.profile_entries = {}
> >
> > @@ -76,9 +85,18 @@ class MaintainersParser:
> > #
> > # Handle profile entries - either as files or as https refs
> > #
> > - match = re.match(r"P:\s*(Documentation/\S+)\.rst", line)
> > + match = re.match(r"P:\s*Documentation(/\S+)\.rst", line)
> > if match:
> > - entry = os.path.relpath(match.group(1), base_path)
> > + entry = os.path.relpath(match.group(1), base_dir)
> > +
> > + #
> > + # When SPHINXDIRS is used, it will try to reference files
> > + # outside srctree, causing warnings. To avoid that, point
> > + # to the latest official documentation
> > + #
> > + if entry.startswith("../"):
> > + entry = KERNELDOC_URL + match.group(1) + ".html"
> > +
> > if "*" in entry:
> > for e in glob(entry):
> > self.profile_toc.add(e)
> > @@ -189,10 +207,10 @@ class MaintainersInclude(Include):
> > """MaintainersInclude (``maintainers-include``) directive"""
> > required_arguments = 0
> >
> > - def emit(self, base_path, path):
> > + def emit(self, path):
> > """Parse all the MAINTAINERS lines into ReST for human-readability"""
> >
> > - output = MaintainersParser(base_path, path).output
> > + output = MaintainersParser(path).output
> >
> > # For debugging the pre-rendered results...
> > #print(output, file=open("/tmp/MAINTAINERS.rst", "w"))
> > @@ -213,11 +231,10 @@ class MaintainersInclude(Include):
> >
> > # Append "MAINTAINERS"
> > path = os.path.join(path, "MAINTAINERS")
> > - base_path = os.path.dirname(self.state.document.document.current_source)
> >
> > try:
> > self.state.document.settings.record_dependencies.add(path)
> > - lines = self.emit(base_path, path)
> > + lines = self.emit(path)
> > except IOError as error:
> > raise self.severe('Problems with "%s" directive path:\n%s.' %
> > (self.name, ErrorString(error)))
> > @@ -227,27 +244,20 @@ class MaintainersInclude(Include):
> > class MaintainersProfile(Include):
> > required_arguments = 0
> >
> > - def emit(self, base_path, path):
> > + def emit(self, path):
> > """Parse all the MAINTAINERS lines looking for profile entries"""
> >
> > - maint = MaintainersParser(base_path, path)
> > + maint = MaintainersParser(path)
> >
> > #
> > # Produce a list with all maintainer profiles, sorted by subsystem name
> > #
> > output = ""
> > -
> > - for profile, entry in maint.profile_entries.items():
> > + for profile, entry in sorted(maint.profile_entries.items()):
> > if entry.startswith("http"):
> > - if profile:
> > - output += f"- `{profile} <{entry}>`_\n"
> > - else:
> > - output += f"- `<{entry}>_`\n"
> > + output += f"- `{profile} <{entry}>`_\n"
> > else:
> > - if profile:
> > - output += f"- :doc:`{profile} <{entry}>`\n"
> > - else:
> > - output += f"- :doc:`<{entry}>`\n"
> > + output += f"- :doc:`{profile} <{entry}>`\n"
> >
> > #
> > # Create a hidden TOC table with all profiles. That allows adding
> > @@ -277,11 +287,10 @@ class MaintainersProfile(Include):
> >
> > # Append "MAINTAINERS"
> > path = os.path.join(path, "MAINTAINERS")
> > - base_path = os.path.dirname(self.state.document.document.current_source)
> >
> > try:
> > self.state.document.settings.record_dependencies.add(path)
> > - lines = self.emit(base_path, path)
> > + lines = self.emit(path)
> > except IOError as error:
> > raise self.severe('Problems with "%s" directive path:\n%s.' %
> > (self.name, ErrorString(error)))
> > @@ -289,6 +298,15 @@ class MaintainersProfile(Include):
> > return []
> >
> > def setup(app):
> > + global base_dir
> > +
> > + #
> > + # partition will pick the path after Documentation.
> > + # NOTE: we're using os.fspath() here because of a Sphinx warning:
> > + # RemovedInSphinx90Warning: Sphinx 9 will drop support for representing paths as strings. Use "pathlib.Path" or "os.fspath" instead.
> > + #
> > + _, _, base_dir = os.fspath(app.srcdir).partition("Documentation")
> > +
> > app.add_directive("maintainers-include", MaintainersInclude)
> > app.add_directive("maintainers-profile-toc", MaintainersProfile)
> > return dict(
>
> With that patch I still see 6 warnings:
>
> linux-next/Documentation/filesystems/nfs/nfsd-maintainer-entry-profile.rst: WARNING: document isn't included in any toctree [toc.not_included]
> linux-next/Documentation/process/maintainer-kvm-x86.rst: WARNING: document isn't included in any toctree [toc.not_included]
> linux-next/Documentation/process/maintainer-netdev.rst: WARNING: document isn't included in any toctree [toc.not_included]
> linux-next/Documentation/process/maintainer-soc.rst: WARNING: document isn't included in any toctree [toc.not_included]
> linux-next/Documentation/process/maintainer-soc-clean-dts.rst: WARNING: document isn't included in any toctree [toc.not_included]
> linux-next/Documentation/process/maintainer-tip.rst: WARNING: document isn't included in any toctree [toc.not_included]
Heh, dealing with patches is tricky. At least on my tests, things seem
to be working fine at v2 of this series:
https://lore.kernel.org/linux-doc/cover.1776405189.git.mchehab+huawei@kernel.org/T/#t
here, I tested building docs with and without SPHINXDIRS=process and
O=DOCS, but it is nice if you can re-test it.
Basically, when SPHINXDIRS=process is used, instead of generating
wakings for docs outside process/ directory, it converts them to
hyperlinks to their corresponding name inside
https://docs.kernel.org/ (*).
(*) The logic assumes that the file would exist there, but doesn't
check.
Thanks,
Mauro
^ permalink raw reply
* Re: [PATCH V10 00/10] famfs: port into fuse
From: Gregory Price @ 2026-04-17 6:46 UTC (permalink / raw)
To: Joanne Koong
Cc: John Groves, Darrick J. Wong, Miklos Szeredi, Bernd Schubert,
John Groves, Dan Williams, Bernd Schubert, Alison Schofield,
John Groves, Jonathan Corbet, Shuah Khan, Vishal Verma,
Dave Jiang, Matthew Wilcox, Jan Kara, Alexander Viro,
David Hildenbrand, Christian Brauner, Randy Dunlap, Jeff Layton,
Amir Goldstein, Jonathan Cameron, Stefan Hajnoczi, Josef Bacik,
Bagas Sanjaya, Chen Linxuan, James Morse, Fuad Tabba,
Sean Christopherson, Shivank Garg, Ackerley Tng, Aravind Ramesh,
Ajay Joshi, venkataravis@micron.com, linux-doc@vger.kernel.org,
linux-kernel@vger.kernel.org, nvdimm@lists.linux.dev,
linux-cxl@vger.kernel.org, linux-fsdevel@vger.kernel.org, djbw
In-Reply-To: <CAJnrk1ad6t6CJV+xnXwhoNHrHYA3htuaVdDq47FeT60cPBzj7g@mail.gmail.com>
On Thu, Apr 16, 2026 at 06:24:02PM -0700, Joanne Koong wrote:
> On Thu, Apr 16, 2026 at 1:14 PM Gregory Price <gourry@gourry.net> wrote:
> >
> > I worry that this discussion is going to turn towards implementing a
> > solution grounded in parsing arbitrary formats and how to store them,
> > and that is completely detached from why FAMFS went this route in the
> > first place.
> >
> > I question whether the actual issue here lies in the interface APPEARING
> > more general purpose than it actually is - and therefore inviting
> > attempts to over-genericize it.
>
> Would you mind clarifying this part? Are you saying that the interface
> and logic is *already* generic and usable for other dax-backed
> servers, just that everything is *named* famfs but it's not really
> famfs specific?
Yes.
If you just find/replace "famfs" with "dax_iomap", the structures
here don't really seem all *that* crazy specific - they're just
optimized for memory speeds instead of I/O.
There is a circular nature to this - FAMFS figured it out first, in
what we think is a reasonably generic way, but we can't know for sure.
John, Dan, and Darrick have all proposed reasonable ways to hedge
against the obvious fact the interface will not be perfect - which
incorporates your BPF proposal along with a reasonably straight forward
deprecation path that's not always possible in other arenas.
All that while solving a real (and novel) problem.
That's actually pretty damn cool.
I would urge you to consider these proposals earnestly.
~Gregory
^ permalink raw reply
* [PATCH 0/2] Improve the crypto library documentation
From: Eric Biggers @ 2026-04-17 6:55 UTC (permalink / raw)
To: linux-crypto
Cc: linux-kernel, Ard Biesheuvel, Jason A . Donenfeld, Herbert Xu,
linux-doc, Jonathan Corbet, Mauro Carvalho Chehab, Randy Dunlap,
Eric Biggers
While the crypto library already has a lot of kerneldoc, it's not being
included in the HTML or PDF documentation. Update Documentation/crypto/
to include it, and also add a high-level overview of the library.
I'd like to take this series via the libcrypto tree for 7.1.
Eric Biggers (2):
docs: kdoc: Expand 'at_least' when creating parameter list
lib/crypto: docs: Add rst documentation to Documentation/crypto/
Documentation/crypto/index.rst | 2 +-
.../crypto/libcrypto-blockcipher.rst | 19 ++
Documentation/crypto/libcrypto-hash.rst | 86 +++++++++
Documentation/crypto/libcrypto-signature.rst | 11 ++
Documentation/crypto/libcrypto-utils.rst | 6 +
Documentation/crypto/libcrypto.rst | 167 ++++++++++++++++++
Documentation/crypto/sha3.rst | 2 +
tools/lib/python/kdoc/kdoc_parser.py | 5 +
8 files changed, 297 insertions(+), 1 deletion(-)
create mode 100644 Documentation/crypto/libcrypto-blockcipher.rst
create mode 100644 Documentation/crypto/libcrypto-hash.rst
create mode 100644 Documentation/crypto/libcrypto-signature.rst
create mode 100644 Documentation/crypto/libcrypto-utils.rst
create mode 100644 Documentation/crypto/libcrypto.rst
base-commit: 3cd8b194bf3428dfa53120fee47e827a7c495815
--
2.53.0
^ permalink raw reply
* [PATCH 1/2] docs: kdoc: Expand 'at_least' when creating parameter list
From: Eric Biggers @ 2026-04-17 6:55 UTC (permalink / raw)
To: linux-crypto
Cc: linux-kernel, Ard Biesheuvel, Jason A . Donenfeld, Herbert Xu,
linux-doc, Jonathan Corbet, Mauro Carvalho Chehab, Randy Dunlap,
Eric Biggers
In-Reply-To: <20260417065529.64925-1-ebiggers@kernel.org>
sphinx doesn't know that the kernel headers do:
#define at_least static
Do this replacement before declarations are passed to it.
This prevents errors like the following from appearing once the
lib/crypto/ kerneldoc is wired up to the sphinx build:
linux/Documentation/crypto/libcrypto:128: ./include/crypto/sha2.h:773: WARNING: Error in declarator or parameters
Error in declarator or parameters
Invalid C declaration: Expected ']' in end of array operator. [error at 59]
void sha512_final (struct sha512_ctx *ctx, u8 out[at_least SHA512_DIGEST_SIZE])
Signed-off-by: Eric Biggers <ebiggers@kernel.org>
---
tools/lib/python/kdoc/kdoc_parser.py | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/tools/lib/python/kdoc/kdoc_parser.py b/tools/lib/python/kdoc/kdoc_parser.py
index 74af7ae47aa47..f982db7fddac2 100644
--- a/tools/lib/python/kdoc/kdoc_parser.py
+++ b/tools/lib/python/kdoc/kdoc_parser.py
@@ -437,10 +437,15 @@ class KernelDoc:
for arg in args.split(splitter):
# Ignore argument attributes
arg = KernRe(r'\sPOS0?\s').sub(' ', arg)
+ # Replace '[at_least ' with '[static '. This allows sphinx to parse
+ # array parameter declarations like 'char A[at_least 4]', where
+ # 'at_least' is #defined to 'static' by the kernel headers.
+ arg = KernRe(r'\[at_least ').sub('[static ', arg)
+
# Strip leading/trailing spaces
arg = arg.strip()
arg = KernRe(r'\s+').sub(' ', arg, count=1)
if arg.startswith('#'):
--
2.53.0
^ permalink raw reply related
* [PATCH 2/2] lib/crypto: docs: Add rst documentation to Documentation/crypto/
From: Eric Biggers @ 2026-04-17 6:55 UTC (permalink / raw)
To: linux-crypto
Cc: linux-kernel, Ard Biesheuvel, Jason A . Donenfeld, Herbert Xu,
linux-doc, Jonathan Corbet, Mauro Carvalho Chehab, Randy Dunlap,
Eric Biggers
In-Reply-To: <20260417065529.64925-1-ebiggers@kernel.org>
Add a documentation file Documentation/crypto/libcrypto.rst which
provides a high-level overview of lib/crypto/.
Also add several sub-pages which include the kerneldoc for the
algorithms that have it. This makes the existing, quite extensive
kerneldoc start being included in the HTML documentation.
Note that the intent is very much *not* that everyone has to read these
Documentation/ files. The library is intended to be straightforward and
use familiar conventions; generally it should be possible to dive right
into the kerneldoc. You shouldn't need to read a lot of documentation
to just call `sha256()`, for example, or to run the unit tests if you're
already familiar with KUnit. (This differs from the traditional crypto
API which has a larger barrier to entry.)
Nevertheless, this seems worth adding. Hopefully it is useful and makes
LWN no longer consider the library to be "meticulously undocumented".
Signed-off-by: Eric Biggers <ebiggers@kernel.org>
---
Documentation/crypto/index.rst | 2 +-
.../crypto/libcrypto-blockcipher.rst | 19 ++
Documentation/crypto/libcrypto-hash.rst | 86 +++++++++
Documentation/crypto/libcrypto-signature.rst | 11 ++
Documentation/crypto/libcrypto-utils.rst | 6 +
Documentation/crypto/libcrypto.rst | 167 ++++++++++++++++++
Documentation/crypto/sha3.rst | 2 +
7 files changed, 292 insertions(+), 1 deletion(-)
create mode 100644 Documentation/crypto/libcrypto-blockcipher.rst
create mode 100644 Documentation/crypto/libcrypto-hash.rst
create mode 100644 Documentation/crypto/libcrypto-signature.rst
create mode 100644 Documentation/crypto/libcrypto-utils.rst
create mode 100644 Documentation/crypto/libcrypto.rst
diff --git a/Documentation/crypto/index.rst b/Documentation/crypto/index.rst
index 4ee667c446f99..705f186d662ba 100644
--- a/Documentation/crypto/index.rst
+++ b/Documentation/crypto/index.rst
@@ -11,10 +11,11 @@ for cryptographic use cases, as well as programming examples.
.. toctree::
:caption: Table of contents
:maxdepth: 2
+ libcrypto
intro
api-intro
architecture
async-tx-api
@@ -25,6 +26,5 @@ for cryptographic use cases, as well as programming examples.
api
api-samples
descore-readme
device_drivers/index
krb5
- sha3
diff --git a/Documentation/crypto/libcrypto-blockcipher.rst b/Documentation/crypto/libcrypto-blockcipher.rst
new file mode 100644
index 0000000000000..dd5ce2f8b5151
--- /dev/null
+++ b/Documentation/crypto/libcrypto-blockcipher.rst
@@ -0,0 +1,19 @@
+.. SPDX-License-Identifier: GPL-2.0-or-later
+
+Block ciphers
+=============
+
+AES
+---
+
+Support for the AES block cipher.
+
+.. kernel-doc:: include/crypto/aes.h
+
+DES
+---
+
+Support for the DES block cipher. This algorithm is obsolete and is supported
+only for backwards compatibility.
+
+.. kernel-doc:: include/crypto/des.h
diff --git a/Documentation/crypto/libcrypto-hash.rst b/Documentation/crypto/libcrypto-hash.rst
new file mode 100644
index 0000000000000..ccffe8c3398eb
--- /dev/null
+++ b/Documentation/crypto/libcrypto-hash.rst
@@ -0,0 +1,86 @@
+.. SPDX-License-Identifier: GPL-2.0-or-later
+
+Hash functions, MACs, and XOFs
+==============================
+
+BLAKE2s
+-------
+
+Support for the BLAKE2s cryptographic hash function.
+
+.. kernel-doc:: include/crypto/blake2s.h
+
+BLAKE2b
+-------
+
+Support for the BLAKE2b cryptographic hash function.
+
+.. kernel-doc:: include/crypto/blake2b.h
+
+AES-CMAC and AES-XCBC
+---------------------
+
+Support for the AES-CMAC and AES-XCBC message authentication codes.
+
+.. kernel-doc:: include/crypto/aes-cbc-macs.h
+
+GHASH and POLYVAL
+-----------------
+
+Support for the GHASH and POLYVAL universal hash functions. These algorithms
+are used only as internal components of other algorithms.
+
+.. kernel-doc:: include/crypto/gf128hash.h
+
+MD5
+---
+
+Support for the MD5 cryptographic hash function and HMAC-MD5. This algorithm is
+obsolete and is supported only for backwards compatibility.
+
+.. kernel-doc:: include/crypto/md5.h
+
+NH
+--
+
+Support for the NH universal hash function. This algorithm is used only as an
+internal component of other algorithms.
+
+.. kernel-doc:: include/crypto/nh.h
+
+Poly1305
+--------
+
+Support for the Poly1305 universal hash function. This algorithm is used only
+as an internal component of other algorithms.
+
+.. kernel-doc:: include/crypto/poly1305.h
+
+SHA-1
+-----
+
+Support for the SHA-1 cryptographic hash function and HMAC-SHA1. This algorithm
+is obsolete and is supported only for backwards compatibility.
+
+.. kernel-doc:: include/crypto/sha1.h
+
+SHA-2
+-----
+
+Support for the SHA-2 family of cryptographic hash functions, including SHA-224,
+SHA-256, SHA-384, and SHA-512. Also support for their corresponding HMACs:
+HMAC-SHA224, HMAC-SHA256, HMAC-SHA384, and HMAC-SHA512.
+
+.. kernel-doc:: include/crypto/sha2.h
+
+SHA-3
+-----
+
+The SHA-3 functions are documented in :ref:`sha3`.
+
+SM3
+---
+
+Support for the SM3 cryptographic hash function.
+
+.. kernel-doc:: include/crypto/sm3.h
diff --git a/Documentation/crypto/libcrypto-signature.rst b/Documentation/crypto/libcrypto-signature.rst
new file mode 100644
index 0000000000000..e80d59fa51b6a
--- /dev/null
+++ b/Documentation/crypto/libcrypto-signature.rst
@@ -0,0 +1,11 @@
+.. SPDX-License-Identifier: GPL-2.0-or-later
+
+Digital signature algorithms
+============================
+
+ML-DSA
+------
+
+Support for the ML-DSA digital signature algorithm.
+
+.. kernel-doc:: include/crypto/mldsa.h
diff --git a/Documentation/crypto/libcrypto-utils.rst b/Documentation/crypto/libcrypto-utils.rst
new file mode 100644
index 0000000000000..9d833f47ed390
--- /dev/null
+++ b/Documentation/crypto/libcrypto-utils.rst
@@ -0,0 +1,6 @@
+.. SPDX-License-Identifier: GPL-2.0-or-later
+
+Utility functions
+=================
+
+.. kernel-doc:: include/crypto/utils.h
diff --git a/Documentation/crypto/libcrypto.rst b/Documentation/crypto/libcrypto.rst
new file mode 100644
index 0000000000000..32bb61df9e4c3
--- /dev/null
+++ b/Documentation/crypto/libcrypto.rst
@@ -0,0 +1,167 @@
+.. SPDX-License-Identifier: GPL-2.0-or-later
+
+==============
+Crypto library
+==============
+
+``lib/crypto/`` provides faster and easier access to cryptographic algorithms
+than the traditional crypto API.
+
+Each cryptographic algorithm is supported via a set of dedicated functions.
+"Crypto agility", where needed, is left to calling code.
+
+The crypto library functions are intended to be boring and straightforward, and
+to follow familiar conventions. Their primary documentation is their (fairly
+extensive) kerneldoc. This page just provides some extra high-level context.
+
+Note that the crypto library is not entirely new. ``lib/`` has contained some
+crypto functions since 2005. Rather, it's just an approach that's been expanded
+over time as it's been found to work well. It also largely just matches how the
+kernel already does things elsewhere.
+
+Scope and intended audience
+===========================
+
+The crypto library documentation is primarily meant for kernel developers who
+need to use a particular cryptographic algorithm(s) in kernel code. For
+example, "I just need to compute a SHA-256 hash." A secondary audience is
+developers working on the crypto algorithm implementations themselves.
+
+If you're looking for more general information about cryptography, like the
+differences between the different crypto algorithms or how to select an
+appropriate algorithm, you should refer to external sources which cover that
+type of information much more comprehensively. If you need help selecting
+algorithms for a new kernel feature that doesn't already have its algorithms
+predefined, please reach out to ``linux-crypto@vger.kernel.org`` for advice.
+
+Code organization
+=================
+
+- ``lib/crypto/*.c``: the crypto algorithm implementations
+
+- ``lib/crypto/$(SRCARCH)/``: architecture-specific code for crypto algorithms.
+ It is here rather than somewhere in ``arch/`` partly because this allows
+ generic and architecture-optimized code to be easily built into a single
+ loadable module (when the algorithm is set to 'm' in the kconfig).
+
+- ``lib/crypto/tests/``: KUnit tests for the crypto algorithms
+
+- ``include/crypto/``: crypto headers, both for the crypto library and the
+ traditional crypto API
+
+Generally, there is one kernel module per algorithm. Sometimes related
+algorithms are grouped into one module. There is intentionally no common
+framework, though there are some utility functions that multiple algorithms use.
+
+Each algorithm module is controlled by a tristate kconfig symbol
+``CRYPTO_LIB_$(ALGORITHM)``. As is the norm for library functions in the
+kernel, these are hidden symbols which don't show up in the kconfig menu.
+Instead, they are just selected by all the kconfig symbols that need them.
+
+Many of the algorithms have multiple implementations: a generic implementation
+and architecture-optimized implementation(s). Each module initialization
+function, or initcall in the built-in case, automatically enables the best
+implementation based on the available CPU features.
+
+Note that the crypto library doesn't use the ``crypto/``,
+``arch/$(SRCARCH)/crypto/``, or ``drivers/crypto/`` directories. These
+directories are used by the traditional crypto API. When possible, algorithms
+in the traditional crypto API are implemented by calls into the library.
+
+Advantages
+==========
+
+Some of the advantages of the library over the traditional crypto API are:
+
+- The library functions tend to be much easier to use. For example, a hash
+ value can be computed using only a single function call.
+
+- The library functions are usually faster, especially for short inputs. They
+ call the crypto algorithms directly without inefficient indirect calls, memory
+ allocations, string parsing, lookups in an algorithm registry, and other
+ unnecessary API overhead. Architecture-optimized code is enabled by default.
+
+- Most of the library functions return void and never fail. Thus, in most cases
+ callers don't need to handle errors.
+
+- Most of the library functions operate on standard virtual addresses, rather
+ than scatterlists which are difficult and less efficient to work with.
+
+- The library functions use standard link-time dependencies instead of
+ error-prone dynamic loading by name.
+
+- The library focuses on the approach that works the best on the vast majority
+ of systems: CPU-based implementations of the crypto algorithms, utilizing
+ on-CPU acceleration (such as AES instructions) when available.
+
+- The library uses standard KUnit tests, rather than custom ad-hoc tests.
+
+- The library tends to have higher assurance implementations of the crypto
+ algorithms. This is both due to its simpler design and because more of its
+ code is being regularly tested.
+
+- The library supports features that don't fit into the rigid framework of the
+ traditional crypto API, for example interleaved hashing and XOFs.
+
+When to use it
+==============
+
+In-kernel users should use the library (rather than the traditional crypto API)
+whenever possible. Many subsystems have already been converted. It usually
+simplifies their code significantly and improves performance.
+
+Some kernel features allow userspace to provide an arbitrary string that selects
+an arbitrary algorithm from the traditional crypto API by name. These features
+generally will have to keep using the traditional crypto API for backwards
+compatibility.
+
+Note: new kernel features should not support every algorithm, but rather make a
+deliberate choice about what algorithm(s) to support. History has shown that
+making a deliberate, thoughtful choice greatly simplifies code maintenance,
+reduces the chance for mistakes (such as using an obsolete, insecure, or
+inappropriate algorithm), and makes your feature easier to use.
+
+Testing
+=======
+
+The crypto library uses standard KUnit tests. Like many of the kernel's other
+KUnit tests, they are included in the set of tests that are run by
+``tools/testing/kunit/kunit.py run --alltests``.
+
+A ``.kunitconfig`` file is also provided to run just the crypto library tests.
+For example, here's how to run them in user-mode Linux:
+
+.. code-block::
+
+ tools/testing/kunit/kunit.py run --kunitconfig=lib/crypto/
+
+Many of the crypto algorithms have architecture-optimized implementations.
+Testing those requires building an appropriate kernel and running the tests
+either in QEMU or on appropriate hardware. Here's one example with QEMU:
+
+.. code-block::
+
+ tools/testing/kunit/kunit.py run --kunitconfig=lib/crypto/ --arch=arm64 --make_options LLVM=1
+
+Depending on the code being tested, flags may need to be provided to QEMU to
+emulate the correct type of hardware for the code to be reached.
+
+Since correctness is essential in cryptographic code, new architecture-optimized
+code is accepted only if it can be tested in QEMU.
+
+Note: the crypto library also includes FIPS 140 self-tests. These are
+lightweight, are designed specifically to meet FIPS 140 requirements, and exist
+*only* to meet those requirements. Normal testing done by kernel developers and
+integrators should use the much more comprehensive KUnit tests instead.
+
+API documentation
+=================
+
+.. toctree::
+ :maxdepth: 2
+
+ libcrypto-blockcipher
+ libcrypto-hash
+ libcrypto-signature
+ libcrypto-utils
+ sha3
diff --git a/Documentation/crypto/sha3.rst b/Documentation/crypto/sha3.rst
index 37640f295118b..250669c98f6ba 100644
--- a/Documentation/crypto/sha3.rst
+++ b/Documentation/crypto/sha3.rst
@@ -1,7 +1,9 @@
.. SPDX-License-Identifier: GPL-2.0-or-later
+.. _sha3:
+
==========================
SHA-3 Algorithm Collection
==========================
.. contents::
--
2.53.0
^ permalink raw reply related
* Re: [PATCH 1/2] docs: kdoc: Expand 'at_least' when creating parameter list
From: Jonathan Corbet @ 2026-04-17 7:18 UTC (permalink / raw)
To: Eric Biggers, linux-crypto
Cc: linux-kernel, Ard Biesheuvel, Jason A . Donenfeld, Herbert Xu,
linux-doc, Mauro Carvalho Chehab, Randy Dunlap, Eric Biggers
In-Reply-To: <20260417065529.64925-2-ebiggers@kernel.org>
Eric Biggers <ebiggers@kernel.org> writes:
> sphinx doesn't know that the kernel headers do:
>
> #define at_least static
>
> Do this replacement before declarations are passed to it.
>
> This prevents errors like the following from appearing once the
> lib/crypto/ kerneldoc is wired up to the sphinx build:
>
> linux/Documentation/crypto/libcrypto:128: ./include/crypto/sha2.h:773: WARNING: Error in declarator or parameters
> Error in declarator or parameters
> Invalid C declaration: Expected ']' in end of array operator. [error at 59]
> void sha512_final (struct sha512_ctx *ctx, u8 out[at_least SHA512_DIGEST_SIZE])
>
> Signed-off-by: Eric Biggers <ebiggers@kernel.org>
> ---
> tools/lib/python/kdoc/kdoc_parser.py | 5 +++++
> 1 file changed, 5 insertions(+)
>
> diff --git a/tools/lib/python/kdoc/kdoc_parser.py b/tools/lib/python/kdoc/kdoc_parser.py
> index 74af7ae47aa47..f982db7fddac2 100644
> --- a/tools/lib/python/kdoc/kdoc_parser.py
> +++ b/tools/lib/python/kdoc/kdoc_parser.py
> @@ -437,10 +437,15 @@ class KernelDoc:
>
> for arg in args.split(splitter):
> # Ignore argument attributes
> arg = KernRe(r'\sPOS0?\s').sub(' ', arg)
>
> + # Replace '[at_least ' with '[static '. This allows sphinx to parse
> + # array parameter declarations like 'char A[at_least 4]', where
> + # 'at_least' is #defined to 'static' by the kernel headers.
> + arg = KernRe(r'\[at_least ').sub('[static ', arg)
> +
This could be a regular string replacement rather than a regex. Not
something I'm willing to dig in my heels on, though... so if you want to
push this, either way:
Acked-by: Jonathan Corbet <corbet@lwn.net>
Thanks,
jon
^ permalink raw reply
* Re: [PATCH 0/2] Improve the crypto library documentation
From: Jonathan Corbet @ 2026-04-17 7:22 UTC (permalink / raw)
To: Eric Biggers, linux-crypto
Cc: linux-kernel, Ard Biesheuvel, Jason A . Donenfeld, Herbert Xu,
linux-doc, Mauro Carvalho Chehab, Randy Dunlap, Eric Biggers
In-Reply-To: <20260417065529.64925-1-ebiggers@kernel.org>
Eric Biggers <ebiggers@kernel.org> writes:
> While the crypto library already has a lot of kerneldoc, it's not being
> included in the HTML or PDF documentation. Update Documentation/crypto/
> to include it, and also add a high-level overview of the library.
>
> I'd like to take this series via the libcrypto tree for 7.1.
>
> Eric Biggers (2):
> docs: kdoc: Expand 'at_least' when creating parameter list
> lib/crypto: docs: Add rst documentation to Documentation/crypto/
>
> Documentation/crypto/index.rst | 2 +-
> .../crypto/libcrypto-blockcipher.rst | 19 ++
> Documentation/crypto/libcrypto-hash.rst | 86 +++++++++
> Documentation/crypto/libcrypto-signature.rst | 11 ++
> Documentation/crypto/libcrypto-utils.rst | 6 +
> Documentation/crypto/libcrypto.rst | 167 ++++++++++++++++++
> Documentation/crypto/sha3.rst | 2 +
> tools/lib/python/kdoc/kdoc_parser.py | 5 +
> 8 files changed, 297 insertions(+), 1 deletion(-)
I think this is great - sorry about the snide comment on LWN, but this
will make this documentation much more accessible.
Thanks,
jon
^ permalink raw reply
* Re: [PATCH V10 00/10] famfs: port into fuse
From: Christoph Hellwig @ 2026-04-17 8:04 UTC (permalink / raw)
To: Miklos Szeredi
Cc: Joanne Koong, John Groves, Bernd Schubert, John Groves,
Dan Williams, Bernd Schubert, Alison Schofield, John Groves,
Jonathan Corbet, Shuah Khan, Vishal Verma, Dave Jiang,
Matthew Wilcox, Jan Kara, Alexander Viro, David Hildenbrand,
Christian Brauner, Darrick J . Wong, Randy Dunlap, Jeff Layton,
Amir Goldstein, Jonathan Cameron, Stefan Hajnoczi, Josef Bacik,
Bagas Sanjaya, Chen Linxuan, James Morse, Fuad Tabba,
Sean Christopherson, Shivank Garg, Ackerley Tng, Gregory Price,
Aravind Ramesh, Ajay Joshi, venkataravis@micron.com,
linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org,
nvdimm@lists.linux.dev, linux-cxl@vger.kernel.org,
linux-fsdevel@vger.kernel.org, djbw
In-Reply-To: <CAJfpegvVTcV89=q3L326aGQjhduBcv7PVg5QKftGLjNZmCLmaw@mail.gmail.com>
This is the first mail without annoying and pointless full quotes,
so chiming in here. Sorry if I missed something important in all the
noise.
On Tue, Apr 14, 2026 at 03:19:36PM +0200, Miklos Szeredi wrote:
> On Fri, 10 Apr 2026 at 21:44, Joanne Koong <joannelkoong@gmail.com> wrote:
>
> > Overall, my intention with bringing this up is just to make sure we're
> > at least aware of this alternative before anything is merged and
> > permanent. If Miklos and you think we should land this series, then
> > I'm on board with that.
>
> TBH, I'd prefer not to add the famfs specific mapping interface if not
> absolutely necessary.
Yes, fuse needing support for a specific file systems sounds like a
design mistake.
>This was the main sticking point originally,
> but there seemed to be no better alternative.
>
> However with the bpf approach this would be gone, which is great.
So what is this bpf magic actually trying to solve?
^ permalink raw reply
* Re: [RFC PATCH] mm: net: disable kswapd for high-order network buffer allocation
From: wang lian @ 2026-04-17 8:11 UTC (permalink / raw)
To: willy
Cc: 21cnbao, corbet, davem, edumazet, hannes, horms, jackmanb, kuba,
kuniyu, linux-doc, linux-kernel, linux-mm, linyunsheng, mhocko,
netdev, pabeni, surenb, v-songbaohua, vbabka, willemb, zhouhuacai,
ziy, wang lian
In-Reply-To: <aO11jqD6jgNs5h8K@casper.infradead.org>
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset=y, Size: 4537 bytes --]
Hi Matthew, Barry,
> So, we try to do an order-3 allocation. kswapd runs and ...
> succeeds in creating order-3 pages? Or fails to?
From our reproducer runs, both happen. We observe intermittent order-3
successes, but also frequent high-order failures followed by order-0
fallback.
> If it fails, that's something we need to sort out.
Agreed. In this workload, the bottleneck appears to be contiguity, not
raw reclaimable memory shortage. Order-0 memory remains available while
suitable order-3 blocks are often unavailable.
> If it succeeds, now we have several order-3 pages, great. But where do
> they all go that we need to run kswapd again?
In our runs, order-3 pockets do show up, but they do not last long.
They get consumed quickly by ongoing skb demand, and the pressure returns.
To investigate this, we built a reproducer that keeps creating memory fragments
while the network stack continuously requests order-3 allocations.[1][2]
Raw sample output (trimmed):
---------------------------------------------------------------------------------------------------
TIME | BUDDYINFO (Normal Zone) | MEMINFO | KSWAPD CPU & VMSTAT
---------------------------------------------------------------------------------------------------
11:08:11 | ord0:11622 ord3:0 | Free:96MB Avail:1309MB | CPU: 10.0% scan:83107932
[*] PHASE 3: Triggering Order-3 Pressure (UDP Storm).
11:08:15 | ord0:52079 ord3:0 | Free:273MB Avail:1300MB | CPU: 90.9% scan:85328881
11:08:16 | ord0:102895 ord3:0 | Free:477MB Avail:1309MB | CPU: 60.0% scan:85873777
11:08:17 | ord0:115459 ord3:5 | Free:517MB Avail:1284MB | CPU: 54.5% scan:86584389
11:08:18 | ord0:115164 ord3:0 | Free:509MB Avail:1107MB | CPU: 36.4% scan:87083561
---------------------------------------------------------------------------------------------------
The current phenomenon we observed is: Free memory is plentiful, Order-0
pages are abundant, and the network allocation has already successfully
entered the fallback-to-order-0 path. Everything seems normal on the
surface, yet kswapd remains trapped in a futile loop.
It appears that kswapd is stuck in the following logic:
wakeup_kswapd -> pgdat_balance -> __zone_watermark_ok.
Specifically, in __zone_watermark_ok():
/* For a high-order request, check at least one suitable page is free */
for (o = order; o < NR_PAGE_ORDERS; o++) {
struct free_area *area = &z->free_area[o];
int mt;
if (!area->nr_free)
continue;
for (mt = 0; mt < MIGRATE_PCPTYPES; mt++) {
if (!free_area_empty(area, mt))
return true;
}
}
Because our reproducer keeps creating fragmentation while the network
stack requests order-3, this loop continues to return 'false' for the
high-order requirement, even though the system is functionally fine
with order-0. To be clear, we are not intentionally creating "artificial"
fragments just for the sake of it. Rather, we designed this reproducer to
effectively stress-test and expose the existing feedback gap in the
reclaim/compaction logic—helping to pinpoint why kswapd continues
thumping CPU cycles to satisfy a watermark that the allocator has
already abandoned in favor of order-0 fallback.
A related discussion in [3] helps reduce vmpressure noise in this area.
Useful, but it does not close the contiguity gap by itself: high-order
wake/reclaim can still repeat when contiguous blocks cannot be formed.
It seems the current situation directs us to take a much closer look at
how kswapd behaves in these scenarios. After carefully reviewing
everyone's input, we believe it is time to do some targeted work on
handling these high-order page issues.
We already have some rough ideas and plan to conduct further experiments
in this area. We would appreciate a broader discussion to help address
this potential oversight that we might have collectively missed.
Links:
[1] https://github.com/hack-kernel-just-for-fun/kswap/blob/main/kswapd_spin_repro.c
[2] https://github.com/hack-kernel-just-for-fun/kswap/blob/main/kswapd.sh
[3] https://lore.kernel.org/all/20260406195014.112521-1-jp.kobryn@linux.dev/#r
This was reproduced and cross-checked independently by our team
(Wang Lian <lianux.mm@gmail.com> and Kunwu Chan <kunwu.chan@gmail.com>).
--
Best Regards,
wang lian
^ permalink raw reply
* Re: [PATCH V10 00/10] famfs: port into fuse
From: Christoph Hellwig @ 2026-04-17 8:13 UTC (permalink / raw)
To: Darrick J. Wong
Cc: Dan Williams, Gregory Price, Joanne Koong, John Groves,
Miklos Szeredi, Bernd Schubert, John Groves, Dan J Williams,
Bernd Schubert, Alison Schofield, John Groves, Jonathan Corbet,
Shuah Khan, Vishal Verma, Dave Jiang, Matthew Wilcox, Jan Kara,
Alexander Viro, David Hildenbrand, Christian Brauner,
Randy Dunlap, Jeff Layton, Amir Goldstein, Jonathan Cameron,
Stefan Hajnoczi, Josef Bacik, Bagas Sanjaya, Chen Linxuan,
James Morse, Fuad Tabba, Sean Christopherson, Shivank Garg,
Ackerley Tng, Aravind Ramesh, Ajay Joshi, venkataravis@micron.com,
linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org,
nvdimm@lists.linux.dev, linux-cxl@vger.kernel.org,
linux-fsdevel@vger.kernel.org
In-Reply-To: <20260416224331.GD114184@frogsfrogsfrogs>
On Thu, Apr 16, 2026 at 03:43:31PM -0700, Darrick J. Wong wrote:
> ...the memory interleaving is a rather interesting quality of famfs.
> There's no good way to express a formulaic meta-mapping in traditional
> iomap parlance, and famfs needs that to interleave across memory
> controllers/dimm boxen/whatever. Throwing individual iomaps at the
> kernel is a very inefficient way to do that. So I don't think there's a
> good reason to get rid of GET_FMAP at this time...
Why no? We can triviall make an iomap point to multiple backing
devices and throw in a stride/offset. That would make btrfs
striping (and non-degraded parity RAID reads) a lot more efficient.
> ...however the strongest case (IMO) would be if (having merged famfs) we
> then merge fuse-iomap after famfs. Then we extend the existing
> fuse-iomap-bpf prototype to allow per-mount and per-inode iomap bpf ops.
> That enables us to analyze thoroughly the performance characteristics of:
Don't go there. I think that you two are comining up with two
interfaces for roughly the same thing is a pretty clear indicator
that this needs to be fully hashed out as a single interface first,
and any kind of preliminary merging is just going to create problems.
^ permalink raw reply
* Re: [PATCH V10 00/10] famfs: port into fuse
From: Christoph Hellwig @ 2026-04-17 8:17 UTC (permalink / raw)
To: Darrick J. Wong
Cc: Joanne Koong, Dan Williams, Gregory Price, John Groves,
Miklos Szeredi, Bernd Schubert, John Groves, Dan J Williams,
Bernd Schubert, Alison Schofield, John Groves, Jonathan Corbet,
Shuah Khan, Vishal Verma, Dave Jiang, Matthew Wilcox, Jan Kara,
Alexander Viro, David Hildenbrand, Christian Brauner,
Randy Dunlap, Jeff Layton, Amir Goldstein, Jonathan Cameron,
Stefan Hajnoczi, Josef Bacik, Bagas Sanjaya, Chen Linxuan,
James Morse, Fuad Tabba, Sean Christopherson, Shivank Garg,
Ackerley Tng, Aravind Ramesh, Ajay Joshi, venkataravis@micron.com,
linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org,
nvdimm@lists.linux.dev, linux-cxl@vger.kernel.org,
linux-fsdevel@vger.kernel.org
In-Reply-To: <20260417054031.GA7727@frogsfrogsfrogs>
On Thu, Apr 16, 2026 at 10:40:31PM -0700, Darrick J. Wong wrote:
> > > ...the memory interleaving is a rather interesting quality of famfs.
> > > There's no good way to express a formulaic meta-mapping in traditional
> > > iomap parlance, and famfs needs that to interleave across memory
> > > controllers/dimm boxen/whatever. Throwing individual iomaps at the
> > > kernel is a very inefficient way to do that. So I don't think there's a
> > > good reason to get rid of GET_FMAP at this time...
> >
> > So could we make the interleaving part generic then? Striped /
> > interleaved layouts are used elsewhere (eg RAID-0, md-stripe, etc.) -
> > could we add a generic interleave descriptor to the uapi and use that
> > for what famfs needs?
>
> I doubt it. md-raid presents a unified LBA address space, which means
> that the filesystem doesn't have to know anything about whatever
> translations might happen underneath it.
Unless that translation happens in the file system. It does for btrfs
right now, and it does for pNFS blocklayout. The former is using iomap
for direct I/O (and has old code and vague plans for using it for
buffered I/O maybe eventually), the latter does not currently but would
benefit a lot, although wiring it through the NFS code will be painful.
> Most filesystems that implement striping themselves don't restrict
> themselves to monotonically increasing LBA ranges rotored across each
> device like md-raid0 does.
Mappings can be more flexible, but they usually would not in a single
iomap iteration.
> But for whatever reason, pmem/dax don't have remapping layers like
> md/dm so filesystems have to do that on their own if the hardware
> doesn't do it for them.
DM actually supports DAX. I don't think that's a very good way as it
adds a lot of overhead for little gain for striping.
^ permalink raw reply
* [PATCH RFC v3 2/9] iio: frequency: ad9910: initial driver implementation
From: Rodrigo Alencar via B4 Relay @ 2026-04-17 8:17 UTC (permalink / raw)
To: linux-iio, devicetree, linux-kernel, linux-doc, linux-hardening
Cc: Lars-Peter Clausen, Michael Hennerich, Jonathan Cameron,
David Lechner, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Philipp Zabel, Jonathan Corbet, Shuah Khan,
Kees Cook, Gustavo A. R. Silva, Rodrigo Alencar
In-Reply-To: <20260417-ad9910-iio-driver-v3-0-29b93712a228@analog.com>
From: Rodrigo Alencar <rodrigo.alencar@analog.com>
Add the core AD9910 DDS driver infrastructure with single tone mode
support. This includes SPI register access, profile management via GPIO
pins, PLL/DAC configuration from firmware properties, and single tone
frequency/phase/amplitude control through IIO attributes.
Signed-off-by: Rodrigo Alencar <rodrigo.alencar@analog.com>
---
MAINTAINERS | 1 +
drivers/iio/frequency/Kconfig | 18 +
drivers/iio/frequency/Makefile | 1 +
drivers/iio/frequency/ad9910.c | 1052 ++++++++++++++++++++++++++++++++++++++++
4 files changed, 1072 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 2ca8b68e5daa..6403439b530d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1636,6 +1636,7 @@ L: linux-iio@vger.kernel.org
S: Supported
W: https://ez.analog.com/linux-software-drivers
F: Documentation/devicetree/bindings/iio/frequency/adi,ad9910.yaml
+F: drivers/iio/frequency/ad9910.c
ANALOG DEVICES INC MAX22007 DRIVER
M: Janani Sunil <janani.sunil@analog.com>
diff --git a/drivers/iio/frequency/Kconfig b/drivers/iio/frequency/Kconfig
index 583cbdf4e8cd..180e74f62d11 100644
--- a/drivers/iio/frequency/Kconfig
+++ b/drivers/iio/frequency/Kconfig
@@ -23,6 +23,24 @@ config AD9523
endmenu
+menu "Direct Digital Synthesis"
+
+config AD9910
+ tristate "Analog Devices AD9910 Direct Digital Synthesizer"
+ depends on SPI
+ depends on GPIOLIB
+ help
+ Say yes here to build support for Analog Devices AD9910
+ 1 GSPS, 14-Bit DDS with integrated DAC.
+
+ Supports single tone mode with 8 configurable profiles
+ and digital ramp generation.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad9910.
+
+endmenu
+
#
# Phase-Locked Loop (PLL) frequency synthesizers
#
diff --git a/drivers/iio/frequency/Makefile b/drivers/iio/frequency/Makefile
index 70d0e0b70e80..39271dd209ca 100644
--- a/drivers/iio/frequency/Makefile
+++ b/drivers/iio/frequency/Makefile
@@ -5,6 +5,7 @@
# When adding new entries keep the list in alphabetical order
obj-$(CONFIG_AD9523) += ad9523.o
+obj-$(CONFIG_AD9910) += ad9910.o
obj-$(CONFIG_ADF4350) += adf4350.o
obj-$(CONFIG_ADF4371) += adf4371.o
obj-$(CONFIG_ADF4377) += adf4377.o
diff --git a/drivers/iio/frequency/ad9910.c b/drivers/iio/frequency/ad9910.c
new file mode 100644
index 000000000000..e9005037db1a
--- /dev/null
+++ b/drivers/iio/frequency/ad9910.c
@@ -0,0 +1,1052 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * AD9910 SPI DDS (Direct Digital Synthesizer) driver
+ *
+ * Copyright 2026 Analog Devices Inc.
+ */
+
+#include <linux/array_size.h>
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device/devres.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/log2.h>
+#include <linux/math64.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/property.h>
+#include <linux/regulator/consumer.h>
+#include <linux/reset.h>
+#include <linux/spi/spi.h>
+#include <linux/sysfs.h>
+#include <linux/types.h>
+#include <linux/units.h>
+#include <linux/unaligned.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+/* Register addresses */
+#define AD9910_REG_CFR1 0x00
+#define AD9910_REG_CFR2 0x01
+#define AD9910_REG_CFR3 0x02
+#define AD9910_REG_AUX_DAC 0x03
+#define AD9910_REG_IO_UPDATE_RATE 0x04
+#define AD9910_REG_FTW 0x07
+#define AD9910_REG_POW 0x08
+#define AD9910_REG_ASF 0x09
+#define AD9910_REG_MULTICHIP_SYNC 0x0A
+#define AD9910_REG_DRG_LIMIT 0x0B
+#define AD9910_REG_DRG_STEP 0x0C
+#define AD9910_REG_DRG_RATE 0x0D
+#define AD9910_REG_PROFILE0 0x0E
+#define AD9910_REG_PROFILE1 0x0F
+#define AD9910_REG_PROFILE2 0x10
+#define AD9910_REG_PROFILE3 0x11
+#define AD9910_REG_PROFILE4 0x12
+#define AD9910_REG_PROFILE5 0x13
+#define AD9910_REG_PROFILE6 0x14
+#define AD9910_REG_PROFILE7 0x15
+#define AD9910_REG_RAM 0x16
+
+#define AD9910_REG_NUM_CACHED 0x16
+
+#define AD9910_REG_PROFILE(x) (AD9910_REG_PROFILE0 + (x))
+#define AD9910_REG_HIGH32_FLAG_MSK BIT(8)
+
+/* CFR1 bit definitions */
+#define AD9910_CFR1_RAM_ENABLE_MSK BIT(31)
+#define AD9910_CFR1_RAM_PLAYBACK_DEST_MSK GENMASK(30, 29)
+#define AD9910_CFR1_OSK_MANUAL_EXT_CTL_MSK BIT(23)
+#define AD9910_CFR1_INV_SINC_EN_MSK BIT(22)
+#define AD9910_CFR1_INT_PROFILE_CTL_MSK GENMASK(20, 17)
+#define AD9910_CFR1_SELECT_SINE_MSK BIT(16)
+#define AD9910_CFR1_LOAD_LRR_IO_UPDATE_MSK BIT(15)
+#define AD9910_CFR1_AUTOCLR_DIG_RAMP_ACCUM_MSK BIT(14)
+#define AD9910_CFR1_AUTOCLR_PHASE_ACCUM_MSK BIT(13)
+#define AD9910_CFR1_CLEAR_DIG_RAMP_ACCUM_MSK BIT(12)
+#define AD9910_CFR1_CLEAR_PHASE_ACCUM_MSK BIT(11)
+#define AD9910_CFR1_LOAD_ARR_IO_UPDATE_MSK BIT(10)
+#define AD9910_CFR1_OSK_ENABLE_MSK BIT(9)
+#define AD9910_CFR1_SELECT_AUTO_OSK_MSK BIT(8)
+#define AD9910_CFR1_DIGITAL_POWER_DOWN_MSK BIT(7)
+#define AD9910_CFR1_DAC_POWER_DOWN_MSK BIT(6)
+#define AD9910_CFR1_REFCLK_INPUT_POWER_DOWN_MSK BIT(5)
+#define AD9910_CFR1_AUX_DAC_POWER_DOWN_MSK BIT(4)
+#define AD9910_CFR1_SOFT_POWER_DOWN_MSK GENMASK(7, 4)
+#define AD9910_CFR1_EXT_POWER_DOWN_CTL_MSK BIT(3)
+#define AD9910_CFR1_SDIO_INPUT_ONLY_MSK BIT(1)
+#define AD9910_CFR1_LSB_FIRST_MSK BIT(0)
+
+/* CFR2 bit definitions */
+#define AD9910_CFR2_AMP_SCALE_SINGLE_TONE_MSK BIT(24)
+#define AD9910_CFR2_INTERNAL_IO_UPDATE_MSK BIT(23)
+#define AD9910_CFR2_SYNC_CLK_EN_MSK BIT(22)
+#define AD9910_CFR2_DRG_DEST_MSK GENMASK(21, 20)
+#define AD9910_CFR2_DRG_ENABLE_MSK BIT(19)
+#define AD9910_CFR2_DRG_NO_DWELL_HIGH_MSK BIT(18)
+#define AD9910_CFR2_DRG_NO_DWELL_LOW_MSK BIT(17)
+#define AD9910_CFR2_DRG_NO_DWELL_MSK GENMASK(18, 17)
+#define AD9910_CFR2_READ_EFFECTIVE_FTW_MSK BIT(16)
+#define AD9910_CFR2_IO_UPDATE_RATE_CTL_MSK GENMASK(15, 14)
+#define AD9910_CFR2_PDCLK_ENABLE_MSK BIT(11)
+#define AD9910_CFR2_PDCLK_INVERT_MSK BIT(10)
+#define AD9910_CFR2_TXENABLE_INVERT_MSK BIT(9)
+#define AD9910_CFR2_MATCHED_LATENCY_EN_MSK BIT(7)
+#define AD9910_CFR2_DATA_ASM_HOLD_LAST_MSK BIT(6)
+#define AD9910_CFR2_SYNC_TIMING_VAL_DISABLE_MSK BIT(5)
+#define AD9910_CFR2_PARALLEL_DATA_PORT_EN_MSK BIT(4)
+#define AD9910_CFR2_FM_GAIN_MSK GENMASK(3, 0)
+
+/* CFR3 bit definitions */
+#define AD9910_CFR3_OPEN_MSK 0x08070000
+#define AD9910_CFR3_DRV0_MSK GENMASK(29, 28)
+#define AD9910_CFR3_VCO_SEL_MSK GENMASK(26, 24)
+#define AD9910_CFR3_ICP_MSK GENMASK(21, 19)
+#define AD9910_CFR3_REFCLK_DIV_BYPASS_MSK BIT(15)
+#define AD9910_CFR3_REFCLK_DIV_RESETB_MSK BIT(14)
+#define AD9910_CFR3_PFD_RESET_MSK BIT(10)
+#define AD9910_CFR3_PLL_EN_MSK BIT(8)
+#define AD9910_CFR3_N_MSK GENMASK(7, 1)
+
+/* Auxiliary DAC Control Register Bits */
+#define AD9910_AUX_DAC_FSC_MSK GENMASK(7, 0)
+
+/* ASF Register Bits */
+#define AD9910_ASF_RAMP_RATE_MSK GENMASK(31, 16)
+#define AD9910_ASF_SCALE_FACTOR_MSK GENMASK(15, 2)
+#define AD9910_ASF_STEP_SIZE_MSK GENMASK(1, 0)
+
+/* Multichip Sync Register Bits */
+#define AD9910_MC_SYNC_VALIDATION_DELAY_MSK GENMASK(31, 28)
+#define AD9910_MC_SYNC_RECEIVER_ENABLE_MSK BIT(27)
+#define AD9910_MC_SYNC_GENERATOR_ENABLE_MSK BIT(26)
+#define AD9910_MC_SYNC_GENERATOR_POLARITY_MSK BIT(25)
+#define AD9910_MC_SYNC_STATE_PRESET_MSK GENMASK(23, 18)
+#define AD9910_MC_SYNC_OUTPUT_DELAY_MSK GENMASK(15, 11)
+#define AD9910_MC_SYNC_INPUT_DELAY_MSK GENMASK(7, 3)
+
+/* Profile Register Format (Single Tone Mode) */
+#define AD9910_PROFILE_ST_ASF_MSK GENMASK_ULL(61, 48)
+#define AD9910_PROFILE_ST_POW_MSK GENMASK_ULL(47, 32)
+#define AD9910_PROFILE_ST_FTW_MSK GENMASK_ULL(31, 0)
+
+/* Device constants */
+#define AD9910_PI_NANORAD 3141592653UL
+
+#define AD9910_MAX_SYSCLK_HZ (1000 * HZ_PER_MHZ)
+#define AD9910_MAX_PHASE_MICRORAD (AD9910_PI_NANORAD / 500)
+
+#define AD9910_ASF_MAX GENMASK(13, 0)
+#define AD9910_POW_MAX GENMASK(15, 0)
+#define AD9910_NUM_PROFILES 8
+
+/* PLL constants */
+#define AD9910_PLL_MIN_N 12
+#define AD9910_PLL_MAX_N 127
+
+#define AD9910_PLL_IN_MIN_FREQ_HZ (3200 * HZ_PER_KHZ)
+#define AD9910_PLL_IN_MAX_FREQ_HZ (60 * HZ_PER_MHZ)
+
+#define AD9910_PLL_OUT_MIN_FREQ_HZ (420 * HZ_PER_MHZ)
+#define AD9910_PLL_OUT_MAX_FREQ_HZ (1000 * HZ_PER_MHZ)
+
+#define AD9910_VCO0_RANGE_AUTO_MAX_HZ (457 * HZ_PER_MHZ)
+#define AD9910_VCO1_RANGE_AUTO_MAX_HZ (530 * HZ_PER_MHZ)
+#define AD9910_VCO2_RANGE_AUTO_MAX_HZ (632 * HZ_PER_MHZ)
+#define AD9910_VCO3_RANGE_AUTO_MAX_HZ (775 * HZ_PER_MHZ)
+#define AD9910_VCO4_RANGE_AUTO_MAX_HZ (897 * HZ_PER_MHZ)
+#define AD9910_VCO_RANGE_NUM 6
+
+#define AD9910_REFCLK_OUT_DRV_DISABLED 0
+
+#define AD9910_ICP_MIN_uA 212
+#define AD9910_ICP_MAX_uA 387
+#define AD9910_ICP_STEP_uA 25
+
+#define AD9910_DAC_IOUT_MAX_uA 31590
+#define AD9910_DAC_IOUT_DEFAULT_uA 20070
+#define AD9910_DAC_IOUT_MIN_uA 8640
+
+#define AD9910_REFDIV2_MIN_FREQ_HZ (120 * HZ_PER_MHZ)
+#define AD9910_REFDIV2_MAX_FREQ_HZ (1900 * HZ_PER_MHZ)
+
+#define AD9910_SPI_DATA_IDX 1
+#define AD9910_SPI_DATA_LEN_MAX sizeof(__be64)
+#define AD9910_SPI_MESSAGE_LEN_MAX (AD9910_SPI_DATA_IDX + AD9910_SPI_DATA_LEN_MAX)
+#define AD9910_SPI_READ_MSK BIT(7)
+#define AD9910_SPI_ADDR_MSK GENMASK(4, 0)
+
+/**
+ * enum ad9910_channel - AD9910 channel identifiers in priority order
+ *
+ * @AD9910_CHANNEL_PHY: Physical output channel
+ * @AD9910_CHANNEL_PROFILE_0: Profile 0 output channel
+ * @AD9910_CHANNEL_PROFILE_1: Profile 1 output channel
+ * @AD9910_CHANNEL_PROFILE_2: Profile 2 output channel
+ * @AD9910_CHANNEL_PROFILE_3: Profile 3 output channel
+ * @AD9910_CHANNEL_PROFILE_4: Profile 4 output channel
+ * @AD9910_CHANNEL_PROFILE_5: Profile 5 output channel
+ * @AD9910_CHANNEL_PROFILE_6: Profile 6 output channel
+ * @AD9910_CHANNEL_PROFILE_7: Profile 7 output channel
+ */
+enum ad9910_channel {
+ AD9910_CHANNEL_PHY = 100,
+ AD9910_CHANNEL_PROFILE_0 = 101,
+ AD9910_CHANNEL_PROFILE_1 = 102,
+ AD9910_CHANNEL_PROFILE_2 = 103,
+ AD9910_CHANNEL_PROFILE_3 = 104,
+ AD9910_CHANNEL_PROFILE_4 = 105,
+ AD9910_CHANNEL_PROFILE_5 = 106,
+ AD9910_CHANNEL_PROFILE_6 = 107,
+ AD9910_CHANNEL_PROFILE_7 = 108,
+};
+
+enum {
+ AD9910_CHAN_IDX_PHY,
+ AD9910_CHAN_IDX_PROFILE_0,
+ AD9910_CHAN_IDX_PROFILE_1,
+ AD9910_CHAN_IDX_PROFILE_2,
+ AD9910_CHAN_IDX_PROFILE_3,
+ AD9910_CHAN_IDX_PROFILE_4,
+ AD9910_CHAN_IDX_PROFILE_5,
+ AD9910_CHAN_IDX_PROFILE_6,
+ AD9910_CHAN_IDX_PROFILE_7,
+ AD9910_CHAN_IDX_PARALLEL_PORT,
+ AD9910_CHAN_IDX_DRG,
+ AD9910_CHAN_IDX_DRG_RAMP_UP,
+ AD9910_CHAN_IDX_DRG_RAMP_DOWN,
+ AD9910_CHAN_IDX_RAM,
+ AD9910_CHAN_IDX_OSK,
+};
+
+enum {
+ AD9910_POWERDOWN,
+};
+
+struct ad9910_data {
+ u32 sysclk_freq_hz;
+ u32 dac_output_current;
+
+ u16 pll_charge_pump_current;
+ u8 refclk_out_drv;
+ bool pll_enabled;
+};
+
+union ad9910_reg {
+ u64 val64;
+ u32 val32;
+ u16 val16;
+};
+
+struct ad9910_state {
+ struct spi_device *spi;
+ struct clk *refclk;
+
+ struct gpio_desc *gpio_pwdown;
+ struct gpio_desc *gpio_update;
+ struct gpio_descs *gpio_profile;
+
+ /* cached registers */
+ union ad9910_reg reg[AD9910_REG_NUM_CACHED];
+
+ /* Lock for accessing device registers and state variables */
+ struct mutex lock;
+
+ struct ad9910_data data;
+ u8 profile;
+
+ union {
+ __be64 be64;
+ __be32 be32;
+ __be16 be16;
+ } rx_buf;
+ /*
+ * RAM loading requires a reasonable amount of bytes, at the same time
+ * DMA capable SPI drivers requires the transfer buffers to live in
+ * their own cache lines.
+ */
+ u8 tx_buf[AD9910_SPI_MESSAGE_LEN_MAX] __aligned(IIO_DMA_MINALIGN);
+};
+
+/**
+ * ad9910_rational_scale() - Perform scaling of input given a reference.
+ * @input: The input value to be scaled.
+ * @scale: The numerator of the scaling factor.
+ * @reference: The denominator of the scaling factor.
+ *
+ * Closest rounding with mul_u64_add_u64_div_u64
+ *
+ * Return: The scaled value.
+ */
+#define ad9910_rational_scale(input, scale, reference) ({ \
+ u64 _tmp = (reference); \
+ mul_u64_add_u64_div_u64(input, scale, _tmp >> 1, _tmp); \
+})
+
+static int ad9910_io_update(struct ad9910_state *st)
+{
+ if (st->gpio_update) {
+ gpiod_set_value_cansleep(st->gpio_update, 1);
+ udelay(1);
+ gpiod_set_value_cansleep(st->gpio_update, 0);
+ }
+
+ return 0;
+}
+
+static inline int ad9910_spi_read(struct ad9910_state *st, u8 reg, size_t len)
+{
+ st->tx_buf[0] = AD9910_SPI_READ_MSK |
+ FIELD_PREP(AD9910_SPI_ADDR_MSK, reg);
+ return spi_write_then_read(st->spi, st->tx_buf, 1, &st->rx_buf, len);
+}
+
+static inline int ad9910_spi_write(struct ad9910_state *st, u8 reg, size_t len,
+ bool update)
+{
+ int ret;
+
+ st->tx_buf[0] = FIELD_PREP(AD9910_SPI_ADDR_MSK, reg);
+ ret = spi_write(st->spi, st->tx_buf, AD9910_SPI_DATA_IDX + len);
+ if (!ret && update)
+ return ad9910_io_update(st);
+
+ return ret;
+}
+
+#define AD9910_REG_READ_FN(nb) \
+static int ad9910_reg##nb##_read(struct ad9910_state *st, u8 reg, \
+ u##nb * data) \
+{ \
+ int ret; \
+ \
+ ret = ad9910_spi_read(st, reg, sizeof(*data)); \
+ if (ret) \
+ return ret; \
+ \
+ *data = be##nb##_to_cpu(st->rx_buf.be##nb); \
+ return ret; \
+}
+
+AD9910_REG_READ_FN(16)
+AD9910_REG_READ_FN(32)
+AD9910_REG_READ_FN(64)
+
+#define AD9910_REG_WRITE_FN(nb) \
+static int ad9910_reg##nb##_write(struct ad9910_state *st, u8 reg, \
+ u##nb data, bool update) \
+{ \
+ int ret; \
+ \
+ put_unaligned_be##nb(data, &st->tx_buf[AD9910_SPI_DATA_IDX]); \
+ ret = ad9910_spi_write(st, reg, sizeof(data), update); \
+ if (ret) \
+ return ret; \
+ \
+ st->reg[reg].val##nb = data; \
+ return ret; \
+}
+
+AD9910_REG_WRITE_FN(16)
+AD9910_REG_WRITE_FN(32)
+AD9910_REG_WRITE_FN(64)
+
+#define AD9910_REG_UPDATE_FN(nb) \
+static int ad9910_reg##nb##_update(struct ad9910_state *st, \
+ u8 reg, u##nb mask, \
+ u##nb data, bool update) \
+{ \
+ u##nb reg_val = (st->reg[reg].val##nb & ~mask) | (data & mask); \
+ \
+ if (reg_val == st->reg[reg].val##nb && !update) \
+ return 0; \
+ \
+ return ad9910_reg##nb##_write(st, reg, reg_val, update); \
+}
+
+AD9910_REG_UPDATE_FN(16)
+AD9910_REG_UPDATE_FN(32)
+AD9910_REG_UPDATE_FN(64)
+
+static int ad9910_set_dac_current(struct ad9910_state *st, bool update)
+{
+ u32 fsc_code;
+
+ /* FSC = (86.4 / Rset) * (1 + CODE/256) where Rset = 10k ohms */
+ fsc_code = DIV_ROUND_CLOSEST(st->data.dac_output_current, 90) - 96;
+ fsc_code &= 0xFFU;
+
+ return ad9910_reg32_write(st, AD9910_REG_AUX_DAC, fsc_code, update);
+}
+
+static int ad9910_set_sysclk_freq(struct ad9910_state *st, u32 freq_hz,
+ bool update)
+{
+ u32 sysclk_freq_hz, refclk_freq_hz = clk_get_rate(st->refclk);
+ u32 tmp32, vco_sel;
+ int ret;
+
+ if (st->data.pll_enabled) {
+ if (refclk_freq_hz < AD9910_PLL_IN_MIN_FREQ_HZ ||
+ refclk_freq_hz > AD9910_PLL_IN_MAX_FREQ_HZ) {
+ dev_err(&st->spi->dev,
+ "REF_CLK frequency %u Hz is out of PLL input range\n",
+ refclk_freq_hz);
+ return -ERANGE;
+ }
+
+ tmp32 = DIV_ROUND_CLOSEST(freq_hz, refclk_freq_hz);
+ tmp32 = clamp(tmp32, AD9910_PLL_MIN_N, AD9910_PLL_MAX_N);
+ sysclk_freq_hz = refclk_freq_hz * tmp32;
+
+ if (sysclk_freq_hz < AD9910_PLL_OUT_MIN_FREQ_HZ ||
+ sysclk_freq_hz > AD9910_PLL_OUT_MAX_FREQ_HZ) {
+ dev_err(&st->spi->dev,
+ "PLL output frequency %u Hz is out of range\n",
+ sysclk_freq_hz);
+ return -ERANGE;
+ }
+
+ if (sysclk_freq_hz <= AD9910_VCO0_RANGE_AUTO_MAX_HZ)
+ vco_sel = 0;
+ else if (sysclk_freq_hz <= AD9910_VCO1_RANGE_AUTO_MAX_HZ)
+ vco_sel = 1;
+ else if (sysclk_freq_hz <= AD9910_VCO2_RANGE_AUTO_MAX_HZ)
+ vco_sel = 2;
+ else if (sysclk_freq_hz <= AD9910_VCO3_RANGE_AUTO_MAX_HZ)
+ vco_sel = 3;
+ else if (sysclk_freq_hz <= AD9910_VCO4_RANGE_AUTO_MAX_HZ)
+ vco_sel = 4;
+ else
+ vco_sel = 5;
+
+ ret = ad9910_reg32_update(st, AD9910_REG_CFR3,
+ AD9910_CFR3_N_MSK | AD9910_CFR3_VCO_SEL_MSK,
+ FIELD_PREP(AD9910_CFR3_N_MSK, tmp32) |
+ FIELD_PREP(AD9910_CFR3_VCO_SEL_MSK, vco_sel),
+ update);
+ if (ret)
+ return ret;
+ } else {
+ tmp32 = DIV_ROUND_CLOSEST(refclk_freq_hz, freq_hz);
+ tmp32 = clamp(tmp32, 1, 2);
+ sysclk_freq_hz = refclk_freq_hz / tmp32;
+ tmp32 = FIELD_PREP(AD9910_CFR3_REFCLK_DIV_BYPASS_MSK, tmp32 % 2);
+ ret = ad9910_reg32_update(st, AD9910_REG_CFR3,
+ AD9910_CFR3_REFCLK_DIV_BYPASS_MSK,
+ tmp32, update);
+ if (ret)
+ return ret;
+ }
+
+ st->data.sysclk_freq_hz = sysclk_freq_hz;
+
+ return 0;
+}
+
+static int ad9910_profile_set(struct ad9910_state *st, u8 profile)
+{
+ DECLARE_BITMAP(values, BITS_PER_TYPE(profile));
+
+ st->profile = profile;
+ values[0] = profile;
+ gpiod_multi_set_value_cansleep(st->gpio_profile, values);
+
+ return 0;
+}
+
+static int ad9910_powerdown_set(struct ad9910_state *st, bool enable)
+{
+ return gpiod_set_value_cansleep(st->gpio_pwdown, enable);
+}
+
+static ssize_t ad9910_ext_info_read(struct iio_dev *indio_dev,
+ uintptr_t private,
+ const struct iio_chan_spec *chan,
+ char *buf)
+{
+ struct ad9910_state *st = iio_priv(indio_dev);
+ int val;
+
+ guard(mutex)(&st->lock);
+
+ switch (private) {
+ case AD9910_POWERDOWN:
+ val = !!FIELD_GET(AD9910_CFR1_SOFT_POWER_DOWN_MSK,
+ st->reg[AD9910_REG_CFR1].val32);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return iio_format_value(buf, IIO_VAL_INT, 1, &val);
+}
+
+static ssize_t ad9910_ext_info_write(struct iio_dev *indio_dev,
+ uintptr_t private,
+ const struct iio_chan_spec *chan,
+ const char *buf, size_t len)
+{
+ struct ad9910_state *st = iio_priv(indio_dev);
+
+ u32 val32;
+ int ret;
+
+ ret = kstrtou32(buf, 10, &val32);
+ if (ret)
+ return ret;
+
+ guard(mutex)(&st->lock);
+
+ switch (private) {
+ case AD9910_POWERDOWN:
+ val32 = val32 ? AD9910_CFR1_SOFT_POWER_DOWN_MSK : 0;
+ ret = ad9910_reg32_update(st, AD9910_REG_CFR1,
+ AD9910_CFR1_SOFT_POWER_DOWN_MSK,
+ val32, true);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return ret ?: len;
+}
+
+#define AD9910_EXT_INFO_TMPL(_name, _ident, _shared, _fn_desc) { \
+ .name = _name, \
+ .read = ad9910_ ## _fn_desc ## _read, \
+ .write = ad9910_ ## _fn_desc ## _write, \
+ .private = _ident, \
+ .shared = _shared, \
+}
+
+#define AD9910_EXT_INFO(_name, _ident, _shared) \
+ AD9910_EXT_INFO_TMPL(_name, _ident, _shared, ext_info)
+
+static const struct iio_chan_spec_ext_info ad9910_phy_ext_info[] = {
+ AD9910_EXT_INFO("powerdown", AD9910_POWERDOWN, IIO_SEPARATE),
+ { }
+};
+
+#define AD9910_PROFILE_CHAN(idx) { \
+ .type = IIO_ALTVOLTAGE, \
+ .indexed = 1, \
+ .output = 1, \
+ .channel = AD9910_CHANNEL_PROFILE_ ## idx, \
+ .address = AD9910_CHAN_IDX_PROFILE_ ## idx, \
+ .scan_index = -1, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_ENABLE) | \
+ BIT(IIO_CHAN_INFO_FREQUENCY) | \
+ BIT(IIO_CHAN_INFO_PHASE) | \
+ BIT(IIO_CHAN_INFO_SCALE), \
+}
+
+static const struct iio_chan_spec ad9910_channels[] = {
+ [AD9910_CHAN_IDX_PHY] = {
+ .type = IIO_ALTVOLTAGE,
+ .indexed = 1,
+ .output = 1,
+ .channel = AD9910_CHANNEL_PHY,
+ .address = AD9910_CHAN_IDX_PHY,
+ .scan_index = -1,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .ext_info = ad9910_phy_ext_info,
+ },
+ [AD9910_CHAN_IDX_PROFILE_0] = AD9910_PROFILE_CHAN(0),
+ [AD9910_CHAN_IDX_PROFILE_1] = AD9910_PROFILE_CHAN(1),
+ [AD9910_CHAN_IDX_PROFILE_2] = AD9910_PROFILE_CHAN(2),
+ [AD9910_CHAN_IDX_PROFILE_3] = AD9910_PROFILE_CHAN(3),
+ [AD9910_CHAN_IDX_PROFILE_4] = AD9910_PROFILE_CHAN(4),
+ [AD9910_CHAN_IDX_PROFILE_5] = AD9910_PROFILE_CHAN(5),
+ [AD9910_CHAN_IDX_PROFILE_6] = AD9910_PROFILE_CHAN(6),
+ [AD9910_CHAN_IDX_PROFILE_7] = AD9910_PROFILE_CHAN(7),
+};
+
+static int ad9910_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long info)
+{
+ struct ad9910_state *st = iio_priv(indio_dev);
+ u64 tmp64;
+ u32 tmp32;
+
+ guard(mutex)(&st->lock);
+
+ switch (info) {
+ case IIO_CHAN_INFO_ENABLE:
+ switch (chan->channel) {
+ case AD9910_CHANNEL_PROFILE_0 ... AD9910_CHANNEL_PROFILE_7:
+ tmp32 = (chan->channel - AD9910_CHANNEL_PROFILE_0);
+ *val = (tmp32 == st->profile);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_FREQUENCY:
+ switch (chan->channel) {
+ case AD9910_CHANNEL_PROFILE_0 ... AD9910_CHANNEL_PROFILE_7:
+ tmp32 = chan->channel - AD9910_CHANNEL_PROFILE_0;
+ tmp32 = FIELD_GET(AD9910_PROFILE_ST_FTW_MSK,
+ st->reg[AD9910_REG_PROFILE(tmp32)].val64);
+ break;
+ default:
+ return -EINVAL;
+ }
+ tmp64 = (u64)tmp32 * st->data.sysclk_freq_hz;
+ *val = tmp64 >> 32;
+ *val2 = ((tmp64 & GENMASK_ULL(31, 0)) * MICRO) >> 32;
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_CHAN_INFO_PHASE:
+ switch (chan->channel) {
+ case AD9910_CHANNEL_PROFILE_0 ... AD9910_CHANNEL_PROFILE_7:
+ tmp32 = chan->channel - AD9910_CHANNEL_PROFILE_0;
+ tmp64 = FIELD_GET(AD9910_PROFILE_ST_POW_MSK,
+ st->reg[AD9910_REG_PROFILE(tmp32)].val64);
+ tmp32 = (tmp64 * AD9910_MAX_PHASE_MICRORAD) >> 16;
+ *val = tmp32 / MICRO;
+ *val2 = tmp32 % MICRO;
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->channel) {
+ case AD9910_CHANNEL_PROFILE_0 ... AD9910_CHANNEL_PROFILE_7:
+ tmp32 = chan->channel - AD9910_CHANNEL_PROFILE_0;
+ tmp64 = FIELD_GET(AD9910_PROFILE_ST_ASF_MSK,
+ st->reg[AD9910_REG_PROFILE(tmp32)].val64);
+ *val = 0;
+ *val2 = tmp64 * MICRO >> 14;
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ switch (chan->channel) {
+ case AD9910_CHANNEL_PHY:
+ *val = st->data.sysclk_freq_hz;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ad9910_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long info)
+{
+ struct ad9910_state *st = iio_priv(indio_dev);
+ u64 tmp64;
+ u32 tmp32;
+
+ guard(mutex)(&st->lock);
+
+ switch (info) {
+ case IIO_CHAN_INFO_ENABLE:
+ switch (chan->channel) {
+ case AD9910_CHANNEL_PROFILE_0 ... AD9910_CHANNEL_PROFILE_7:
+ tmp32 = chan->channel - AD9910_CHANNEL_PROFILE_0;
+ if (!val) {
+ if (tmp32 != st->profile)
+ return 0;
+
+ tmp32 = (tmp32 + 1) % AD9910_NUM_PROFILES;
+ }
+
+ return ad9910_profile_set(st, tmp32);
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_FREQUENCY:
+ if (!in_range(val, 0, st->data.sysclk_freq_hz / 2))
+ return -EINVAL;
+
+ tmp64 = ad9910_rational_scale((u64)val * MICRO + val2, BIT_ULL(32),
+ (u64)MICRO * st->data.sysclk_freq_hz);
+ tmp64 = min(tmp64, U32_MAX);
+ switch (chan->channel) {
+ case AD9910_CHANNEL_PROFILE_0 ... AD9910_CHANNEL_PROFILE_7:
+ tmp32 = chan->channel - AD9910_CHANNEL_PROFILE_0;
+ tmp64 = FIELD_PREP(AD9910_PROFILE_ST_FTW_MSK, tmp64);
+ return ad9910_reg64_update(st, AD9910_REG_PROFILE(tmp32),
+ AD9910_PROFILE_ST_FTW_MSK,
+ tmp64, true);
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_PHASE:
+ if (val < 0 || val2 < 0)
+ return -EINVAL;
+
+ switch (chan->channel) {
+ case AD9910_CHANNEL_PROFILE_0 ... AD9910_CHANNEL_PROFILE_7:
+ tmp32 = chan->channel - AD9910_CHANNEL_PROFILE_0;
+ tmp64 = (u64)val * MICRO + val2;
+ if (tmp64 >= AD9910_MAX_PHASE_MICRORAD)
+ return -EINVAL;
+
+ tmp64 <<= 16;
+ tmp64 = DIV_U64_ROUND_CLOSEST(tmp64, AD9910_MAX_PHASE_MICRORAD);
+ tmp64 = min(tmp64, AD9910_POW_MAX);
+ tmp64 = FIELD_PREP(AD9910_PROFILE_ST_POW_MSK, tmp64);
+ return ad9910_reg64_update(st, AD9910_REG_PROFILE(tmp32),
+ AD9910_PROFILE_ST_POW_MSK,
+ tmp64, true);
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_SCALE:
+ if (val < 0 || val > 1 || (val == 1 && val2 > 0))
+ return -EINVAL;
+
+ switch (chan->channel) {
+ case AD9910_CHANNEL_PROFILE_0 ... AD9910_CHANNEL_PROFILE_7:
+ tmp32 = chan->channel - AD9910_CHANNEL_PROFILE_0;
+ tmp64 = ((u64)val * MICRO + val2) << 14;
+ tmp64 = DIV_U64_ROUND_CLOSEST(tmp64, MICRO);
+ tmp64 = min(tmp64, AD9910_ASF_MAX);
+ tmp64 = FIELD_PREP(AD9910_PROFILE_ST_ASF_MSK, tmp64);
+ return ad9910_reg64_update(st, AD9910_REG_PROFILE(tmp32),
+ AD9910_PROFILE_ST_ASF_MSK,
+ tmp64, true);
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return ad9910_set_sysclk_freq(st, val, true);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ad9910_write_raw_get_fmt(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ long mask)
+{
+ switch (mask) {
+ case IIO_CHAN_INFO_ENABLE:
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_FREQUENCY:
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_CHAN_INFO_PHASE:
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->channel) {
+ case AD9910_CHANNEL_PROFILE_0 ... AD9910_CHANNEL_PROFILE_7:
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ad9910_debugfs_reg_read(struct ad9910_state *st, bool high32,
+ unsigned int reg, unsigned int *readval)
+{
+ union ad9910_reg tmp;
+ int ret;
+
+ switch (reg) {
+ case AD9910_REG_DRG_LIMIT:
+ case AD9910_REG_DRG_STEP:
+ case AD9910_REG_PROFILE0 ... AD9910_REG_PROFILE7:
+ ret = ad9910_reg64_read(st, reg, &tmp.val64);
+ if (ret)
+ return ret;
+ *readval = high32 ? upper_32_bits(tmp.val64) :
+ lower_32_bits(tmp.val64);
+ return 0;
+ case AD9910_REG_POW:
+ ret = ad9910_reg16_read(st, reg, &tmp.val16);
+ if (ret)
+ return ret;
+ *readval = tmp.val16;
+ return 0;
+ default:
+ ret = ad9910_reg32_read(st, reg, &tmp.val32);
+ if (ret)
+ return ret;
+ *readval = tmp.val32;
+ return 0;
+ }
+}
+
+static int ad9910_debugfs_reg_write(struct ad9910_state *st, bool high32,
+ unsigned int reg, unsigned int writeval)
+{
+ switch (reg) {
+ case AD9910_REG_DRG_LIMIT:
+ case AD9910_REG_DRG_STEP:
+ case AD9910_REG_PROFILE0 ... AD9910_REG_PROFILE7:
+ if (high32)
+ return ad9910_reg64_update(st, reg, GENMASK_ULL(63, 32),
+ FIELD_PREP(GENMASK_ULL(63, 32), writeval),
+ true);
+
+ return ad9910_reg64_update(st, reg, GENMASK_ULL(31, 0),
+ writeval, true);
+ case AD9910_REG_POW:
+ return ad9910_reg16_write(st, reg, writeval, true);
+ default:
+ return ad9910_reg32_write(st, reg, writeval, true);
+ }
+}
+
+static int ad9910_debugfs_reg_access(struct iio_dev *indio_dev,
+ unsigned int reg, unsigned int writeval,
+ unsigned int *readval)
+{
+ bool high32 = FIELD_GET(AD9910_REG_HIGH32_FLAG_MSK, reg);
+ struct ad9910_state *st = iio_priv(indio_dev);
+
+ /*
+ * REG_HIGH32_FLAG is only used for regiter access to indicate upper 32
+ * bits of 64-bit registers. It is a workaround for debugfs_reg_access()
+ * limitation which only supports 32-bit values.
+ */
+ reg &= ~AD9910_REG_HIGH32_FLAG_MSK;
+ if (reg >= AD9910_REG_RAM)
+ return -EINVAL;
+
+ guard(mutex)(&st->lock);
+
+ if (readval)
+ return ad9910_debugfs_reg_read(st, high32, reg, readval);
+ else
+ return ad9910_debugfs_reg_write(st, high32, reg, writeval);
+}
+
+static const struct iio_info ad9910_info = {
+ .read_raw = ad9910_read_raw,
+ .write_raw = ad9910_write_raw,
+ .write_raw_get_fmt = ad9910_write_raw_get_fmt,
+ .debugfs_reg_access = &ad9910_debugfs_reg_access,
+};
+
+static int ad9910_cfg_sysclk(struct ad9910_state *st, bool update)
+{
+ u32 tmp32, cfr3 = AD9910_CFR3_OPEN_MSK;
+
+ cfr3 |= AD9910_CFR3_VCO_SEL_MSK |
+ FIELD_PREP(AD9910_CFR3_DRV0_MSK, st->data.refclk_out_drv);
+
+ if (st->data.pll_enabled) {
+ tmp32 = st->data.pll_charge_pump_current - AD9910_ICP_MIN_uA;
+ tmp32 = DIV_ROUND_CLOSEST(tmp32, AD9910_ICP_STEP_uA);
+ cfr3 |= FIELD_PREP(AD9910_CFR3_ICP_MSK, tmp32) |
+ AD9910_CFR3_PLL_EN_MSK;
+ } else {
+ cfr3 |= AD9910_CFR3_REFCLK_DIV_RESETB_MSK |
+ AD9910_CFR3_PFD_RESET_MSK;
+ }
+ st->reg[AD9910_REG_CFR3].val32 = cfr3;
+
+ return ad9910_set_sysclk_freq(st, AD9910_PLL_OUT_MAX_FREQ_HZ, update);
+}
+
+static int ad9910_parse_fw(struct ad9910_state *st)
+{
+ static const char * const refclk_out_drv0[] = {
+ "disabled", "low", "medium", "high",
+ };
+ struct device *dev = &st->spi->dev;
+ u32 tmp;
+ int ret;
+
+ st->data.pll_enabled = device_property_read_bool(dev, "adi,pll-enable");
+ if (st->data.pll_enabled) {
+ tmp = AD9910_ICP_MIN_uA;
+ device_property_read_u32(dev, "adi,charge-pump-current-microamp", &tmp);
+ if (tmp < AD9910_ICP_MIN_uA || tmp > AD9910_ICP_MAX_uA)
+ return dev_err_probe(dev, -ERANGE,
+ "invalid charge pump current %u\n", tmp);
+ st->data.pll_charge_pump_current = tmp;
+
+ st->data.refclk_out_drv = AD9910_REFCLK_OUT_DRV_DISABLED;
+ ret = device_property_match_property_string(dev,
+ "adi,refclk-out-drive-strength",
+ refclk_out_drv0,
+ ARRAY_SIZE(refclk_out_drv0));
+ if (ret >= 0)
+ st->data.refclk_out_drv = ret;
+ }
+
+ tmp = AD9910_DAC_IOUT_DEFAULT_uA;
+ device_property_read_u32(dev, "adi,dac-output-current-microamp", &tmp);
+ if (tmp < AD9910_DAC_IOUT_MIN_uA || tmp > AD9910_DAC_IOUT_MAX_uA)
+ return dev_err_probe(dev, -ERANGE,
+ "Invalid DAC output current %u uA\n", tmp);
+ st->data.dac_output_current = tmp;
+
+ return 0;
+}
+
+static int ad9910_setup(struct ad9910_state *st, struct reset_control *dev_rst)
+{
+ u32 reg32;
+ int ret;
+
+ ret = reset_control_deassert(dev_rst);
+ if (ret)
+ return ret;
+
+ ret = ad9910_reg32_write(st, AD9910_REG_CFR1,
+ AD9910_CFR1_SDIO_INPUT_ONLY_MSK, false);
+ if (ret)
+ return ret;
+
+ reg32 = AD9910_CFR2_AMP_SCALE_SINGLE_TONE_MSK |
+ AD9910_CFR2_SYNC_TIMING_VAL_DISABLE_MSK |
+ AD9910_CFR2_DRG_NO_DWELL_MSK |
+ AD9910_CFR2_DATA_ASM_HOLD_LAST_MSK |
+ AD9910_CFR2_SYNC_CLK_EN_MSK |
+ AD9910_CFR2_PDCLK_ENABLE_MSK;
+ ret = ad9910_reg32_write(st, AD9910_REG_CFR2, reg32, false);
+ if (ret)
+ return ret;
+
+ ret = ad9910_cfg_sysclk(st, false);
+ if (ret)
+ return ret;
+
+ ret = ad9910_set_dac_current(st, false);
+ if (ret)
+ return ret;
+
+ return ad9910_io_update(st);
+}
+
+static void ad9910_release(void *data)
+{
+ struct ad9910_state *st = data;
+
+ if (!ad9910_powerdown_set(st, true))
+ return;
+
+ ad9910_reg32_update(st, AD9910_REG_CFR1,
+ AD9910_CFR1_SOFT_POWER_DOWN_MSK,
+ AD9910_CFR1_SOFT_POWER_DOWN_MSK,
+ true);
+}
+
+static int ad9910_probe(struct spi_device *spi)
+{
+ static const char * const supplies[] = {
+ "dvdd-io33", "avdd33", "dvdd18", "avdd18",
+ };
+ struct reset_control *dev_rst;
+ struct gpio_desc *io_rst_gpio;
+ struct device *dev = &spi->dev;
+ struct iio_dev *indio_dev;
+ struct ad9910_state *st;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+ st->spi = spi;
+
+ st->refclk = devm_clk_get_enabled(dev, "ref_clk");
+ if (IS_ERR(st->refclk))
+ return dev_err_probe(dev, PTR_ERR(st->refclk),
+ "Failed to get reference clock\n");
+
+ ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(supplies), supplies);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to get regulators\n");
+
+ ret = devm_mutex_init(dev, &st->lock);
+ if (ret)
+ return ret;
+
+ indio_dev->name = "ad9910";
+ indio_dev->info = &ad9910_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = ad9910_channels;
+ indio_dev->num_channels = ARRAY_SIZE(ad9910_channels);
+
+ dev_rst = devm_reset_control_get_optional_exclusive(dev, NULL);
+ if (IS_ERR(dev_rst))
+ return dev_err_probe(dev, PTR_ERR(dev_rst),
+ "failed to get device reset control\n");
+
+ /*
+ * The IO RESET pin is not used in this driver, as we assume that all
+ * SPI transfers are complete, but if it is wired up, we need to make
+ * sure it is not floating. We can use either a reset controller or a
+ * GPIO for this.
+ */
+ io_rst_gpio = devm_gpiod_get_optional(dev, "io-reset", GPIOD_OUT_LOW);
+ if (IS_ERR(io_rst_gpio))
+ return dev_err_probe(dev, PTR_ERR(io_rst_gpio),
+ "failed to get io reset gpio\n");
+
+ st->gpio_pwdown = devm_gpiod_get_optional(dev, "powerdown",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(st->gpio_pwdown))
+ return dev_err_probe(dev, PTR_ERR(st->gpio_pwdown),
+ "failed to get powerdown gpio\n");
+
+ st->gpio_update = devm_gpiod_get_optional(dev, "update", GPIOD_OUT_LOW);
+ if (IS_ERR(st->gpio_update))
+ return dev_err_probe(dev, PTR_ERR(st->gpio_update),
+ "failed to get update gpio\n");
+
+ st->gpio_profile = devm_gpiod_get_array_optional(dev, "profile",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(st->gpio_profile))
+ return dev_err_probe(dev, PTR_ERR(st->gpio_profile),
+ "failed to get profile gpios\n");
+
+ ret = ad9910_parse_fw(st);
+ if (ret)
+ return ret;
+
+ ret = ad9910_setup(st, dev_rst);
+ if (ret)
+ return dev_err_probe(dev, ret, "device setup failed\n");
+
+ ret = devm_add_action_or_reset(dev, ad9910_release, st);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to add release action\n");
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static const struct spi_device_id ad9910_id[] = {
+ { "ad9910" },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, ad9910_id);
+
+static const struct of_device_id ad9910_of_match[] = {
+ { .compatible = "adi,ad9910" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ad9910_of_match);
+
+static struct spi_driver ad9910_driver = {
+ .driver = {
+ .name = "ad9910",
+ .of_match_table = ad9910_of_match,
+ },
+ .probe = ad9910_probe,
+ .id_table = ad9910_id,
+};
+module_spi_driver(ad9910_driver);
+
+MODULE_AUTHOR("Rodrigo Alencar <rodrigo.alencar@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD9910 DDS driver");
+MODULE_LICENSE("GPL");
--
2.43.0
^ permalink raw reply related
* [PATCH RFC v3 0/9] AD9910 Direct Digital Synthesizer
From: Rodrigo Alencar via B4 Relay @ 2026-04-17 8:17 UTC (permalink / raw)
To: linux-iio, devicetree, linux-kernel, linux-doc, linux-hardening
Cc: Lars-Peter Clausen, Michael Hennerich, Jonathan Cameron,
David Lechner, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Philipp Zabel, Jonathan Corbet, Shuah Khan,
Kees Cook, Gustavo A. R. Silva, Rodrigo Alencar
This patch series adds support for the Analog Devices AD9910 DDS.
This is a RFC so that we can agree/discuss on the design that follows:
This is a follow-up of the V2 discussion. For V1, we reached into
this channel composition agreement where physical channels may have
sub-channels. That adds the flexibility necessary for this design.
During V2, some feedback indicated that the ABI is too device-specific,
so DRG/RAM destination and operating modes are configured through
alternate paths and profile channels are created.
The AD9910 DDS core can be driven through several independent mechanisms:
single tone profiles, a digital ramp generator, an internal RAM playback
engine, a parallel data port, and output shift keying. Each of these
represents a distinct signal path into the DDS accumulator, so the driver
models them as separate IIO output channels (all IIO_ALTVOLTAGE type).
This per-channel separation allows userspace to configure each mode
independently through its own set of sysfs attributes, and to
enable/disable modes individually via IIO_CHAN_INFO_ENABLE, relying on
the hardware's own mode selection architecture.
The AD9910 register map is not suited for the regmap framework: register
widths vary across the map (16, 32, and 64 bits). The driver instead
implements direct SPI access helpers with a software register cache, using
type-specific read/write/update functions (ad9910_reg{16,32,64}_{read,
write,update}) that handle endianness conversion and cache coherency.
Registers are cached for several reasons. The control/function registers
(CFR1, CFR2) are frequently queried to determine the current operating
mode (e.g., checking RAM_ENABLE before every profile register access),
and caching avoids repeated SPI read transactions for what are
essentially state checks. The cache also enables efficient
read-modify-write updates on multi-byte registers: the update functions
merge new field values with the cached register content without issuing
a SPI read, and skip the write entirely when the value is unchanged.
Finally, the profile registers serve dual purposes depending on whether
RAM mode is active -- they hold single tone parameters (FTW, POW, ASF)
in normal operation but are repurposed for RAM playback configuration
(start/end address, step rate, operating mode) when RAM is enabled. A
shadow register array (reg_profile[]) preserves the inactive mode's
settings across transitions, so no state is lost when switching between
single tone and RAM operation.
RAM data is loaded through firmware upload infrastructure. Userspace
writes the waveform data as a raw binary buffer (up to 4096 bytes for
the full 1024x32-bit RAM), and the driver reverses the byte array and
transfers it to the device in a single SPI transaction. Per-profile
start/end addresses and playback parameters (operating mode, step rate,
no-dwell control) are also configured through firmware update, using
metadata in the header.
Streaming data to the DDS core through the parallel data port at the
PD_CLK rate is not covered by this series. That functionality would
be added in a separate patch series, building on top of the IIO backend
infrastructure to provide a proper buffered data path.
As I am pushing implementation, as lot has been done already without much
supervision or agreement, still I would be interested on hearing about
the design choices discussed above.
Kind regards,
Rodrigo Alencar
Signed-off-by: Rodrigo Alencar <rodrigo.alencar@analog.com>
---
Changes in v3:
- RAM custom configs (address range, destination, modes) loaded during firmware write.
- DRG destination defined when attrs are written.
- DRG modes broken down into enable attrs for ramp up/down channels.
- Add separate profile channels, switching done through enable attr
- Link to v2: https://lore.kernel.org/r/20260318-ad9910-iio-driver-v2-0-e79f93becf11@analog.com
Changes in v2:
- Device-tree bindings changes.
- RAM loading to use firmware update interface.
- Rearrange of channels into a hierarchy.
- Link to v1: https://lore.kernel.org/r/20260220-ad9910-iio-driver-v1-0-3b264aa48a10@analog.com
---
Rodrigo Alencar (9):
dt-bindings: iio: frequency: add ad9910
iio: frequency: ad9910: initial driver implementation
iio: frequency: ad9910: add simple parallel port mode support
iio: frequency: ad9910: add digital ramp generator support
iio: frequency: ad9910: add RAM mode support
iio: frequency: ad9910: add output shift keying support
iio: frequency: ad9910: add channel labels
Documentation: ABI: testing: add docs for ad9910 sysfs entries
docs: iio: add documentation for ad9910 driver
.../ABI/testing/sysfs-bus-iio-frequency-ad9910 | 62 +
.../bindings/iio/frequency/adi,ad9910.yaml | 189 ++
Documentation/iio/ad9910.rst | 586 ++++++
Documentation/iio/index.rst | 1 +
MAINTAINERS | 10 +
drivers/iio/frequency/Kconfig | 20 +
drivers/iio/frequency/Makefile | 1 +
drivers/iio/frequency/ad9910.c | 2118 ++++++++++++++++++++
8 files changed, 2987 insertions(+)
---
base-commit: ff0843ceb1fb11a6b73e0e77b932ef7967aecd4b
change-id: 20260218-ad9910-iio-driver-9b3d214c251f
Best regards,
--
Rodrigo Alencar <rodrigo.alencar@analog.com>
^ permalink raw reply
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